Что это и блок в Ruby? И как это передается в методе здесь? - PullRequest
50 голосов
/ 02 мая 2009

Увидел этот кусок кода в книге по Ruby on Rails. Этот первый вид, а второй - вспомогательный модуль. Я не понимаю, как работает &block и attributes={}. Кто-нибудь может привести меня к какому-то уроку, объясняющему это?

<% hidden_div_if(@cart.items.empty?, :id => "cart") do %>
 <%= render(:partial => "cart", :object => @cart) %>
<% end %>

module StoreHelper
 def hidden_div_if(condition, attributes = {}, &block)
  if condition
   attributes["style"] = "display: none"
  end
   content_tag("div", attributes, &block)
  end
end

Ответы [ 6 ]

91 голосов
/ 02 мая 2009

Блоки - довольно простая часть рубина. Они ограничены либо do |arg0,arg1| ... end, либо { |arg0,arg1,arg2| ... }.

Они позволяют вам указать обратный вызов для передачи методу. Этот обратный вызов может быть вызван двумя способами - либо путем захвата указав последний аргумент с префиксом & или используя ключевое слово yield:

irb> def meth_captures(arg, &block)
       block.call( arg, 0 ) + block.call( arg.reverse , 1 )
     end
#=> nil
irb> meth_captures('pony') do |word, num|
       puts "in callback! word = #{word.inspect}, num = #{num.inspect}"
       word + num.to_s
     end
in callback! word = "pony" num = 0
in callback! word = "ynop" num = 1
#=> "pony0ynop1" 
irb> def meth_yields(arg)
       yield(arg, 0) + yield(arg.upcase, 1)
     end
#=> nil
irb> meth_yields('frog') do |word, num|
       puts "in callback! word = #{word.inspect}, num = #{num.inspect}"
       word + num.to_s
     end
in callback! word = "frog", num = 0
in callback! word = "FROG", num = 1
#=> "frog0FROG1"

Обратите внимание, что наш обратный вызов был одинаковым в каждом случае - мы можем удалить повторение путем сохранения нашего обратного вызова в объекте, а затем передать его каждому метод. Это можно сделать с помощью lambda для захвата обратного вызова в объекте, и затем передается методу путем добавления префикса &.

irb> callback = lambda do |word, num|
       puts "in callback! word = #{word.inspect}, num = #{num.inspect}"
       word + num.to_s
     end
#=> #<Proc:0x0052e3d8@(irb):22>
irb> meth_captures('unicorn', &callback)
in callback! word = "unicorn", num = 0
in callback! word = "nrocinu", num = 1
#=> "unicorn0nrocinu1"
irb> meth_yields('plate', &callback)
in callback! word = "plate", num = 0
in callback! word = "PLATE", num = 1
#=> "plate0PLATE1"

Важно понимать различные варианты использования & здесь как префикса к последнему аргументу функции

  • в определении функции захватывает любой переданный блок в этот объект
  • при вызове функции расширяет данный объект обратного вызова в блок

Если вы посмотрите вокруг, блоки используются повсеместно, особенно в итераторах, например Array#each.

18 голосов
/ 10 июля 2010

Блоки, процы и лямбды (в компьютерных науках называются замыканиями) являются одним из самых мощных аспектов Ruby, а также одним из самых неправильно понятых. Вероятно, это потому, что Ruby обрабатывает замыкания довольно уникальным способом. Ситуация усложняется тем, что в Ruby есть четыре различных способа использования замыканий, каждый из которых немного отличается, а иногда и бессмысленен. Существует довольно много сайтов с очень хорошей информацией о том, как замыкания работают в Ruby. Но я еще не нашел хорошего, окончательного руководства там.

class Array
  def iterate!(&code)
    self.each_with_index do |n, i|
      self[i] = code.call(n)
    end
  end
end

array = [1, 2, 3, 4]

array.iterate! do |n|
  n ** 2
end

Процедуры, AKA, Procs

Блоки очень удобны и синтаксически просты, однако мы можем захотеть иметь в своем распоряжении много разных блоков и использовать их несколько раз. Таким образом, пропуск одного и того же блока снова и снова потребует от нас повторения. Однако, поскольку Ruby полностью объектно-ориентирован, это можно сделать довольно аккуратно, сохранив повторно используемый код как сам объект. Этот повторно используемый код называется Proc (сокращение от процедуры). Единственная разница между блоками и процессами заключается в том, что блок - это процесс, который не может быть сохранен и, как таковой, является решением для одноразового использования. Работая с Procs, мы можем начать делать следующее:

class Array
  def iterate!(code)
    self.each_with_index do |n, i|
      self[i] = code.call(n)
    end
  end
end

array_1 = [1, 2, 3, 4]
array_2 = [2, 3, 4, 5]

square = Proc.new do |n|
  n ** 2
end

Лямбда

До сих пор вы использовали Procs двумя способами, передавая их непосредственно как атрибут и сохраняя их как переменную. Эти Procs действуют очень похоже на то, что другие языки называют анонимными функциями или лямбдами. Чтобы сделать вещи более интересными, лямбды также доступны в Ruby. Взгляните:

class Array
  def iterate!(code)
    self.each_with_index do |n, i|
      self[i] = code.call(n)
    end
  end
end

array = [1, 2, 3, 4]

array.iterate!(lambda { |n| n ** 2 })

puts array.inspect

Блоки

Самый распространенный, самый простой и, пожалуй, самый «похожий на Ruby» способ использования замыканий в Ruby - это блоки. Они имеют следующий знакомый синтаксис:

array = [1, 2, 3, 4]

array.collect! do |n|
  n ** 2
end

puts array.inspect

# => [1, 4, 9, 16]
11 голосов
/ 02 мая 2009

&block - это способ отправки части кода Ruby в метод и последующей оценки этого кода в области действия этого метода. В приведенном выше примере кода это означает, что частичная именованная корзина будет отображаться в div. Я думаю, что термин замыкание используется для этого в компьютерных науках.

Итак, в вашем примере &block это:

<%= render(:partial => "cart", :object => @cart) %>

Некоторое хорошее чтение и объяснение блоков, проков и лямд можно найти в блоге Роберта Сосинского .

4 голосов
/ 02 мая 2009

Re attributes = {}, это просто аргумент метода со значением по умолчанию. Поэтому, если вы вызываете hidden_div_if(whatever), то есть, передавая только первый аргумент, attributes по умолчанию будет пустым хешем.

Это полезно, потому что упрощает настройку attributes["style"] позже, так как attributes не нужно сначала инициализировать в хеш. (Тем не менее, это можно сделать просто как (attributes ||= {})["style"] = ….)


&block только немного сложнее.

Методы Ruby могут принимать последний аргумент, который является блоком, используя специальный синтаксис method(args) { |block_args| block_code }. &block в основном захватывает этот блок в переменную block как объект Proc. Так что block - это просто переменная, указывающая здесь на анонимную процедуру.

Когда позже вызывается content_tag и передается &block в качестве последнего аргумента, он расширяется в блок, как если бы вызов действительно был content_tag(…) { block originally passed to hidden_if_div }


Так что, может быть, я действительно запутался здесь. То, что вы должны искать в Google, это "аргументы ruby ​​по умолчанию" и "блоки ruby".

3 голосов
/ 21 мая 2010

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

a = ["dog", "cat", "bird"]
a.alter_each! do |n, i|
  "#{i}_#{n}"
end

Так что здесь происходит?

Мы начнем с массива имен животных и вызываем alter_each! метод передачи блока. В этом блоке кода мы можем указать, как мы хотим изменить каждый элемент. В нашем примере каждое имя животного будет добавлено к его положению в массиве. Как alter_each! Метод выполняет итерацию по каждому элементу, он будет выполнять наш блок, передавая значение и индекс. Наш блок захватывает эти параметры, добавляет префикс к имени и возвращает результат. Теперь давайте посмотрим на alter_each! способ.

Обратите внимание, что метод не указывает никаких параметров, потому что блок автоматически назначается для ключевого слова yield. yield вызывается как функция, передающая значение и индекс каждого элемента в массиве и переопределяющая исходное значение.

class Array
  def alter_each!
    self.each_with_index do |n, i|
      self[i] = yield(n,i)
    end
  end
end

Что если вам нужно передать параметр этому методу?

Вы можете изменить сигнатуру метода так, чтобы он принимал параметры и, наконец, перехватывал блок с параметром, начинающимся с амперсанда. В приведенном ниже примере наш блок будет записан с параметром & block, который мы будем вызывать методом вызова. Это вместо использования yield

class Array
  def modify_each!(add_one = true, &block)
    self.each_with_index do |n, i|
      j = (add_one) ? (i + 1) : i
      self[i] = block.call(n,j)
    end
  end
end

Полный текст статьи о рубиновых блоках

1 голос
/ 02 мая 2009

Работает так:

@cart.items.empty? это кодировка

:id => "cart" Становится атрибутом в соответствии с соглашением, которое вы можете удалить {} для хэша параметра, если он последний.

блок

render(:partial => "cart", :object => @cart)

поэтому в функции, если корзина пуста, добавится атрибут стиль со значением "display: none"

Затем он создаст тег div, заполненный содержимым результата выполнения блока, который будет результатом рендеринга корзины частичного просмотра с содержимым @ cart.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...