2015-07-21 127 views
0

我對ruby非常陌生,我只是花時間研究github中現有的ruby項目的模式。現在,我降落在twitter's ruby project,並注意到在其配置這些行:Ruby配置技巧

client = Twitter::REST::Client.new do |config| 
    config.consumer_key  = "YOUR_CONSUMER_KEY" 
    config.consumer_secret  = "YOUR_CONSUMER_SECRET" 
    config.access_token  = "YOUR_ACCESS_TOKEN" 
    config.access_token_secret = "YOUR_ACCESS_SECRET" 
end 

在這種方法調用的declaration,我也注意到了這一點:

module Twitter 
    class Client 
    include Twitter::Utils 
    attr_accessor :access_token, :access_token_secret, :consumer_key, :consumer_secret, :proxy 

    def initialize(options = {}) 
     options.each do |key, value| 
     instance_variable_set("@#{key}", value) 
     end 
     yield(self) if block_given? 
    end 
... 

現在,我做我的做法,我複製了相同的邏輯,但觀察「初始化」方法的內容。

module Main 
    class Sample  
     attr_accessor :hello, :foo 

     def initialize(options={})    
      yield(self) if block_given? 
     end 

     def test 
      @hello 
     end   
    end  
end 

,並呼籲它(同樣在Twitter上面的代碼怎麼做)

sample = Main::Sample.new do |config| 
    config.hello = "world" 
    config.foo = "bar" 
end 

puts "#{sample.hello} #{sample.foo}" # outputs => world bar 
puts sample.test # outputs => world 

現在,我的問題是,即使我沒有在我的這幾行代碼(見代碼塊從嘰嘰喳喳以上),在我的 「初始化」 的方法,

options.each do |key, value| 
    instance_variable_set("@#{key}", value) 
end 

代碼 puts "#{sample.hello} #{sample.foo}"puts sample.test仍然正常工作。這是爲什麼?這個實例變量是如何真正設置的?

回答

1

這是因爲你用config.hello=config.foo=之類的東西手動調用它們。

沒有什麼的代碼塊將無法正常工作是這樣的:

Main::Sample.new(hello: 'world') 

你需要的那部分拿起選擇和應用它們。

Twitter版本相當鬆弛。通常情況下,你會想要測試是否有一個名稱屬性,而不是隨機分配實例變量。這通常與某種白名單進行:

ATTRIBUTES = %i[ hello world ] 

attr_accessor *ATTRIBUTES 

def initialize(options = nil) 
    options and options.each do |attr, value| 
    if (ATTRIBUTES.include?(attr)) 
     send("#{attr}=", value) 
    else 
     raise "Unknown attribute #{attr.inspect}" 
    end 
    end 

    yield self if (block_given?) 
end 

,如果您使用無效選項調用將引發異常。

+0

啊哈!我知道了。我懂了。所以原因是當我產生(self)'時,它實際上將實例傳遞給塊,這就是我能夠手動配置或賦值給他們的方式。我太傻了。無論如何,再次感謝人! – Aldee

+0

您的風格實際上很安全且易於追蹤。是。我同意Twitter的實施實際上是鬆懈的。 – Aldee

+0

在這樣的方法中,你會看到'yield(self)if(block_given?)'。這是一種模式,它可以回退正在進行中的對象,您可以在最終確定之前進行修改。當你在一個函數調用中構建一個對象時,這個函數會派上用場,你只需要調整它:'func(Thing.new {| t | t.name ='test'})''例如。 – tadman