Class: Object

Inherits:
BasicObject
Defined in:
lib/add_hook.rb

Instance Method Summary collapse

Instance Method Details

#add_hook(method_name, &block) ⇒ Object

Add as a callback to an instance method

Usage

>> x = [1, 2]
=> [1, 2]
>> x.add_hook(:<<){|the_array, *args| puts "called #{ the_array.inspect } << #{ args.inspect }" }
=> nil
>> x << 'foo'
called [1, 2] << ["foo"]
=> [1, 2, "foo"]

Parameters

method_name<~to_s>

The name of the method to add a hook to

&block

A block to run everytime the method gets called. The block is passed multiple arguments. The first argument is self, the instance that the method is being called on. The rest of the arguments are the arguments that were passed to the method.



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/add_hook.rb', line 25

def add_hook method_name, &block

  # protect against infinite looping
  return if method_name.to_s == 'add_hook'

  # if we haven't hooked into any methods on this instance yet 
  # then we need to add a way to persist our hooked methods and callbacks
  unless self.respond_to? :hooked_methods

    # add some things to this particular object
    class << self
      attr_accessor :hooked_methods, :hooked_method_hooks
    end

    # initalize attributes
    self.hooked_methods      ||= { }
    self.hooked_method_hooks ||= { }

  end

  # for now, assume this is an *instance method* that we're hooking
  method     = self.class.instance_method(method_name.to_s)
  method_key = method_name.to_s + Time.now.to_s

  if method

    self.hooked_methods[ method_key ]      = method # store the original method
    self.hooked_method_hooks[ method_key ] = block  # store the block to call

    # using eval because define_method can't create
    # a method that accepts a block
    eval %{
      def self.#{ method_name } *args #, &block
        self.hooked_method_hooks[#{ method_key.inspect }].call(self, *args)
        self.hooked_methods[#{ method_key.inspect }].bind(self).call(*args) # add block
      end
    }

  else
    raise "Cannot find method #{ method_name.inspect } to add hook to"
  end
end