=begin
作者:Taroxd
-------------------------------------------------------
2014/2/14 更新
修改了def系列的实现方式,不会污染名称空间
def系列会同步方法的访问限制
修改了一些方法名
2014/2/15 更新
可以使用return,并支持block参数。
2014/2/18 更新
添加def_chain,彻底抛弃alias。
def_chain可以完全替代以前的alias
2014/4/6 更新
添加了singleton_def系列,可以用来重定义类方法等
2014/8/27 更新
def_before 与 def_after 添加了 hook 参数
对顶层环境添加了 def 系列的支持
2014/10/08 更新
对 def_with 与 def_chain 同样添加了 hook 参数
2014/12/16 更新
重构代码,使之更加易于扩展。
增加了 def_after!, def_and, def_or, def_if, def_unless
-------------------------------------------------------
使用示例可以参考 https://rpg.blue/forum.php?mod=forumdisplay&action=list&fid=539#groupnav
里面有大量的使用示例
------------------------------------------------------
=end
module Taroxd end
module Taroxd::Def
# 导入
Singleton = Module.new { Object.send :include, self }
Module.send :include, self
# 获取方法的访问限制
def get_access_control(sym)
return :public if public_method_defined? sym
return :protected if protected_method_defined? sym
return :private if private_method_defined? sym
nil
end
template = lambda do |singleton|
if singleton
klass = 'singleton_class'
get_method = 'method'
define = 'define_singleton_method'
else
klass = 'self'
get_method = 'instance_method'
define = 'define_method'
end
%(
def <name>(sym, hook = nil, &b)
access = #{klass}.get_access_control sym
old = #{get_method} sym
if b
#{define} sym, &b
hook = #{get_method} sym
end
if hook.respond_to? :to_sym
hook = hook.to_sym
#{define} sym do |*args, &block|
<pattern_sym>
end
elsif hook.respond_to? :call
#{define} sym do |*args, &block|
<pattern_call>
end
elsif hook.kind_of? UnboundMethod
#{define} sym do |*args, &block|
<pattern_unbound>
end
end
#{klass}.send access, sym
sym
end
)
end
# 保存模板和替换 'hook(' 字符串的字符
template = {false => template.call(false), true => template.call(true)}
# 替换掉 pattern 中的语法
gsub_pattern = lambda do |pattern, singleton|
old = singleton ? 'old' : 'old.bind(self)'
pattern.gsub('*', '*args, &block')
.gsub(/old(\()?/) { $1 ? "#{old}.call(" : old }
end
# 存入代替 "hook(" 的字符串
template['sym'] = '__send__(hook, '
template['call'] = 'hook.call('
template['unbound'] = 'hook.bind(self).call('
# 获取定义方法内容的字符串
code = lambda do |name, pattern, singleton|
pattern = gsub_pattern.call(pattern, singleton)
template[singleton]
.sub('<name>', name)
.gsub(/<pattern_(\w+?)>/) { pattern.gsub('hook(', template[$1]) }
end
main = TOPLEVEL_BINDING.eval('self')
# 定义 def_ 系列方法的方法
define_singleton_method :def_ do |name, pattern|
name = "#{__method__}#{name}"
module_eval code.call(name, pattern, false)
Singleton.module_eval code.call("singleton_#{name}", pattern, true)
main.define_singleton_method name, &Kernel.method(name)
end
# 实际定义 def_ 系列的方法
def_ :after, 'ret = old(*); hook(*); ret'
def_ :after!, 'old(*); hook(*)'
def_ :before, 'hook(*); old(*)'
def_ :with, 'hook(old(*), *)'
def_ :chain, 'hook(old, *)'
def_ :and, 'old(*) && hook(*)'
def_ :or, 'old(*) || hook(*)'
def_ :if, 'hook(*) && old(*)'
def_ :unless, '!hook(*) && old(*)'
end