therubyway:ruby way的动态特性的 2

  1 得到所定义实体列表

  ruby反射api能够使我们在运行时检测类和对象因此我们下面将会介绍Module, Class, 和Object中定义些思路方法

  Module模块有个constants 思路方法它将会返回系统中所有常量名包括类名和模块名nesting 思路方法则是返回当前点上嵌套模块列表.

  Ruby代码

list = Math.constants  # ["E", "PI"] 

  Module#ancestors 返回指定类或者模块所有包含类或者模块.

  Ruby代码  

list = Array.ancestors 
# [Array, Enumerable, Object, Kernel] 


  _variables 思路方法返回给定类和他超类所有类变量个表d_modules 思路方法列出包含在这个类中所有模块

  Ruby代码

 Parent 
 @@var1 = nil 
end 
 
 Child < Parent 
 @@var2 = nil 
end 
 
list1 = Parent._variables  # ["@@var1"] 
list2 = Array.d_modules  # [Enumerable, Kernel]


  Class思路方法instance_methods和public_instance_methods 是同义他们返回这个类所有公有思路方法private_instance_methods和 protected_instance_methods 也就是返回私有和保护例子思路方法这几个思路方法都还带有个参数默认是true如果被设置为false超类将不会被搜索

  Ruby代码

n1 = Array.instance_methods.size         # 121 
n2 = Array.public_instance_methods.size     # 121 
n3 = Array.private_instance_methods.size     # 71 
n4 = Array.protected_instance_methods.size    # 0 
n5 = Array.public_instance_methods(falsee).size # 71


  Object 类有很多操作例子类似思路方法methods 思路方法将会返回这个对象所有可以被思路方法 public_methods 思路方法将会返回所有公有思路方法他也有个参数来判断是否去父类搜索private_methods, protected_methods,和singleton_methods 也都有类似参数

  Ruby代码

 SomeClass 
 
 def initialize 
  @a = 1 
  @b = 2 
 end 
 
 def mymeth 
  #... 
 end 
 
 protected :mymeth 
 
end 
 
 
x = SomeClass. 
 
def x.meth 
 # ... 
end 
 
iv = x.instance_variables    # ["@b", "@a"] 
 
p x.methods.size          # 42 
 
p x.public_methods.size      # 41 
p x.public_methods(false).size   # 1 
 
p x.private_methods.size      # 71 
p x.private_methods(false).size  # 1 
 
p x.protected_methods.size     # 1 
p x.singleton_methods.size     # 1


  2 测试

  有时我们想要知道我们者是谁这个有时是非常有用看下面例子:

  Ruby代码

def func1 
 puts caller[0] 
 end 
 
def func2 
 func1 
end 
 
func2       # 打印出fucn1在那里被


  3 监控执行

  个ruby能够内省或者说是检测他自己执行很多都用到了这个功能有兴趣话可以看看这几个ruby源码:debug.rb, profile.rb, 和 tracer.rb

  我们能够使用 _trace_func思路方法他接受个block作为参数无论在执行中发生任何事情时这个block都会被我们看下面例子:

  Ruby代码 def meth(n) 
 sum = 0 
 for i in 1..n 
  sum  i 
 end 
 sum 
end 
 
_trace_func(proc do |event, file, line, 
            id, binding, klass, *rest| 
 prf "%8s %s:%d %s/%s\n", event, file, line, 
                klass, id 
end) 
 
meth(2) 


  可以看到输出类似这样:

  引用

line D:/develop/rubyWorkspace/RubyWay/lib/DynamicFeaturesTest.rb:171  false/

    call D:/develop/rubyWorkspace/RubyWay/lib/DynamicFeaturesTest.rb:157  Object/meth

............................


  还有个思路方法是Kernel#trace_var它是当全局变量被赋值时才会被自动

  假设你想要在外面得到运行轨迹最简单思路方法就是使用tracer 库假设有个prog.rb文件:

  Ruby代码

def meth(n) 
 (1..n).each {|i| puts i} 
end 
 
meth(3)


  然后我们在命令行load TRacer :

  引用

% ruby -r tracer prog.rb
#0:prog.rb:1::-:     def meth(n)
#0:prog.rb:1:Module:>:     def meth(n)


  当源代码执行时每个事件类型都包含"'-'",'>'表示'<' 表示个返回'C'代表个类'E' 代表结束

  4 Traversing the Object Space

  ruby运行系统需要保存所有已知对象踪迹(只是为了能够垃圾回收那些没有长时间使用引用也就是gc)这个信息是通过 ObjectSpace.each_object 来得到

  Ruby代码

ObjectSpace.each_object do |obj| 
 prf "%20s: %s\n", obj., obj.inspect 
end


  如果你指定个类名或者模块名给each_object它就只会返回这种类型对象.

  5 使用method_missing

  起始和前面const_missing差不多也就是说当你在这个对象上个不存在思路方法时它就会默认method_missing 思路方法:

  Ruby代码

 CommandWrapper 
 
 def method_missing(method, *args) 
  system(method.to_s, *args) 
 end 
 
end 
 
 
cw = CommandWrapper. 
cw.date          # Sat Apr 28 22:50:11 CDT 2001 
cw.du '-s', '/tmp'    # 166749 /tmp 


  在Object中定义method_missing思路方法是默认抛出个异常.

  6 跟踪类或者对象改变

  我们现在想要写个模块它能够被任何类所包含然后在这个类中个思路方法都会打印出相应信息比如我们所期待是这样:

  Ruby代码

 MyClass 
  Tracing 
 
 def one 
 end 
 
 def two(x, y) 
 end 
 
end 
 
m = MyClass. 
m.one         # one called. Params = 
m.two(1, 'cat')    # two called. Params = 1, cat


  对于子类我们也是能够跟踪:

  Ruby代码

 Fred < MyClass 
 
 def meth(*a) 
 end 
 
end 
 
Fred..meth(2,3,4,5)  # meth called. Params = 2, 3, 4, 5


  下面来看它实现:

  Ruby代码

module Tracing 
    def Tracing.d(o) 
     o.instance_methods(false).each { |m| 
Tracing.hook_method(o, m) } 
     def o.method_added(meth) 
      unless @adding 
       @adding = true 
       Tracing.hook_method(self, meth) 
       @adding = false 
      end 
     end 
    end 
 
    def Tracing.hook_method(klass, meth) 
     klass._eval do 
      alias_method "old_#{meth}", "#{meth}" 
      _method(meth) do |*args| 
       puts "#{meth} called. Params = #{args.join(', ')}" 
       self.send("old_#{meth}",*args) 
      end 
     end 
    end 
   end 
 
    MyClass 
     Tracing 
 
    def first_meth 
    end 
 
    def second_meth(x, y) 
    end 
  end 
 
 
 
m = MyClass. 
p m.first_meth         # one called. Params = 
p m.second_meth(1, 'cat')    # two called. Params = 1, cat


  这个代码其实很简单.首先它有两个主思路方法个是d,它是个回调思路方法当这个模块被插入到个类中时就会这个思路方法在我们上面例子中他做了两件事件是为这个模块个思路方法hook_method思路方法第 2件事是为这个类重新定义了 method_added 思路方法这就意味着这个类如果加思路方法就会这个思路方法也就是说会被检测到

  而hook_method实现也是很漂亮使用了_method来动态定义思路方法打印出信息后再使用send来思路方法

  这里还要注意个alias_method 它和alias很类似只不过它只能用在思路方法并且他自己就是个思路方法:

  Ruby代码

# Two other ways to write that line... 
 
# Symbols with erpolation: 
alias_method :"old_#{meth}", :"#{meth}" 
 
# Strings converted via to_sym: 
alias_method "old_#{meth}".to_sym, meth.to_sym


  检测个新类思路方法被加到个类或者模块我们能够定义个类思路方法singleton_method_added :

  Ruby代码

 MyClass 
 
 def MyClass.singleton_method_added(sym) 
  puts "Added method #{sym.to_s} to  MyClass." 
 end 
 
 def MyClass.meth1 
  puts "I'm meth1." 
 end 
 
end 
 
def MyClass.meth2 
 puts "And I'm meth2." 
end


  输出将会是这样子:

  引用

Added method singleton_method_added to MyClass.
Added method meth1 to MyClass.
Added method meth2 to MyClass.


  inherited 思路方法使用也很类似个类被子类化就会这个思路方法:

  Ruby代码

 MyClass 
 
 def MyClass.inherited(sub) 
  puts "#{sub} inherits from MyClass." 
 end 
 
 # ... 
 
end 
 
 
 OtherClass < MyClass 
 
 # ... 
 
end 
 
# Output: OtherClass inherits from MyClass.


  我们也能够跟踪加个模块例子思路方法到个对象extend_object 思路方法就是做这个:

  Ruby代码

module MyMod 
 
 def MyMod.extend_object(obj) 
  puts "Extending object id #{obj.object_id},  #{obj.}" 
  super 
 end 
 
 # ... 
 
end 
 
 
x = [1, 2, 3] 
x.extend(MyMod) 
 
# Output: 
# Extending object id 36491192, type Array


  这里要注意super是必须和append_features 重原因

  7 为对象定义个Finalizers

  ruby类有构造器可是没有析构器原因很简单就是ruby使用了mark-and-sweep garbage collection 来删除没有引用对象

  虽然在ruby中不能真正做到析构器来删除对象这里有个思路方法_finalizer它是当个对象被gc时就会它:

  Ruby代码

a = "hello" 
puts "The  'hello' has an object id #{a.object_id}" 
ObjectSpace._finalizer(a) { |id| puts "Destroying #{id}" } 
puts "Nothing to tidy" 
GC.start 
a = nil 
puts "The original  is now a candidate for collection" 
GC.start


  输出结果类似这样:

  Ruby代码 The  'hello' has an object id 21089680 
 
Nothing to tidy 
 
The original  is now a candidate for collection 
 
Destroying 21089680


  当finalizer 被同时对象就被销毁了如果此时你ObjectSpace._id2ref然后参数为刚才那个对象id,则会报个RangeError:

  Ruby代码

ObjectSpace._id2ref 21089630 #in `_id2ref': 0x141cd5e is recycled object (RangeError)

  由于ruby使用是mark-and-sweep GC 策略那么他就不能保证当结束的前对象什么时候被GC

  可是所有切都是不确定在ruby中经常使用block来压缩个源(也就是段代码)使用,在block结尾这个源将会被删除 (也就是说然后其他对象没有任何改变(其实这个也就是说讲代码封装在block里面比较好当block退出时那些局部变量或者说传进来哪些参数都会被GC掉,因此在ruby1.9里面block里面局部变量和外面变量已经不是同个变量了(假设名字相同))看下面代码:



  Ruby代码 File.open("myfile.txt") do |file| 
 line1 = file.read 
 # ... 
end


  在这里当block退出时file 被删除切都是在open思路方法里控制如果你想要实现个open思路方法子集你可以这么做:

  Ruby代码

def File.open(name, mode = "r") 
 f = os_file_open(name, mode) 
  block_given? 
  begin 
   yield f 
  ensure 
   f.close 
  end 
   nil 
  
   f 
 end 
end


  这边使用begin ensure是要做到就算异常被抛出文件也要被关闭.



Tags:  ruby安装 ruby是什么意思 rubyonrails therubyway

延伸阅读

最新评论

发表评论