module StateMachine::EvalHelpers

Provides a set of helper methods for evaluating methods within the context of an object.

Public Instance Methods

evaluate_method(object, method, *args, &block) click to toggle source

Evaluates one of several different types of methods within the context of the given object. Methods can be one of the following types:

  • Symbol

  • Method / Proc

  • String

Examples ↑

Below are examples of the various ways that a method can be evaluated on an object:

class Person
  def initialize(name)
    @name = name
  end

  def name
    @name
  end
end

class PersonCallback
  def self.run(person)
    person.name
  end
end

person = Person.new('John Smith')

evaluate_method(person, :name)                            # => "John Smith"
evaluate_method(person, PersonCallback.method(:run))      # => "John Smith"
evaluate_method(person, Proc.new {|person| person.name})  # => "John Smith"
evaluate_method(person, lambda {|person| person.name})    # => "John Smith"
evaluate_method(person, '@name')                          # => "John Smith"

Additional arguments ↑

Additional arguments can be passed to the methods being evaluated. If the method defines additional arguments other than the object context, then all arguments are required.

For example,

person = Person.new('John Smith')

evaluate_method(person, lambda {|person| person.name}, 21)                              # => "John Smith"
evaluate_method(person, lambda {|person, age| "#{person.name} is #{age}"}, 21)          # => "John Smith is 21"
evaluate_method(person, lambda {|person, age| "#{person.name} is #{age}"}, 21, 'male')  # => ArgumentError: wrong number of arguments (3 for 2)
   # File lib/state_machine/eval_helpers.rb
53 def evaluate_method(object, method, *args, &block)
54   case method
55     when Symbol
56       klass = (class << object; self; end)
57       args = [] if (klass.method_defined?(method) || klass.private_method_defined?(method)) && object.method(method).arity == 0
58       object.send(method, *args, &block)
59     when Proc, Method
60       args.unshift(object)
61       arity = method.arity
62       
63       # Procs don't support blocks in < Ruby 1.9, so it's tacked on as an
64       # argument for consistency across versions of Ruby
65       if block_given? && Proc === method && arity != 0
66         if [1, 2].include?(arity)
67           # Force the block to be either the only argument or the 2nd one
68           # after the object (may mean additional arguments get discarded)
69           args = args[0, arity - 1] + [block]
70         else
71           # Tack the block to the end of the args
72           args << block
73         end
74       else
75         # These method types are only called with 0, 1, or n arguments
76         args = args[0, arity] if [0, 1].include?(arity)
77       end
78       
79       method.is_a?(Proc) ? method.call(*args) : method.call(*args, &block)
80     when String
81       eval(method, object.instance_eval {binding}, &block)
82     else
83       raise ArgumentError, 'Methods must be a symbol denoting the method to call, a block to be invoked, or a string to be evaluated'
84   end
85 end