Module: BBLib::Hooks
- Defined in:
- lib/bblib/core/mixins/hooks.rb
Overview
Adds method hooking capability to a class. Intended to be used as a mixin.
Instance Method Summary collapse
- #_add_hooked_method(type, hook, method) ⇒ Object
- #_defining_hook? ⇒ Boolean
-
#_hook_after_method(method, hook, opts = {}) ⇒ Object
Current opts: send_args - Sends the arguments of the method to the after method.
-
#_hook_before_method(method, hook, opts = {}) ⇒ Object
Current opts: send_args - Sends the arguments of the method to the before hook.
- #_hook_method(method, force: false) ⇒ Object
- #_hooked_methods ⇒ Object
- #_hooks ⇒ Object
-
#_superclass_hooks ⇒ Object
def _hook_all _hooks.each do |type, hooks| hooks.each do |hook, data| data.each do |method| _hook_method(method) end end end end.
- #method_added(method) ⇒ Object
- #singleton_method_added(method) ⇒ Object
Instance Method Details
#_add_hooked_method(type, hook, method) ⇒ Object
71 72 73 74 75 76 |
# File 'lib/bblib/core/mixins/hooks.rb', line 71 def _add_hooked_method(type, hook, method) hook = hook.object_id if hook.is_a?(Proc) history = _hooked_methods[type] history[hook] = {} unless history[hook] history[hook][method] = instance_method(method) end |
#_defining_hook? ⇒ Boolean
78 79 80 |
# File 'lib/bblib/core/mixins/hooks.rb', line 78 def _defining_hook? @_defining_hook ||= false end |
#_hook_after_method(method, hook, opts = {}) ⇒ Object
Current opts: send_args - Sends the arguments of the method to the after method. send_value - Sends the return value of the method to the hook method. send_value_ary - Sends the return value of the method to the hook method
> with the splat operator.
modify_value - Opts must also include one of the two above. Passes the returned
> value of the method to the hook and returns the hooks value
> rather than the original methods value.
send_all - Sends a hash containing the args, method and value (return).
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 |
# File 'lib/bblib/core/mixins/hooks.rb', line 121 def _hook_after_method(method, hook, opts = {}) return false if method == hook _add_hooked_method(:after, hook, method) original = instance_method(method) @_defining_hook = true define_method(method) do |*args, &block| rtr = original.bind(self).call(*args, &block) if opts[:send_args] (hook.is_a?(Proc) ? hook : method(hook)).call(*args) elsif opts[:send_return] || opts[:send_value] result = (hook.is_a?(Proc) ? hook : method(hook)).call(rtr) rtr = result if opts[:modify_value] || opts[:modify_return] elsif opts[:send_return_ary] || opts[:send_value_ary] result = (hook.is_a?(Proc) ? hook : method(hook)).call(*rtr) rtr = result if opts[:modify_value] || opts[:modify_return] elsif opts[:send_all] result = (hook.is_a?(Proc) ? hook : method(hook)).call(args: args, value: rtr, method: method) else (hook.is_a?(Proc) ? hook : method(hook)).call end rtr end @_defining_hook = false true end |
#_hook_before_method(method, hook, opts = {}) ⇒ Object
Current opts: send_args - Sends the arguments of the method to the before hook. modify_args - Replaces the original args with the returned value of the send_method - Sends the method name as an argument to the hooked method.
before hook method.
try_first - Sends the args to the desired hook first and if the result
is non-nil, the result is sent instead of calling the hooked
method.
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/bblib/core/mixins/hooks.rb', line 90 def _hook_before_method(method, hook, opts = {}) return false if method == hook _add_hooked_method(:before, hook, method) original = instance_method(method) @_defining_hook = true define_method(method) do |*args, &block| if opts[:send_args] || opts[:send_arg] || opts[:modify_args] || opts[:send_method] || opts[:try_first] margs = args margs = [method] + args if opts[:send_method] margs = args + [opts[:add_args]].flatten(1) if opts[:add_args] result = (hook.is_a?(Proc) ? hook : method(hook)).call(*margs) return result if result && opts[:try_first] args = result if opts[:modify_args] else hook.is_a?(Proc) ? hook.call : method(hook).call end original.bind(self).call(*args, &block) end @_defining_hook = false true end |
#_hook_method(method, force: false) ⇒ Object
33 34 35 36 37 38 39 40 41 42 |
# File 'lib/bblib/core/mixins/hooks.rb', line 33 def _hook_method(method, force: false) return false if _defining_hook? [:before, :after].each do |hook_type| _hooks[hook_type].find_all { |hook, data| data[:methods].include?(method) || data[:opts][:block] && hook == method }.to_h.each do |hook, data| hook = data[:opts][:block] if data[:opts][:block] next if !force && _hooked_methods[hook_type] && _hooked_methods[hook_type][hook.is_a?(Proc) ? hook.object_id : hook] && _hooked_methods[hook_type][hook.is_a?(Proc) ? hook.object_id : hook].include?(method) send("_hook_#{hook_type}_method", method, hook, data[:opts]) end end end |
#_hooked_methods ⇒ Object
67 68 69 |
# File 'lib/bblib/core/mixins/hooks.rb', line 67 def _hooked_methods @_hooked_methods ||= { before: {}, after: {} } end |
#_hooks ⇒ Object
63 64 65 |
# File 'lib/bblib/core/mixins/hooks.rb', line 63 def _hooks @_hooks ||= _superclass_hooks end |
#_superclass_hooks ⇒ Object
def _hook_all
_hooks.each do |type, hooks|
hooks.each do |hook, data|
data[:methods].each do |method|
_hook_method(method)
end
end
end
end
54 55 56 57 58 59 60 61 |
# File 'lib/bblib/core/mixins/hooks.rb', line 54 def _superclass_hooks hooks = { before: {}, after: {} } ancestors.reverse.each do |ancestor| next if ancestor == self hooks = hooks.deep_merge(ancestor.send(:_hooks)) if ancestor.respond_to?(:_hooks) end hooks end |
#method_added(method) ⇒ Object
17 18 19 20 21 22 23 |
# File 'lib/bblib/core/mixins/hooks.rb', line 17 def method_added(method) if _defining_hook? @_defining_hook = false else _hook_method(method) end end |
#singleton_method_added(method) ⇒ Object
25 26 27 28 29 30 31 |
# File 'lib/bblib/core/mixins/hooks.rb', line 25 def singleton_method_added(method) if _defining_hook? @_defining_hook = false else self.singleton_class.send(:_hook_method, method, force: true) if self.singleton_class.respond_to?(:_hook_method) end end |