`
yueguangyuan
  • 浏览: 332868 次
  • 性别: Icon_minigender_1
  • 来自: 新加坡
社区版块
存档分类
最新评论

[转贴]About ruby’s “open class”

阅读更多
一个有关Ruby的特性,是一个朋友私有博客上写的,我拿过来帮着宣传 :P
至于本文内容,我只能说我在技术方面比较肤浅,很少去研究深层次的东西 :(。
ruby 代码
  1.  
  2. [本文发出的第二天,我不得不对此文进行修改  ]   
  3. Ruby的open class算是是他众多优秀特性中最耀眼的一个,active_support就用这个特性玩出了众多花样,但是今天有人提出了这格特性给ruby带来的安全问题,我在同一个名字空间下写一个跟你的类同名的类覆盖或者添加一个方法,然后在你的类加载之后加载(能出现这个过程的情况估计也很少,有这个条件和功力可以直接改你的source了),岂不是很危险,想想有点道理,怎么才能把某些类的open class给禁了呢?   
  4.   
  5. 1.   
  6.   
  7. 比较简单:   
  8.   
  9. class Test    
  10.      
  11.   def test_method    
  12.     #do somthong...    
  13.   end    
  14.      
  15.   self.freeze    
  16.      
  17. end   
  18. 这样,在class定义结束前(一定要在类末尾,或者干脆在类定义完成后Test.freeze一下)将它freeze一下,就什么都改不了了,搞定。    
  19.   
  20. 这个方案昨天我还以为万无一失,但是turbowolf轻而易举的把freeze flag给去掉了:   
  21.   
  22. require 'dl/struct'    
  23.      
  24. module Internal    
  25.   extend DL::Importable    
  26.      
  27.   typealias "VALUE",nil,nil,nil,"unsigned long"    
  28.   typealias "ID",nil,nil,nil,"unsigned long"    
  29.      
  30.   Basic=["long flags","VALUE klass"]    
  31.   RBasic=struct Basic    
  32.   RObject=struct(Basic+["st_table *iv_tbl"])    
  33.      
  34.   FL_FREEZE=1<<10    
  35. end    
  36.      
  37. class Object    
  38.   def immediate?    
  39.     [Fixnum,Symbol,NilClass,TrueClass,FalseClass].any?{ |klass| klass===self}    
  40.   end    
  41.      
  42.   def unfreeze    
  43.     return self if immediate?    
  44.     Internal::RObject.new(DL::PtrData.new(self.object_id * 2)).flags &= ~ Internal::FL_FREEZE    
  45.     self    
  46.   end    
  47.      
  48. end    
  49.      
  50. class A    
  51.      
  52.   def test_a    
  53.     puts "This is class A"    
  54.   end    
  55.      
  56.   def test_b    
  57.     puts "test_b method in class A"    
  58.   end    
  59.      
  60.   self.freeze    
  61. end    
  62.      
  63.      
  64. class A    
  65.   self.unfreeze    
  66.   def test_a    
  67.       puts "This is class A, modified"    
  68.   end    
  69. end    
  70.      
  71. a=A.new    
  72. a.test_a    
  73. a.test_b   
  74. 所以只能在method_added上下功夫.   
  75. 2.   
  76.   
  77. class Object    
  78.      
  79.   @@hold_methods=Hash.new([])    
  80.      
  81.   def no_instance_method_override    
  82.     class << self    
  83.       def method_added(new_method)    
  84.         if @@hold_methods[self].include?(new_method)    
  85.           raise "You can *NOT* override method \"#{new_method}\" by redefind class #{self}!"    
  86.         else    
  87.           @@hold_methods[self] << new_method    
  88.         end    
  89.       end    
  90.     end    
  91.   end    
  92.      
  93. end   
  94. 保存这段代码,定义class的时候require这个文件,然后作些手脚:   
  95.   
  96. require "...."    
  97.      
  98. class Test    
  99.      
  100.   no_instance_method_override    
  101.      
  102.   def a    
  103.     puts "method a"    
  104.   end    
  105.      
  106. end    
  107.      
  108. class Test    
  109.      
  110.   def a    
  111.     puts "method a modified!"    
  112.   end    
  113.      
  114.   def b    
  115.      puts "method b"    
  116.   end    
  117.      
  118. end   
  119. 这样似乎就达到目的了,你运行的时候会 RuntimeError:You can *NOT* override method “a” by redefind class Test! (RuntimeError),但是只是RuntimeError而已,我rescue一下又会怎样?   
  120.   
  121. require "...."    
  122.      
  123. class Test    
  124.      
  125.   no_instance_method_override    
  126.      
  127.   def a    
  128.     puts "method a"    
  129.   end    
  130.      
  131. end    
  132.      
  133. class Test    
  134.      
  135.   begin    
  136.     def a    
  137.       puts "method a modified!"    
  138.     end    
  139.   rescue    
  140.   end    
  141.      
  142.      
  143.   def b    
  144.      puts "method b"    
  145.   end    
  146.      
  147. end    
  148.      
  149. Test.new.a   
  150. 看到了,a方法还是被修改了,所以raise “Exception”似乎不够狠,来干脆的,exit之 :   
  151.   
  152. class Object    
  153.      
  154.   @@hold_methods=Hash.new([])    
  155.      
  156.   def no_instance_method_override    
  157.     class << self    
  158.       def method_added(new_method)    
  159.         if @@hold_methods[self].include?(new_method)    
  160.           puts "You can *NOT* override method \"#{new_method}\" by redefind class #{self}!"    
  161.           exit(1)    
  162.         else    
  163.           @@hold_methods[self] << new_method    
  164.         end    
  165.       end    
  166.     end    
  167.   end    
  168.      
  169. end   
  170. 这样就好了,不过有点那个…   
  171. 不过由此我发现,thesorensens.org的finalizer是有bug的:   
  172. gem install finalizer之后(上不去rubyforge的去这里下载),   
  173.   
  174. require "rubygems"    
  175. require_gem "finalizer"    
  176. require "finalizer"    
  177.      
  178. class Foo    
  179.    final :bar    
  180.      
  181.    def bar    
  182.      puts 'bar'    
  183.    end    
  184. end    
  185.      
  186. class Bar < Foo    
  187.   begin    
  188.     def bar    
  189.       puts 'mybar'    
  190.     end    
  191.   rescue    
  192.   end    
  193. end    
  194.      
  195. Bar.new.bar   
  196. 你看到了什么?final的method bar被修改了,  所以exit虽然狠了点,但是raise的确是不担当任这个任务。   
分享到:
评论
1 楼 KDr2 2007-02-08  
method_added也被绕过了,可以去我那里看看,不知道有没有别的办法。

相关推荐

Global site tag (gtag.js) - Google Analytics