Ruby: cheatsheet для изучения. Часть 1

от автора

Еще когда учился в школе, и интересовался языками, я считал, что нужно покупать две книги по любому языку: первую — «для чайников» и вторую — «мастер». В первой нужно было прочитать все hints — а остальное пролистать, а вторая — настольная (сейчас ее заменил Google). И подход вполне работал. Теперь, с развитием интернета, такой формат куда-то улетучился, и при самостоятельном изучении нужно по крупицам собирать данные — все материалы либо уже «pro», либо наоборот — азы, без «самого сока» и многих на практике важных вещей, или же — все сразу и в кучу на >9999 страницах.

Это — статья-roadmap-cheatsheet для изучающих Ruby и Rails (Rails и Gems запланированы на вторую часть). Вместо того, чтобы рассказывать очередной how-to я постараюсь расписать все те вещи, которые, на мой взгляд, можно изложить кратко и емко, с упором на то, что программисту пришедшему с других языков и платформ может показаться не очевидным, антипоисковым и проcто затратным по времени на изучение без подсказок — на остальное просто дам ссылки (и есть Google — но ведь не всегда, особенно в начале пути, очевидно что у него нужно спрашивать). Не смотря на то, что в природе существует множество туториалов/кастов/книг/чего угодно по чему угодно — именно такого формата мне самому всегда не хватает. И по сути, это те вещи, которые чаще всего рассказываю при вопросах «а как оно вообще?», «с чего начать?», «а как делается такая вот штука?», «а какой gem лучше?» — теперь буду кидать ссылку сюда) Кстати, пока работал над этой статьей на Хабре появилась похожая про Python — видимо, идея витает в воздухе.

Около Ruby — слухи, интриги, расследования

Сразу оговорюсь, что текст под этим спойлером отличается от основной «объективной» части статьи, т.к. здесь изложено мое личное мнение и впечатление.

Язык программирования — это не только синтаксис, сборщик мусора, не только парадигма языка, и даже не столько его философия, в первую очередь — это коммьюнити и та кодовая база, которую это коммьюнити создало. Особенно сейчас, в эпоху OpenSource. И тут у Ruby первый жирный плюс в карму. Одна из особенностей — во всем читается прагматичная лень, начиная c опциональности скобок при вызове методов и точек с запятой в конце строки, продолжая емким и выразительным синтаксисом, и заканчивая общим ощущением от проектов — многие из них сделаны работающими из коробки, и требующих минимальных усилий, чтобы сконфигурировать их.

Многие выбирают Ruby, потому что это комфортно и приятно. Удовольствие и радость от программирования можно получить на разных языках — и в Ruby оно похоже на езду на хорошем авто. Не на суперкаре, подвеска которого пересчитывает каждую колдобину, но зато все вокруг в восторге, не на вседорожнике, на котором если вдруг приспичит можно помесить грязь, не на «зажигалке», чтобы со всеми гоняться с перекрестка, и не малолитражке (зато дешево и экологично) — именно на хорошей достойной машине, сидя за рулем которой будешь думать не столько о дороге, сколько о более важных вещах, хотя бы о маршруте и конечной цели поездки. И слушать любимую музыку, конечно.
Или же, можно сказать, что программирование на Ruby похоже на игру в Lego (и это во многом благодаря Gems). Хотя кто-то любит сварку арматуры, а кому-то достаточно картона и клея.

Но аналогии аналогиями. Есть несколько утверждений, которые иногда можно встретить о Ruby и Rails — попробую внести в них ясность.

Известно утверждение о том, что Ruby медленный. И поспорить сложно, ведь Ruby интерпретируемый язык. И что характерно, чаще всего я это слышу от тех кто пишет (исключительно) на PHP, который тоже интерпретируем, и по скорости в синтетических тестах находится примерно на том же уровне. Скорее всего, это отголоски дурной славы старых версий. По факту, мне приходилось работать и с Rails и с Symfony2 — на реальных приложениях Rails быстрее при одинаковом уровне оптимизации (и в обоих правильное кэширование — залог успеха). Если нужна скорость и компактность в памяти — пишите на ассемблере используйте Node.js. Но парадокс в том, что на нем как раз часто пишут рубисты — когда это действительно оправданно. И дело тут вот в чем: важна не только скорость работы, но и скорость написания приложения. И Ruby, и все то, что можно найти на github всячески помогает достичь действительно высокой производительности программиста — в том числе и в возможностях оптимизации приложения. И не нужно забывать, что и главная нагрузка, и бутылочные горлышки, зачастую — это базы данных, а веб-приложения это всего-лишь прослойка бизнес-логики. А такая прослойка неплохо масштабируется.

Есть еще своеобразный миф про высокие зарплаты Ruby-разработчиков с одной стороны, с другой — что работы на Ruby мало. Обычно сравнивают с зарплатами со средней по рынку на PHP, и количеством работы на нем же. Средний уровень зарплат на PHP — это классическая «средняя зарп температура по больнице». Если же сравнить специалистов по Yii/Symfony/Zend и Rails (можно еще добавить Django на Python) — картина выйдет совсем другая, а именно: и зарплаты и объем рынка примерно одинаковы. Действительно, хорошим программистам неплохо платят. И более того, когда берешься за проект с нуля — зачастую выбор платформы за тобой и твоей командой, а не заказчиком/начальством.

Так что, есть множество прекрасных языков и фреймворков для написания веб-приложений, и Ruby с Rails не являются серебряной пулей, которая убьет всех зайцев кроликов-оборотней сразу. Просто, на мой взгляд, если брать совокупность важных критериев, а не пытаться выбрать по одному-двум из них, RoR набирает действительно много очков. В том числе и по зрелости — это уже давно не хипстерская платформа, в которой есть только потенциал, но еще и не старичок (а примеров и тех и других уйма). И старичком, я думаю, еще долго не станет, т.к. потенциал для роста и развития еще есть и у Ruby и у Rails — но это тема для отдельной статьи.

И конечно, несмотря на направленность статьи на Rails, и популярность именно этой платформы — Ruby это далеко не только Rails.

В любом случае, я убежден, что программист не должен сидеть в своем маленьком мирке того, за что ему сегодня платят зарплату. Ruby дает в этом смысле не только +1 язык в резюме, но и, за счет своей ооп-направленности и широким возможностям метапрограммирования, опыт, который пригодится на разных языках и платформах.

Язык Ruby: история становления и перспективы развития

Ruby

Начало

Как установить Ruby на #{ os_name }?

Сами ссылки:

  • win. По той же ссылке можно найти DevKit, которой пригодится для работы с БД и установки Native Extensions
  • *nix — ищите в своих репозиториях, или legacy

Кроме того, есть такая штука как RVM. Она позволяет установить несколько версий Ruby на одной ОС и переключаться между ними. На старте в ней потребности нет, но бывает полезна, если уже есть несколько проектов на локальной машине или на сервере — обновляться на всех разом до новой версии сразу не особо здорово. Пока — просто имейте ввиду, что она есть.
Подробно про RVM на хабре.

Тут же стоит упомянуть про среду разработки. Я приверженец продуктов JetBrains, поэтому рекомендую RubyMine, но ввиду того, что продукт коммерческий кому-то может прийтись по вкусу свободная Aptana Studio. Если же приятнее пользоваться легкими текстовыми редакторами — синтаксис Ruby поддерживается многими.

Прямо в браузере в интерактивном режиме Ruby можно попробовать на tryruby.org

Все — объект

Включая числа, строки и даже nil — они все наследуются от класса Object. Можно у чего угодно вызывать методы вроде nil?, class, methods и respond_to?:

'Hello world'.class # String nil.class # NilClass String.class # Class nil.nil? # true Object.new.methods # вернет методы объекта класса Object; тут же видно как создается новый объект - тоже вызовам метода nil.respond_to?('nil?') # true 

По поводу последнего: это важно для утиной типизации.

Синтаксис и пудра

Ruby сильно приправлен «синтаксическим сахаром», благодаря которому зачастую не нужно городить кашу из различных скобок, точек с запятыми и прочего. А под ним — прост и логичен, и сохраняет парадигму «все — объект».

a == b # то же, что и a.==(b) # то есть .==() - это метод 

В вызовах методов, в if можно не ставить скобки, если нет неоднозначности

nil.respond_to?('nil?') # true nil.respond_to? 'nil?' # true # все однозначно: if nil.respond_to? 'nil?'   puts 'ok' end # тоже if 10.between? 1, 5   puts 'ok' end # а вот так получится не тот приоритет вызовов, поэтому нужны скобки if 10.between?(1, 50) && 20.between?(1, 50)   puts 'ok' end 

А еще — есть символы. По сути, символы — это неизменяемые строки. Например, они часто используются как ключи в хэшах.

a = :nil? # символ b = 'nil?' # строка nil.respond_to? a # true nil.respond_to? b # true # поиграемся a == b # false a.to_s == b && a == b.to_sym # true; символ и строка преобразуются друг в друга 

С ними связано еще немного пудры:

a = {:key1 => 'value1', :key2 => 'value2'} # создаем хэш (ассоциативный массив) a = {key1: 'value1', key2: 'value2'} # если ключи - символы, то можно так (Ruby >= 1.9.3) 

Раз уж тема зашла:

a = ['value1', 'value2'] # это массив s = 'String' s = "Double-quoted #{s}" # "Double-quoted String" - это, думаю, ясно 

И добьем тему припудривания немного уродливым, но зато удобным способом записи:

%w[value1 value2] # ["value1", "value2"] - тот же массив, что и выше %i[value1 value2] # [:value1, :value2] - массив символов, (Ruby >= 2.0) s = %q(String) s = %Q(Double-quoted #{s}) %x('ls') # команда консоли %r(.*) == /.*/ # true; два способа создать регулярное выражение 

Кстати, в Ruby есть многим до боли знакомая точка с запятой — пригодиться она может чтобы записать несколько выражений в одну строку

a = 10; puts a # выведет в консоль 10 if nil.respond_to? 'nil?'; puts 'ok'; end # чтобы было понятно - так тоже можно 

Но однострочные if лучше писать так

puts 'ok' if nil.respond_to? 'nil?' 

Вводные статьи и доки

Стоит почитать В Ruby из других языков и Ruby за двадцать минут, ну а главный друг и помощник ruby-doc.org.
Сразу стоит посмотреть методы базовых классов, во всех — какие бывают each_ и to_.
String (тут — первым делом match, sub)
Array (map, join, include?)
Hash (has_key?, has_value?, merge)
Если хочется продолжения банкета: Интересный паблик с подборкой магии на Руби.

Классы, методы, модули и метапрограммирование

Классы в Ruby вполне очевидны, но в возможностях работы с ними кроется вся сила и прелесть Ruby.

class Foo   def bar     10 # любой метод возвращает значение - результат выполнения последнего выражения   end   def baz(a)     bar + 20   end end puts Foo.new.baz(10) # 30 

Module — к нему можно относиться и как к примеси, и как к пространству имен. Внезапно? По сути — это набор классов, методов и констант, и пользоваться им можно на свое усмотрение.

module B # как пространство имен   class Foo     # конструктор     def initialize(k) @k = k end # запишем методы в одну строку     def bar; @k + 20 end   end end puts B::Foo.new(3).bar # 23  module C # как примесь   def bar; @k + 25 end end class Foo   include C;   def initialize(k) @k = k end end puts Foo.new(3).bar # 28 

В Ruby классы мутабельны, и их можно патчить после их создания. И тут будьте осторожны: здесь начинаются те самые возможности, при помощи которых можно «выстрелить себе в ногу» — так что, делая что-то подобное нужно всегда отдавать отчет: зачем, что получится, кто виноват и что делать и если что — виноваты сами.

class Foo   def bar; 20 end end class Foo # патчим   def baz; bar + 2 end end puts Foo.new.baz # 22 # или лучше - так: сразу понятно что это патч Foo.class_eval do   def bazz; bar + 3 end end puts Foo.new.bazz # 23 # но если нет на то веской причины - используйте примеси или наследование class Boo < Foo   def boo; bar + 2 end end puts Boo.new.boo # 22 # или же - можно пропатчить только объект, а не сам класс a = Foo.new a.instance_eval do # патчим объект   def booo; bar + 3 end end puts a.booo # 23 puts Foo.new.booo rescue puts 'error' # error; кстати, так перехватываются ошибки puts a.respond_to? :booo # true puts Foo.new.respond_to? :booo # false 

А что если сделать instance_eval для класса? Конечно же, добавятся статические методы.

class Foo   def self.bar; 10 end # обычный статический метод end puts Foo.bar # 10 Foo.instance_eval do   def baz; bar + 1 end end puts Foo.baz # 11 

Играться с этим можно вдоволь, главное помните — берегите ноги.

class Foo   def self.add_bar # добавим статический метод, который патчит класс 	self.class_eval do 	  def bar; 'bar' end 	end   end end puts Foo.new.respond_to? :bar # false class Boo < Foo # отнаследуемся   add_bar # пропатчим end puts Boo.new.bar # bar  # а теперь все то же самое, но пропатчим уже существующий класс Foo.instance_eval do   def add_baz     self.class_eval do       def baz; 'baz' end     end	   end end class Baz < Foo   add_baz end puts Baz.new.baz # baz 

Как раз такой подход используется на практике — по сути, получается что-то вроде примеси, в которую можно передавать параметры. Это кажется магией, если не знать, как это делается. Патчить можно и базовые классы, особенно любимы Array и String — но всегда стоит подумать трижды, прежде чем начинать их мучить: одно дело методы вроде .blank? (его добавляет Rails: что-то вроде def blank?; nil? || empty? end), другое — когда код метода специфичен для проекта, тогда логично предположить, что он относится к каким-то классам внутри проекта.

По такому принципу работает, например acceessor. Что мы сделаем, чтобы добавить публичный параметр в Ruby-класс?

class Foo   def bar # геттер     @bar # возвращаем параметр   end   def bar=(val) # сеттер     @bar = val # присваиваем значение параметру   end end 

Представляете так писать для десятка-другого параметров? В Ruby много кода по накатанному — смертный грех: DRY. Так что, можно сделать короче.

class Foo   attr_accessor :bar, :baz # добавим сразу парочку атрибутов   attr_reader :boo # только геттер   attr_writer :booo # только сеттер end 

Готовы дальше? Тогда:
Вникаем в метаклассы Ruby
Metaprogramming patterns — про monkey patching, Reuse в малом — bang!, eval

Замыкания и блоки

Некоторое недоумение поначалу вызывают блоки, замыкания и класс Proc — зачем столько всего? Вкратце — на самом деле, есть только Proc.
Подробно — ссылки внизу, а сейчас вкратце — на что стоит обратить внимание.
Первыми рассмотрим блоки. Блок — это просто кусок куда, и даже не объект, просто часть синтаксиса Ruby. Блок используется чтобы передать какой-то код в метод. И уже в методе он оказывается завернут его в класс Proc.
В разделе про классы и методы они уже использовались:

Foo.instance_eval do # передаем в метод instance_eval блок с определением метода   def baz; bar + 1 end end 

Но возьмем более базовый пример, он же «как сделать foreach в Ruby»:

[1,2,3].each do |val|   p val # кстати, p(val) - это shortcut для puts(val.inspect) end # выводит 1 2 3  # то же, в одну строчку  [1,2,3].each { |val| p val } # выводит 1 2 3 [1,2,3].each_with_index { |val, i| puts val.to_s + ' ' + i.to_s } # на заметку 

Если же мы хотим передавать блок в собственный метод:

def foo   puts yield # 2; выполняем блок   puts yield + yield # 4; и еще, и еще выполняем end foo { 2 } def bar(&block) # или, если поменьше пудры   puts yield block # 3 end bar { 3 } 

Стоит отметить, что блок идет всегда последним параметром, и если нужно передать несколько блоков — нужно их передавать как обычные параметры, а значит создавать Proc.new, или lambda.

Из блока всегда получается объект класса Proc, но сам блок это часть синтаксиса: мы можем его передать в метод, где он станет Proc, мы можем его передать в конструктор Proc, или использовать lambda, но нельзя просто записать блок в переменную.

proc = Proc.new { |a| a - 1 } # через конструктор p proc.call(10) #9 p proc.class # Proc l = lambda { |a| a + 1 } # создаем лямбду p l.call(10) #11 p l.class # Proc new_l = ->(a) { a + 2 } # тоже лямбда (Ruby >= 2.0) p new_l.call(10) #12 

Различия в поведении Proc созданными разными способами есть, читаем статью.
Здесь замыкания и блоки в Ruby разобраны вдоль и попрек
На Хабре про блоки, про замыкания

Стиль

По поводу как принято писать в Ruby есть целый гайд на GitHub
Вкратце, frequently used:

  • Отступы в 2 пробела
  • Названия методов строчными буквами через подчеркивание: def method_name
  • Названия классов и модулей с больших букв: class ClassName
  • Если метод возвращает true/false, название должно заканчиваться на вопрос (тот же, nil?)
  • Если есть два метода, один из которых изменяет объект, а другой возвращает новый — первый оканчивается на ‘!’ (например )
  • Вместо if(!value) лучше использовать алиас unless(value)
  • Блок однострочный берется в скобки {… }, а многострочный — в do… end

RubyGems — пакетный менеджер

Мир Ruby, наверное, был бы совсем другим, если бы не RubyGems.
С ними процесс дополнения проекта библиотеками выглядит очень просто:

  • выбираем что нам нужно с rubygems.org (или через ruby-toolbox.com), например, json_pure — JSON парсер на чистом Ruby (без C)
  • вводим в консоли gem install json_pure
  • а в нашем rb-файле:
    require 'json/pure' # какой именно путь должен быть стоит в доках посмотреть, обычно на github

Для удобства управления зависимостями есть bundler:

  • gem install bundler
  • bundler init
  • в появившемся Gemfile вписываем зависимости:
    source 'https://rubygems.org' gem 'json_pure' 
  • bundler install
  • И в своем rb-файле
    require 'rubygems' require 'bundler/setup' 

В реальных проектах список зависимостей может разрастись на несколько экранов, поэтому при переносе проекта на сервер гораздо удобнее выполнять bundle install, чем вручную смотреть что нужно и для каждого гема выполнять gem install. Ну а в Rails bundler используется из коробки.

Сами Gems обязательно рассмотрю в статье про Rails.

Ruby и C

Далеко не последняя по важности возможность RubyGems — Native Extensions. Все просто — вместе с гемом может быть скомпилирован прилагающийся код на C и вызван из кода самого гема. Благодаря этому, среди них есть достаточно шустрые парсеры, можно установить JavaScript’овый V8 в виде гема, и много других вкусностей.

А это нужно просто иметь ввиду: Inline C, может пригодится. Если коротко — в коде Ruby можно исполнять код на C, что полезно, например, при реализации численных алгоритмов.

Введение в расширения Ruby на C

Зоопарк версий

MRI (Matz’s Ruby Interpreter) — основная ветка Ruby от его создателя Yukihiro Matsumoto, или просто, Matz. Реализована на C. Когда просто говорят «Ruby» обычно имеют ввиду ее. Одна из основных ее особенностей — так называемый GIL (Global Interpreter Lock), на Хабре про нее писали (с продолжением).

jRuby, который написан на Java и работает в JVM, и соответственно интегрируется с котом на Java. К сожалению, версия языка отстает от MRI, но тот же Rails заводится на нем с пол оборота.

Rubinius, который основан на MRI, но использует потоки операционной системы, а так же по максимуму написан на самом Ruby. По версии обычно up do date.

Скорость всех трех реализаций относительно друг друга разнится от задачи к задаче, а по потреблению памяти из троицы самый прожорливый — jRuby.

MacRuby — он уже стоит на вашем MacBook и работает в связке с Objective-C. Его, к примеру, использует RubyMotion — инструмент для разработки iOS приложений.

Opal — транслятор Ruby в JavaScript. Но не надо ждать от него чего-то выдающегося, это не jRuby для Node.js — он просто дает возможность писать ваш jQuery-код на Ruby.

Ruby Enterprise Edition (REE) — Ruby на стероидах. Проект завершил свое существование, т.к. новые версии и без наркотиков неплохо бегают.

Можно упомянуть MagLev — специфическая версия, может пригодится для разворачивания облачной инфраструктуры.

Также интересен проект mruby, в котором участвует Matz. Это — встраиваемый Ruby. Проект еще не закончен, но весьма многообещающе выглядит. Так, что ждем ruby на Arduino. mruby-arduino

ссылка на оригинал статьи http://habrahabr.ru/post/206416/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *