Skip to content

Commit

Permalink
add posts
Browse files Browse the repository at this point in the history
  • Loading branch information
steven committed Oct 28, 2017
1 parent 7a7b209 commit f79d5c6
Show file tree
Hide file tree
Showing 36 changed files with 342 additions and 342 deletions.
14 changes: 7 additions & 7 deletions _posts/2017-09-01-Ruby元编程读书笔记一.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ tags: Ruby 元编程

## Open Classes

  定义一个函数,去掉字符串中的标点符号和特殊字符,只保留字母、数字和空格:
定义一个函数,去掉字符串中的标点符号和特殊字符,只保留字母、数字和空格:
```ruby
def to_alphanumeric(s)
s.gsub /[^\w\s]/, ''
end
```
  用更加面向对象的方法,应该是打开 String 类,植入 to_alphanumeric() 方法:
用更加面向对象的方法,应该是打开 String 类,植入 to_alphanumeric() 方法:
```ruby
class String
def to_alphanumeric
Expand All @@ -24,7 +24,7 @@ tags: Ruby 元编程

## Inside Class Defintions

  在 Ruby 中,定义类的语句和其他语句没有本质区别,可以在类定义中放置任何语句:
在 Ruby 中,定义类的语句和其他语句没有本质区别,可以在类定义中放置任何语句:
```ruby
3.times do
class C
Expand All @@ -34,9 +34,9 @@ tags: Ruby 元编程
```
>像执行其他代码一样,Ruby 执行了这些在类中定义的代码,注意这里并不是定义三个同名的类。
  Ruby 的 **class** 关键字更像是一个作用域操作符而不是类型声明语句。它的确可以创建一个还不存在的类,不过也可以把这看成是一种副作用。对于 class 关键字,其核心任务是把你带到类的上下文中,让你可以在其中定义方法。
Ruby 的 **class** 关键字更像是一个作用域操作符而不是类型声明语句。它的确可以创建一个还不存在的类,不过也可以把这看成是一种副作用。对于 class 关键字,其核心任务是把你带到类的上下文中,让你可以在其中定义方法。

  我们总是可以重新打开已经存在的类并对它进行动态修改,即使是像 String 或 Array 这样标准库中的类也不例外。这种技术,被称为 **打开类** 技术。打开类技术的隐患在于,如果粗心地为某个类添加了某些方法,可能会无意中覆盖原有的某些方法,这就是 **猴子补丁** 这种说法的由来。
我们总是可以重新打开已经存在的类并对它进行动态修改,即使是像 String 或 Array 这样标准库中的类也不例外。这种技术,被称为 **打开类** 技术。打开类技术的隐患在于,如果粗心地为某个类添加了某些方法,可能会无意中覆盖原有的某些方法,这就是 **猴子补丁** 这种说法的由来。

## The Truth About Classes

Expand Down Expand Up @@ -87,6 +87,6 @@ tags: Ruby 元编程
```
>因此,一个类只不过是一个增强的 Moudle ,增加了三个方法---new, allocate 和 superclass 而已。这几个方法可以让你创建对象并可以把它们纳入到类体系架构中。除此之外,类和模块基本上是一样的。
  正如普通的对象那样,也是通过引用来访问类,obj 和 MyClass 都是引用,唯一的区别在于,obj 是一个变量,而 MyClass 是一个常量。就像类是对象一样,类名也无非就是常量。
正如普通的对象那样,也是通过引用来访问类,obj 和 MyClass 都是引用,唯一的区别在于,obj 是一个变量,而 MyClass 是一个常量。就像类是对象一样,类名也无非就是常量。

  一个模块基本上就是一组实例方法,而类是一个增加了若干新功能(一个 superclass 方法和一个 new 方法)的模块。同时存在模块和类的主要原因在于 **清晰性**。通常,希望它应该在别处被包含(include)时,或者被当成命名空间时,应该选择使用模块;当希望它被实例化或者继承时,应该选择使用类。
一个模块基本上就是一组实例方法,而类是一个增加了若干新功能(一个 superclass 方法和一个 new 方法)的模块。同时存在模块和类的主要原因在于 **清晰性**。通常,希望它应该在别处被包含(include)时,或者被当成命名空间时,应该选择使用模块;当希望它被实例化或者继承时,应该选择使用类。
18 changes: 9 additions & 9 deletions _posts/2017-09-02-Ruby元编程读书笔记二.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@ tags: Ruby 元编程
***关于 load 和 require***

  比如在网上找到一个 motd.rb 文件用来在控制台上显示“当天的消息”, 且想把这段代码集成到最新的程序中去,那么可以使用 load 执行该文件来显示消息: load('motd.rb')。
比如在网上找到一个 motd.rb 文件用来在控制台上显示“当天的消息”, 且想把这段代码集成到最新的程序中去,那么可以使用 load 执行该文件来显示消息: load('motd.rb')。

  不过,使用 load 方法有一个副作用,motd.rb 文件很可能定义了变量和类。**尽管变量在加载完成后会落在当前作用域之外,但常量不会(落在当前作用域中)**。这样,motd.rb 可能会通过它的常量(尤其是类名)污染当前程序的命名空间。
不过,使用 load 方法有一个副作用,motd.rb 文件很可能定义了变量和类。**尽管变量在加载完成后会落在当前作用域之外,但常量不会(落在当前作用域中)**。这样,motd.rb 可能会通过它的常量(尤其是类名)污染当前程序的命名空间。

  可以通过使用第二个可选参数来强制其常量仅在自身范围内有效:load('motd', true)。通过这种方式加载的文件,Ruby 会创建一个匿名模块,使用它作为 **命名空间** 来容纳 motd.rb 中定义的所有常量,加载完成后,该模块会被销毁。
可以通过使用第二个可选参数来强制其常量仅在自身范围内有效:load('motd', true)。通过这种方式加载的文件,Ruby 会创建一个匿名模块,使用它作为 **命名空间** 来容纳 motd.rb 中定义的所有常量,加载完成后,该模块会被销毁。

  require 方法与 load 方法颇为类似,但是它的目的不同。通过 load 方法可以执行代码,而 require 则是用来导入类库。这就是 require 方法没有第二个可选参数的原因。在这些库中的类名通常是你导入这些类库时所希望得到的,因此没有理由在加载后销毁它们。
require 方法与 load 方法颇为类似,但是它的目的不同。通过 load 方法可以执行代码,而 require 则是用来导入类库。这就是 require 方法没有第二个可选参数的原因。在这些库中的类名通常是你导入这些类库时所希望得到的,因此没有理由在加载后销毁它们。

## What Happens When You Call a Method?

Expand All @@ -53,15 +53,15 @@ tags: Ruby 元编程

## Method Lookup

  为了查找一个方法,Ruby首先在 **接收者** 的类中查找,然后一层层地在祖先链中查找,直到找到这个方法为止。可以调用 ancestors 方法来获得一个类的祖先链。
为了查找一个方法,Ruby首先在 **接收者** 的类中查找,然后一层层地在祖先链中查找,直到找到这个方法为止。可以调用 ancestors 方法来获得一个类的祖先链。

  祖先链当中是包含模块的,当你在一个类(甚至可以是另一个模块)中包含(include)一个模块时,Ruby 会创建一个封装该模块的匿名类,并把这个匿名类插入到祖先链中,其位置正好在包含它的类的上方。
祖先链当中是包含模块的,当你在一个类(甚至可以是另一个模块)中包含(include)一个模块时,Ruby 会创建一个封装该模块的匿名类,并把这个匿名类插入到祖先链中,其位置正好在包含它的类的上方。

  Object 类包含了 **Kernel 模块**,因此 Kernel 就进入了每个对象的祖先链,这样在每个对象中可以随意调用 Kernel 模块的方法。如果给 Kernel 模块增加一个方法,这个 **内核方法** 就对所有对象可用,比如 print 方法。
Object 类包含了 **Kernel 模块**,因此 Kernel 就进入了每个对象的祖先链,这样在每个对象中可以随意调用 Kernel 模块的方法。如果给 Kernel 模块增加一个方法,这个 **内核方法** 就对所有对象可用,比如 print 方法。

## Method Execution

  比如调用一个名叫 my_method 的方法,我们先找到了这个方法,发现这个方法定义如下:
比如调用一个名叫 my_method 的方法,我们先找到了这个方法,发现这个方法定义如下:
```ruby
def my_method
temp = @x + 1
Expand All @@ -81,7 +81,7 @@ tags: Ruby 元编程
```
>当开始运行 ruby 程序时,ruby 解释器会创建一个名为 main 的对象作为当前对象,这个对象有时被称为 **顶级上下文**,这个名字的由来是因为这时处在调用堆栈的顶层:这时要么没有调用任何方法,要么调用的所有方法都已经返回了。
  self 的角色通常由最后一个接收到方法调用的对象来充当。不过,在类和模块定义中(并且在任何方法定义之外), self 的角色由这个类或模块担任:
self 的角色通常由最后一个接收到方法调用的对象来充当。不过,在类和模块定义中(并且在任何方法定义之外), self 的角色由这个类或模块担任:
```ruby
class MyClass
self #=> MyClass
Expand Down
2 changes: 1 addition & 1 deletion _posts/2017-09-03-Ruby元编程读书笔记三.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ tags: Ruby 元编程

## What Does Private Method Really Mean?

  既然已经了解了 self,现在就可以重新审视 Ruby 的 private 关键字了。私有方法服从一个简单的规则:**不能明确指定一个接收者来调用一个私有方法**。因此,每次调用一个私有方法时,只能调用于隐含的接收者----self 上。
既然已经了解了 self,现在就可以重新审视 Ruby 的 private 关键字了。私有方法服从一个简单的规则:**不能明确指定一个接收者来调用一个私有方法**。因此,每次调用一个私有方法时,只能调用于隐含的接收者----self 上。
```ruby
class C
def public_method
Expand Down
20 changes: 10 additions & 10 deletions _posts/2017-09-04-Ruby元编程读书笔记四.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ tags: Ruby 元编程

## Calling Methods Dynamically

  当调用一个方法时,通常会使用点(.)标记符,代码如下:
当调用一个方法时,通常会使用点(.)标记符,代码如下:
```ruby
class MyClass
def my_method(my_arg)
Expand All @@ -18,7 +18,7 @@ tags: Ruby 元编程
obj = MyClass.new
obj.my_method(3) #=> 6
```
  可以使用 Object#send() 取代点标记符来调用 MyClass#my_method() 方法:
可以使用 Object#send() 取代点标记符来调用 MyClass#my_method() 方法:
```ruby
obj.send(:my_method, 3) #=> 6
```
Expand All @@ -28,19 +28,19 @@ tags: Ruby 元编程

### 来自 Camping 的例子

  动态派发的例子来自 Camping,这是一个极简主义的 Ruby web 框架。一个 Camping 应用程序将它的配置信息用键值对的方式存储在一个 YAML 格式的文件中。一个博客应用程序的配置文件可能像下面这样:
动态派发的例子来自 Camping,这是一个极简主义的 Ruby web 框架。一个 Camping 应用程序将它的配置信息用键值对的方式存储在一个 YAML 格式的文件中。一个博客应用程序的配置文件可能像下面这样:
```yaml
admin : Bill
title : Rubyland
topic : Ruby and more
```
  Camping 可以把这些键值对从文件中拷贝到自己的配置对象(这个对象的类型是 OpenStruct)中。假设你将文件的配置信息存入 conf 对象中,在理想状态下,存储配置信息的代码应该像下面这样:
Camping 可以把这些键值对从文件中拷贝到自己的配置对象(这个对象的类型是 OpenStruct)中。假设你将文件的配置信息存入 conf 对象中,在理想状态下,存储配置信息的代码应该像下面这样:
```ruby
conf.admin = 'Bill'
conf.title = 'Rubyland'
conf.topic = 'Ruby and more'
```
  但是 Camping 不可能事先知道特定应用中有哪些键值对,因此它无法知道应该去调用哪个方法。它只能在运行时才能根据 YAML 文件的内容发现给定的键值对。于是,Camping 求助于动态派发技术,为每个键值对都构造出一个赋值方法的名字,并且把这个方法发送给 conf 对象:
但是 Camping 不可能事先知道特定应用中有哪些键值对,因此它无法知道应该去调用哪个方法。它只能在运行时才能根据 YAML 文件的内容发现给定的键值对。于是,Camping 求助于动态派发技术,为每个键值对都构造出一个赋值方法的名字,并且把这个方法发送给 conf 对象:
```ruby
if conf.rc and File.exists?(conf.rc)
YAML.load_file(conf.rc).each do |k,v|
Expand All @@ -51,17 +51,17 @@ tags: Ruby 元编程

### 来自 Test::Unit 的例子:

  另外一个动态派发的例子来自 Test::Unit 标准库。Test::Unit 使用一个命名惯例来判定哪些方法是测试方法。一个 TestCase 对象会查找自己的公开方法,并选择其中名字以 test 开头的方法:
另外一个动态派发的例子来自 Test::Unit 标准库。Test::Unit 使用一个命名惯例来判定哪些方法是测试方法。一个 TestCase 对象会查找自己的公开方法,并选择其中名字以 test 开头的方法:
```ruby
method_names = public_instance_methods(true)
tests = method_names.delete_if {|method_name| method_name !~ /^test./}
```
  现在这个 TestCase 对象得到了测试方法数组。后面,它会使用 send() 方法来调用数组中的每个方法。动态派发的这种特殊用法有时被称为 **模式派发**,因为它基于方法名的某种模式来过滤方法。
现在这个 TestCase 对象得到了测试方法数组。后面,它会使用 send() 方法来调用数组中的每个方法。动态派发的这种特殊用法有时被称为 **模式派发**,因为它基于方法名的某种模式来过滤方法。
>TestCase实际使用的是 send() 方法的别名方法 `__send()__`。Object#send() 方法功能非常强大,**可以用 send() 调用任何方法,甚至调用私有方法**
## Defining Methods Dynamically

  可以利用 Module#define_method() 方法定义一个方法,只需要为其提供一个方法名和一个充当方法主体的块即可,代码如下:
可以利用 Module#define_method() 方法定义一个方法,只需要为其提供一个方法名和一个充当方法主体的块即可,代码如下:
```ruby
class MyClass
define_method :my_method do |my_arg|
Expand All @@ -76,7 +76,7 @@ tags: Ruby 元编程
## method_missing()

  在 Ruby 中,编译器并不强制方法调用时的行为,这意味着你甚至可以调用一个并不存在的方法:
在 Ruby 中,编译器并不强制方法调用时的行为,这意味着你甚至可以调用一个并不存在的方法:
```ruby
class Lawyer; end

Expand All @@ -85,7 +85,7 @@ tags: Ruby 元编程
```
>当调用 talk_simple 方法时,Ruby 会到 nick 对象的类中查询它的实例方法。如果在那里找不到 talk_simple 方法,Ruby 会沿着祖先链向上搜寻进入 Object 类,并且最终来到 Kernel 模块。由于在哪里都没找到 talk_simple 方法,所以只好在 nick 上调用一个名为 method_missing 的方法,这个方法是 Kernel 的一个实例方法,而所有的对象都继承自 Kernel 模块。
  可以直接调用 method_missing() 方法来进行实验,尽管这是一个私有方法,但是还是可以通过 send() 方法来做到:
可以直接调用 method_missing() 方法来进行实验,尽管这是一个私有方法,但是还是可以通过 send() 方法来做到:
```ruby
nick.send :method_missing, :my_method

Expand Down
Loading

0 comments on commit f79d5c6

Please sign in to comment.