这个示例将多个 Ruby 概念完美地结合在一起。因此,我将尝试解释所有这些内容。
- Blocks
Ruby 中的方法可以优雅地接受块(代码片段):
def run_code
yield
end
run_code { puts 42 } # => prints 42
- 过程与块类似,但它们是实际的可寻址对象:
deep_thought = proc { puts 42 }
deep_thought.call # => prints 42
- 当调用方法时,您可以将过程转换为块
&
操作员:
def run_code
yield
end
deep_thought = proc { puts 42 }
run_code(&deep_thought) # => prints 42
- 过程和块可以接受参数:
def reveal_answer
yield 5_000
end
deep_thought = proc do |years_elapsed|
years_elapsed >= 7_500_000 ? 42 : 'Still processing'
end
reveal_answer(&deep_thought) # => 'Still processing'
- 您可以使用以下命令将块转换为过程
&
在方法签名中:
def inspector(&block)
puts block.is_a?(Proc)
puts block.call
end
inspector { puts 42 } # => prints true and 42
inspector(&proc { puts 42 }) # => the same
-
Symbol#to_proc
创建一个过程,调用对象上同名的方法:
class Dummy
def talk
'wooooot'
end
end
:talk.to_proc.call(Dummy.new) # => "wooooot"
换句话说,
:bar.to_proc.call(foo)
几乎相当于
foo.bar
-
BasicObject#method_missing
:
当您尝试调用对象的方法时,Ruby 会遍历它的祖先链,搜索具有该名称的方法。链是如何构建的是一个不同的话题,足够长到改天,重要的是如果直到最底层都没有找到方法(BasicObject
),在同一条链上执行第二次搜索 - 这次是针对名为method_missing
。它作为参数传递原始方法的名称加上它收到的任何参数:
class MindlessParrot
def method_missing(method_name, *args)
"You caldt #{method_name} with #{args} on me, argh!"
end
end
MindlessParrot.new.foo # => "You caldt foo with [] on me, argh!"
MindlessParrot.new.bar :baz, 42 # => "You caldt bar with [:baz, 42] on me, argh!"
那么这一切在我们的具体案例中意味着什么呢?让我们假设没有protected
.
translator.speak(&:spanish)
调用方法Translator#speak
with :spanish
转换为块。
Translator#speak
获取该块并将其转换为一个过程,名为language
,并调用它,传递self
作为论证。
self
是一个实例Translator
,因此,它有方法speak
, french
, spanish
, turkey
and method_missing
.
And so:
Translator.new.speak(&:spanish)
相当于:
:spanish.to_proc.call(Translator.new)
这相当于:
Translator.new.spanish
给我们"hola"
.
现在,采取protected
回来,我们所有的语言方法translator
对象仍然存在,但外人无法直接访问它们。
就像你不能打电话一样
Translator.new.spanish
并期望"hola"
回来,你不能打电话
Translator.new.speak(&:spanish)
并且由于该方法不可直接访问,因此被认为未找到并且method_missing
被称为,从而给我们"awkward silence"
.