user1381745
user1381745

Reputation: 3900

Configuration block in a Gem

I'm having a go at writing my first gem, but am a bit confused as to how the typical configuration blocks work. To illustrate, what I want to have is the facility to write something like

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

My ruby knowledge is less than deep, so while I'm happy using blocks, I'm not sure how to write code that expects a block.

How do I write my "MyGem" class to be configured (presumably via an instance variable) in this way?

Upvotes: 4

Views: 163

Answers (1)

Joshua Cheek
Joshua Cheek

Reputation: 31726

This would be the likely way to implement your example:

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">

But I'll also advocate storing state on the instance rather than the class:

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">

If you do it this way, you don't have global state. Notice we could have multiple different configurations (e.g. each client has their own config, or you want to write more than one test, but now each one risks breaking the others). I wrote a blog about many ways I'd thought of to implement the singleton pattern, and why you shouldn't do it: http://blog.8thlight.com/josh-cheek/2012/10/20/implementing-and-testing-the-singleton-pattern-in-ruby.html

Upvotes: 5

Related Questions