Using blocks for configuration
Another common use case for blocks is configuration, which is often seen in gems as a clean and intuitive way for users to set options through code:
module MyConfigurableGem
class << self
attr_accessor :option1, :option2, :option3
end
def self.configure
yield(self)
end
end
MyConfigurableGem.configure do |config|
config.option1 = "Value for option 1"
config.option2 = "Value for option 2"
config.option3 = "Value for option 3"
end
This feels more concise than having to set each option individually with calls like MyConfigurableGem.option1 = .... It also gives the library author more control over potential side effects of setting options. For example, they could trigger some kind of initialization / refresh / validation logic when the block has finished executing.
Another way to implement this pattern is via instance_eval, which evaluates the block in the context of the gem module and lets us avoid having to pass config as an argument:
module MyConfigurableGem
class Configuration
attr_accessor :option1, :option2, :option3
end
class << self
attr_accessor :config
def configure(&)
self.config ||= Configuration.new
instance_eval(&)
end
end
end
MyConfigurableGem.configure do
config.option1 = "Value for option 1"
config.option2 = "Value for option 2"
config.option3 = "Value for option 3"
end
The latter approach is used by Rails when running Rails.application.configure, but the former is more common, likely because of its simplicity and explicitness.