在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
答案 0 :(得分:90)
块是ruby的一个相当基本的部分。它们由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
。
答案 1 :(得分:18)
Blocks,Procs和lambdas(在计算机科学中被称为闭包)是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之间的唯一区别是块是无法保存的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
lambda表达式
到目前为止,您已经以两种方式使用了Procs,将它们作为属性直接传递并将它们保存为变量。这些过程与其他语言称为匿名函数或lambdas的行为非常相似。为了让事情更有趣,lambdas也可以在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 like”方式是使用块。他们有以下熟悉的语法:
array = [1, 2, 3, 4]
array.collect! do |n|
n ** 2
end
puts array.inspect
# => [1, 4, 9, 16]
答案 2 :(得分:11)
&block
是一种将一段Ruby代码发送到方法然后在该方法范围内评估该代码的方法。在上面的示例代码中,它表示将在div中呈现部分命名的购物车。我认为术语closure用于计算机科学。
因此,在您的示例中,&block
是:
<%= render(:partial => "cart", :object => @cart) %>
可以在Robert Sosinski's blog找到一些好的阅读和对块,触发器和lamdas的解释。
答案 3 :(得分: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 arguments”和“ruby blocks”。
答案 4 :(得分:3)
Ruby实现了Blocks,Procs和lambdas,它们在计算机科学界被称为闭包。 如果你开始学习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
如果您需要将参数传递给此方法,该怎么办?
您可以修改方法签名以接受params,最后使用以&符开头的param来捕获块。在下面的示例中,我们将使用&amp; block param捕获块,我们将调用call方法。这取代了使用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
答案 5 :(得分:1)
它的工作原理如下:
@cart.items.empty?
是编码
:id => "cart"
根据惯例成为属性,如果它是最后一个,你可以删除param哈希上的{}。
块是
render(:partial => "cart", :object => @cart)
因此,如果购物车为空,则会在该功能中添加该属性 值为“display:none”的样式
然后它将创建一个div标签,其中填充了执行块的结果的内容,这将是使用@cart的内容呈现局部视图购物车的结果。