То, что могло бы подсунуть ваш радар в ваших чтениях на Ruby, это Symbol#to_proc
, то, что было в Rails с 0.14.4.
Используя этот приятный маленький кусочек Ruby tricky, вы можете упростить код, такой как:
articles.collect { |a| a.title } articles.sort { |a| a.created_at }
в
articles.collect(&:title) articles.sort(&:created_at)
… и на тот случай, если вам интересно, почему &:ruby_is_the_new_black
не работает в прекрасном сценарии оболочки ruby, который вы только что написали, потому что Symbol#to_proc
не является стандартным Symbol#to_proc
Ruby. Процитирую Маурисио Фернандеса, который отметил, что он недавно включен в готовящийся Ruby 1.9 :
Символ # to_proc: впервые обнаружен неким анонимным японским Rubyist (довольно уверенным), вновь обнаруженным Флорианом Гроссом (я видел это, когда я тратил свое время на # ruby-lang), затем популяцией, разгромленной Pragdave и Rails, наконец, официально принятой. Это был долгий путь.
«Это хорошо, — говорите вы, — но я до сих пор не совсем понимаю, о чем весь этот Proc и блочные вещи». Что ж, позвольте мне побаловать вас некоторыми бормотаниями в Ruby.
Я думаю, что лучший способ описать блок, если вы пришли из web-design-land — это думать о нем как о анонимной функции в Javascript.
Например, следующий кусок Ruby:
socceroos.each do |s| s.congratulate end
эквивалентно следующему Javascript:
socceroos.each(function(s) { s.congratulate(); })
Блок, переданный функции, обозначается амперсандом:
class Team def each(&block) for player in @players block.call(player) end end end
Фактически, вышеописанный шаблон вызова блока настолько распространен, что Ruby делает еще один шаг вперед, позволяя просто написать:
class MyTeam def each for member in @members yield member end end end
yield member
— это еще один способ сказать «вызвать связанный блок, передав ему член». Вам даже не нужно добавлять &block
список параметров.
Вы бы видели этот шаблон при работе с ActiveRecord. Класс ActiveRecord::Base
позволяет вам указать блок при создании новых экземпляров. Например, если Player
был подклассом ActiveRecord, вы можете сделать следующее:
@player = Player.new do |p| p.name = "Ronaldo" p.nickname = "Porky" end
Как ActiveRecord предоставляет этот аккуратный API? Легко! Он просто проверяет, был ли указан блок, и, если это так, вызывает блок, передавая ему новый объект Player.
Минимальная логика для достижения этой цели:
class ActiveRecord::Base def initialize yield self if block_given? end end