[转贴]About ruby’s “open class” 博客分类: Ruby on Rails & Ajax Rubyrubygems.netBlog
程序员文章站
2024-03-20 18:50:46
...
一个有关Ruby的特性,是一个朋友私有博客上写的,我拿过来帮着宣传 :P
至于本文内容,我只能说我在技术方面比较肤浅,很少去研究深层次的东西 :(。
ruby 代码
- [本文发出的第二天,我不得不对此文进行修改 ]
- Ruby的open class算是是他众多优秀特性中最耀眼的一个,active_support就用这个特性玩出了众多花样,但是今天有人提出了这格特性给ruby带来的安全问题,我在同一个名字空间下写一个跟你的类同名的类覆盖或者添加一个方法,然后在你的类加载之后加载(能出现这个过程的情况估计也很少,有这个条件和功力可以直接改你的source了),岂不是很危险,想想有点道理,怎么才能把某些类的open class给禁了呢?
- 1.
- 比较简单:
- class Test
- def test_method
- #do somthong...
- end
- self.freeze
- end
- 这样,在class定义结束前(一定要在类末尾,或者干脆在类定义完成后Test.freeze一下)将它freeze一下,就什么都改不了了,搞定。
- 这个方案昨天我还以为万无一失,但是turbowolf轻而易举的把freeze flag给去掉了:
- require 'dl/struct'
- module Internal
- extend DL::Importable
- typealias "VALUE",nil,nil,nil,"unsigned long"
- typealias "ID",nil,nil,nil,"unsigned long"
- Basic=["long flags","VALUE klass"]
- RBasic=struct Basic
- RObject=struct(Basic+["st_table *iv_tbl"])
- FL_FREEZE=1<<10
- end
- class Object
- def immediate?
- [Fixnum,Symbol,NilClass,TrueClass,FalseClass].any?{ |klass| klass===self}
- end
- def unfreeze
- return self if immediate?
- Internal::RObject.new(DL::PtrData.new(self.object_id * 2)).flags &= ~ Internal::FL_FREEZE
- self
- end
- end
- class A
- def test_a
- puts "This is class A"
- end
- def test_b
- puts "test_b method in class A"
- end
- self.freeze
- end
- class A
- self.unfreeze
- def test_a
- puts "This is class A, modified"
- end
- end
- a=A.new
- a.test_a
- a.test_b
- 所以只能在method_added上下功夫.
- 2.
- class Object
- @@hold_methods=Hash.new([])
- def no_instance_method_override
- class << self
- def method_added(new_method)
- if @@hold_methods[self].include?(new_method)
- raise "You can *NOT* override method \"#{new_method}\" by redefind class #{self}!"
- else
- @@hold_methods[self] << new_method
- end
- end
- end
- end
- end
- 保存这段代码,定义class的时候require这个文件,然后作些手脚:
- require "...."
- class Test
- no_instance_method_override
- def a
- puts "method a"
- end
- end
- class Test
- def a
- puts "method a modified!"
- end
- def b
- puts "method b"
- end
- end
- 这样似乎就达到目的了,你运行的时候会 RuntimeError:You can *NOT* override method “a” by redefind class Test! (RuntimeError),但是只是RuntimeError而已,我rescue一下又会怎样?
- require "...."
- class Test
- no_instance_method_override
- def a
- puts "method a"
- end
- end
- class Test
- begin
- def a
- puts "method a modified!"
- end
- rescue
- end
- def b
- puts "method b"
- end
- end
- Test.new.a
- 看到了,a方法还是被修改了,所以raise “Exception”似乎不够狠,来干脆的,exit之 :
- class Object
- @@hold_methods=Hash.new([])
- def no_instance_method_override
- class << self
- def method_added(new_method)
- if @@hold_methods[self].include?(new_method)
- puts "You can *NOT* override method \"#{new_method}\" by redefind class #{self}!"
- exit(1)
- else
- @@hold_methods[self] << new_method
- end
- end
- end
- end
- end
- 这样就好了,不过有点那个…
- 不过由此我发现,thesorensens.org的finalizer是有bug的:
- gem install finalizer之后(上不去rubyforge的去这里下载),
- require "rubygems"
- require_gem "finalizer"
- require "finalizer"
- class Foo
- final :bar
- def bar
- puts 'bar'
- end
- end
- class Bar < Foo
- begin
- def bar
- puts 'mybar'
- end
- rescue
- end
- end
- Bar.new.bar
- 你看到了什么?final的method bar被修改了, 所以exit虽然狠了点,但是raise的确是不担当任这个任务。
下一篇: python 自定义类继承list类