来自:http://cheind.blogspot.com/2008/12/method-hooks-in-ruby.html
我有
# Contains methods to hook method calls
module FollowingHook
module ClassMethods
private
# Hook the provided instance methods so that the block
# is executed directly after the specified methods have
# been invoked.
#
def following(*syms, &block)
syms.each do |sym| # For each symbol
str_id = "__#{sym}__hooked__"
unless private_instance_methods.include?(str_id)
alias_method str_id, sym # Backup original
# method
private str_id # Make backup private
define_method sym do |*args| # Replace method
ret = __send__ str_id, *args # Invoke backup
block.call(self, # Invoke hook
:method => sym,
:args => args,
:return => ret
)
ret # Forward return value of method
end
end
end
end
end
# On inclusion, we extend the receiver by
# the defined class-methods. This is an ruby
# idiom for defining class methods within a module.
def FollowingHook.included(base)
base.extend(ClassMethods)
end
end
然后我有一个类似的课程:
class User
def self.get
#class method
end
def name
#instance method
end
end
在另一个位置/文件中,我重新打开User类并挂钩
class User
include FollowingHooks # include the hook module
following :name do |receiver, args|
#do something. This works!!
end
following :get do |reciever, args|
#do something. THIS DOESNT WORK
# Which is to be expected looking at the FollowingHooks module definition.
end
end
挂钩任何实例方法都有效。然而,尝试挂钩类方法什么也没做,我得到了,因为FollowingHooks模块没有实现它。我如何实现类方法的钩子?我完全无能为力。
答案 0 :(得分:1)
您需要在Class
上包含FollowHook代码,然后调用以下内容以使其适用于类方法。
Class.send(:include, FollowingHook)
class User
class << self
following :get do |reciever, args|
# Your awesome code here
end
end
end
修改强>
这是我完整的工作解决方案,遵循这一建议:
# Contains methods to hook method calls
module FollowingHook
module ClassMethods
private
# Hook the provided instance methods so that the block
# is executed directly after the specified methods have
# been invoked.
#
def following(*syms, &block)
syms.each do |sym| # For each symbol
str_id = "__#{sym}__hooked__"
unless private_instance_methods.include?(str_id)
alias_method str_id, sym # Backup original
# method
private str_id # Make backup private
define_method sym do |*args| # Replace method
ret = __send__ str_id, *args # Invoke backup
block.call(self, # Invoke hook
:method => sym,
:args => args,
:return => ret
)
ret # Forward return value of method
end
end
end
end
end
def self.included(base)
base.send(:extend, FollowingHook::ClassMethods)
end
end
class User
def self.foo
puts "foo"
end
def bar
puts "bar"
end
end
# You can put this in the class << self block if you prefer that the
# methods only be available on the User class.
Class.send(:include, FollowingHook)
class User
include FollowingHook
following :bar do |receiver, args|
puts receiver.inspect
end
class << self
# If you prefer only the User class include FollowingHooks, use the
# following instead of including them in Class.
# include FollowingHook
following :foo do |receiver, args|
puts receiver.inspect
end
end
end
User.foo #=>
# foo
# User
User.new.bar #=>
# bar
# #<User:0x338d9d>
答案 1 :(得分:1)
嗯,这很有效。
def following_c(*syms, &block)
metaclass = (class << self; self; end)
syms.each do |sym|
str_id = "__#{sym}__hooked__"
unless metaclass.send(:private_instance_methods).include?(str_id)
metaclass.send :alias_method, str_id, sym
metaclass.send :private, str_id
metaclass.send :define_method, sym do |*args|
ret = send str_id, *args
block.call(self,
:method => sym,
:args => args,
:return => ret
)
ret
end
end
end
end