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 # => nil
Kernel 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
end
method_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 # => 4
instance_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
end
cleanroom
env = Object.new
@setups.each do |setup|
env.instance_eval &setup
end
env.instance_eval &event
callable 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
end
class 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
end
alias
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 # => 1
instance_eval也可以接收文字,如
array = ['a','b','c']
x = 'd'
array.instance_eval "self[1] = x"
array # => ['a','d','c']