Method: DataMapper::Hook::ClassMethods#install_hook

Defined in:
lib/dm-core/support/hook.rb

#install_hook(type, target_method, method_sym, scope, &block) ⇒ Object

— Add a hook —



307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
# File 'lib/dm-core/support/hook.rb', line 307

def install_hook(type, target_method, method_sym, scope, &block)
  assert_kind_of 'target_method', target_method, Symbol
  assert_kind_of 'method_sym',    method_sym,    Symbol unless method_sym.nil?
  assert_kind_of 'scope',         scope,         Symbol

  if !block_given? and method_sym.nil?
    raise ArgumentError, "You need to pass 2 arguments to \"#{type}\"."
  end

  if method_sym.to_s[-1,1] == '='
    raise ArgumentError, "Methods ending in = cannot be hooks"
  end

  unless [ :class, :instance ].include?(scope)
    raise ArgumentError, 'You need to pass :class or :instance as scope'
  end

  if registered_as_hook?(target_method, scope)
    hooks = hooks_with_scope(scope)

    #if this hook is previously declared in a sibling or cousin we must move the :in class
    #to the common ancestor to get both hooks to run.
    if !(hooks[target_method][:in] <=> self)
      before_hook_name = hook_method_name(target_method, 'execute_before', 'hook_stack')
      after_hook_name  = hook_method_name(target_method, 'execute_after',  'hook_stack')

      hooks[target_method][:in].class_eval <<-RUBY, __FILE__, __LINE__ + 1
        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
      RUBY

      while !(hooks[target_method][:in] <=> self) do
        hooks[target_method][:in] = hooks[target_method][:in].superclass
      end

      define_hook_stack_execution_methods(target_method, scope)
      hooks[target_method][:in].class_eval{define_advised_method(target_method, scope)}
    end
  else
    register_hook(target_method, scope)
    hooks = hooks_with_scope(scope)
  end

  #if  we were passed a block, create a method out of it.
  if block
    method_sym = "__hooks_#{type}_#{quote_method(target_method)}_#{hooks[target_method][type].length}".to_sym
    if scope == :class
      singleton_class.instance_eval do
        define_method(method_sym, &block)
      end
    else
      define_method(method_sym, &block)
    end
  end

  # Adds method to the stack an redefines the hook invocation method
  hooks[target_method][type] << { :name => method_sym, :from => self }
  define_hook_stack_execution_methods(target_method, scope)
end