0

I have written a module to hook methods before or after an instance method call. Id like to apply these hooks to all classes that inherit from the parent class. However if I write the hook in the parent class it wont be executed, it will only be executed if it is present in the child class.

This is the module to create the hooks :

module Hooks

def self.included(base)
 base.send :extend, ClassMethods
end


module ClassMethods
  # everytime we add a method to the class we check if we must redifine it
  def method_added(method)
    if @hooker_before && (@methods_to_hook_before.include?(method)|| @methods_to_hook_after.include?(method)) && !@methods_seen.include?(method)
      hooked_method_before = instance_method(@hooker_before) if @hooker_before
      hooked_method_after =  instance_method(@hooker_after) if @hooker_after
      @methods_to_hook_before.each do |method_name|
        begin
          method_to_hook = instance_method(method_name)
        rescue NameError => e
          return
        end
        @methods_seen.push(method)

        define_method(method_name) do |*args, &block|
          hooked_method_before.bind(self).call if hooked_method_before
          method_to_hook.bind(self).(*args, &block) ## your old code in the method of the class
          hooked_method_after.bind(self).call if hooked_method_after
        end
      end
     end
   end

  def before(*methods_to_hook, hookers)
   @methods_to_hook_before = methods_to_hook
   @methods_to_hook_after = [] unless @methods_to_hook_after
   @hooker_before = hookers[:call]
   @methods_seen = []
  end

  def after(*methods_to_hook, hookers)
    @methods_to_hook_after = methods_to_hook
    @methods_to_hook_before = [] unless @methods_to_hook_before
    @hooker_after = hookers[:call]
    @methods_seen = []
  end
 end
end

module Utilities
  def before_generate
    puts("in before generate utilities")
  end
end

class Parent
  include Utilities
  include Hooks


  def generate
    puts("in generate Parent")
  end
end

class Child < Parent
  before :generate, call: :before_generate

  def generate
    puts("in child generate")
  end
end
class AnotherChild < Parent
      before :generate, call: :before_generate

      def generate
        puts("in child generate")
      end
    end

Child.new.generate() produces the desired output : in before generate utilities in child generate However I would like all class that inherits from Parent to automatically inherit from this behavior. Adding before :generate, call: :before_generate to the parent class won't do the trick. Is there maybe a way by wrapping all the child classes in the same module ? Is there a way to achieve that or shall I reproduce the before call in every single child class where I want it.

David Geismar
  • 3,152
  • 6
  • 41
  • 80

1 Answers1

0

You can use inheritable class instance variables for hooks to make them available in child classes, as described here.

mrzasa
  • 22,895
  • 11
  • 56
  • 94
  • im sorry but I dont really understand the answer you linked to. As I understand it, it creates instance variables in the subclass. I want to create a hook in the subclass, that hook would be inherited from the parent class. – David Geismar Dec 01 '18 at 10:50