Search code examples
rubymodulecallbackmixins

Ruby callback when any module is included


I know that we can define the included callback for any individual module.

Is there any way to define a callback that is invoked whenever any module gets included in another module or class? The callback would then preferably have access to both the module included, and the class/module where it is included.


Solution

  • I cannot think or find a builtin way in Ruby to do it.

    One alternative would be to monkey patch the Module class directly to create the callback. To do it we can add some wrapper methods around the original methods include and extend to force the execution of our defined callbacks each time the include or extend methods are called.

    Something along the following lines should work:

    class Module
    
      def self.before
        m_include = instance_method(:include)
        m_extend = instance_method(:extend)
    
        define_method(:include) do |*args, &block|
          included_callback(args[0])
          m_include.bind(self).call(*args, &block)
        end
    
        define_method(:extend) do |*args, &block|
          extend_callback(args[0])
          m_extend.bind(self).call(*args, &block)
        end
      end
    
      def included_callback(mod_name)
        puts "#{self} now has included Module #{mod_name}"
      end
    
      def extend_callback(mod_name)
        puts "#{self} now has extended Module #{mod_name}"
      end
    
      before
    end
    

    An example to test that it works:

    module Awesome
    
      def bar
        puts "bar"
      end
    
      def self.baz
        puts "baz"
      end
    end
    
    class TestIncludeAwesome
      include Awesome
    end
    
    class TestExtendAwesome
      extend Awesome
    end
    

    The example code should print as output the following:

    > TestIncludeAwesome now has included Module Awesome
    > TestExtendAwesome now has extended Module Awesome