Что это за блок в Ruby? И как это передается в методе здесь?
Пила этот кусок кода в книге 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
Ответы
Ответ 1
Блоки - довольно основная часть рубина. Они разделены символом 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:[email protected](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
.
Ответ 2
Блоки, Procs и lambdas (называемые закрытием в Computer Science) являются одним из самых мощных аспектов 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 (short для процедуры). Единственное различие между блоками и Procs заключается в том, что блок - это 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]
Ответ 3
&block
- это способ отправки части кода Ruby в метод, а затем оценки этого кода в объеме этого метода. В приведенном выше примере кода означает, что частичная именованная тележка будет отображаться в div. Я думаю, что термин closure используется для этого в информатике.
Итак, в вашем примере &block
:
<%= render(:partial => "cart", :object => @cart) %>
Некоторое хорошее чтение и объяснение блоков, procs и lamdas можно найти в блог Роберта Сосински.
Ответ 4
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 default" и "ruby blocks".
Ответ 5
Ruby реализует блоки, Procs и лямбды, которые называются замыканиями в сообществе компьютерных наук.
Если вы начнете изучать 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, который будет вызываться методом вызова. Это вместо использования урожая
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
Полная статья о блоках ruby
Ответ 6
Он работает следующим образом:
@cart.items.empty?
- это кодирование
:id => "cart"
Становится атрибутом по соглашению, которое вы можете удалить {} при хэшировании параметров, если он последний.
блок
render(:partial => "cart", :object => @cart)
поэтому внутри функции, если тележка пуста, она добавит атрибут
стиль со значением "display: none"
Затем он создаст тег div, заполненный содержимым результата выполнения блока, который будет результатом рендеринга частичной тележки с содержимым @cart.