Project1

标题: Monkeypatch 1.0 [打印本页]

作者: LBQ    时间: 2014-2-12 13:18
标题: Monkeypatch 1.0
当年写了一个用class_eval的笨蛋Monkeypacth嗯
于是这次发到外站之后果断有触手帮忙改了>_<,果然我就是个渣
于是把最终版本放这里吧:
RUBY 代码复制
  1. #===============================================================================
  2. #  Monkeypatch v 0.5 by BakaCoder aka XXX
  3. #  rewritten by IceDragon
  4. #===============================================================================
  5. # monkeypatch <method_name (sym)> (&block)
  6. #===============================================================================
  7. proc do
  8. # Example
  9.   class Scene_Base
  10.  
  11.     patch :initialize do
  12.       puts "A Scene has been initialized!"
  13.     end
  14.  
  15.     patch :initialize, :before do
  16.       puts "Scene #{self.class.name} initializing"
  17.     end
  18.  
  19.   end
  20.  
  21.   class Something
  22.  
  23.     def sum
  24.       1 + 1
  25.     end
  26.  
  27.     patch :sum, :chain do |oldsum|
  28.       oldsum + 1
  29.     end
  30.  
  31.   end
  32. # End
  33. end
  34.  
  35. module MonkeyPatch
  36.  
  37.   def monkeypatch(sym, pos=:after, &block)
  38.     meth = instance_method(sym) # original method
  39.     define_method(sym, &block)
  40.     meth2 = instance_method(sym) # alias method
  41.     case pos
  42.     when :after
  43.       define_method(sym) do |*a, &b|
  44.         meth.bind(self).call(*a, &b)
  45.         meth2.bind(self).call(*a, &b)
  46.       end
  47.     when :before
  48.       define_method(sym) do |*a, &b|
  49.         meth2.bind(self).call(*a, &b)
  50.         meth.bind(self).call(*a, &b)
  51.       end
  52.     when :chain
  53.       define_method(sym) do |*a, &b|
  54.         meth2.bind(self).call(meth.bind(self).call(*a, &b), *a, &b)
  55.       end
  56.     end
  57.   end
  58.  
  59.   alias :patch :monkeypatch
  60.  
  61. end
  62.  
  63. class Module
  64.   include MonkeyPatch
  65. 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的时候做了一个猜想 然后试验了一下

果然
RUBY 代码复制
  1. class Sot
  2.   def met
  3.     p "done"
  4.   end
  5.  
  6.   patch :met, :chain do |old|
  7.     p "pre"
  8.     old
  9.     p "aft"
  10.   end
  11. end
  12.  
  13. Sot.new.met
  14. #"done"
  15. #"pre"
  16. #"after"

作者: taroxd    时间: 2014-2-16 12:05
本帖最后由 taroxd 于 2014-2-16 12:16 编辑
余烬之中 发表于 2014-2-15 20:28
看到chain的时候做了一个猜想 然后试验了一下

果然class Sot


没有任何问题啊,原方法的返回值是‘done’

这个方法相当于

RUBY 代码复制
  1. alias new_name old_name
  2. def old_name(*args, &block)
  3.   old_value = new_name(*args, &block)
  4.   # do something with old_value
  5. end


在方法的目标是返回一个值的时候,这个方法很有用处

比如说
RUBY 代码复制
  1. class << BattleManager
  2.   patch :process_escape, :chain do |success|  # success 表示逃跑是否成功
  3.     @escape_ratio -= 0.1 unless success    # 如果没有成功,不改变下一次逃跑的成功率 (原方法会增加0.1)
  4.     success                # 不改变原方法的返回值
  5.   end
  6. 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