# 代码块
一个块包含的代码块。你可以分配一个名称,一个块。块中的代码总是被括在大括号里({})或是do...end里。
~~~
[1, 2, 3].each do |i|
puts i
end
#=> 1
2
3
~~~
上面这个例子, each方法后面加一个do...end结构,那就是一个块。
Ruby中任何一个方法你都可以传递一个块。
~~~
def test;end
test{ puts i}
~~~
~~~
def test
yield
end
test{puts "hello test!"}
def test(x)
yield(x)
end
test('world!'){|x| puts "hello #{x}"}
~~~
yield关键字不仅可以挂载块(block)代码,而且可以给块传递参数。
~~~
def test(&block)
block.call("world")
end
test{|msg| puts "hello #{msg}"}
~~~
block到了方法内部,已经被&转化为了一个Proc对象。
~~~
def test(&block)
inner_test(&block)
end
def inner_test
yield("haha!")
end
test{|msg| puts "hello #{msg}"}
~~~
test方法传进去的block被转化为了Proc对象,而其内部的inner_test又利用「&」把这个Proc对象转化为了块(block)
### 作用域
在Ruby中,关键字class、moduel、def都有自己的作用域范围。
~~~
class People
father = 'God'
def my_father
puts father
end
end
module Faith
def my_father
father = 'My God'
puts father
end
end
~~~
然后我们创建一个对象:
~~~
person = People.new
person.my_father
#=> NameError: undefined local variable or method `father' for #<People:0x00000003248990>
~~~
我们看到,报错了,因为作用域的问题,在my_father方法中,找不到这个father的变量。
~~~
person.extend Faith
person.my_father #=> "My God"
~~~
我们把Faith模块extend到person对象之后,就可以调用my_father方法,这是因为在模块Faith中定义了father变量。两个father变量明显不同。
### 穿透作用域的块
块(block)有个功能,就是可以穿透上面所说的作用域。
~~~
class People
father = 'The God'
define_method :priest do
puts "I can talk with #{father}"
end
end
person = People.new
person.priest #=> "I can talk with The God"
~~~
上面代码中, define_method是可以动态的定义一个方法,使用define_method方法的主要原因是想使用块,因为它后面可以加一个块, 也就是 do ... end中包括的内容。我们可以看到上面块中的代码, 直接使用了Class作用域的father变量,并且成功的输出了结果。
这就证明了block有穿透作用域的能力。
### lambda 和 proc
我们在前面展示了一些block的例子。 我们说Ruby一切皆对象,但是这个block,确不是对象, 不过也不影响那句话,因为block是无法单独存在的,它必须要依靠于一个方法。如果你想让一个block单独被调用,那么就需要把块变成一个Proc对象。
~~~
lambda = ->(x, y){x * y}
#=> #<Proc:0x00000002e593c0@(pry):35 (lambda)>
lambda.call(2,3) #=> 6
#也可以省略call,但不可以省略点「.」
lambda.(2,3) #=> 6
~~~
lambda,是Proc对象的一种类型。它是一个可以被call的对象。
~~~
proc = proc{|x, y| x * y}
#=> #<Proc:0x00000002d1ee38@(pry):38>
proc.call(2, 3) #=> 6
proc.(2, 3) #=> 6
~~~
proc也是一个Proc对象, 注意看lambda和proc生成的Proc对象的差别。
具体的差别可以查看我的blog文章:[大话Rubyblock: http://tao.logdown.com/posts/166766-vernacular-ruby-block](http://tao.logdown.com/posts/166766-vernacular-ruby-block)
### 结语
在Chef中, block的应用是非常常见的, 比如我们随便写个cookbook,都必须得用到, 下面的例子来自于 [chef-server的cookbook](https://github.com/opscode-cookbooks/chef-server/blob/master/recipes/default.rb):
~~~
# Ensure :file_cache_path exists
directory Chef::Config[:file_cache_path] do
owner 'root'
group 'root'
recursive true
action :create
end
~~~
现在你看懂这样的代码了吗?
- 序
- Chapter 1: 初识Chef
- 一些背景
- Chef vs Puppet
- Chapter 2: Chef应用
- Chef架构
- Chef能做什么
- Chef组件
- Chef环境安装
- chef-server
- opscode-chef
- chef-solo
- Chef实战
- 实战前的必修理论
- 使用Chef
- Chapter 3: Ruby基础
- 对象与方法
- 标识符
- 类与模块
- 数据类型
- 真与假
- 控制语句
- 代码块
- Chapter 4: Chef源码架构
- Rubygems与gem
- bundler
- Chef源码组织
- Chapter 5: Rails基础
- Rails是什么
- MVC架构
- Restful
- Rails组成与项目结构
- Chapter 6: Chef Server WebUI
- Chef Server Webui组织结构
- Chef Rest API
- 参考