经常有初学者问,Ruby 水平学到什么程度,可以去学习 Rails 了呢?我来分享一段代码,如果初学者能够看懂,说明水平可以了:
module Foo
def self.included(base)
base.class_eval do
extend ClassMethods
include InstanceMethods
end
end
module ClassMethods
def hello
new.world
end
end
module InstanceMethods
def world
puts "Hello, world"
end
end
end
class Bar
include Foo
hello
end
# 程序最后会输出: Hello, world
----------------- 更好的写法如下 --------------------
module Foo
def self.included(base)
base.class_eval do
extend ClassMethods
end
end
module ClassMethods
def hello
new.world
end
end
def world
puts "Hello, world"
end
end
class Bar
include Foo
hello
end
# 程序最后会输出: Hello, world
self.included(base) 一个调用方法 base.class_eval 将随后的 块中的 方法和类 加入 Foo
include => includes code into class. extend => extends using module, appends class methods.
这里都是 把 两个函数 放到 Foo 中
后面 是对 两个 module 的 定义
Bar 把 Foo 的 两个方法 包含了进来 并且 调用了 hello 不知道还对不对 楼下继续
有一个 疑问 就是 我查了 核心库中并没有 included 这个方法 是不是 self.method_name 会在 类初始化实例的时候 就会执行么
不然它是怎么 引用 两个 module 的呢 没有看见 included
的调用
The self.included function is called when the module is included. It allows methods to be executed in the context of the base (where the module is included).
Modules that will be mixed with a class via the include or extend method could define something like a contructor or initializer method to the module. The module initializer method will be invoked at the time the module is mixed with a class. When a class extends a module the module’s self.extended method will be invoked:
module Math
def self.extended(base)
# Initialize module.
end
end
The self prefix indicates that the method is a static module level method. The base parameter in the static extended method will be either an instance object or class object of the class that extended the module depending whether you extend a object or class, respectively.
When a class includes a module the module’s self.included method will be invoked.
module Stringify
def self.included(base)
# Initialize module.
end
end
The base parameter will be a class object for the class that includes the module.
It is important to note that inside the included and extended initializer methods you can include and extend other modules, here is an example of that:
module Stringify
def self.included(base)
base.extend SomeOtherModule
end
end
Class Bar
def self.hello
new.world # => self.new.world => Bar.new.world
end
def world
puts "Hello, world"
end
end
Bar.hello
module Foo
def self.hello
new.world
end
def world
puts "Hello,world"
end
end
class CInclude
include Foo
end
class CExtend
extend Foo
end
在 CInclude 这个类中,它对 hello 这个类方法怎么处理? 同样,对于 CExtend 来说,它怎么处理 world 这个实例方法?
是不是直接无视?
我想起来了,Rails 中用来初始化一个基类,还有 RSpec 中,大量使用这种办法,在一个类混入一个特定模块时,对这个类执行一些特殊初始化操作。
我觉得这就是 Ruby 可爱的地方。很小的技俩,实现了玄幻的功能。
如果使用 vim+snipmate.vim 的话。假设当前文件名为 foo.rb,输入 mod
后敲 Tab,然后敲 3
,就会有如下补全:
module Foo
module ClassMethods
end
module InstanceMethods
end
def self.included(receiver)
receiver.extend ClassMethods
receiver.send :include, InstanceMethods
end
end
included
里的代码可以更加简洁
base.extend, ClassMethods # or base.send :extend, ClassMethods
base.include, InstanceMethods