在红宝石中与周围方面的怪异

时间:2016-12-20 14:24:23

标签: ruby metaprogramming

我是ruby的新手

尝试写一个方面。我的代码如下

我的代码如下

module Utils
  module Aspects

    def self.included(base)
      base.extend(self)
    end

    def around_aspect(method_name, before_proc, after_proc)

      code = %Q[
        def #{method_name} *args, &block
          #{before_proc.call}
          old_#{method_name} *args, &block
          #{after_proc.call}
        end
      ]

      class_eval %Q[
        alias_method :old_#{method_name}, :#{method_name}
      ]

      class_eval code
    end

    # def before_aspect method_name, before_proc
    #   around_aspect method_name, before_proc, ->(){}
    # end
    #
    # def after_aspect method_name, after_proc
    #   around_aspect method_name, ->(){}, after_proc
    # end
  end
end


class Test
  include Utils::Aspects

  def test
    puts 'test'
  end

  before = ->(){puts 'before'}
  after = ->(){puts 'after'}
  around_aspect :test,before,after
end

Test.new.test

问题在于,当我Test.new.test时,我希望它能打印出来 之前,测试和之后"为了。但是现在它打印"之前,之后和测试"

2 个答案:

答案 0 :(得分:4)

  

问题在于,当我Test.new.test时,我希望它能打印出来   之前,测试和“按顺序。但现在它打印”之前,之后和测试“

不,它没有。致电Test.new.test时,它只会打印test。定义包装方法时,即在调用before时,会打印afteraround_advice

尝试在调用puts和调用around_advice之间设置Test.new.test(并尝试多次调用test)以观察:

puts '______________________'

Test.new.test
Test.new.test

# before
# after
# ______________________
# test
# test

在定义方法时,您只调用lambdas一次:

  code = %Q[
    def #{method_name} *args, &block
      #{before_proc.call}
#     ^^^^^^^^^^^^^^^^^^^
      old_#{method_name} *args, &block
      #{after_proc.call}
#     ^^^^^^^^^^^^^^^^^^
    end
  ]

每次调用方法时都需要调用它们:

  code = %Q[
    def #{method_name} *args, &block
      before_proc.call
      old_#{method_name} *args, &block
      after_proc.call
    end
  ]

然而,使用Module#prepend会更容易,毕竟,这就是它的用途:

module Aspects
  refine Module do
    def around_aspect(method_name, before_proc, after_proc)
      prepend(Module.new do
        define_method(method_name) do |*args, &block|
          before_proc.()
          super(*args, &block)
          after_proc.()
        end
      end)
    end
  end
end

class Test
  using Aspects

  def test
    puts 'test'
  end

  before = -> {puts 'before'}
  after = -> {puts 'after'}
  around_aspect :test, before, after
end

答案 1 :(得分:0)

把我的代码放在这里。这就是我最终实现我想要做的事情,如上所述,module.prepend是另一种方式

module Utils
  module Aspects

    def self.included(base)
      base.extend(self)
    end

    def around_aspect(method_name, before_proc, after_proc)

      new_method_name = Random.new_seed.to_s

      alias_method :"#{new_method_name}", :"#{method_name}"

      define_method "#{method_name}" do |*args, &block|
        before_proc.call
        send(:"#{new_method_name}", *args, &block)
        after_proc.call
      end
    end

    def before_aspect method_name, before_proc
      around_aspect method_name, before_proc, ->(){}
    end

    def after_aspect method_name, after_proc
      around_aspect method_name, ->(){}, after_proc
    end
  end
end


class Test
  include Utils::Aspects

  def test

    puts 'test'
  end

  before = ->(){puts 'before'}
  after = ->(){puts 'after'}

  before_aspect :test, before
  after_aspect :test, after
end


Test.new.test