Ruby 元法术

Andy,ruby

《Ruby元编程》 之后

《Ruby元编程》 (opens in a new tab) 这本书用两个字总结:法术。用一个句话总结:根本没有什么元编程,只有编程而已。

对于 Ruby 来说

我接触到 Ruby 这个语言源于 Rails,但我不希望自己学习 Ruby 只是终于 Rails,终于 Web。
所以我想要熟悉 Ruby,精通 Ruby,想要知道他是什么,他能干些什么,为什么 Rails 能用 Ruby 写出来。

Ruby的元

我对元的理解是,事物对自己的抽象。一个编程语言想要应用到现实世界中,非常需要中间层来一层层地将想要描述的事物抽象出来。 Ruby 的语言设计和元能力有两个优点。

但是 Ruby 语言的性能问题和对应用高并发情况出现的处理能力一直被人诟病。 不过好像2.0++的版本出来后,性能得到了质的飞跃,再也没有什么人说 Ruby 性能的坏话了。

法术

《Ruby元编程》附录中的法术手册就是 Ruby 元编程的一个总结。我认为,假如你把附录中的法术都掌握之后都不用看这本书了。这本书就是 详解剖析这些东西而已。下面的例子在 Ruby 2.1.2版本下测试。 假若你已经对这些字眼非常了解,就不必往下看了。当然我没有非常详细的解析,只是一个括览:

Around Alias 环绕别名

class String
    alias old_reverse reverse
    def reverse
        "x#{old_reverse}x"
    end
end
"abc".reverse # => "xcbax"
"abc".old_reverse # => "cba"

old_reverse 和 reverse互 用,但是重新定义 reverse,old_reverse 指向原来的 reverse。

Blank Slate 白板

class C
    def method_missing(name, *args)
        "a Ghost Method"
    end
end
obj = C.new
obj.to_s # => "#<C:XXXX>"
class C
    instance_methods.each do |m|
        undef_method(m) unless m.to_s =~ /method_missing|resopnd_to?/
    end
end
obj.to_s # => "a Ghost Method"

书里面有个 BUG,用来匹配的正则是“/method_missing|resopnd_to?|^/”,这样把所有方法都匹配到了。
而且 Ruby 这里比较有意思的是“=~”匹配的时候,真返回0,假返回 nil。Ruby 里面的判断有值就为真,而 C 和 C++ 都会将0设为 false,1设为 true。
定义了一个类的 method_missing 方法时候,对象调用了一个不知名方法,就会直接调用 method_missing。method_missing 也是很多 Ruby 魔法的根源。

Class Extyension 类扩展

class C ; end
module M
    def my_method
        "a class method"
    end
end
class << C
    include M
end
C.my_method # => "a class method"
a = C.new
a.my_method # => "NoMethodError: undefined method `my_method' for #<C:0x000000029d5c68>"

这里对类进行方法扩展,但是对于创建类的对象是没有这个方法的。

Class Extension 类扩展混入

module M
    def self.included(base)
        base.extend(ClassMethods)
    end
 
    module ClassMethods
        def my_method
            "a class method"
        end
    end
end
 
class C
    include M
end
C.my_method # => "a class method"

这里的 self.included(base) 使用了类的钩子功能,Ruby 还有其他的钩子方法。
当类C include M的时候,触发了模块M的钩子方法 included,所以 base 就是C,然后C再扩展了M模块的 ClassMethods 的所有方法。

Context Probe 上下文探针

class C
    def initialize
        @x = "some thing"
    end
end
obj = C.new
obj.instance_eval{@x}

instance_eval 可以打开对象自己,操作自己内部。

Dynamic Method 动态方法

class C ; end
C.class_eval do
    define_method :my_method do 
        "a dynamic method"
    end
end
obj = C.new
obj.my_method # => "a dynamic method"

class_eval 可以打开一个类,对类进行操作。

Deferred Evaluation 延迟执行

class C
    def store(&block)
        @my_code_capsule = block
    end
    def execute
        @my_code_capsule.call
    end
end
obj = C.new
obj.store{$X = 1}
$X = 0
obj.execute
$X # => 1

可以动态地将一个块存到对象的一个变量中。

Dynamic Proxy 动态代理

class MyDynamicProxy
    def initialize(target)
        @target = target
    end
    def method_missing(name, *args, &block)
        "result: #{@target.send(name, *args, &block)}"
    end
end
 
obj = MyDynamicProxy.new("a String")
obj.reverse # => "result: gnirtS a"

使用到了 method_missing 和 send。Duang。另外一个魔法诞生了。

Flat Scpoe 扁平作用域

class C
    def an_attribute
        @attr
    end
end
obj = C.new
a_variable = 100
 
obj.instance_eval do
    @attr = a_variable
end
obj.an_attribute # =>100

就是使用了 instance_eval 和闭包嘛。

Kernel Method 内核方法

module Kernel
    def a_method
        "a kernel method"
    end
end
a_method # => "a kernel method"

在 kernel 模块中定义方法,所有对象可见。

Monkeypatch 猴子补丁

class String
    def reverse
        "override"
    end
end
"abc".reverse # => "override"

著名的猴子补丁,在 runtime 中改变类和对象的属性和方法。guerrilla patch 拟音 gorilla patch ,然后变成了 monkey patch。

Object Extension 对象扩展

obj = Object.new
module M
    def my_method
        "a singleton method"
    end
end
class << obj
    include M
end
obj.my_thod # => "a singleton method"

对 obj 的元类进行扩展,混入模块 M 的方法。

Shared Scope 共享作用域

lambda {
    shared = 10
    self.class # => Object
    self.class.class_eval do
        define_method :counter do
            shared
        end
        define_method :down do
            shared -= 1
        end
    end
}.call
 
counter # => 10
3.times {down}
counter # => 7

lambda 对 Object 对象进行操作,并且共享变量。

总结

其实这些东西熟悉了这些东西之后,还是那句话:根本没有什么元编程,只有编程而已。

© FTAndy.RSS