Статьи

Взгляд на Ruby 2.0

С выходом Ruby 2.0, который будет выпущен 24 февраля, как раз в 20-ю годовщину первого дебюта Ruby, я решил написать эту статью, чтобы дать вам краткое изложение некоторых из самых интересных изменений. И если вы хотите поэкспериментировать с этой версией до выхода официального релиза, вы можете сделать это, следуя инструкциям в этой статье.

Установка RC1

Ruby 2.0 отказался от использования syck в пользу psych , а YAML теперь полностью зависит от libyaml . Это означает, что мы должны установить эту библиотеку перед установкой Ruby 2.0.

В любой системе, основанной на * nix, мы можем установить ее, загрузив исходный пакет и собрав его вручную:

$ wget http://pyyaml.org/download/libyaml/yaml-0.1.4.tar.gz $ tar xzvf yaml-0.1.4.tar.gz $ cd yaml-0.1.4 $ ./configure $ make $ make install 

Или, если вы используете Mac, вы можете установить его с помощью homebrew :

 $ brew update $ brew install libyaml 

После установки libyaml мы можем установить RC1 с помощью rvm :

 $ rvm install ruby-2.0.0-rc1 

Далее нам нужно указать rvm использовать эту новую версию Ruby:

 $ rvm use ruby-2.0.0-rc1 

Отлично, теперь мы готовы погрузиться в изменения.

изменения

Изменения, внесенные в 2.0, довольно обширны , но в этой статье я сосредоточусь в основном на следующем:

  • Уточнения
  • Ключевое слово Аргументы
  • Модуль # перед именем
  • Перечислимых # ленивым
  • Языковые изменения

Уточнения (экспериментальные)

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

Давайте посмотрим на конкретный пример исправления обезьян. Скажем, мы хотели расширить класс String и добавить метод bang, который добавляет восклицательный знак после заданной строки. Мы бы сделали что-то вроде этого:

 class String def bang "#{self}!" end end 

Это изменение теперь глобально. Это означает, что любая строка, которая вызывает .bang будет вести себя так:

 > "hello".bang #=> "hello!" 

Чтобы предотвратить это глобальное изменение объема, было предложено усовершенствование . Он работает, используя два новых метода: Module#refine и main.using . Во-первых, это блок, который позволяет выполнять локальные исправления для обезьян. И последнее импортирует уточнения в текущий файл или строку eval, чтобы их можно было использовать в других местах.

Взяв наш предыдущий пример, мы можем безопасно расширить класс String, используя уточнения:

 module StringBang refine String do def bang "#{self}!" end end end 

Теперь, если мы попытаемся вызвать .bang для любой строки, произойдет сбой:

 > "hello".bang #=> NoMethodError: undefined method `bang' for "":String 

Это связано с тем, что изменение класса String содержится в модуле StringBang . После того, как мы импортируем это уточнение с using ключевого слова using , оно будет работать как положено:

 > using StringBang > "hello".bang #=> "hello!" 

ПРЕДУПРЕЖДЕНИЕ. Эта функция все еще является экспериментальной, и ее поведение может измениться в будущих версиях Ruby. У Чарльза Наттера из JRuby есть отличное объяснение проблем, связанных с этим.

Ключевое слово Аргументы

Эта функция, также называемая именованными параметрами , очень полезна, поскольку позволяет объявить метод для получения аргументов ключевого слова. Это используется многими языками и, наконец, интегрировано в Ruby.

По-старому, если вы хотите принять аргументы ключевого слова, вам придется подделать его, получив хеш-аргумент в методе:

 def config(opts={}); end 

И если бы у вас были значения по умолчанию, вы бы объединили предоставленные пользователем аргументы поверх ваших значений по умолчанию:

 def config(opts={}) defaults = {enabled: true, timeout: 300} opts = defaults.merge(opts) end 

Это конечно работает, но это взлом, и я уверен, что мы все использовали это так или иначе.

Забудьте этот старый способ и скажите «привет» истинным ключевым аргументам в Ruby 2.0. Используя тот же пример, что и выше, вот как мы можем определить наш метод config новым способом:

 def config(enabled: true, timeout: 300) [enabled, timeout] end 

Теперь давайте посмотрим, как мы можем вызвать этот метод:

 > config() #no args #=> [true, 300] > config(enabled: false) #only enabled #=> [false, 300] > config(timeout: 20) #only timeout #=> [true, 20] > config(timeout: 10, enabled: false) #inverse order #=> [false, 10] 

Расширяя метод config далее, теперь мы собираемся принять два дополнительных аргумента: value , обязательное значение; и other , необязательный хеш.

 def config(value, enabled: true, timeout: 300, **other) [value, enabled, timeout, **other] end 

И вот разные, что мы можем взаимодействовать этим методом:

 > config() #no args #=> ArgumentError: wrong number of arguments (0 for 1) > config(1) #required value #=> [1, true, 300, {}] > config(1, other: false) #required value, optional hash #=> [1, true, 300, {:other=>false}] > config(1, timeout: 10) #required value, timeout #=> [1, true, 10, {}] > config(1, timeout: 10, other: false) #required value, timeout, optional hash #=> [1, true, 10, {:other=>false}] > config(1, other: false, another: true, timeout: 10, enabled: false) #inverse order #=> [1, false, 10, {:other=>false, :another=>true}] 

Модуль # перед именем

Это похоже на Module#include за исключением того, что он настроен на «наложение констант, методов и переменных модуля» ( Source ) предшествующего модуля. Чтобы лучше понять эту концепцию, давайте посмотрим, как работает Module#include :

 module Foo def baz 'foo-baz' end end class Bar include Foo def baz 'bar-baz' end end 

Здесь мы объявляем модуль и класс, и каждый из них содержит объявление метода baz . Когда мы вызываем метод baz в классе Bar , метод, объявленный в модуле Foo, игнорируется:

 > Bar.new.baz #=> "bar-baz" 

С Module#prepend оно является обратным; объявление в модуле перезаписывает объявление в классе. Переписав приведенный выше пример для использования Module#prepend , вот новый код:

 module Foo def baz 'foo-baz' end end class Bar prepend Foo def baz 'bar-baz' end end 

И при вызове метода baz в классе Bar метод в модуле Foo действительно вызывается:

 > Bar.new.baz #=> "foo-baz" 

Перечислимых # ленивым

Цитируется непосредственно из документации:

Возвращает ленивый перечислитель, методы которого map / collect, flat map / collect concat, select / find all, reject, grep, zip, take, #take while, drop, #drop_ while и циклически перечисляют значения только по мере необходимости , Однако, если блок отдается zip или циклу, значения перечисляются немедленно ».

Теперь, когда у нас есть понимание его концепции, давайте рассмотрим пример:

 > ary = [1,2,3,4,5].select{|n| n > 2} #=> [3, 4, 5] > ary = [1,2,3,4,5].lazy.select{|n| n > 2} #=> #:select> > ary.force #=> [3, 4, 5] 

В первой части, без использования lazy метода, новый массив возвращается сразу после того, как метод select завершает оценку. Во второй части, когда используется lazy метод, возвращается ленивый перечислитель, и код не оценивается, пока мы не вызовем force (или to_a ).

Языковые изменения

Теперь вы можете использовать % i и % I для создания списка символов:

 > %i{this is a list of symbols} #=> [:this, :is, :a, :list, :of, :symbols] 

Вывод

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

Если вы все еще используете Ruby 1.8x, настоятельно рекомендуется обновить его до 1.9.3 как можно скорее, так как он скоро будет устаревшим и более не поддерживается. Что касается совместимости между Ruby 2.0 и 1.9.3, он считается полностью совместимым.

Ресурсы