Ruby开源项目源码中经常会出现Ruby法术(spells),把玩着动态元编程(meta programming)的技巧,为了读懂他们就必须去理解常见的法术。下文摘自于《Ruby元编程》–附录C:法术手册。并加入了一些总结和思考。

索引

环绕别名(Around alias)

通过使用”alias_method” 用来给老方法打补丁

class String
  alias_method :old_reverse, :reverse 
  def reverse
    "x#{old_reverse}x"
  end
end

"abc".reverse #=> "xcbax"

白板类(Blank state)

通过使用”< BasicObject” 来生成白板类, 一旦继承Ruby继承链的最顶层–BasicOject, 将不能自省(inspect查看实例内部),因为BasicObject没有任何方法和变量

class C; end
class D < BasicObject; end
C.new #=> #<D:0x007fb9139f7328> 默认调用C.new.to_s
D.new #=> (Object doesn't support #inspect) D.new没有任何方法可供调用

类扩展(Class extention)

通过” class « “打开类,并include模块来添加(类/单件)方法

class C; end

module M
  def my_method
    'a class method'
  end
end

class << C #   为C添加方法,所以是类方法
  include M # 相当于把module M中的内容复制过来
end

C.my_method #=> "a class method"

# (实现对象扩展 Object Extension)添加单件方法也与之类似: 

obj = "abc"
class << obj
  def my_singleton_method
    "This a singleton method."
  end
end
obj.my_singleton_method #=> "This a singleton method."

类宏(Class Macro)

在类中执行类方法

class C; end
class << C
  def class_method
    "This is class method."
  end
end
class C
  class_method() 
end #=> "This is class method."

类实例变量(Class instance variable)

class C
  @my_class_instance_variable = " class variable "
  def self.class_instance_variable
    @my_class_instance_variable
  end
end
C.class_instance_variable #=> " class variable "

洁净室(Clean Room)

使用一个对象调用“instance_eval”方法来作为执行一个代码块的环境(执行环境是目的?)

class CleanRoom
  def a_method(x)
    x*x*x
  end
end
CleanRoom.new.instance_eval{ a_method(2) } #=>8

代码处理器(Code processor)

使用“eval”来执行字符串

File.readlines("a_file_contains_lines_of_ruby.txt").each do |line|
  puts "run #{line.chomp}, result: #{eval(line)}"
end

上下文探针(Context probe)

使用实例调用“instance_eval”可以打破封装, 查看实例内部私有变量和上下文信息(详细参数信息)

def C
  def initialize
    @x = "a private instance variable."
  end
end
C.new.instance_eval{ @x } #=> "a private instance variable."

延迟执行(Defered evaluation)

把上下文信息通过块(block)/lambda/proc 传入类变量中,用于稍后执行,历史上下文信息保持不变

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

动态派发(Dynamic dispatch)

通过实例对象调用“send”方法,在运行时(runtime)决定调用哪个方法,可以增加逻辑批量的调用,可以运用到Rails开发中更新某个对象的多个字段

  method_to_call = :reverse
  obj = "abc"
  obj.send(method_to_call) #=> "cba"

动态方法(Dynamic Method)

在运行时决定去定义一个方法。通过类调用“class_eval”方法获得一个block的环境,在block中调用“define_method”去定义一个方法。

class C; end
C.class_eval do
  define_method :dynamic_method do
    "this is a Dynamic Method" 
  end
end
C.new.dynamic_method #=> "this is a Dynamic Method"

幽灵方法(Ghost Method)

响应一个找不到的方法调用

class C
  def method_missing(name, *args)
    name.to_s.reverse
  end
end
C.new.wtf #=> "ftw"

钩子方法(Ghost Method)

执行特定语句时会触发特定的代码(常用:include, inherited)

$INHERITORS = [] #定义全局变量
class C
  def self.inherited(subclass)
    $INHERITORS << subclass
  end
end
class A < C; end
class B < C; end
class E < C; end
$INHERITORS #=> [A, B, E]

内核方法(Kernel Method)

可以供任何对象调用的方法(可不用指定调用对象),用途: 可以通过定义Kernel的方法来伪造一个关键字。

module Kernel
  def wtf
    "What the fuck."
  end
end
wtf #=> "What the fuck."
C.wtf #=> "What the fuck."
C.new.wtf #=> "What the fuck."

惰性实例变量(Lazy Variable Method)

等第一次访问一个实例变量是才会进行初始化

class C
  def attribute
    @attribute = @attribute || "a lazy instance variable"
  end
end
obj = C.new
obj.attribute #=> "a lazy instance variable"

猴子补丁(Monkey Patch)

修改已有类的特性,打开已有类的已有方法进行修改后直接覆盖(override)原有方法

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

"abc".reverse #=>"override reverse"
# 打开类(Open Class)对已有类进行修改,“猴子补丁”是“更新”已有类的方法,“打开类”是“增加”方法
class Sring
  def open_class_method
    "a open class method"
  end
end
"abc".open_class_method #=> "a open class method"

命名空间(Namespace)

为了防止重名,在定义常量时外面包一层模块(module),调用时: “XXX::CONST_NAME”

module MyNameSpace
  class String
    def to_s
      "myNameSpace"
    end
  end
end
Array.new.to_s #=> "[]" 
MyNameSpace::Array.new.to_s #=> "myNameSpace"

空指针保护(Nil Guard)

用“或||”操作符复写一个可能为空的引用,可以避免报“nil”的错误

x = nil
y = x || "no nil" #=> "no nil"

下包含包装器(Prepended Wrapper)

通过“prepend关键字”复写方法。将需要复写的同名方法定义在module中,再通过“prepend”引入

module M
  def reverse
    "x#{super}x"
  end
end
String.class_eval do
  prepend M
end
"abc".reverse #=> "xcbax"

细化(Refinement)

使用关键字“refine”为类打补丁的另一种方式,作用范围从“using some_module_name”语句开始一直到文件结束,或仅限于包含module的作用域中。

module MyRefinement
  refine String do
    def reverse
      "refine string reverse"
    end
  end
end
"abc".reverse #=> "cba"
using MyRefinement
"abc".reverse #=> "refine string reverse"

细化封装器(Refinement Wrapper)

在细化中调用非细化的方法,使用“super”关键字调用。

moddule StringRefinement
  refine String do
    def reverse
      "x#{super}x"
    end
  end
end
using StringRefinement
"abc".reverse #=> "xcbax"

作用域门(Scope Gate)

class module def 关键字可以隔离作用域

a = 1
defined? a #=>"local-variable"

module m
  b = 1
  defined? a #=> "nil"
  defined? b #=> "local-variable"
end

defined? b #=>"nil"

Self Yield

把self传递给当前代码块

class Person
  attr_accessor :name, :surname

  def initialize #=> "Person.new()"
    yield self   #=> "{ code block }"
  end
end
tommy = Person.new do |p|
  p.name = "Tommy"
  p.surname = "William"
end

共享作用域(Shared Scope)

使用”Class.new“来代替”class“,”Module.new“代替”module“,“Module#define_method”代替“def”,就可以通过闭包(block)传递变量,达到扁平化作用域的效果。

lambda {
  shared = 10
  self.class.class.eval {
    define_method :shared do
      shared
    end
    define_method :down do
      shared -= 1
    end
  }
}.call
shared #=> "10"
down #=> "9"
down #=> "8"

单例方法(Singleton Method)

在一个对象上定义方法

obj = "asd"
class << obj # 可以理解为:将obj单独看作一类,并给obj类定义类方法
  def my_singleton_method
    "x"
  end
end
obj.my_singleton_method #=>"x"

符号转换为Proc(Symbol to Proc)

[1,2,3,4].map(&:odd?) #=> "[true, false, true, false]"
# 等价于 [1,2,3,4].map{|i|i.odd?}

索引