Module: DataMapper::Hook::ClassMethods

### Home Classes Methods

### In Files

- lib/dm-core/support/hook.rb

### Methods

- #after
- #after_class_method
- #args_for
- #before
- #before_class_method
- #class_hooks
- #define_advised_method
- #define_hook_stack_execution_methods
- #hook_method_name
- #hooks_with_scope
- #inline_call
- #install_hook
- #instance_hooks
- #method_with_scope
- #process_method_added
- #quote_method
- #register_class_hooks
- #register_hook
- #register_instance_hooks
- #registered_as_hook?
- #reset_hook!

### Included Modules

- DataMapper::Assertions

### Files

- LICENSE
- README.rdoc

### Class/Module Index

Quicksearch

- DataMapper
- DataMapper::Adapters
- DataMapper::Adapters::AbstractAdapter
- DataMapper::Adapters::InMemoryAdapter
- DataMapper::Assertions
- DataMapper::Associations
- DataMapper::Associations::ManyToMany
- DataMapper::Associations::ManyToMany::Collection
- DataMapper::Associations::ManyToMany::Relationship
- DataMapper::Associations::ManyToOne
- DataMapper::Associations::ManyToOne::Relationship
- DataMapper::Associations::OneToMany
- DataMapper::Associations::OneToMany::Collection
- DataMapper::Associations::OneToMany::Relationship
- DataMapper::Associations::OneToOne
- DataMapper::Associations::OneToOne::Relationship
- DataMapper::Associations::Relationship
- DataMapper::Chainable
- DataMapper::Collection
- DataMapper::Deprecate
- DataMapper::DescendantSet
- DataMapper::Equalizer
- DataMapper::Ext
- DataMapper::Ext::Array
- DataMapper::Ext::Hash
- DataMapper::Ext::Module
- DataMapper::Ext::Object
- DataMapper::Ext::String
- DataMapper::Hook
- DataMapper::Hook::ClassMethods
- DataMapper::IdentityMap
- DataMapper::ImmutableDeletedError
- DataMapper::ImmutableError
- DataMapper::IncompleteModelError
- DataMapper::Inflector
- DataMapper::Inflector::Inflections
- DataMapper::LocalObjectSpace
- DataMapper::Logger
- DataMapper::Mash
- DataMapper::Model
- DataMapper::Model::Hook
- DataMapper::Model::Hook::MethodCommand
- DataMapper::Model::Hook::Methods
- DataMapper::Model::Hook::ProcCommand
- DataMapper::Model::Is
- DataMapper::Model::Property
- DataMapper::Model::Relationship
- DataMapper::Model::Scope
- DataMapper::NamingConventions
- DataMapper::NamingConventions::Field
- DataMapper::NamingConventions::Field::Underscored
- DataMapper::NamingConventions::Field::UnderscoredAndPluralized
- DataMapper::NamingConventions::Field::UnderscoredAndPluralizedWithoutModule
- DataMapper::NamingConventions::Field::Yaml
- DataMapper::NamingConventions::Resource
- DataMapper::NamingConventions::Resource::Underscored
- DataMapper::NamingConventions::Resource::UnderscoredAndPluralized
- DataMapper::NamingConventions::Resource::UnderscoredAndPluralizedWithoutLeadingModule
- DataMapper::NamingConventions::Resource::UnderscoredAndPluralizedWithoutModule
- DataMapper::NamingConventions::Resource::Yaml
- DataMapper::ObjectNotFoundError
- DataMapper::OrderedSet
- DataMapper::OrderedSet::Cache
- DataMapper::OrderedSet::Cache::API
- DataMapper::PersistenceError
- DataMapper::PluginNotFoundError
- DataMapper::Property
- DataMapper::Property::Binary
- DataMapper::Property::Boolean
- DataMapper::Property::Class
- DataMapper::Property::Date
- DataMapper::Property::DateTime
- DataMapper::Property::Decimal
- DataMapper::Property::Discriminator
- DataMapper::Property::Discriminator::Model
- DataMapper::Property::Float
- DataMapper::Property::Integer
- DataMapper::Property::Lookup
- DataMapper::Property::Numeric
- DataMapper::Property::Object
- DataMapper::Property::PassThroughLoadDump
- DataMapper::Property::Serial
- DataMapper::Property::String
- DataMapper::Property::Text
- DataMapper::Property::Time
- DataMapper::Property::Typecast
- DataMapper::Property::Typecast::Numeric
- DataMapper::Property::Typecast::Time
- DataMapper::PropertySet
- DataMapper::Query
- DataMapper::Query::Conditions
- DataMapper::Query::Conditions::AbstractComparison
- DataMapper::Query::Conditions::AbstractOperation
- DataMapper::Query::Conditions::AndOperation
- DataMapper::Query::Conditions::Comparison
- DataMapper::Query::Conditions::EqualToComparison
- DataMapper::Query::Conditions::FlattenOperation
- DataMapper::Query::Conditions::GreaterThanComparison
- DataMapper::Query::Conditions::GreaterThanOrEqualToComparison
- DataMapper::Query::Conditions::InclusionComparison
- DataMapper::Query::Conditions::LessThanComparison
- DataMapper::Query::Conditions::LessThanOrEqualToComparison
- DataMapper::Query::Conditions::LikeComparison
- DataMapper::Query::Conditions::NotOperation
- DataMapper::Query::Conditions::NullOperation
- DataMapper::Query::Conditions::Operation
- DataMapper::Query::Conditions::OrOperation
- DataMapper::Query::Conditions::RegexpComparison
- DataMapper::Query::Conditions::RelationshipHandler
- DataMapper::Query::Direction
- DataMapper::Query::Operator
- DataMapper::Query::Path
- DataMapper::Query::Sort
- DataMapper::RelationshipSet
- DataMapper::Repository
- DataMapper::RepositoryNotSetupError
- DataMapper::Resource
- DataMapper::Resource::PersistenceState
- DataMapper::Resource::PersistenceState::Clean
- DataMapper::Resource::PersistenceState::Deleted
- DataMapper::Resource::PersistenceState::Dirty
- DataMapper::Resource::PersistenceState::Immutable
- DataMapper::Resource::PersistenceState::Persisted
- DataMapper::Resource::PersistenceState::Transient
- DataMapper::SaveFailureError
- DataMapper::Spec
- DataMapper::Spec::Adapters
- DataMapper::Spec::Adapters::Adapter
- DataMapper::Spec::Adapters::Helpers
- DataMapper::Spec::Adapters::InMemoryAdapter
- DataMapper::Spec::CollectionHelpers
- DataMapper::Spec::CollectionHelpers::GroupMethods
- DataMapper::Spec::Helpers
- DataMapper::Spec::PendingHelpers
- DataMapper::Subject
- DataMapper::SubjectSet
- DataMapper::SubjectSet::NameCache
- DataMapper::Undefined
- DataMapper::UnknownRelationshipError
- DataMapper::UnsavedParentError
- DataMapper::UpdateConflictError
- Blog
- Blog::Article
- ChildSubType
- CounterAdapter
- Heffalump
- Kernel
- LazyArray
- Object
- OtherComparison
- Pathname
- SubType
- Symbol

No matching classes.

# 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<Symbol> the name of the instance method to inject after @param method_sym<Symbol> the name of the method to run after the

    target_method

@param block<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
    def after(target_method, method_sym = nil, &block)
      install_hook :after, target_method, method_sym, :instance, &block
    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<Symbol> the name of the class method to inject after @param method_sym<Symbol> the name of the method to run after the target_method @param block<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
    def after_class_method(target_method, method_sym = nil, &block)
      install_hook :after, target_method, method_sym, :class, &block
    end

args_for(method) click to toggle source

--- Helpers ---

    # File lib/dm-core/support/hook.rb, line 376
    def args_for(method)
      if method.arity == 0
        "&block"
      elsif method.arity > 0
        "_" << (1 .. method.arity).to_a.join(", _") << ", &block"
      elsif (method.arity + 1) < 0
        "_" << (1 .. (method.arity).abs - 1).to_a.join(", _") << ", *args, &block"
      else
        "*args, &block"
      end
    end

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

Inject code that executes before the target instance method.

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

    target_method

@param block<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
    def before(target_method, method_sym = nil, &block)
      install_hook :before, target_method, method_sym, :instance, &block
    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<Symbol> the name of the class method to inject before @param method_sym<Symbol> the name of the method to run before the

    target_method

@param block<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
    def before_class_method(target_method, method_sym = nil, &block)
      install_hook :before, target_method, method_sym, :class, &block
    end

class_hooks() click to toggle source

    # File lib/dm-core/support/hook.rb, line 148
    def class_hooks
      self.const_get("CLASS_HOOKS")
    end

define_advised_method(target_method, scope) click to toggle source

    # File lib/dm-core/support/hook.rb, line 275
    def define_advised_method(target_method, scope)
      args = args_for(method_with_scope(target_method, scope))

      renamed_target = hook_method_name(target_method, 'hookable_', 'before_advised')

      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

      if scope == :instance && !instance_methods(false).any? { |m| m.to_sym == target_method }
        send(:alias_method, renamed_target, target_method)

        proxy_module = Module.new
        proxy_module.class_eval(source, __FILE__, __LINE__)
        self.send(:include, proxy_module)
      else
        source = %{alias_method :#{renamed_target}, :#{target_method}\n#{source}}
        source = %{class << self\n#{source}\nend} if scope == :class
        class_eval(source, __FILE__, __LINE__)
      end
    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
    def define_hook_stack_execution_methods(target_method, scope)
      unless registered_as_hook?(target_method, scope)
        raise ArgumentError, "#{target_method} has not be registered as a hookable #{scope} method"
      end

      hooks = hooks_with_scope(scope)

      before_hooks = hooks[target_method][:before]
      before_hooks = before_hooks.map{ |info| inline_call(info, scope) }.join("\n")

      after_hooks  = hooks[target_method][:after]
      after_hooks  = after_hooks.map{ |info| inline_call(info, scope) }.join("\n")

      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           #{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
    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
    def hook_method_name(target_method, prefix, suffix)
      target_method = target_method.to_s

      case target_method[-1,1]
        when '?' then "#{prefix}_#{target_method[0..-2]}_ques_#{suffix}"
        when '!' then "#{prefix}_#{target_method[0..-2]}_bang_#{suffix}"
        when '=' then "#{prefix}_#{target_method[0..-2]}_eq_#{suffix}"
        # I add a _nan_ suffix here so that we don't ever encounter
        # any naming conflicts.
        else "#{prefix}_#{target_method[0..-1]}_nan_#{suffix}"
      end
    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
    def hooks_with_scope(scope)
      case scope
        when :class    then class_hooks
        when :instance then instance_hooks
        else raise ArgumentError, 'You need to pass :class or :instance as scope'
      end
    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
    def inline_call(method_info, scope)
      DataMapper::Hook::ClassMethods.hook_scopes << method_info[:from]
      name = method_info[:name]
      if scope == :instance
        args = method_defined?(name) && instance_method(name).arity != 0 ? '*args' : ''
        %(#{name}(#{args}) if self.class <= DataMapper::Hook::ClassMethods.object_by_id(#{method_info[:from].object_id}))
      else
        args = respond_to?(name) && method(name).arity != 0 ? '*args' : ''
        %(#{name}(#{args}) if self <= DataMapper::Hook::ClassMethods.object_by_id(#{method_info[:from].object_id}))
      end
    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
    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               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

          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

instance_hooks() click to toggle source

    # File lib/dm-core/support/hook.rb, line 152
    def instance_hooks
      self.const_get("INSTANCE_HOOKS")
    end

method_with_scope(name, scope) click to toggle source

    # File lib/dm-core/support/hook.rb, line 388
    def method_with_scope(name, scope)
      case scope
        when :class    then method(name)
        when :instance then instance_method(name)
        else raise ArgumentError, 'You need to pass :class or :instance as scope'
      end
    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
    def process_method_added(method_name, scope)
      hooks_with_scope(scope).each do |target_method, hooks|
        if hooks[:before].any? { |hook| hook[:name] == method_name }
          define_hook_stack_execution_methods(target_method, scope)
        end

        if hooks[:after].any? { |hook| hook[:name] == method_name }
          define_hook_stack_execution_methods(target_method, scope)
        end
      end
    end

quote_method(name) click to toggle source

    # File lib/dm-core/support/hook.rb, line 396
    def quote_method(name)
      name.to_s.gsub(/\?$/, '_q_').gsub(/!$/, '_b_').gsub(/=$/, '_eq_')
    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<Symbol> The name of the class method that should

    be hookable

- @api public

    # File lib/dm-core/support/hook.rb, line 115
    def register_class_hooks(*hooks)
      hooks.each { |hook| register_hook(hook, :class) }
    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
    def register_hook(target_method, scope)
      if scope == :instance && !method_defined?(target_method)
        raise ArgumentError, "#{target_method} instance method does not exist"
      elsif scope == :class && !respond_to?(target_method)
        raise ArgumentError, "#{target_method} class method does not exist"
      end

      hooks = hooks_with_scope(scope)

      if hooks[target_method].nil?
        hooks[target_method] = {
          # We need to keep track of which class in the Inheritance chain the
          # method was declared hookable in. Every time a child declares a new
          # hook for the method, the hook stack invocations need to be redefined
          # in the original Class. See #define_hook_stack_execution_methods
          :before => [], :after => [], :in => self
        }

        define_hook_stack_execution_methods(target_method, scope)
        define_advised_method(target_method, scope)
      end
    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<Symbol> The name of the instance method that should

    be hookable

- @api public

    # File lib/dm-core/support/hook.rb, line 127
    def register_instance_hooks(*hooks)
      hooks.each { |hook| register_hook(hook, :instance) }
    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
    def registered_as_hook?(target_method, scope)
      ! hooks_with_scope(scope)[target_method].nil?
    end

reset_hook!(target_method, scope) click to toggle source

Not yet implemented

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

[Validate]

Generated with the Darkfish Rdoc Generator 2.