Metaprogramming Ruby
第一章,object model
class与module
- class是object,class name是定数。object的instance value住在object里面,object的method住在class里面。
- class只是一个拥有allocate, new, superclass这三个函数的module。那么什么时候用class,什么时候用module呢?一般当要被include的时候用module(或者当作namespace使用的时候),要生成或者继承的时候使用class。
String.instance_methods == 'abc'.methods # => true
Class.instance_methods(false)            # => [:allocate, :new, :superclass]
# class
'hello'.class # => String
String.class  # => Class
Class.class   #=> Class
# superclass
Myclass.superclass        # => Object
String.superclass         # => Object
Class.superclass          # => Module
Module.superclass         # => Object
Object.superclass         # => BasicObject
BasicObject.superclass    # => nilKernel module
Object Class include 了 Kernel 这个module,Kernel module里面有print()之类的method,因此可以直接在任何地方呼叫print(),如果展开Kernel,在里面定义自己的method的话,也可以在任何地方呼叫自己的method。
private真正的意义
用private关键词定义method有一个规则,那就是不可以指定receiver(接收对象),比如
class C
  def pub
    self.pri #这里是错误的
    pri   #这里是正确的
  end
  private
  def pri;end
end然而呼叫一般的method也有一个规则,就是除了自己,呼叫任何method都必须明确制定receiver。private method也要遵守这个规则,就是说private method只可以呼叫自己。那么从superclass继承的private method呼叫么?答案当然是可以,因为继承的class里面也可以不用明确的制定receiver的。
定数
module或者class可以看成dirictory,定数可以看成file
module Mymodule
  MyConstant = 'outer const'
  class Myclass
    MyConstant = 'inner const'
  end
end
# path
module M
  class C
    X = 'const'
  end
  C::X  # => 'const'
end
M::C::X # => 'const'为了避免自己的class名字跟其他library里面的class名字冲突,可以用module来定义自己的class。比如
module Mymodule
  class C1
  end
  class C2
  end
end第二章,method
重复code的排除
动态呼叫method
class MyClass
  def my_method(arg)
    arg*2
  end
end
obj = MyClass.new
obj.send(:my_method, 3)动态定义method
define_method
class Computer
  def initialize(id, source)
    @id = id
	@data_source = source
  end
  def sef.define_component(method_name)
    define_method(method_name){
	  info = @data_source.send "get_#{method_name}_info", @id
	  price = @data_source.send "get_#{method_name}_price", @id
	  result = "#{method_name.to_s.capitalize}: #{info} ($#{price})"
	  return "* #{result}" if price >= 100
	  result
	}
  end
  define_component :mouse
  define_component :cpu
  define_component :keyboard
endmethod_missing()
obj.mymethod的时候会先向右在obj的class里查找mymethod,如果class里面没有就向上查找superclass,然后一直向上查找到BasicObject,如果BasicObject里面也没有的话就会呼叫obj的method_missing,如果重新定义methodmissing的话就可以呼叫没有定义过的method,这个method就叫做__ghost method__
class Lawyer
  def method_missing(method, *args)
    puts "#{method}(#{args.join(", ")})"
	puts "block" if block_given?
  end
end
bob = Lawyer.new
bob.talk_simple("a", "b") do
end
# => talk_simple(a, b)
# => block与method_missing一样,Module#const_missing是Module下面的method
Module#undef_method(), Module#remove_method()
undef_method会删除class里已经继承这个class里的所有method,remove_method则只会删除receiver里面的method
class Computer
  instance_methods.each do |m|
    undef_method m unless m.to_s =~ /^__|method_missing|respond_to?/
  end
  #...
end第三章,block
Kernel#block_given?()
def a_method
  return yield if block_given?
  "No block"
end闭包
单独的block code是无法执行的,要执行block还要有local variable,instance variable,self等环境,这个环境在ruby里面也叫做束缚(翻译的可能不太准确),block由code和束缚组成。
def my_method
  x = "goodbye"
  yield("cruel")
end
x = "hello"
my_method{|y| "#{x}, #{y} world"} # => "hello, cruel world"这个block取得了x = hello这个束缚。这个特性也被称作闭包。
scope gata
ruby切换scope,然后打开一个新的scope有3个地方,分别是:1,定义class。2,定义module。3,呼叫method, 也就是__class, module, def__这三个关键字。那么如何突破scope,传递束缚呢?其实很简单,定义class,或者定义method的时候不用这3个关键字就可以了,比如定义class的时候可以使用Class.new,定义method的时候可以用difine_method。可以看下面这个例子
my_var = "success"
MyClass = Class.new do
  puts "#{my_var}"
  define_method :my_method do
    puts "#{my_var}"
  end
end下面这个例子是共享scope
def define_methods
  shared = 0
  Kernel.send :define_method, :counter do
    shared
  end
  Kernel.send :define_method, :inc do |x|
    shared += x
  end
end
define_methods
counter # => 0
inc(4)
counter # => 4instance_eval()
在block里面评价object的context(上下文)
class MyClass
  def initialize
    @v = 1
  end
end
obj = MyClass.new
obj.instance_eval do
  self   # => <MyClass:0x3340dc @v=1>
  @v     # => 1
  @v = 2 # => 2
endcleanroom
env = Object.new
@setups.each do |setup|
  env.instance_eval &setup
end
env.instance_eval &eventcallable object
Proc
block转换成Proc,在block前面加上&修饰符就可以了。block通过yield来呼叫,而Proc通过call来呼叫。Proc转换成block,也是在Proc前面加上&就可以了。在1.9里面,Proc通过Proc.new或者Kernel#proc()来创建。
lambda与Proc的区别
lambda通过Kernel#lambda()或者 ー>(将来是否可以用还不知道)来创建,lambda像是一个匿名函数,因此在return的时候,lambda会跳出block,而Proc则会跳出block所在的method。另外一方面,lambda在传递引数的时候比较严格,而Proc则比较宽松,比如
p = Proc.new {|a, b| [a, b]}
p.call(1,2,3) # => [1,2]
p.call(1)     # => [1.nil]Method object
Method通过Object#method()来取得,通过Method#call()来呼叫。Method跟lambda基本一样,不一样的是lambda在scope里面评价,而Method在附属的object里面评价。
第四章 class的定义
class
class_eval(),别名module_eval()
在block里面评价Class的context(上下文)
class instance value
class MyClass
  @var = 1
endclass value则是@@var,一般不建议使用
class的名字只是单纯的__定数__。
c = Class.new(Array) do
  def mymethod 'hello' end
end
MyClass = c特异method
str = "strrrr"
def str.title?
  self.upcase == self
end这里的title?就是str object的特异method
class method 就是 class的特异method
特异class
class << an_object
  # code
end特异method就住在特异class里面。
大统一理论:1,object只有一种,那就是通常的object或者module。2,module只有一种,那就是module或者class或者特异class或者proxy class。3,method只有一种,就住在module(大部分是class)里面。4,所有的object都拥有class,这个class可能是一般的class或者特异class。5,所有的class都有super class(BasicObject除外),所有的class都继承自BasicObject。6,object的特异class的superclass是object的class。class的特异class的superclass是class的superclas的特异class。7,method呼叫的时候先向右一步找他的class,再向上找superclass。
class MyClass
  class << self
    include MyModule
  end
end
# 等同于
Myclass.extend Mymodule
# 等同于
class MyClass
  extend Mymodule
endalias
class String
  alias :real_length :length
  def length
    real_length > 5 ? 'long' : 'short'
  end
end第五章,code来写code
Kernel#eval
class MyClass
  def my_method
    @x = 1
	binding
  end
end
eval "@x", MyClass.new.my_method # => 1instance_eval也可以接收文字,如
array = ['a','b','c']
x = 'd'
array.instance_eval "self[1] = x"
array # => ['a','d','c']