Project1
标题: Monkeypatch 1.0 [打印本页]
作者: LBQ 时间: 2014-2-12 13:18
标题: Monkeypatch 1.0
当年写了一个用class_eval的笨蛋Monkeypacth嗯
于是这次发到外站之后果断有触手帮忙改了>_<,果然我就是个渣
于是把最终版本放这里吧:
#===============================================================================
# Monkeypatch v 0.5 by BakaCoder aka XXX
# rewritten by IceDragon
#===============================================================================
# monkeypatch <method_name (sym)> (&block)
#===============================================================================
proc do
# Example
class Scene_Base
patch :initialize do
puts "A Scene has been initialized!"
end
patch :initialize, :before do
puts "Scene #{self.class.name} initializing"
end
end
class Something
def sum
1 + 1
end
patch :sum, :chain do |oldsum|
oldsum + 1
end
end
# End
end
module MonkeyPatch
def monkeypatch(sym, pos=:after, &block)
meth = instance_method(sym) # original method
define_method(sym, &block)
meth2 = instance_method(sym) # alias method
case pos
when :after
define_method(sym) do |*a, &b|
meth.bind(self).call(*a, &b)
meth2.bind(self).call(*a, &b)
end
when :before
define_method(sym) do |*a, &b|
meth2.bind(self).call(*a, &b)
meth.bind(self).call(*a, &b)
end
when :chain
define_method(sym) do |*a, &b|
meth2.bind(self).call(meth.bind(self).call(*a, &b), *a, &b)
end
end
end
alias :patch :monkeypatch
end
class Module
include MonkeyPatch
end
#===============================================================================
# Monkeypatch v 0.5 by BakaCoder aka XXX
# rewritten by IceDragon
#===============================================================================
# monkeypatch <method_name (sym)> (&block)
#===============================================================================
proc do
# Example
class Scene_Base
patch :initialize do
puts "A Scene has been initialized!"
end
patch :initialize, :before do
puts "Scene #{self.class.name} initializing"
end
end
class Something
def sum
1 + 1
end
patch :sum, :chain do |oldsum|
oldsum + 1
end
end
# End
end
module MonkeyPatch
def monkeypatch(sym, pos=:after, &block)
meth = instance_method(sym) # original method
define_method(sym, &block)
meth2 = instance_method(sym) # alias method
case pos
when :after
define_method(sym) do |*a, &b|
meth.bind(self).call(*a, &b)
meth2.bind(self).call(*a, &b)
end
when :before
define_method(sym) do |*a, &b|
meth2.bind(self).call(*a, &b)
meth.bind(self).call(*a, &b)
end
when :chain
define_method(sym) do |*a, &b|
meth2.bind(self).call(meth.bind(self).call(*a, &b), *a, &b)
end
end
end
alias :patch :monkeypatch
end
class Module
include MonkeyPatch
end
使用说明啥的基本不需要了,反正就是
patch :method_name do
#TODO
end
不过据说有奇怪的bug._.?
作者: 天地有正气 时间: 2014-2-12 13:20
于是看到了大大又发布我看不懂的高端东东.........还是膜拜吧…………
决定偷偷和大大搞好关系~
作者: 浩然-Shuenhoy 时间: 2014-2-13 16:36
强迫症表示 直接用before和after做方法名是不是好一点0 0
作者: 柍若 时间: 2014-2-13 23:17
强迫症再表示,L姐姐你第一行的patch拼错了
作者: taroxd 时间: 2014-2-15 18:45
本帖最后由 taroxd 于 2014-2-15 19:26 编辑
总觉得想法相当类似……
http://rpg.blue/thread-347871-1-1.html
第39行果断学习了!这方法我怎么没想到呢……我原来是用instance_exec的,然后一直纠结于怎么传block参数……
这样return也可以用了……总之太感动了……
于是可耻的把第39行抄袭走了~ 嗯……
另外,暂时没发现奇怪的bug
作者: 余烬之中 时间: 2014-2-15 20:28
看到chain的时候做了一个猜想 然后试验了一下
果然class Sot
def met
p "done"
end
patch :met, :chain do |old|
p "pre"
old
p "aft"
end
end
Sot.new.met
#"done"
#"pre"
#"after"
class Sot
def met
p "done"
end
patch :met, :chain do |old|
p "pre"
old
p "aft"
end
end
Sot.new.met
#"done"
#"pre"
#"after"
作者: taroxd 时间: 2014-2-16 12:05
本帖最后由 taroxd 于 2014-2-16 12:16 编辑
余烬之中 发表于 2014-2-15 20:28
看到chain的时候做了一个猜想 然后试验了一下
果然class Sot
没有任何问题啊,原方法的返回值是‘done’
这个方法相当于
alias new_name old_name
def old_name(*args, &block)
old_value = new_name(*args, &block)
# do something with old_value
end
alias new_name old_name
def old_name(*args, &block)
old_value = new_name(*args, &block)
# do something with old_value
end
在方法的目标是返回一个值的时候,这个方法很有用处
比如说
class << BattleManager
patch :process_escape, :chain do |success| # success 表示逃跑是否成功
@escape_ratio -= 0.1 unless success # 如果没有成功,不改变下一次逃跑的成功率 (原方法会增加0.1)
success # 不改变原方法的返回值
end
end
class << BattleManager
patch :process_escape, :chain do |success| # success 表示逃跑是否成功
@escape_ratio -= 0.1 unless success # 如果没有成功,不改变下一次逃跑的成功率 (原方法会增加0.1)
success # 不改变原方法的返回值
end
end
作者: 余烬之中 时间: 2014-2-16 12:17
taroxd 发表于 2014-2-16 12:05
没有任何问题啊,原方法的返回值是‘done’
这个方法相当于
我知道
我的意思是 在调用chain后的新方法时 会先执行旧方法内容 而不是临时执行 【旧方法求值→新内容1→使用旧值→新内容2】
如果旧方法和新内容对一个公用的变量进行了操作 就有一定的危险性 (但是只要注意到了就没有问题)
而晴兰的语法糖是在执行前进行预编译(宏替换?) 所以是这样的【新内容1→旧方法→新内容2】
作者: taroxd 时间: 2014-2-16 12:31
本帖最后由 taroxd 于 2014-2-16 12:35 编辑
余烬之中 发表于 2014-2-16 12:17
我知道
我的意思是 在调用chain后的新方法时 会先执行旧方法内容 而不是临时执行 【旧方法求值→新内容1 ...
是啊,看出来了。
所以这里的参数是旧方法的返回值,而晴兰的语法糖是把old作为关键字代替旧方法
如果一定要吹毛求疵用旧方法也不是不行,在chain的时候用字符串的class_eval就行,不过比block形式麻烦多了,而且传字符串参数也不是很美观
其实我的想法是,如果要这么灵活地调用旧方法的话,还不如直接alias算了,这么弄也简单不了多少。所以我没有实现这个功能
话说,如果可以的话,用Ruby2.0的prepend功能也是个很好的解决方案,直接用super代原方法,可惜这里是1.9
欢迎光临 Project1 (https://rpg.blue/) |
Powered by Discuz! X3.1 |