Ruby元编程技术1-对象模型(Ruby Meta Programming - Object Models)

最近睡前把《Ruby元编程》作为枕边书,复习一些元编程技巧。顺手整理记录下这些技巧的同时,也为了这门语言能更广泛地传播,希望有更多的人喜欢Ruby这个神器。

一切都是对象

来到Ruby的世界,请你首先不要被这一切搞晕,实际上这些反而让Ruby的对象模型概念变得更加清晰。一切都是对象,对象是一个类,类也是一个对象。而实际上类是一个带有特殊功能的模块。

1
2
3
4
5
6
7
8
9
10
11
2.5.1 :001 > Class.superclass
=> Module
2.5.1 :002 > Module.superclass
=> Object
2.5.1 :003 > Object.superclass
=> BasicObject
2.5.1 :004 > BasicObject.superclass
=> nil
2.5.1 :005 > Kernel.class
=> Module

模块的超类是BasicObject,BasicObject才是Ruby类系统的根。BasicObject派生了Objce和Kernel,Object又派生出了Module,Module派生出Class。
这像不像道德经里所说的“道生一,一生二,二生三,三生万物”? 再说下去更迷糊了,去Ruby的世界里自己领悟吧。Ruby诞生自亚洲,一直觉得它是一门充满东方哲学的编程语言。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2.5.1 :001 > Class.ancestors
=> [Class, Module, Object, Kernel, BasicObject]
2.5.1 :002 > Class.superclass
=> Module
2.5.1 :003 > Module.ancestors
=> [Module, Object, Kernel, BasicObject]
2.5.1 :004 > Module.superclass
=> Object
2.5.1 :005 > Object.ancestors
=> [Object, Kernel, BasicObject]
2.5.1 :006 > Object.superclass
=> BasicObject
2.5.1 :007 > BasicObject.superclass
=> nil
2.5.1 :008 > Kernel.ancestors
=> [Kernel]
2.5.1 :009 > BasicObject.ancestors
=> [BasicObject]

猴子补丁(Monkey Patch)

在元编程的所有技巧中,首先需要了解的就是Ruby的“猴子补丁”,这是一种打开类的技巧。不同于其他面向对象语言,Ruby的任何类都可以在运行态打“补丁”的,称之为Monkey Patch。

例如,对字符类String,可以在任何地方打开这个类,对它添加一些方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
2.5.1 :001 > class String
2.5.1 :002?> def test_method
2.5.1 :003?> return self+"AAA"
2.5.1 :004?> end
2.5.1 :005?> end
=> :test_method
2.5.1 :006 > s="aaa"
=> "aaa"
2.5.1 :007 > s.class
=> String
2.5.1 :008 > s.test_method
=> "aaaAAA"
2.5.1 :009 >

在上面的操作中,我们可以看到,对于Ruby的内置类String,我们为其添加了test_method方法,在下文中,任何的String对象便具有了新添加的这个方法,这就是Ruby打开类的魔术。

你甚至可以对内核模块Kernel打一个Monkey Patch,让你的方法在下文中得到全局支持:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2.5.1 :001 > test
Traceback (most recent call last):
3: from /home/chorder/.rvm/rubies/ruby-2.5.1/bin/irb:11:in <main>
2: from (irb):1
1: from (irb):1:in test
ArgumentError (wrong number of arguments (given 0, expected 2..3))
2.5.1 :002 > module Kernel
2.5.1 :003?> def test
2.5.1 :004?> puts "OK"
2.5.1 :005?> end
2.5.1 :006?> end
=> :test
2.5.1 :007 > test
OK
=> nil
2.5.1 :008 >

不同的是,Kernel是一个模块,需要用module关键字来打开。test方法一开始是不存在的,把它作为“补丁”加入到Kernel模块以后,就可以在下文调用了。

Monkey Patch在有些编程语言里是个贬义词,但在Ruby里,它是一个很实用的功能。

细化(refine)

如你所见,Ruby打开类的作用是如此神奇,以至于Monkey Patch一不小心可能会引发一些大的问题。因此有时候需要配合类的细化功能一起使用。

细化的用法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2.5.1 :001 > module Refine
2.5.1 :002?> refine String do
2.5.1 :003 > def test
2.5.1 :004?> return self+"TEST"
2.5.1 :005?> end
2.5.1 :006?> end
2.5.1 :007?> end
=> #<refinement:String@Refine>
2.5.1 :008 > "AAA".test
Traceback (most recent call last):
2: from /home/chorder/.rvm/rubies/ruby-2.5.1/bin/irb:11:in <main>
1: from (irb):8
NoMethodError (private method test called for "AAA":String)
2.5.1 :009 > module Test
2.5.1 :010?> using Refine
2.5.1 :011?> "AAA".test
2.5.1 :012?> end
=> "AAATEST"
2.5.1 :013 >

在一个模块中使用refine关键字定义细化之后,在另一个模块中使用using关键字引入这个用于定义细化的模块,细化定义的作用于就只限于调用细化模块的模块内部。

命名空间(Name Space)

在Ruby中,使用::标识引入不同作用域中的类和模块,用于界定类和模块,防止命名冲突,也让源代码的结构更加清晰。

1
2
3
4
5
6
7
8
9
10
11
12
13
2.5.1 :001 > module A
2.5.1 :002?> module B
2.5.1 :003?> class C
2.5.1 :004?> D="Constant"
2.5.1 :005?> end
2.5.1 :006?> end
2.5.1 :007?> end
=> "Constant"
2.5.1 :008 >
2.5.1 :009 > A::B::C::D
=> "Constant"
2.5.1 :010 >

Rails中使用CoffeeScript集成Simditor与ActiveStorage的正确姿势(Rails, CoffeeScript, Simditor, and ActiveStorage) Metasploit Resource 脚本简要指南 (Creating Metasploit Resource Script )
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×