Indifferent Eval

When you're making a Ruby DSL, you often have to decide between:

# Use a block variable to access the MyConfig instance.
# This gives you the benefit of having access to the outer "self" inside of the block.
MyApp.config do |config|
  config.verbosity true
  config.foo       :bar
end

and:

# Don't pass a block variable.  All method calls are sent to the MyConfig instance.
# This has the benefit of looking really clean (but no access to "self" inside the block).
MyApp.config do
  verbosity true
  foo       :bar
end

There are a number of pros and cons to each of these approaches.

Wouldn't it be nice if you could support both? That's what #indifferent_eval is for!

Install

# In your Gemfile
gem "indifferent_eval"

The Code

The code is tiny. This gem exists only for convenience.

module IndifferentEval
  def indifferent_eval(object_to_eval_on = self, &block)
    if block.arity == 0 || block.arity == -1
      object_to_eval_on.instance_eval &block
    else  
      block.call object_to_eval_on
    end
  end
end

(Annotated source code)

Usage

require "indifferent_eval"

class MyLibrary
  def self.config(&block)
    @config = MyConfig.new.indifferent_eval(&block)
  end

  class MyConfig
    attr_writer :some_setting

    def some_setting(value = nil)
      @some_setting = value if value
      @some_setting
    end
  end
end

# Use a block variable to access the MyConfig instance.
# This gives you the benefit of having access to the outer "self" inside of the block.
MyLibrary.config do |config|
  config.some_setting = "Called a method with a block variable"
end

# Don't pass a block variable.  All method calls are sent to the MyConfig instance.
# This has the benefit of looking really clean (but no access to "self" inside the block).
MyLibrary.config do
  some_setting "Called a method without a block variable"
end

License

indifferent_eval is released under the MIT license.