Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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
like image 410
Breno Perucchi Avatar asked Apr 22 '15 00:04

Breno Perucchi


2 Answers

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

like image 164
Frederick Cheung Avatar answered Sep 28 '22 07:09

Frederick Cheung


@rathrio This is my implementation using method_added that you talked. Thanks

module ExecutionHooks

  def validation
    p "works1"
  end

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

module ClassMethods
  attr_writer :hooked
  
  def hooked
    @hooked ||= []
  end

  def method_added(method)
    return if @hooks.nil? 
    return unless @hooks.include?(method)
    m = self.instance_method(method)
    unless hooked.include?(method)
      hooked << method
      define_method(method) do |*args, &block|  
        validation      
        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
  end

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

class BalanceChart < BalanceFind
 include ExecutionHooks
 before_hook :months_data, :months_used, :debits_amount, :test

  def test
    "test"
  end
end
like image 37
Breno Perucchi Avatar answered Sep 28 '22 06:09

Breno Perucchi