Ruby Gem中的配置块

Ruby Gem中的配置块,ruby,gem,Ruby,Gem,我正在尝试编写我的第一个gem,但是对于典型的配置块是如何工作的,我有点困惑。举例来说,我想要的是能够写出类似的东西 MyGem.configure do |c| c.property1 = 1 c.property2 = 'some string' end 我对ruby的了解还不够深入,所以虽然我喜欢使用块,但我不知道如何编写需要块的代码 如何编写“MyGem”类以这种方式进行配置(可能是通过实例变量进行配置)?这是实现示例的可能方式: class MyGem Confi

我正在尝试编写我的第一个gem,但是对于典型的配置块是如何工作的,我有点困惑。举例来说,我想要的是能够写出类似的东西

MyGem.configure do |c|
    c.property1 = 1
    c.property2 = 'some string'
end
我对ruby的了解还不够深入,所以虽然我喜欢使用块,但我不知道如何编写需要块的代码


如何编写“MyGem”类以这种方式进行配置(可能是通过实例变量进行配置)?

这是实现示例的可能方式:

class MyGem
  Config = Struct.new :property1, :property2  # => MyGem::Config

  def self.configure(&config_block)
    config_block.call config         # => "some string"
  end                                # => :configure

  def self.config
    @config ||= Config.new  # => #<struct MyGem::Config property1=nil, property2=nil>, #<struct MyGem::Config property1=1, property2="some string">
  end                       # => :config
end                         # => :config

MyGem.configure do |c|         # => MyGem
  c.property1 = 1              # => 1
  c.property2 = 'some string'  # => "some string"
end                            # => "some string"

MyGem.config  # => #<struct MyGem::Config property1=1, property2="some string">
classmygem
Config=Struct.new:property1,:property2#=>MyGem::Config
def自我配置(&config_块)
config_block.call config#=>“一些字符串”
结束#=>:配置
def self.config
@config | |=config.new#=>##
结束#=>:配置
结束#=>:配置
MyGem.configure do | c |#=>MyGem
c、 属性1=1#=>1
c、 property2='一些字符串'#=>“一些字符串”
结束#=>“一些字符串”
MyGem.config#=>#
但我也主张将状态存储在实例而不是类上:

class MyGem
  Config = Struct.new :property1, :property2  # => MyGem::Config
  def initialize(&config_block)
    config_block.call config                  # => "some string"
  end                                         # => :initialize

  def config
    @config ||= Config.new  # => #<struct MyGem::Config property1=nil, property2=nil>, #<struct MyGem::Config property1=1, property2="some string">
  end                       # => :config
end                         # => :config

instance = MyGem.new do |c|    # => MyGem
  c.property1 = 1              # => 1
  c.property2 = 'some string'  # => "some string"
end                            # => #<MyGem:0x007fc1691ecb20 @config=#<struct MyGem::Config property1=1, property2="some string">>

instance.config  # => #<struct MyGem::Config property1=1, property2="some string">
classmygem
Config=Struct.new:property1,:property2#=>MyGem::Config
def初始化(&config_块)
config_block.call config#=>“一些字符串”
结束#=>:初始化
def配置
@config | |=config.new#=>##
结束#=>:配置
结束#=>:配置
instance=MyGem.new do | c |#=>MyGem
c、 属性1=1#=>1
c、 property2='一些字符串'#=>“一些字符串”
结束#=>#
instance.config#=>#

如果你这样做,你就没有全局状态。请注意,我们可能有多个不同的配置(例如,每个客户端都有自己的配置,或者您想编写多个测试,但现在每个都有可能破坏其他配置)。我写了一篇博客,介绍了实现单例模式的许多方法,以及为什么不应该这样做:

如果给定块,通常最好是
yield(config)
,而不是捕获块并对其进行混乱的
调用。这在很大程度上是一个风格上的问题,但在捕获块时可能会有性能方面的考虑。我不同意这种看法:你做什么最终是无关紧要的。屈服稍微快一点,但这几乎不重要,特别是对于配置块。显式地获取和命名变量更清晰(您可以在签名中看到块,并且名称可以传达它的用途),并且最终更强大,因为它可以将块传递给另一个方法。为了获得收益,您必须求助于令人难以置信的hacky
def mymeth()yourmeth&Proc.new end
,因为传递捕获方法显然更干净,这一点不容否认。这取决于具体情况。回答得很好。我说的“很少有关系”是指它无关紧要,担心这些事情简直是浪费你的时间,除非你能找到一种方法来扩展它,就像把它放在一个普通呼叫链的底部一样,它在诺克斯堡的硬币大小上。我只是在这里给出一个替代方案,这是惯例使用的东西,人们需要熟悉。即使在Ruby 2.1.5中,
yield
方法的执行速度也比Ruby快4倍多,因此您有性能敏感的代码,每一点都有帮助。否则,正如您所建议的,清晰可能是一个优先事项。