2017-03-22 8 views
3

私は、メソッドが入力されたときにログメソッドを持つクラスを持っているので、同様に終了しました:ロガーを持つメソッドを囲むメタプログラミング方法があった場合、私は思っていた
def methodName1(args) @logger.debug(">>#{callee}") ... @logger.debug("<<#{callee}") end
使用メタプログラミング

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

電話? これは、最初に囲まれているクラス内のすべてのメソッドを識別し、それらを囲むことになります。

答えて

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

周囲エイリアスを使用できます。エイリアスのオリジナルの方法、その後、余分なコードでそれを再定義:

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

Iが印刷後、クラスにそのインスタンスメソッドと同じ名前のクラスのインスタンスメソッドを呼び出すsuperを使用して動的に作成された匿名のモジュールを付加するように傾斜されますmethod-entryメッセージを出力し、method-exitメッセージを出力します。

最初に2つのインスタンスメソッドを持つクラスを作成します.1つはインボーク時にブロックを渡します。

ここで、匿名モジュールを作成し、それをクラスの先頭に追加するメソッドを作成します。

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')のどちらもこのメソッドでエラーを送出しません。

関連する問題