# 代碼塊
一個塊包含的代碼塊。你可以分配一個名稱,一個塊。塊中的代碼總是被括在大括號里({})或是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
- 參考