Module: AbAdmin::Utils::EvalHelpers

Included in:
CarrierWave::BaseUploader, Menu::BaseGroup, Menu::Item, CsvDocument, XlsDocument, Admin::ManagerController
Defined in:
lib/ab_admin/utils/eval_helpers.rb

Overview

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

Instance Method Summary collapse

Instance Method Details

#call_method_or_proc_on(obj, symbol_or_proc, options = {}) ⇒ Object


87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/ab_admin/utils/eval_helpers.rb', line 87

def call_method_or_proc_on(obj, symbol_or_proc, options = {})
  exec = options[:exec].nil? ? true : options[:exec]
  case symbol_or_proc
    when Symbol, String
      obj.send(symbol_or_proc.to_sym, *options[:attrs])
    when Proc
      if exec
        obj.instance_exec(&symbol_or_proc)
      else
        symbol_or_proc.call(obj)
      end
  end
end

#evaluate_method(object, method, *args, &block) ⇒ Object

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)

54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/ab_admin/utils/eval_helpers.rb', line 54

def evaluate_method(object, method, *args, &block)
  case method
    when Symbol
      object.method(method).arity == 0 ? object.send(method, &block) : object.send(method, *args, &block)
    when Proc, Method
      args.unshift(object)
      arity = method.arity
      limit = [0, 1].include?(arity) ? arity : args.length
      
      # Procs don't support blocks in < Ruby 1.8.6, so it's tacked on as an
      # argument for consistency across versions of Ruby (even though 1.9
      # supports yielding within blocks)
      if block_given? && Proc === method && arity != 0
        if [1, 2].include?(arity)
          # Force the block to be either the only argument or the 2nd one
          # after the object (may mean additional arguments get discarded)
          limit = arity
          args.insert(limit - 1, block)
        else
          # Tack the block to the end of the args
          limit += 1 unless limit < 0
          args.push(block)
        end
      end
      
      method.call(*args[0, limit], &block)
    when String
      eval(method, object.instance_eval {binding}, &block)
    else
      raise ArgumentError, 'Methods must be a symbol denoting the method to call, a block to be invoked, or a string to be evaluated'
    end
end