I've seen this both in several plugins and multiple places within ActiveRecord where an argument needs to be evaluated like so:
With Object obj, Method m, and Arguments a
- If m is a symbol, then call m on obj with the arguments a
- If m is a string, then evaluate m with a binding to obj
- If m is an object that responds to call (like Proc and Method), then call m.call with obj as the first argument + any additional arguments a
I feel this should by DRYed and available to all Objects. I, myself, am using it to duplicate functionality similar to how :if works on validations. I'd like to be able to use all of these different configurations without duplicating code and having to maintain it along with everything else. In addition, you get to DRY up some of the code in ActiveRecord's validations.
I'm proposing adding a new method to all Objects, eval_call, which will take 2 parameters: a symbol/string/Proc/Method/etc. and additional arguments (just like send).
So you would have something like the following (as shown in the comments and tests of the patch):
class SwitchChanger
def self.call(switch)
switch.state = "on"
end
end
class Switch
attr_accessor :state
def initialize
state = "on"
end
def change_state(value)
state = value
true
end
end
s = Switch.new
s.eval_call("@state") # => "on"
s.eval_call(Proc.new {|switch| switch.state}) # => "on"
s.eval_call(:change_state, "off") # => true
s.state # => "off"
s.eval_call(SwitchChanger) # => "on"
s.state # => "on"