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
endand:
# 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
endThere 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
endUsage
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"
endBut I don't want every Ruby object to be extended with this!
If you don't want to include IndifferentEval into Object, you don't have to.
# This simply requires the IndifferentEval module without
# automatically icluding the module into any classes.
require "indifferent_eval/module"If you only want to call #indifferent_eval" on certain types of objects, you could ...
class Configuration
include IndifferentEval
end
Configuration.new.indifferent_eval(&block)Or, if you want to call #indifferent_eval on different types objects,
#indifferent_eval takes an argument for the object to evaluate, so you could ...
class MyClass
include IndifferentEval
def do_something
indifferent_eval some_object, &some_block
indifferent_eval another_object, &another_block
end
endNothing magical :)
License
indifferent_eval is released under the MIT license.