2015-07-21 89 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