为Ruby模块中的每个方法调用执行代码

为Ruby模块中的每个方法调用执行代码,ruby,methods,module,metaprogramming,Ruby,Methods,Module,Metaprogramming,我正在用Ruby 1.9.2编写一个模块,它定义了几个方法。当调用这些方法中的任何一个时,我希望它们中的每一个都首先执行某个语句 module MyModule def go_forth a re-used statement # code particular to this method follows ... end def and_multiply a re-used statement # then something completely

我正在用Ruby 1.9.2编写一个模块,它定义了几个方法。当调用这些方法中的任何一个时,我希望它们中的每一个都首先执行某个语句

module MyModule
  def go_forth
    a re-used statement
    # code particular to this method follows ...
  end

  def and_multiply
    a re-used statement
    # then something completely different ...
  end
end
但是我想避免在每个方法中都显式地使用
重复使用的语句。有没有办法做到这一点


(如果重要的话,
一个重复使用的语句
会让每个方法在被调用时都打印出自己的名称。它会通过
的一些变体puts
来实现)

元编程是可能的


另一种选择是。水族馆是一个为Ruby实现面向方面编程(AOP)的框架。AOP允许您跨正常对象和方法边界实现功能。在每个方法上应用预操作的用例是AOP的基本任务。

您可以通过元编程技术来实现这一点,下面是一个示例:

module YourModule
  def included(mod)
    def mod.method_added(name)
      return if @added 
      @added = true
      original_method = "original #{name}"
      alias_method original_method, name
      define_method(name) do |*args|
        reused_statement
        result = send original_method, *args
        puts "The method #{name} called!"
        result
      end
      @added = false
    end
  end

  def reused_statement
  end
end

module MyModule
  include YourModule

  def go_forth
  end

  def and_multiply
  end
end
仅适用于ruby 1.9及更高版本


更新:并且也不能使用block,即实例方法中没有收益

您可以通过代理模块使用
方法_missing
实现它,如下所示:

module MyModule

  module MyRealModule
    def self.go_forth
      puts "it works!"
      # code particular to this method follows ...
    end

    def self.and_multiply
      puts "it works!"
      # then something completely different ...
    end
  end

  def self.method_missing(m, *args, &block)
    reused_statement
    if MyModule::MyRealModule.methods.include?( m.to_s )
      MyModule::MyRealModule.send(m)
    else
      super
    end
  end

  def self.reused_statement
    puts "reused statement"
  end
end

MyModule.go_forth
#=> it works!
MyModule.stop_forth
#=> NoMethodError...
像这样:

module M
  def self.before(*names)
    names.each do |name|
      m = instance_method(name)
      define_method(name) do |*args, &block|  
        yield
        m.bind(self).(*args, &block)
      end
    end
  end
end

module M
  def hello
    puts "yo"
  end

  def bye
    puts "bum"
  end

  before(*instance_methods) { puts "start" }
end

class C
  include M
end

C.new.bye #=> "start" "bum"
C.new.hello #=> "start" "yo"

我不知道为什么我被否决了——但是一个合适的AOP框架比元编程黑客更好。这就是OP想要达到的目标

另一个解决办法可以是:

module Aop
  def self.included(base)
    base.extend(ClassMethods)
  end

  module ClassMethods
    def before_filter(method_name, options = {})
      aop_methods = Array(options[:only]).compact
      return if aop_methods.empty?
      aop_methods.each do |m|
        alias_method "#{m}_old", m
        class_eval <<-RUBY,__FILE__,__LINE__ + 1
          def #{m}
            #{method_name}
            #{m}_old
          end
        RUBY
      end
    end
  end
end

module Bar
  def hello
    puts "Running hello world"
  end
end

class Foo
  include Bar
  def find_hello
    puts "Running find hello"
  end
  include Aop
  before_filter :find_hello, :only => :hello
end

a = Foo.new()
a.hello()
模块Aop
def自带(基本)
扩展(类方法)
结束
模块类方法
def before_filter(方法_名称,选项={})
aop_方法=数组(选项[:仅]).compact
如果aop_methods.empty返回?
aop|U方法。每个方法|
别名#u方法“#{m}_old”,m

class\u eval这正是创建的目的

使用aspector,您不需要编写样板元编程代码。您甚至可以更进一步,将公共逻辑提取到一个单独的方面类中,并对其进行独立测试

require 'aspector'

module MyModule
  aspector do
    before :go_forth, :add_multiply do
      ...
    end
  end

  def go_forth
    # code particular to this method follows ...
  end

  def and_multiply
    # then something completely different ...
  end
end

+我喜欢它。但是Ruby 1.8.7不支持它<对于M:Module`@fl00r,要使其在1.8.7中工作,您需要更改的只是proc调用语法,我使用的是
)(仅限1.9)而不是
。call()
嗨,您能解释一下M.bind(self)(*args,&block)到底是做什么的吗?我已经从谷歌搜索了ruby文档和许多页面,但我仍然不知道它是如何工作的。许多thx需要帮助。@reizals见。(回复仅供大家参考。)那么,绑定的位置很重要吗?我们不能在要求类定义的时候做到?我也不知道为什么这被否决了。也许这是因为没有一个例子,只有一个链接。对随机库的链接进行向下投票,而没有解释为什么我应该点击链接。这个问题与Ruby 1.9.2有关。但是现在,如果你刚刚发现这个问题,你可能正在使用Ruby 2+。在Ruby 2中,
prepend
是一个很好的选择。参见,例如。,