`
simohayha
  • 浏览: 1400894 次
  • 性别: Icon_minigender_1
  • 来自: 火星
社区版块
存档分类
最新评论

ruby way之动态特性之二

    博客分类:
  • ruby
阅读更多
1 得到所定义的实体的列表

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

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

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


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

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


class_variables 方法返回给定的类和他的超类的所有类变量的一个表。included_modules 方法列出包含在这个类中的所有模块。

class Parent
  @@var1 = nil
end

class Child < Parent
  @@var2 = nil
end

list1 = Parent.class_variables   # ["@@var1"]
list2 = Array.included_modules   # [Enumerable, Kernel]


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

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 也都有类似的参数。

class SomeClass

  def initialize
    @a = 1
    @b = 2
  end

  def mymeth
    #...
  end

  protected :mymeth

end


x = SomeClass.new

def x.newmeth
  # ...
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 测试调用栈

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

def func1
  puts caller[0]
 end

def func2
  func1
end

func2              # 打印出fucn1在那里被调用



3 监控程序的执行

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

我们能够使用 set_trace_func方法,他接受一个block作为参数。无论在程序的执行中发生任何事情时,这个block都会被调用,我们看下面的例子:

def meth(n)
  sum = 0
  for i in 1..n
    sum += i
  end
  sum
end

set_trace_func(proc do |event, file, line,
                        id, binding, klass, *rest|
  printf "%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的文件:

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 来得到的。

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


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

5 使用method_missing


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

class CommandWrapper

  def method_missing(method, *args)
    system(method.to_s, *args)
  end

end


cw = CommandWrapper.new
cw.date                   # Sat Apr 28 22:50:11 CDT 2001
cw.du '-s', '/tmp'        # 166749  /tmp


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

6 跟踪类或者对象的改变

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

class MyClass
  include Tracing

  def one
  end

  def two(x, y)
  end

end

m = MyClass.new
m.one                 # one called. Params =
m.two(1, 'cat')       # two called. Params = 1, cat


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

class Fred < MyClass

  def meth(*a)
  end

end

Fred.new.meth(2,3,4,5)   # meth called. Params = 2, 3, 4, 5


下面来看它的实现:

module Tracing
       def Tracing.included(into)
         into.instance_methods(false).each { |m|
Tracing.hook_method(into, m) }
         def into.method_added(meth)
           unless @adding
             @adding = true
             Tracing.hook_method(self, meth)
             @adding = false
           end
         end
       end

       def Tracing.hook_method(klass, meth)
         klass.class_eval do
           alias_method "old_#{meth}", "#{meth}"
           define_method(meth) do |*args|
             puts "#{meth} called. Params = #{args.join(', ')}"
             self.send("old_#{meth}",*args)
           end
         end
       end
     end

     class MyClass
       include Tracing

       def first_meth
        end

       def second_meth(x, y)
       end
   end



m = MyClass.new
p m.first_meth                 # one called. Params =
p m.second_meth(1, 'cat')       # two called. Params = 1, cat


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

而hook_method的实现也是很漂亮,使用了define_method来动态的定义方法,打印出信息后,再使用send来调用老的方法。

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

# Two other ways to write that line...

# Symbols with interpolation:
alias_method :"old_#{meth}", :"#{meth}"

# Strings converted via to_sym:
alias_method "old_#{meth}".to_sym, meth.to_sym


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

class MyClass

  def MyClass.singleton_method_added(sym)
    puts "Added method #{sym.to_s} to class 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 class MyClass.
Added method meth1 to class MyClass.
Added method meth2 to class MyClass.


inherited 方法使用也很类似,当一个类被子类化,就会调用这个方法:

class MyClass

  def MyClass.inherited(subclass)
    puts "#{subclass} inherits from MyClass."
  end

  # ...

end


class OtherClass < MyClass

  # ...

end

# Output: OtherClass inherits from MyClass.


我们也能够跟踪加一个模块的实例方法到一个对象。extend_object 方法就是做这个的:

module MyMod

  def MyMod.extend_object(obj)
    puts "Extending object id #{obj.object_id}, class #{obj.class}"
    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中不能真正的做到调用析构器来删除对象。这里有个方法define_finalizer,它是当一个对象被gc时,就会调用它:

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


输出的结果类似这样的:

The string 'hello' has an object id 21089680

Nothing to tidy

The original string is now a candidate for collection

Destroying 21089680


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

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里面的局部变量和外面的变量已经不是同一个变量了(假设名字相同)。)。看下面的代码:

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


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

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


这边使用begin ensure是因为要做到就算异常被抛出,文件也要被关闭.
7
0
分享到:
评论
2 楼 simohayha 2008-01-25  
Module#ancestors 返回指定的类或者模块的所有存取方法。
--------------------
这个说错了吧,是返回所有包含的模块

哈哈,恩,错了。。。。
1 楼 dennis_zane 2008-01-25  
Module#ancestors 返回指定的类或者模块的所有存取方法。
--------------------
这个说错了吧,是返回所有包含的模块

相关推荐

    the ruby way 2ed

    这本书的第二版在2006年出版,由Addison-Wesley出版,作者通过深入浅出的方式,揭示了Ruby语言的强大功能和优雅特性。 Ruby是一种动态类型、面向对象的编程语言,它的设计理念是注重简洁和生产力,让开发者能够更...

    Ruby-Way.rar_About Language

    这本书深入浅出地探讨了Ruby语言的各种特性,旨在帮助读者理解并掌握这门强大的动态脚本语言。Ruby以其简洁、优雅的语法和强大的元编程能力而备受赞誉,是许多开发者首选的编程工具。 在Ruby语言中,一切都是对象,...

    the ruby way

    通过学习《The Ruby Way》第二版,读者不仅可以掌握Ruby编程的基本技巧,还能了解到一些高级特性,从而在实践中发挥Ruby的最大潜力。无论是初学者还是有经验的开发者,这本书都能为他们提供宝贵的指导和启示。

    Addison.Wesley.The.Ruby.Way.2nd.Edition.Oct.2006.chm

    《The Ruby Way》第二版是Addison-Wesley出版社在2006年推出的一本深入浅出的Ruby编程语言教程。这本书专为初学者设计,旨在帮助读者快速掌握Ruby语言的基础和高级特性,从而轻松入门Ruby编程。Ruby是一种动态、开放...

    ruby中英文api

    Ruby是一种面向对象的、动态类型的编程语言,以其简洁、优雅的语法著称,尤其适合Web开发,其中Rails框架是其最著名的应用之一。API(Application Programming Interface)是软件开发者用来与操作系统、库或服务进行...

    ruby入门书籍

    2. **动态类型**:Ruby中不需要预先声明变量类型,类型会随着赋值自动确定,这使得代码更简洁,但同时也可能导致运行时错误。 3. **块和闭包**:Ruby中的块是代码段,可以与方法一起使用,闭包是能够记住其定义环境...

    ruby test document

    元编程在Ruby中尤为重要,因为它使得动态功能成为可能,这是Ruby语言最吸引人的特性之一。书中提到了Ruby on Rails的早期思想受到了第一版《The Ruby Way》中的一些概念的启发,特别是书中现在被称为第11章的内容,...

    关于ruby的几本电子书

    Ruby是一种面向对象的脚本语言,以其简洁、优雅的语法和强大的元编程能力著称。在编程领域,它被广泛用于Web开发,尤其是与Rails框架的结合...同时,关注Ruby和Rails的最新动态,了解新版本的特性,以便保持与时俱进。

    Ruby之路

    第二版于2006年10月由Addison-Wesley出版,它全面地涵盖了Ruby语言的核心概念、语法特性以及高级编程技巧。 Ruby是一种动态类型、面向对象的脚本语言,由日本的松本行弘(Yukihiro Matsumoto)于1995年设计并开发。...

    ruby实战书、代码书.rar

    2. **编程技巧**:如何利用Ruby的特性编写出高效且易于维护的代码,如上下文敏感的语法糖和内建函数的巧妙使用。 3. **模块化设计**:书中可能会讲解如何通过模块来组织代码,以及如何使用模块实现命名空间和混入...

    ruby on rails资料

    Ruby on Rails,简称Rails...总的来说,这些资源为学习Ruby和Ruby on Rails提供了一个全面的学习路径,从基础语言特性到Web开发的最佳实践,通过理论与实践相结合的方式,助力开发者快速上手并精通这个强大的开发框架。

    Ruby语言入门教程

    最后,元编程部分将探讨Ruby语言中动态添加方法和属性,以及操作类和模块的能力,这是Ruby作为一种高度动态语言的独特之处。 整个教程不仅提供编程语言的基本知识,还提供了一些编码建议,帮助学习者避免常见错误并...

    Ruby语言教程&案例&相关项目资源.docx

    - **Web开发**:Ruby最著名的应用领域之一是Web开发,尤其是通过Ruby on Rails框架简化了Web应用的开发过程。 - **系统管理和自动化**:Ruby常用于系统管理和自动化脚本的编写,因其优雅的语法和强大的元编程能力。 ...

    the rails way

    《The Rails Way》一书由Obie Fernandez撰写,属于Addison-Wesley Professional Ruby系列,该系列致力于为读者提供实用、面向人且深入的信息,帮助他们利用Ruby平台创建动态技术解决方案。这一系列书籍的创立基于一...

    ruby入门书中文版

    Ruby是一种面向对象的编程语言,以其简洁、优雅的语法而闻名,特别适合初学者入门。...记得实践是检验真理的唯一标准,理论学习之余,多动手编写代码,结合实际项目经验,将使你的Ruby之旅更加丰富多彩。

Global site tag (gtag.js) - Google Analytics