突然想起rails有before_filter,想了一下怎么实现(看源码太费劲,机器上也没rails),就动手试了一下。
简单介绍一下before_filter:
比如以下代码:
class Person
before :bef
after :aft
def initialize name
@name = name
end
def make_new_friend
puts "Nice to see you"
end
def introduce_self
puts "Bla Bla Bla..."
end
def bef
puts "Hi, I'm #{@name}"
end
def aft
puts "Bye, contact me, I'm #{@name}"
end
end
p =Person.new('Xiao Ping')
p.make_new_friend
p.introduce_self
我想每次运行Person的某个方法前,都自动执行一下bef方法(在第3行声明过,在18行定义),运行方法后执行aft方法。(例如rails中可以在bef方法中写验证用户是否有效),既方法make_new_friend
虽然目前看只输出Nice to see you,但是我实际要输出
Hi, I'm Xiao Ping
Nice to see you
Bye, contact me, I'm Xiao Ping
既
bef
make_new_friend
aft
三个方法的组合。
实现的方式我目前猜测了几种,并自己写了一种供大家分享。
原理很简单,就是通过method_added这个钩子方法hack新添加的方法
class Base
def self.method_added m
m_name = m.to_s
# 下面几个判断都是过滤掉不需要hack的方法。
@@registered_methods ||= []
unless m_name =~ /original/ || m_name == 'initialize'
unless protected_instance_methods.include?(m)
unless @@registered_methods.include?(m_name)
# 保存原来的方法
alias_method "original_#{m_name}".to_sym, m_name.to_sym
@@registered_methods << m_name
self.class_eval do
parameters = public_instance_method(m).parameters.collect {|i| i[1]}
# 创建新的同名方法
define_method m do |*parameters|
self.class.class_variable_get(:@@before).each do |m|
__send__(m)
end
rtn = __send__("original_#{m_name}".to_sym, *parameters)
self.class.class_variable_get(:@@after).each do |m|
__send__(m)
end
rtn
end
end
end
end
end
end
def self.before *methods
@@before ||= []
methods.each do |m|
@@before << m unless @@before.include?(m)
end
end
def self.after *methods
@@after ||= []
methods.each do |m|
@@after << m unless @@after.include?(m)
end
end
end
class Person < Base
before :bef
after :aft
def initialize name
@name = name
end
def make_new_friend
puts "Nice to see you"
end
def introduce_self
puts "Bla Bla Bla..."
end
protected
def bef
puts "Hi, I'm #{@name}"
end
def aft
puts "Bye, contact me, I'm #{@name}"
end
end
p =Person.new('Xiao Ping')
p.make_new_friend
p.introduce_self
有点瑕疵,改了之后容易让人confused,所以就没改,希望能对大家有帮助。