Module: TinyHooks::ClassMethods

Defined in:
lib/tiny_hooks.rb

Overview

Class methods

Instance Method Summary collapse

Instance Method Details

#define_hook(kind, target, hook_method_name = nil, terminator: :abort, if: nil, class_method: false, &block) ⇒ Object

Define hook with kind and target method

Parameters:

  • kind (Symbol, String)

    the kind of the hook, possible values are: :before, :after and :around

  • target (Symbol, String)

    the name of the targeted method

  • hook_method_name (Symbol, String) (defaults to: nil)

    the name of a method which should be called as a hook

  • terminator (Symbol) (defaults to: :abort)

    choice for terminating execution, default is throwing abort symbol

  • if (Symbol) (defaults to: nil)

    condition to determine if it should define callback. Block is evaluated in context of self

  • class_method (Boolean) (defaults to: false)

    treat target as class method

Raises:

  • (ArgumentError)


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
# File 'lib/tiny_hooks.rb', line 54

def define_hook(kind, target, hook_method_name = nil, terminator: :abort, if: nil, class_method: false, &block) # rubocop:disable Naming/MethodParameterName
  raise ArgumentError, 'You must provide a block or hook_method_name' unless block || hook_method_name
  raise ArgumentError, 'terminator must be one of the following: :abort or :return_false' unless %i[abort return_false].include? terminator.to_sym
  raise TinyHooks::TargetError, "Hook for #{target} is not allowed" if @_targets != UNDEFINED_TARGETS && !@_targets.include?(target)

  if class_method
    is_private = private_methods.include?(target.to_sym)

    begin
      original_method = @_public_only ? public_method(target) : method(target)
    rescue NameError => e
      raise unless e.message.include?('private')

      raise TinyHooks::PrivateError, "Public only mode is on and hooks for private methods (#{target} for this time) are not available."
    end
    @_class_originals[target.to_sym] = original_method unless @_class_originals[target.to_sym]

    block ||= -> { __send__(hook_method_name) }
    body = method_body(kind, original_method, terminator, binding.local_variable_get(:if), &block)
    singleton_class.class_eval do
      undef_method(target)
      define_method(target, &body)
      private target if is_private
    end
  else # instance method
    is_private = private_instance_methods.include?(target.to_sym)

    begin
      original_method = @_public_only ? public_instance_method(target) : instance_method(target)
    rescue NameError => e
      raise unless e.message.include?('private')

      raise TinyHooks::PrivateError, "Public only mode is on and hooks for private methods (#{target} for this time) are not available."
    end
    @_originals[target.to_sym] = original_method unless @_originals[target.to_sym]

    block ||= -> { __send__(hook_method_name) }

    undef_method(target)
    define_method(target, &method_body(kind, original_method, terminator, binding.local_variable_get(:if), &block))
    private target if is_private
  end
end

#include_private!Object

Disable public only mode



140
141
142
# File 'lib/tiny_hooks.rb', line 140

def include_private!
  @_public_only = false
end

#public_only!Object

Enable public only mode



135
136
137
# File 'lib/tiny_hooks.rb', line 135

def public_only!
  @_public_only = true
end

#restore_original(target, class_method: false) ⇒ Object

Restore original method

Parameters:

  • target (Symbol, String)
  • class_method (Boolean) (defaults to: false)

    treat target as class method



102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/tiny_hooks.rb', line 102

def restore_original(target, class_method: false)
  if class_method
    original_method = @_class_originals[target.to_sym] || method(target)
    singleton_class.class_eval do
      undef_method(target)
      define_method(target, original_method)
    end
  else
    original_method = @_originals[target.to_sym] || instance_method(target)
    undef_method(target)
    define_method(target, original_method)
  end
end

#target!(include_pattern: nil, exclude_pattern: nil) ⇒ Object

Defines target for hooks

Parameters:

  • include_pattern (Regexp) (defaults to: nil)
  • exclude_pattern (Regexp) (defaults to: nil)

Raises:

  • (ArgumentError)


119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/tiny_hooks.rb', line 119

def target!(include_pattern: nil, exclude_pattern: nil)
  raise ArgumentError if include_pattern.nil? && exclude_pattern.nil?

  candidates = @_public_only ? instance_methods : instance_methods + private_instance_methods
  candidates += @public_only ? methods : methods + private_methods
  @_targets = if include_pattern && exclude_pattern
                targets = candidates.grep(include_pattern)
                targets.grep_v(exclude_pattern)
              elsif include_pattern
                candidates.grep(include_pattern)
              else
                candidates.grep_v(exclude_pattern)
              end
end