【问题标题】:Cross-cutting logging in RubyRuby 中的横切日志记录
【发布时间】:2010-01-25 21:42:50
【问题描述】:

我正在尝试从外部将日志记录添加到方法中(面向方面​​的风格)

class A
  def test
    puts "I'm Doing something..."
  end
end

class A # with logging!
  alias_method :test_orig, :test
  def test
    puts "Log Message!"
    test_orig
  end
end

a = A.new
a.test

上面的工作正常,除了如果我需要再次为该方法设置别名,它会进入无限循环。我想要更像超级的东西,我可以根据需要多次扩展它,并且每个扩展都带有其父级的别名。

【问题讨论】:

    标签: ruby metaprogramming aop


    【解决方案1】:

    另一种选择是使用未绑定的方法:

    class A
      original_test = instance_method(:test)
      define_method(:test) do
        puts "Log Message!"
        original_test.bind(self).call
      end
    end
    
    class A
      original_test = instance_method(:test)
      counter = 0
      define_method(:test) do
        counter += 1
        puts "Counter = #{counter}"
        original_test.bind(self).call
      end
    end
    
    irb> A.new.test
    Counter = 1
    Log Message!
    #=> #....
    irb> A.new.test
    Counter = 2
    Log Message!
    #=> #.....
    

    如果你想创建一个类方法add_logging 或你有什么,它的优点是它不会用额外的方法名称污染命名空间,并且相当容易抽象。

    class Module
      def add_logging(*method_names)
        method_names.each do |method_name|
          original_method = instance_method(method_name)
          define_method(method_name) do |*args,&blk|
            puts "logging #{method_name}"
            original_method.bind(self).call(*args,&blk)
          end
        end
      end
    end
    
    class A
      add_logging :test
    end
    

    或者,如果您希望能够在没有大量样板的情况下做一堆方面,您可以编写一个方法来编写方面添加方法!

    class Module
      def self.define_aspect(aspect_name, &definition)
        define_method(:"add_#{aspect_name}") do |*method_names|
          method_names.each do |method_name|
            original_method = instance_method(method_name)
            define_method(method_name, &(definition[method_name, original_method]))
          end
        end
      end
      # make an add_logging method
      define_aspect :logging do |method_name, original_method|
        lambda do |*args, &blk|
          puts "Logging #{method_name}"
          original_method.bind(self).call(*args, &blk)
        end
      end
      # make an add_counting method
      global_counter = 0
      define_aspect :counting do |method_name, original_method|
         local_counter = 0
         lambda do |*args, &blk|
           global_counter += 1
           local_counter += 1
           puts "Counters: global@#{global_counter}, local@#{local_counter}"
           original_method.bind(self).call(*args, &blk)
         end
      end      
    end
    
    class A
      def test 
        puts "I'm Doing something..." 
      end
      def test1 
        puts "I'm Doing something once..." 
      end
      def test2
        puts "I'm Doing something twice..." 
        puts "I'm Doing something twice..." 
      end
      def test3
        puts "I'm Doing something thrice..." 
        puts "I'm Doing something thrice..." 
        puts "I'm Doing something thrice..." 
      end
      def other_tests
        puts "I'm Doing something else..." 
      end
    
      add_logging :test, :test2, :test3
      add_counting :other_tests, :test1, :test3
    end
    

    【讨论】:

    • “这样做的好处是它不会用其他方法名称污染命名空间”:是的!鉴于这种污染首先是 OPs 问题的根源,这是一个相当引人注目的优势。
    • 等等...所以我原来的类必须首先将它的方法写成未绑定的?这有点烦人
    • @Sean Clark Hess:不,一点也不。在最后一个示例中,我添加方面的所有方法(test、test1、test2、test3、other_tests)只是以正常方式定义的正常方法。
    【解决方案2】:

    第一选择:子类而不是覆盖:

    class AWithLogging < A\
      def test
        puts "Log Message!"
        super
      end
    end
    

    第二种选择:更仔细地命名您的原始方法:

    class A # with logging!
      alias_method :test_without_logging, :test
      def test
        puts "Log Message!"
        test_without_logging
      end
    end
    

    然后另一个方面使用不同的原始名称:

    class A # with frobnication!
      alias_method :test_without_frobnication, :test
      def test
        Frobnitz.frobnicate(self)
        test_without_frobnication
      end
    end
    

    【讨论】:

    • 问题是,这些方法首先不需要有名称。请参阅@rampion 的答案以获得更多更好的解决方案。
    • 是的,我也更喜欢他的解决方案。谢谢。
    猜你喜欢
    • 1970-01-01
    • 2015-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-22
    • 2021-10-31
    • 2011-05-10
    相关资源
    最近更新 更多