Included Modules

Class Index [+]

Quicksearch

DataMapper::Hook::ClassMethods

Public Instance Methods

after(target_method, method_sym = nil, &block) click to toggle source

Inject code that executes after the target instance method.

@param target_method the name of the instance method to inject after @param method_sym the name of the method to run after the

  target_method

@param block the code to run after the target_method

@note

  Either method_sym or block is required.
  • @api public

     # File lib/dm-core/support/hook.rb, line 103
103:       def after(target_method, method_sym = nil, &block)
104:         install_hook :after, target_method, method_sym, :instance, &block
105:       end
after_class_method(target_method, method_sym = nil, &block) click to toggle source

Inject code that executes after the target class method.

@param target_method the name of the class method to inject after @param method_sym the name of the method to run after the target_method @param block the code to run after the target_method

@note

  Either method_sym or block is required.
  • @api public

    # File lib/dm-core/support/hook.rb, line 71
71:       def after_class_method(target_method, method_sym = nil, &block)
72:         install_hook :after, target_method, method_sym, :class, &block
73:       end
args_for(method) click to toggle source

— Helpers —

     # File lib/dm-core/support/hook.rb, line 376
376:       def args_for(method)
377:         if method.arity == 0
378:           "&block"
379:         elsif method.arity > 0
380:           "_" << (1 .. method.arity).to_a.join(", _") << ", &block"
381:         elsif (method.arity + 1) < 0
382:           "_" << (1 .. (method.arity).abs - 1).to_a.join(", _") << ", *args, &block"
383:         else
384:           "*args, &block"
385:         end
386:       end
before(target_method, method_sym = nil, &block) click to toggle source

Inject code that executes before the target instance method.

@param target_method the name of the instance method to inject before @param method_sym the name of the method to run before the

  target_method

@param block the code to run before the target_method

@note

  Either method_sym or block is required.
  • @api public

    # File lib/dm-core/support/hook.rb, line 87
87:       def before(target_method, method_sym = nil, &block)
88:         install_hook :before, target_method, method_sym, :instance, &block
89:       end
before_class_method(target_method, method_sym = nil, &block) click to toggle source

Inject code that executes before the target class method.

@param target_method the name of the class method to inject before @param method_sym the name of the method to run before the

  target_method

@param block the code to run before the target_method

@note

  Either method_sym or block is required.
  • @api public

    # File lib/dm-core/support/hook.rb, line 56
56:       def before_class_method(target_method, method_sym = nil, &block)
57:         install_hook :before, target_method, method_sym, :class, &block
58:       end
class_hooks() click to toggle source
     # File lib/dm-core/support/hook.rb, line 148
148:       def class_hooks
149:         self.const_get("CLASS_HOOKS")
150:       end
define_advised_method(target_method, scope) click to toggle source
     # File lib/dm-core/support/hook.rb, line 275
275:       def define_advised_method(target_method, scope)
276:         args = args_for(method_with_scope(target_method, scope))
277: 
278:         renamed_target = hook_method_name(target_method, 'hookable_', 'before_advised')
279: 
280:         source =           def #{target_method}(#{args})            retval = nil            catch(:halt) do              #{hook_method_name(target_method, 'execute_before', 'hook_stack')}(#{args})              retval = #{renamed_target}(#{args})              #{hook_method_name(target_method, 'execute_after', 'hook_stack')}(retval, #{args})              retval            end          end
281: 
282:         if scope == :instance && !instance_methods(false).any? { |m| m.to_sym == target_method }
283:           send(:alias_method, renamed_target, target_method)
284: 
285:           proxy_module = Module.new
286:           proxy_module.class_eval(source, __FILE__, __LINE__)
287:           self.send(:include, proxy_module)
288:         else
289:           source = %{alias_method :#{renamed_target}, :#{target_method}\n#{source}}
290:           source = %{class << self\n#{source}\nend} if scope == :class
291:           class_eval(source, __FILE__, __LINE__)
292:         end
293:       end
define_hook_stack_execution_methods(target_method, scope) click to toggle source

Defines two methods. One method executes the before hook stack. The other executes the after hook stack. This method will be called many times during the Class definition process. It should be called for each hook that is defined. It will also be called when a hook is redefined (to make sure that the arity hasn’t changed).

     # File lib/dm-core/support/hook.rb, line 226
226:       def define_hook_stack_execution_methods(target_method, scope)
227:         unless registered_as_hook?(target_method, scope)
228:           raise ArgumentError, "#{target_method} has not be registered as a hookable #{scope} method"
229:         end
230: 
231:         hooks = hooks_with_scope(scope)
232: 
233:         before_hooks = hooks[target_method][:before]
234:         before_hooks = before_hooks.map{ |info| inline_call(info, scope) }.join("\n")
235: 
236:         after_hooks  = hooks[target_method][:after]
237:         after_hooks  = after_hooks.map{ |info| inline_call(info, scope) }.join("\n")
238: 
239:         before_hook_name = hook_method_name(target_method, 'execute_before', 'hook_stack')
240:         after_hook_name  = hook_method_name(target_method, 'execute_after',  'hook_stack')
241: 
242:         hooks[target_method][:in].class_eval           #{scope == :class ? 'class << self' : ''}          private          remove_method :#{before_hook_name} if instance_methods(false).any? { |m| m.to_sym == :#{before_hook_name} }          def #{before_hook_name}(*args)            #{before_hooks}          end          remove_method :#{after_hook_name} if instance_methods(false).any? { |m| m.to_sym == :#{after_hook_name} }          def #{after_hook_name}(*args)            #{after_hooks}          end          #{scope == :class ? 'end' : ''}, __FILE__, __LINE__ + 1
243:       end
hook_method_name(target_method, prefix, suffix) click to toggle source

Generates names for the various utility methods. We need to do this because the various utility methods should not end in = so, while we’re at it, we might as well get rid of all punctuation.

     # File lib/dm-core/support/hook.rb, line 196
196:       def hook_method_name(target_method, prefix, suffix)
197:         target_method = target_method.to_s
198: 
199:         case target_method[1,1]
200:           when '?' then "#{prefix}_#{target_method[0..-2]}_ques_#{suffix}"
201:           when '!' then "#{prefix}_#{target_method[0..-2]}_bang_#{suffix}"
202:           when '=' then "#{prefix}_#{target_method[0..-2]}_eq_#{suffix}"
203:           # I add a _nan_ suffix here so that we don't ever encounter
204:           # any naming conflicts.
205:           else "#{prefix}_#{target_method[0..-1]}_nan_#{suffix}"
206:         end
207:       end
hooks_with_scope(scope) click to toggle source

Returns the correct HOOKS Hash depending on whether we are working with class methods or instance methods

     # File lib/dm-core/support/hook.rb, line 140
140:       def hooks_with_scope(scope)
141:         case scope
142:           when :class    then class_hooks
143:           when :instance then instance_hooks
144:           else raise ArgumentError, 'You need to pass :class or :instance as scope'
145:         end
146:       end
inline_call(method_info, scope) click to toggle source

Returns ruby code that will invoke the hook. It checks the arity of the hook method and passes arguments accordingly.

     # File lib/dm-core/support/hook.rb, line 263
263:       def inline_call(method_info, scope)
264:         DataMapper::Hook::ClassMethods.hook_scopes << method_info[:from]
265:         name = method_info[:name]
266:         if scope == :instance
267:           args = method_defined?(name) && instance_method(name).arity != 0 ? '*args' : ''
268:           %(#{name}(#{args}) if self.class <= DataMapper::Hook::ClassMethods.object_by_id(#{method_info[:from].object_id}))
269:         else
270:           args = respond_to?(name) && method(name).arity != 0 ? '*args' : ''
271:           %(#{name}(#{args}) if self <= DataMapper::Hook::ClassMethods.object_by_id(#{method_info[:from].object_id}))
272:         end
273:       end
install_hook(type, target_method, method_sym, scope, &block) click to toggle source

— Add a hook —

     # File lib/dm-core/support/hook.rb, line 307
307:       def install_hook(type, target_method, method_sym, scope, &block)
308:         assert_kind_of 'target_method', target_method, Symbol
309:         assert_kind_of 'method_sym',    method_sym,    Symbol unless method_sym.nil?
310:         assert_kind_of 'scope',         scope,         Symbol
311: 
312:         if !block_given? and method_sym.nil?
313:           raise ArgumentError, "You need to pass 2 arguments to \"#{type}\"."
314:         end
315: 
316:         if method_sym.to_s[1,1] == '='
317:           raise ArgumentError, "Methods ending in = cannot be hooks"
318:         end
319: 
320:         unless [ :class, :instance ].include?(scope)
321:           raise ArgumentError, 'You need to pass :class or :instance as scope'
322:         end
323: 
324:         if registered_as_hook?(target_method, scope)
325:           hooks = hooks_with_scope(scope)
326: 
327:           #if this hook is previously declared in a sibling or cousin we must move the :in class
328:           #to the common ancestor to get both hooks to run.
329:           if !(hooks[target_method][:in] <=> self)
330:             before_hook_name = hook_method_name(target_method, 'execute_before', 'hook_stack')
331:             after_hook_name  = hook_method_name(target_method, 'execute_after',  'hook_stack')
332: 
333:             hooks[target_method][:in].class_eval               remove_method :#{before_hook_name} if instance_methods(false).any? { |m| m.to_sym == :#{before_hook_name} }              def #{before_hook_name}(*args)                super              end              remove_method :#{after_hook_name} if instance_methods(false).any? { |m| m.to_sym == :#{before_hook_name} }              def #{after_hook_name}(*args)                super              end, __FILE__, __LINE__ + 1
334: 
335:             while !(hooks[target_method][:in] <=> self) do
336:               hooks[target_method][:in] = hooks[target_method][:in].superclass
337:             end
338: 
339:             define_hook_stack_execution_methods(target_method, scope)
340:             hooks[target_method][:in].class_eval{define_advised_method(target_method, scope)}
341:           end
342:         else
343:           register_hook(target_method, scope)
344:           hooks = hooks_with_scope(scope)
345:         end
346: 
347:         #if  we were passed a block, create a method out of it.
348:         if block
349:           method_sym = "__hooks_#{type}_#{quote_method(target_method)}_#{hooks[target_method][type].length}".to_sym
350:           if scope == :class
351:             singleton_class.instance_eval do
352:               define_method(method_sym, &block)
353:             end
354:           else
355:             define_method(method_sym, &block)
356:           end
357:         end
358: 
359:         # Adds method to the stack an redefines the hook invocation method
360:         hooks[target_method][type] << { :name => method_sym, :from => self }
361:         define_hook_stack_execution_methods(target_method, scope)
362:       end
instance_hooks() click to toggle source
     # File lib/dm-core/support/hook.rb, line 152
152:       def instance_hooks
153:         self.const_get("INSTANCE_HOOKS")
154:       end
method_with_scope(name, scope) click to toggle source
     # File lib/dm-core/support/hook.rb, line 388
388:       def method_with_scope(name, scope)
389:         case scope
390:           when :class    then method(name)
391:           when :instance then instance_method(name)
392:           else raise ArgumentError, 'You need to pass :class or :instance as scope'
393:         end
394:       end
process_method_added(method_name, scope) click to toggle source

This will need to be refactored

     # File lib/dm-core/support/hook.rb, line 210
210:       def process_method_added(method_name, scope)
211:         hooks_with_scope(scope).each do |target_method, hooks|
212:           if hooks[:before].any? { |hook| hook[:name] == method_name }
213:             define_hook_stack_execution_methods(target_method, scope)
214:           end
215: 
216:           if hooks[:after].any? { |hook| hook[:name] == method_name }
217:             define_hook_stack_execution_methods(target_method, scope)
218:           end
219:         end
220:       end
quote_method(name) click to toggle source
     # File lib/dm-core/support/hook.rb, line 396
396:       def quote_method(name)
397:         name.to_s.gsub(/\?$/, '_q_').gsub(/!$/, '_b_').gsub(/=$/, '_eq_')
398:       end
register_class_hooks(*hooks) click to toggle source

Register a class method as hookable. Registering a method means that before hooks will be run immediately before the method is invoked and after hooks will be called immediately after the method is invoked.

@param hookable_method The name of the class method that should

  be hookable
  • @api public

     # File lib/dm-core/support/hook.rb, line 115
115:       def register_class_hooks(*hooks)
116:         hooks.each { |hook| register_hook(hook, :class) }
117:       end
register_hook(target_method, scope) click to toggle source

Registers a method as hookable. Registering hooks involves the following process

  • Create a blank entry in the HOOK Hash for the method.

  • Define the methods that execute the before and after hook stack. These methods will be no-ops at first, but everytime a new hook is defined, the methods will be redefined to incorporate the new hook.

  • Redefine the method that is to be hookable so that the hook stacks are invoked approprietly.

     # File lib/dm-core/support/hook.rb, line 165
165:       def register_hook(target_method, scope)
166:         if scope == :instance && !method_defined?(target_method)
167:           raise ArgumentError, "#{target_method} instance method does not exist"
168:         elsif scope == :class && !respond_to?(target_method)
169:           raise ArgumentError, "#{target_method} class method does not exist"
170:         end
171: 
172:         hooks = hooks_with_scope(scope)
173: 
174:         if hooks[target_method].nil?
175:           hooks[target_method] = {
176:             # We need to keep track of which class in the Inheritance chain the
177:             # method was declared hookable in. Every time a child declares a new
178:             # hook for the method, the hook stack invocations need to be redefined
179:             # in the original Class. See #define_hook_stack_execution_methods
180:             :before => [], :after => [], :in => self
181:           }
182: 
183:           define_hook_stack_execution_methods(target_method, scope)
184:           define_advised_method(target_method, scope)
185:         end
186:       end
register_instance_hooks(*hooks) click to toggle source

Register aninstance method as hookable. Registering a method means that before hooks will be run immediately before the method is invoked and after hooks will be called immediately after the method is invoked.

@param hookable_method The name of the instance method that should

  be hookable
  • @api public

     # File lib/dm-core/support/hook.rb, line 127
127:       def register_instance_hooks(*hooks)
128:         hooks.each { |hook| register_hook(hook, :instance) }
129:       end
registered_as_hook?(target_method, scope) click to toggle source

Is the method registered as a hookable in the given scope.

     # File lib/dm-core/support/hook.rb, line 189
189:       def registered_as_hook?(target_method, scope)
190:         ! hooks_with_scope(scope)[target_method].nil?
191:       end
reset_hook!(target_method, scope) click to toggle source

Not yet implemented

     # File lib/dm-core/support/hook.rb, line 132
132:       def reset_hook!(target_method, scope)
133:         raise NotImplementedError
134:       end

Disabled; run with --debug to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.