Module: Callbacks

Defined in:
lib/ruby-callbacks.rb

Overview

module Callbackable allows to add callbacks before and after methods

Example

require "ruby-callbacks"

class Cat 
  include Callbacks

  attr_accessor :name, :colour

  callback :after, :eat, :play
  callback :before, :eat, :sleep

  def initialize(name, colour)
    @name = name
    @colour = colour
  end

  def sleep
    puts "#{@name} the cat is sleeping"
  end

  def eat
    puts "#{@name} the cat  is eating"
  end

  def play
    puts "#{@name} the cat  is playing"
  end

end

kitty = Cat.new("Kitty", "black")

kitty.eat 
#   => Kitty the cat is sleeping
#   => Kitty the cat is eating
#   => Kitty the cat is playing

Defined Under Namespace

Classes: UnknownEventException

Constant Summary collapse

SALT =

md5 for word “method”

"ea9f6aca279138c58f705c8d4cb4b8ce"

Class Method Summary collapse

Class Method Details

.included(base) ⇒ Object



49
50
51
52
53
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/ruby-callbacks.rb', line 49

def self.included(base)

  base.class_eval do 
    @@_method_chain = Hash.new
    @@_callbacks_running = Array.new
    # show current_method_chain
    def self.method_chain
      @@_method_chain
    end

    # allows to add a callback to already initialized class
    # == Example 
    #   class Cat
    #     include Callbacks
    #  
    #     def purr
    #       # purr
    #     end
    #     
    #     def sleep
    #       # sleep
    #     end
    #   end
    #  
    #   Cat.add_callback(:after, :purr, :sleep)
    #   Cat.new.purr #   purr, then sleep
    def self.add_callback(event, method, callback)
      if callbacks_exist? method 
        callback(event, method, callback)
      else
        callback(event, method, callback)        
        run_callback(method)
      end
    end

    # Delete existing callback
    # if callback == nil deletes all callbacks on given *method*
    # == Example 
    #   class Cat 
    #     include Callbacks
    #     callback :after, :purr, :sleep
    #     def purr
    #       # purr
    #     end
    #     
    #     def sleep
    #       # sleep
    #     end
    #   end
    #   
    #   Cat.callbacks_exist?(:purr) # => true
    #   Cat.del_callback(:purr)
    #   Cat.callbacks_exist?(:purr) # => false
    def self.del_callback(method, callback = nil)
      if callback
        @@_method_chain[method].delete_if{|c| c == callback }
        del_all_callbacks(method) if @@_method_chain[method].size == 1
      else
        del_all_callbacks(method)
      end
    end
    
    # Are callback *callback* on this method already exists?
    def self.callback_exists?(method, callback)
      !@@_method_chain[method.to_sym].detect{|c| c == callback.to_sym }.nil?
    end
    
    # Are callbacks on this method already exists?
    def self.callbacks_exist?(method)
      !@@_method_chain[method.to_sym].nil?
    end
    
    class << self 
      def new_method_name(method)
        "#{method}_#{SALT}"
      end
      
      def method_added(method)
        if callbacks_exist?(method) && !@@_callbacks_running.include?(method)
          run_callback(method)
        end
      end
      
      private
      # Register new callback 
      # See main example
      def callback(event, method, callback)
        reg_callback(event.to_sym, method.to_sym, callback.to_sym)
      end
    
      def del_all_callbacks(method)
        undef_method method
        alias_method method, new_method_name(method)
        @@_callbacks_running.delete_if {|m| m == method}
        @@_method_chain[method] = nil
      end
      
      def run_callback(method)
        return if @@_callbacks_running.include? method
        alias_method new_method_name(method), method 
        undef_method method 
        
        @@_callbacks_running.push method
        
        define_method method do
          call_method_chain(method)
        end          
      end

      def reg_callback(event, method, callback)
        raise UnknownEventException if (event != :before &&  event != :after)
        @@_method_chain[method] = [new_method_name(method)] unless @@_method_chain[method]
      
        if event === :before
          reg_before_callback(method, callback)
        elsif event === :after
          reg_after_callback(method, callback)
        end        
      end 

      def reg_before_callback(method, callback)
        @@_method_chain[method].insert \
           (@@_method_chain[method].index new_method_name(method)), callback
      end

      def reg_after_callback(method, callback)
        @@_method_chain[method].push callback
      end
    
    end
    
    def call_method_chain(method)
      result = nil
      @@_method_chain[method].each do |action|
        if action == self.class.new_method_name(method) 
          result = self.send(action)
        else
          self.send(action)
        end
      end
      return result 
    end
    
  end
end