在Ruby模块中执行每个方法调用的代码
我在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
但是我想避免在每一个方法中明确地a re-used statement
代码。 有没有办法做到这一点?
(如果重要的话, a re-used statement
会在每个方法被调用时打印自己的名字,这将通过puts __method__
一些变体来puts __method__
。
喜欢这个:
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"
这正是aspector创build的。
使用aspector,您不需要编写样板程序代码。 你甚至可以进一步将共同的逻辑提取到一个单独的方面类,并独立地进行testing。
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
我不知道,为什么我被低估了 – 但一个合适的AOP框架比元编程hackery更好。 那是OP想要达到的目标。
http://debasishg.blogspot.com/2006/06/does-ruby-need-aop.html
另一个解决scheme是:
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()
您可以通过代理模块通过method_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 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
只能在1.9以上的ruby
更新:也不能使用块,即在实例方法中没有收益
元编程是可能的。
另一种select是水族馆 。 Aquarium是一个为Ruby实现面向方面编程(AOP)的框架。 AOP允许您在正常的对象和方法边界上实现function。 您的使用案例,对每种方法应用预先操作,是AOP的基本任务。