Search code examples
rubymodelmetaprogramming

call before methods in model on ruby


This my implementation to developing way to run code before all method in your model

The call "before_hook :months_used" method need to be on bottom of class to the ExecutionHooks can get the instance_method loaded in the module. I would like to load the instance methods on top

class BalanceChart < BalanceFind
 include ExecutionHooks

 attr_reader :options

 def initialize(options = {})
  @options = options
  @begin_at = @options[:begin_at]
 end

 def months_used
  range.map{|date| I18n.l date, format: :month_year}.uniq!
 end

 before_hook :months_used
end

module ExecutionHooks

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

module ClassMethods
  def before
   @hooks.each do |name|
    m = instance_method(name)
    define_method(name) do |*args, &block|  

      return if @begin_at.blank? ## the code you can execute before methods

      m.bind(self).(*args, &block) ## your old code in the method of the class
    end
   end
  end

  def before_hook(*method_name)
   @hooks = method_name
   before
  end

  def hooks
   @hooks ||= []
  end
 end
end

Solution

  • You can do this with prepend. prepend is like include in that it adds a module to the ancestors of the class, however instead of adding it after the class it adds it before.

    This means that if a method exists both in the prepended module and the class then the module implementation is called first (and it can optionally call super if it wants to call the base class).

    This allows you to write a hooks module like so:

    module Hooks
      def before(*method_names)
        to_prepend = Module.new do
          method_names.each do |name| 
            define_method(name) do |*args, &block|
              puts "before #{name}"
              super(*args,&block)
            end
          end
        end
        prepend to_prepend
      end
    end
    
    
    class Example
      extend Hooks
      before :foo, :bar
    
      def foo
        puts "in foo"
      end
      def bar
        puts "in bar"
      end
    end
    

    In real use you would probably want to stash that module somewhere so that each call to before doesn't create a new module but that is just an inplementation detail