2015-04-22 13 views
5

To moja implementacja do opracowania sposobu na uruchomienie kodu, zanim wszystkie metody w modeluwezwanie przed metod w modelu Ruby

Wezwanie „before_hook: months_used” metoda musi być na dole klasy do ExecutionHooks można uzyskać instance_method załadowane do modułu. Chciałbym załadować metody instancji na najwyższym

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 
+0

Masz na myśli, że chcesz zadzwonić before_hook na górę swojej klasy? –

+0

Tak @FrederickCheung –

Odpowiedz

4

Możesz to zrobić z prepend. prepend jest jak include, ponieważ dodaje moduł do przodków klasy, jednak zamiast dodawać go po klasie, dodaje go wcześniej.

Oznacza to, że jeśli metoda istnieje zarówno w module, jak i w klasie poprzedzającej, wówczas implementacja modułu jest wywoływana jako pierwsza (i opcjonalnie może wywołać super, jeśli chce wywołać klasę podstawową).

Pozwala to napisać moduł haki tak:

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 

rzeczywistego użytkowania będzie prawdopodobnie chcesz, aby schować ten moduł gdzieś tak, że każde wywołanie before nie utworzyć nowy moduł, ale to tylko szczegóły wykonania

+0

dzięki. Spróbuję przetestować twój kod i przesłać opinię –

+0

To działało idealnie i świetna implementacja. –

+0

Umm .. Wydaje się, że nie działa dla mnie. Próbowałem w 'irb' (Ruby-2.0.0-p247). Wywołanie 'Example.new.foo' wydrukuje tylko' before foo' 'in foo'. czy robię coś źle? –

1

Zamiast przedefiniowanie metody Dzwoniąc before_hook, można zastąpić hak method_added do Poprzedź swój przed hakami sposobu tuż po tym jak został zdefiniowany. W ten sposób twoje połączenia before_hook mogą być (faktycznie muszą być) umieszczone na górze definicji klasy.

1

@rathrio To jest moja implementacja za pomocą metody method_added, którą rozmawiałeś. Dzięki

module ExecutionHooks 

def validation 
    p "works1" 
end 

def self.included(base) 
    base.send :extend, ClassMethods 
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