2017-03-22 12 views
3

我有進入了一個方法,當登錄方法類和退出,像這樣:
def methodName1(args) @logger.debug(">>#{callee}") ... @logger.debug("<<#{callee}") end
使用元編程包圍在一個類中的方法

def methodName2(args) @logger.debug(">>#{callee}") ... @logger.debug("<<#{callee}") end

我在想,如果有一個元編程的方式包圍與記錄方法電話? 這將涉及識別我想先包圍並圍繞它們的課程中的所有方法。

A

回答

1

我想這個解決方案應該幫助:

class Foo 
    def initialize 
     (self.methods - Object.methods).each do |method| 
      # we need to make alias for method 
      self.class.send(:alias_method, "#{method}_without_callback", method) 
      # save method params, and destroy old method 
      params = self.method(method).parameters.map(&:last).join(',') 
      self.class.send(:undef_method, method) 
      # creating new method with old name, and pass params to this 
      eval(" 
      self.class.send(:define_method, method) do |#{params}| 
       puts 'Call_before' 
       self.send('#{method}_without_callback', #{params}) 
       puts 'Call_after' 
      end 
      ") 
     end 
    end 
    def say_without_param 
     puts "Hello!" 
    end 
    def say_hi(par1) 
     puts "Hi, #{par1}" 
    end 

    def say_good_bye(par1, par2) 
     puts "Good bye, #{par1} #{par2}" 
    end 
end 

所以,當我們創建了一個對象,初始化後會有創建別名方法,破壞了舊的方法,並與call_backs將創建新的方法;

用例:

obj = Foo.new 

obj.say_without_param # => Call_before 
          Hello! 
          Call_after 

obj.say_hi('Johny') # => Call_before 
          Hi, Johny 
          Call_after 

obj.say_good_bye('mr.', 'Smith') => Call_before 
            Good bye, mr. Smith 
            Call_after 
+0

正是我所需要的。謝謝 – amadain

1

您可以使用around別名。別名原來的方法,然後用額外的代碼重新定義它:

alias_method :bare_methodname1, :methodname1 

def methodname1(*args) 
    @logger.debug(">>#{callee}") 
    result = bare_methodname1(*args) 
    @logger.debug("<<#{callee}") 
    result 
end 

這是不是從你現在已經有了什麼太大的不同,但是當你與方法名稱的數組結合起來,你會得到更多的你想要什麼:

method_names_ary.each do |name| 
    alias_method "bare_" + name, name 
    define_method(name) do |*args| 
    @logger.debug(">>#{callee}") 
    result = send("bare_" + name, *args) 
    @logger.debug("<<#{callee}") 
    result 
    end 
end 

任何方法之外把這個目標類中,它應該重新定義你的陣列中的所有方法,有你想要的額外的代碼。

2

我會傾向於預先考慮到類動態創建匿名模塊,其實例方法使用super調用類的同名的實例方法中,印刷後方法輸入消息以及打印方法退出消息之前。

讓我們開始創建一個具有兩個實例方法的類,其中一個在參與時通過一個塊。

class C 
    def mouse(nbr_mice, impairment) 
    puts "%d %s mice" % [nbr_mice, impairment] 
    end 
    def hubbard(*args) 
    puts yield args 
    end 
end 

C.ancestors 
    #=> [C, Object, Kernel, BasicObject] 
c = C.new 
c.mouse(3, 'blind') 
    # 3 blind mice 
c.hubbard('old', 'mother', 'hubbard') { |a| a.map(&:upcase).join(' ') } 
    # OLD MOTHER HUBBARD 

現在我們構建一個方法,創建匿名模塊並將其前置到類中。

def loggem(klass, *methods_to_log) 
    log_mod = Module.new do 
    code = methods_to_log.each_with_object('') { |m,str| str << 
     "def #{m}(*args); puts \"entering #{m}\"; super; puts \"leaving #{m}\"; end\n" } 
    class_eval code 
    end 
    klass.prepend(log_mod) 
end 

現在,我們準備調用該方法等於給該模塊將前置,並會記錄在該類的實例方法的類的參數。

loggem(C, :mouse, :hubbard) 

C.ancestors 
    #=> [#<Module:0x007fedab9ccf48>, C, Object, Kernel, BasicObject] 

c = C.new 
c.method(:mouse).owner 
    #=> #<Module:0x007fedab9ccf48> 
c.method(:mouse).super_method 
    #=> #<Method: Object(C)#mouse> 
c.method(:hubbard).owner 
    #=> #<Module:0x007fedab9ccf48> 
c.method(:hubbard).super_method 
    #=> #<Method: Object(C)#hubbard> 

c.mouse(3, 'blind') 
    # entering mouse 
    # 3 blind mice 
    # leaving mouse 
c.hubbard('old', 'mother', 'hubbard') { |a| a.map(&:upcase).join(' ') } 
    # entering hubbard 
    # OLD MOTHER HUBBARD 
    #leaving hubbard 

Module::newModule#prepend

1

您可以創建類似於def的類方法,爲您添加監視器。這會稍微改變方法定義語法,但可能會使代碼更具可讀性。

module MethodLogging 
    def log_def(method_name, &definition) 
    define_method(method_name) do |*args| 
     @logger.debug(">>#{__callee__}") 
     definition.call(*args) 
     @logger.debug("<<#{__callee__}") 
    end 
    end 
end 

class MyClass 
    extend MethodLogging 

    def initialize 
    # make sure class has @logger defined, or else include it in some way in the MethodLogging module 
    @logger = Logger.new(STDOUT) 
    end 

    def regular_method(x) 
    puts x 
    end 

    log_def :logged_method do |x| 
    puts x 
    end 
end 

instance = MyClass.new 
instance.regular_method(3) 
# hello 
instance.logged_method(3) 
# D, [2017-03-22T14:59:18.889285 #58206] DEBUG -- : >>logged_method 
# world 
# D, [2017-03-22T14:59:18.889440 #58206] DEBUG -- : <<logged_method 

除了新的方法定義語法,有一個小缺點,你得到奇怪的行爲,如果你不尊重法的元數。這種方法instance.logged_method()instance.logged_method('hello', 'world')都不會引發錯誤。

相關問題