Ruby 进入 Ruby 的世界——基础篇

zuoninger · October 13, 2016 · 12786 hits

第 1 章 进入 Ruby 的世界

本章主要内容

  • Ruby 语法的生存工具箱[1]

  • Ruby 基础编程指引:程序编写、保存、运行和错误检查

  • Ruby 安装指南

  • Ruby 的扩展机制

  • Ruby 中易用的命令行工具,包括交互式 Ruby 解释器(irb)

本书的内容是 Ruby 基础,而本章是基础中的基石。本章的目标是让读者在开始学习 Ruby 之前掌握足够的知识和技巧。

接下来读者将看到 Ruby 的基本语法和技术,以及 Ruby 的运行机制:如何写一个程序,怎样使用 Ruby 运行程序,以及如何把一个程序分写到多个文件中。此外,读者还将学习到一些开关(switch)[2]的用法,即它们如何改变 Ruby 解释器(名为ruby的程序,用于执行用 Ruby 语言写的程序文件)的作用,也会学习使用一些重要的辅助工具,让读者的 Ruby 程序员生涯更加轻松和高效。

本章将 Ruby 领域的观点分为以下 3 个基本层次。

  • 语言核心:设计原则、语法、语义。
  • Ruby 支持的扩展(extension)和类库(library),以及用户自己添加到扩展中的工具。
  • Ruby 自带的命令行工具,用于运行解释器和其他一些重要工具。

这 3 个部分在一个系统中环环相扣,因此在本书中它们的内容会相互贯穿,不过在本章中会尽量分开讨论。尽管如此,这 3 个部分的内容都会贯穿在书的每一章中。

{Ruby,ruby,还是 RUBY?!!!}

Ruby 是一门编程语言。我们会谈论“学习 Ruby”,还会问一些问题,比如“你知道 Ruby 吗?”ruby是指一个计算机程序,特指 Ruby 的解释器,它可以读取并运行程序。可以看到这个命名方式在一些文章中出现,如“你用ruby运行我的文件,但什么也没发生。”,或者“ruby可执行文件的完全路径是什么?”最后是 RUBY,准确来说,没有这样的写法。Ruby 不是一个缩略语词汇,所有字母都大写的拼写方式向来都是错的。人们在对待 Perl 语言的时候也出现过同样的错误,或许是因为他们看到了如 BASIC 和 COBOL 这样的拼写方式,但 Ruby 不同。一般用“Ruby”来表示编程语言,用“ruby”来表示 Ruby 的解释器。

第 1 章不是只为服务于其他章节而存在的,它也有自己存在的价值:学习真正的 Ruby 技术和这门语言设计中的要点。其目标是为了引导和启发读者,但即使如此,学习过程还是会深入 Ruby 语言的一些关键层面。

1.1 Ruby 语言基础知识

本节的目标是让读者开始接触 Ruby。这里采用广度优先的方法:大致围绕语法学习、代码编写、程序运行这样一个循环过程展开。

说到这里,读者需要在自己的电脑上安装好 Ruby。书中的例子都使用 Ruby 2.1.0[3]编写。还需要准备一个文本编辑器(任何偏好的编辑器都可以,也可以用纯文本编辑器,但文字处理软件不行)和一个存放 Ruby 程序文件的目录(亦称文件夹)。可以命名目录为 rubycode 或者 rubysamples,无论什么名字都可以,只要能够区别于其他工作区并方便地找到练习程序文件即可。

{交互式 Ruby 控制台程序(irb)是最好的朋友!!}

随 Ruby 发布的 irb 实用工具,是一个 Ruby 命令行工具,irb 使用的广泛程度高于 Ruby 解释器本身。启动 irb 之后,键入 Ruby 代码,它将执行代码并输出结果值。

在命令行中键入irb,然后输入在文中所见的示例代码。例如:

{:--}..\1.tif{20%}

打开 irb 会话意味着可以随时测试任意数量的 Ruby 代码段。大多数 Ruby 程序员都发现 irb 是不可缺少的,而且本章中的一些示例也是使用 irb 运行的。

读者将看到下面 irb 的示例使用了命令行选项,它的输出结果更易于阅读。

..\2.tif{40%}

运行 irb 时加入此选项或者不加,就可以看到--simple-prompt选项的效果。正如所见,这个简单的提示符选项将使屏幕更简洁明了。默认的(非简单的)提示符显示了更多的信息,如在交互会话时的行号统计。但从众多的示例可以看出,简单的提示符已足够使用。

因为 irb 是一个用于运行 Ruby 语言的命令行工具,所以直到 1.4.2 节才会详细讨论它。如有需要,可以转到那一节去看一下,这是最直接的方式。

完成 Ruby 的安装,并创建好工作目录的,就继续学习 Ruby 吧,这样我们就能分享 Ruby 学习和探索中的见地。掌握 Ruby 的语法将会是一个良好的开端。

1.1.1 Ruby 语法生存包

下面 3 个表总结了 Ruby 的一些特性,这对于理解本章的例子和开始体验 Ruby 语言大有益处。不必记住它们,只要看一下并在稍后用到的时候回查即可。

表 1-1 包含了 Ruby 的一些基本运算。表 1-2 中介绍了获取基础键盘输入、将输出发送到屏幕以及基本的条件语句。表 1-3 简要描述了 Ruby 的特殊对象和注释的语法。

表 1-1 Ruby 的基本运算

操 作 示 例 注 解
算术运算 2+3(加法)
2–3(减法)
2*3(乘法)
2/3(除法)
10.3 + 20.25
103 - 202.5
32.9 * 10
100.0 / 0.23
所有的运算都使用整型或浮点型。如例子中所示,混合使用整型和浮点型数值进行运算,其结果为浮点型



注意:需要使用0.23而不是.23
赋值 x=1
string = "Hello"
本操作将一个对象(右边)绑定到一个局部变量(左边)之上。与此同时,把对象看作是用变量表示的一个值
值比较 x == y 注意:使用两个等号(不同于赋值使用一个等号)
字符串到数值的转换 x = "100".to _ i
s = "100"
x = s.to _ i
为了算术运算的顺利执行,必须保证使用的是数字而不是字符构成的字符串。to_i用于执行字符串到整型的转换

表 1-2 Ruby 的基本 I/O(输入/输出)方法和流控制

操 作 示 例 注 解
打印到屏幕 print "Hello" puts "Hello" x = "Hello" puts x x = "Hello" print x
 x = "Hello" px 如果字符串的行尾没有包含换行符,puts就会添加一个,但print不会 print会精确打印内容并让光标留在末尾(提示:在某些系统平台,在程序输出的末尾会自动换行) P会输出一个审查字符串,它通常会包含一些额外的输出信息
获取键盘单行输入 gets string = gets 直接将输入的一行赋值给变量(第二个例子中的变量string
条件语句执行 if x == y  puts "Yes!" else  puts "No!" end 条件语句总是以单词end结束(更多细参见第 6 章)

表 1-3 Ruby 的特殊对象和注释

操 作 示 例 注 解
特殊值对象 true false nil 对象truefalse通常用于返回条件表达式的值。对象 nil 是“nonobject”的一种,表明值或者结果的缺失。false 和nil使得条件表达式失败,所有的其他对象(包括 true,当然也包括0和空字符串)使得其成功(更多细节参见 第7章)
默认对象 self 关键字self引用默认对象。self 是一个依赖于运行时上下文,由不同对象扮演的角色。没有指定调用者的方法,调用的时候会默认为被 self 调用(更多细节参见第 5 章)
代码中的注释 # 一个注释x = 1 # 一个注释 注释部分,会被编译器忽略

以上摘要表中已经包含了许多 Ruby 的基础和语法。读者需要能够辨认出 Ruby 标识符(identifier)的几种不同写法,尤其是对 Ruby 中的对象和方法调用有一个感性认识。我们稍后将会谈论这些内容。

1.1.2 多种多样的 Ruby 标识符

Ruby 的标识符类型很少,一眼就能辨认和区分它们。标识符体系如下。

  • 变量(variable):
    • 局部变量(local variable);
    • 实例变量(instance variable);
    • 类变量(class variable);
    • 全局变量(global variable)。
  • 常量(constant);
  • 关键字(keyword);
  • 方法名(method name)。

这是一个很小的体系,很容易掌握,这一节将讨论它们。记住本节的目标是学习辨认不同的标识符。本书后面将学习如何使用和使用它们的时机。这只是标识符知识的第一课。

1.变量

局部变量以小写字母或者下划线开头,包含字母、下划线或数字。xstringabcstart_valuefirstName都是有效的局部变量命名方式。然而,值得注意的是,在组合多个单词以命名局部变量时,Ruby的约定是使用下划线作为命名规范,而不使用驼峰命名法,如使用first_name而不使用firstName

实例变量为独立的对象存储信息,它通常以一个单独的符号(@)开头,后面的字符使用与局部变量相同的命名规则,如@age@last_name。尽管局部变量不能以大写字母开头,但是实例变量可以在@符号之后的第一个位置使用大写字母(但不能使用数字)。但是通常来说,@符号之后还是使用小写字母。

类变量在每一个类层级上存储信息(同样,现在也不用担心它的语义)。它与实例变量使用相同的命名规则,只有一点不同,它以两个@符号(@@)开头,如@@running_total

全局变量可以通过它的美元引导符号($)辨认出来,如$population。跟在$符号之后的语句不使用局部变量的命名规则。有一些全局变量名为$:$1$/,还有$stdin$LOAD_PATH。但只要以$符号开头,它就是一个全局变量。这些非字母的标识符是预定义的,因此不必担心其中的标点符号是否合法。

表 1-4 总结了 Ruby 变量的命令规范。

表 1-4 总结 Ruby 变量命名规范

变 量 类 型 Ruby 规范 非 规 范
局部变量 first _ name FirstName 、 _ firstName 、 __ firstName 、 name1
实例变量 @first _ name @First _ name 、 @firstName 、 @name1
类变量 @@first _ name @@First _ name 、 @@firstName 、 @@name1
全局变量 $FIRST _ NAME $first _ name 、 $firstName 、 $name1

2.常量

常量使用大写字母开头。AStringFirstNameSTDIN都是有效的常量命名。在 Ruby 命名规范中,如遇到命名多词组合的常量时,可以使用驼峰命名法(FirstName)也可以使用下划线分隔且所有字母大写(FIRST_NAME)的方式。

3.关键字

Ruby 拥有很多的关键字,它们是预定义的保留词,与特定编程任务和上下文关联。关键字包括def(用于方法定义)、class(用于类定义)、if(条件执行)和FILE(当前被执行文件的名称)。关键字大约有 40 个,通常是简短的、单一词汇(与下划线组合多单词方式相反)的标识符。

4.方法名

Ruby 中的方法命名遵从与局部变量相同的规则和约定(除了以?!=结尾的符号,其重要作用稍后会讲述)。这是一种设计理念:方法并不因其自身作为方法而被人关注,而是简单地作为提供值的表达式融入到程序的结构中。在一些上下文中,很难一眼就区分出一个表达式是一个局部变量还是一个方法名,这一切源自设计,是有意为之。

讲完方法,现在已经有了一张 Ruby 标识符的“路线图”,让我们谈一下编程语言的语义,尤其是对象及其方法的重要作用。

1.1.3 方法调用、消息和 Ruby 对象

Ruby 把所有的数据结构和值都看做对象,从整数和字符串这样简单的标量(原子的)值,到数组(array)这样的复杂的数据结构一概如此。每个对象都能响应一组特定的消息,对象能够接收的每个消息直接对应一个方法——有名称的、可以被有执行能力的对象触发的可执行例程。

对象也可以用字面量构造器表示,如字符串用双引号,或者已绑定值的变量。消息通过特殊的点运算符(.)送达:点右边的消息被发送到点左边的对象上。(另外,有许多特殊的给对象发送消息的方式,但是点是最常用和基础的方式。)参见表 1-1 中的示例:

{:--}..\1.tif{25%}

点(.)意味着消息to_i被发送给字符串对象"100"。字符串对象"100"作为消息的接收者被调用。也可以说是方法to_i被字符串对象"100"调用。方法调用的结果是生成整数 100,然后作为赋值运算的右表达式赋值给变量x

{为什么使用两种术语?!!}

何苦为该说“发送to_i消息”还是说“调用to_i方法”而烦恼?对于同一操作为什么有两种不同描述?因为它们不完全相同。大多数时候,发送消息给接收的对象,对象就会执行对应的方法。但有些时候是没有对应方法的,对于点右边的任意标识符,并不能确保接收者拥有的方法与发送的消息相匹配。

这听起来有些混乱,其实不然。因为对象可以拦截未知的消息并使它们拥有具体含义。例如,Ruby on Rails Web 开发框架大量使用了如下的技术:发送未知消息到对象并拦截那些消息,然后能够在使用当前数据库表的列名作为动态条件的情况下顺畅运行。

方法可以带有参数,这个参数同时也是对象。(虽然有些用于创建和操作对象的语法结构本身不是对象,但在 Ruby 中几乎所有一切都是对象。)下面是一个带有参数的方法调用:

..\2.tif{30%}

字符串对象 100 调用方法to_i并传递参数 9,生成一个九进制的 100 所得到的十进制整数,因此x现在等于十进制的 81。

这个例子也同时展示了使用圆括号包含参数的方式。这些圆括号通常情况下是可选的,但是在大多数复杂的情况下,为了避免语法上的歧义,圆括号的使用是必要的。大部分程序员都尽可能在方法调用时使用圆括号,这样做是为了避免歧义。

完整的 Ruby 程序是由对象以及发送给对象的消息所组成。作为一个 Ruby 程序员,大多数时间要么是定义对象所能完成的任务(定义方法),要么是请求对象完成这些任务(给对象发送消息)。

接下来在书中将会深入地讲述这些内容。再说明一下,这一段简短的概述是引领读者进入 Ruby 世界的其中一步。当看到点出现在某些令人费解的位置时,应该把它理解为一个发送给对象(左边)的消息(右边)。同时,也该记住一些裸词(bareword)风格的方法调用方式,例如puts在如下示例中的调用方式:

..\1.tif{20%}

这里尽管缺少消息发送所需要的点以及该消息的显式接收者,却依然发送了消息puts并传递了参数"Hello."给一个对象:默认对象self。在程序运行期间,虽然作为self的对象会通过特定规则发生改变,但self总是被预定义好的。在第 5 章中将会对self进行详细阐述。现在,只要知道像puts这样的裸词的方法调用方式即可。

对象的概念在 Ruby 中是最为重要的,与此紧密相关并扮演重要角色的概念是类(class)。

用类解释对象的由来

类定义了一组行为和功能,每一个对象是一个具体类的实例。Ruby 提供了大量的内置类,它们代表了重要的功能数据类型(如StringArrayFixnum)。每次创建一个字符串对象的同时,就创建了一个String类的实例。

用户可以编写自己的类,甚至可以修改已经存在的 Ruby 类。如果不喜欢字符串或者数组的行为,可以修改它。这看起来虽然不好,但是在 Ruby 中是允许这样做的。(第 13 章中将描述修改内置类的利弊。)

尽管每一个 Ruby 对象都是类的一个实例,但是类的概念却不如对象的概念那么重要。那是因为对象可以发生改变,它可以获得在类中没有定义过的方法和行为。类负责将对象变为实际的存在,这就是耳熟能详的实例化(instantiation)过程。而对象在实例化之后,就进入了自己的生命周期。

对象有能力包含一个在类中没有定义的行为,这是设计 Ruby 作为一门编程语言时最为核心的原则之一。正如读者所猜测,书中将在不同的上下文中不断地回到这一点来讨论。对目前而言,只要意识到尽管每个对象对应一个类,但对象的行为不由对象的类唯一决定即可。

了解了 Ruby 的知识(疑惑之时回看一下这些内容)后,让我们尝试着运行一个程序吧。

1.1.4 编写和保存一个简单程序

在本节中,将会在之前创建的 Ruby 示例程序目录中创建一个程序文件。第一个程序是一个摄氏—华氏度转换工具。

{注意} 当然,在真实场景中,温度的转换使用的是浮点数。而这里在输入输出时使用整数,主要是为了专注于程序的结构和程序的执行。

这个例子将被反复提到,并根据本书的需要进行添加和修改。它遵循如下的迭代过程。

  • 整理程序的输出结果。
  • 从用户的键盘输入中接收输入数据。
  • 从文件中读取数值。
  • 将程序结果写入一个文件。

第 1 个版本很简单,仅关注文件创建和程序运行的过程,而不用深入程序逻辑的细节。

创建第一个程序文件

使用一个纯文本编辑器,输入代码清单 1-1 所示的代码到一个文本文件并保存为 c2f.rb 到示例目录中。

代码清单 1-1 简单的专用摄氏—华氏度转换工具(c2f.rb)

..\..\页面提取自- Black_TWGR_e-Book2_June19.tif{60%}

{注意} 根据用户的操作系统的不同,Ruby 程序文件有可能仅用文件名或一个短名称就可以独立运行,并不需要使用文件扩展名。尽管如此,请记住,.rb 的文件扩展名在一些情况下是强制的,这主要是涉及拥有多个文件(后面会详述)的程序和有文件间相互查找机制的程序。在本书中,所有 Ruby 程序文件名都以.rb 结尾,这是为了确保示例程序可以在尽可能多的平台上运行,同时尽可能少地对系统进行管理干预。

现在,已经有了一个完整的(虽然很小)Ruby 程序,可以运行它了。

1.1.5 给 Ruby 提供程序

运行一个 Ruby 程序需要给 Ruby 解释器传递程序的源码文件(或者多个文件),这个 Ruby 解释器名为ruby,后面会依次解释。在提供一个程序给ruby而不是请求 Ruby 运行程序之后,它会检查程序代码的语法错误。

1.检查语法错误

如果在转换公式中使用 31 替换 32,就会发生一个程序性错误。Ruby 还是会适时地运行程序并给出有缺陷的结果。但是假如程序的第 2 行中不小心遗漏了右圆括号,那就是语法错误,Ruby 将不能运行这个程序。

..\..\0.tif

(错误出现在第 5 行,即程序的最后一行,因为 Ruby 一直在耐心等候右圆括号出现,结果却没有。)

Ruby 解释器提供一种便捷的方式来检查语法错误而不必运行程序。它会读取文件并指出语法是否有错。为了检查源文件的语法错误,可以这样做:

..\..\0.tif{30%}

命令行中的-cw标志(flag)是两个标志的简写,它们分别是:-c-w。标志-c意味着检查语法错误。标志-w可以激活高级别的警告:如果程序都合乎 Ruby 语法,Ruby 就不会发出警告,除非有比语法更值得商榷的理由。

假如输入的文件正确,将在屏幕上会看到如下信息:

..\..\0.tif{18%}

2.运行程序

为了运行程序,再次提供源文件给解释器,但是这一次不用加入-c-w标志。

..\..\0.tif{25%}

如果一切顺利,将可以看到计算的结果输出如下:

..\..\0.tif{25%}

计算的结果正确,但是计算的结果超过了 3 行,这看起来不够好。

3.温度转换器的第二次迭代

问题要追溯到puts命令和print命令的区别。假如字符串没有以一个已经存在的换行符结束,puts命令会在它打印的字符串尾部插入新换行符。相反,print打印字符串之后就停止了,它不会自动跳转到下一行。

为了修正这个问题,把前两行的puts命令改为print

..\..\0.tif{50%}

(注意,is后面的空格,它是为了确保在is和数值之间有一个空格。)现在输出的是:

..\..\页面提取自- Black_TWGR_e-Book2_June19-2.tif{40%}

puts是“put(就是 print)string”的缩写。尽管 put 没有直观的表示会调用换行符,但是puts会这样做:如同print,打印用户的数据,之后自动地转到新一行。假如让puts打印已经以换行符结束的一行,它不会再次添加换行符。

假如读者已经在其他编程语言中使用过打印的工具,而这些工具没有自动添加换行符(如 Perl 语言的print函数),那么可以自己编写类似 Ruby 中的实现,一个能打印值并添加换行符的工具:

..\..\0.tif{45%}

尽管如此,大可不必这样去做,puts已经实现了。习惯使用puts吧,并在这个过程中遵循使用其他的 Ruby 习语和约定。

{警告!!} 在一些操作系统平台中(尤其是在 Windows 中),程序运行的结尾会打印输出额外的换行符。这意味着实际上print已经代替了puts,而puts很难被系统检测到。意识到这两者的区别,并在最常用的场景中使用其中一个,可以充分确信得到所期望的结果。

看一下屏幕的输出,接下来将扩展一点 I/O 领域的知识,包括键盘的输入和文件的操作。

1.1.6 键盘和文件 I/O

Ruby 提供了很多在程序执行过程中读取数据的技术,包括从键盘读取和从磁盘文件读取。它们有许多用途:不仅仅在编写每个应用程序的过程中会用到,在编写维护、转换、管理或者操纵用户的工作环境的代码时也几乎一定会用到。本章中包括了一些输入处理的技术,更多关于 I/O 操作的技术详见第 12 章。

1.键盘输入

反复执行 100 摄氏度等于 212 华氏度的程序,其价值是有限的,更有价值的程序则是可以自由指定华氏温度并获得对应的摄氏温度。

修改程序完成如上的功能需要几个步骤,分别需要使用表 1-1 和表 1-2 中提及的方法gets(获取键盘输入的一行数据)和to_i(转换为整型),读者应该已经熟悉它们其中的一个。由于这不仅仅是修订版本而是一个新程序,所以把代码清单 1-2 中的代码版本放到名为 c2fi.rb 的新文件中(i意味着交互)。

代码清单 1-2 交互式温度转换器(c2fi.rb)

..\..\页面提取自- Black_TWGR_e-Book2_June19-3.tif{80%}

..\..\0.tif{70%}

下面为新程序的一组运行结果:

..\..\0.tif{80%}

{简化代码!!}

将代码清单 1-2 中代码的输入、计算、输出操作进行简化。简化重写后如下:

..\..\0.tif{}

虽然这个版本确实减少了变量使用,但是会要求阅读代码的人接受这组有些密集(但是简短)的表达式。任何既定的程序通常都需要在长代码(也许更清晰)和短代码(也许有点晦涩)之间进行抉择。而有时候,短代码则更为清晰,这就是 Ruby 的编码风格。

如果不深究更多细节,现在这个版本已经是一个通用的摄氏度转华氏度的解决方案。接下来,让我们学习文件的读取。

2.读取文件

从 Ruby 程序中读取文件并不困难,至少在大多数情况下,比键盘输入还要容易。温度转换器的下一个版本将从一个文件中读取一个数组,然后从摄氏度转换为华氏度。

首先,创建一个文件并命名为 temp.dat(温度数据),同时包含一个数字:

..\..\页面提取自- Black_TWGR_e-Book2_June19-5.tif{5%}

现在,创建第三个程序,命名为 c2fin.rb(in 意为文件输入),如代码清单 1-3 所示。

代码清单 1-3 使用文件输入的温度转换器(c2fin.rb)

..\..\页面提取自- Black_TWGR_e-Book2_June19-4.tif{}

这一次,示例运行的输出结果如下:

..\..\0.tif{}

自然地,如果在文件中改变数字,结果将会不同。那么,怎样将计算的结果写入文件中呢?

3.写入文件

从最简单的操作来说,文件写入要比文件读取复杂一些。正如代码清单 1-4 所示,执行写入文件的操作时,主要的额外步骤是要指定文件的模式,在这个例子中是w(意为写入)。保存代码清单 1-4 所示的这个版本,命名为 c2fout.rb 并运行它。

代码清单 1-4 使用文件输入的温度转换器(c2fout.rb)

..\..\页面提取自- Black_TWGR_e-Book2_June19-5.tif{90%}

调用方法 fh.puts Fahrenheit 的作用,是将 Fahrenheit 的值输出到由fh对象进行写入处理的文件中。如果检查文件 temp.out,无论输入什么数字,都可以看到转换好的华氏温度值。

作为练习,可以尝试着把前面的例子进行合并,让它从一个文件读取数字,转换为华氏度,之后把结果写入不同的文件中。在适时引入一些 Ruby 语法的同时,下一节会检验 Ruby 的安装,然后很快还会依次看到 Ruby 如何管理扩展和库。

1.2 剖析 Ruby 的安装

在系统上安装 Ruby 意味着在许多磁盘目录中安装了 Ruby 语言的库和支持文件。大多数时候,Ruby 都知道如何找到其所需要的这些目录而不用弹出提示。但是了解 Ruby 安装的知识对了解 Ruby 本身大有益处。

{查看 Ruby 的源代码!!}

除了 Ruby 安装目录体系之外,Ruby 的源代码目录也安装好了。如果没有,可以到 Ruby 的主页中下载。源代码目录中包含了许多在最终安装中出现的 Ruby 文件和许多已编译为目标文件并安装好的 C 语言文件。另外,源代码目录包含了一些如 ChangeLog 和软件授权文件这样的资料类型文件。

Ruby 安装文件的位置很容易获取。要得到这些信息,只需要在 irb 会话中加载名为rbconfig的 Ruby 库。rbconfig是一个接口,通过它可以得到关于 Ruby 安装的许多内部编译的配置信息,可以通过 irb 的命令行标志-r和指定的包名调用 irb 去加载它:

..\0.tif{60%}

现在可以获取这些信息了。例如,可以找到 Ruby 可执行文件(包括 ruby 和 irb)的安装目录:

..\页面提取自- Black_TWGR_e-Book2_June19.tif{50%}

RbConfig::CONFIG是一个引用散列(hash,是一种数据结构)的常量,用于在 Ruby 中保存配置信息。字符串"bindir"是散列的主键。用"bindir"这个主键查询散列将返回对应的值,这个值是安装二进制文件的目录名。

其余的配置信息也使用相同的方式获取:通过散列的主键访问配置信息中的值。如果要获得其他安装信息,在irb命令中替换bindir为其他词语。但是每次都要遵循相同的基本原则:RbConfig::CONFIG["术语"]。表 1-5 概括了这些术语及其指向的目录。

表 1-5 关键的 Ruby 目录和它们的RbConfig术语

术 语 目 录 内 容
rubylibdir Ruby 标准库
bindir Ruby 命令行工具
archdir 特定架构的扩展和库文件(已编译,二进制文件)
sitedir 用户自己或第三方扩展和库文件(用 Ruby 编写)
vendordir 第三方扩展和库文件(用 Ruby 编写)
sitelibdir 用户自己的 Ruby 语言扩展(用 Ruby 语言编写)
sitearchdir 用户自己的 Ruby 语言扩展(用 C 语言编写)

这就是对 Ruby 主要的安装目录和其包含内容的一个概述。此刻不必记住它们,但要意识到需要时如何找到它们(或者好奇时浏览一下并查看 Ruby 的代码示例!)。

1.2.1 Ruby 标准库子目录(RbConfig::CONFIG[rubylibdir]

在 rubylibdir 目录中,可以看到用 Ruby 编写的程序文件。这些文件提供了标准库的功能,如果需要它们提供的功能,可以在程序中请求(require)它们。在本目录中可以找到以下文件。

  • cgi.rb:CGI 编程工具库。
  • fileutils.rb:易用的文件操作实用工具。
  • tempfile.rb:自动创建临时目录的机制。
  • drb.rb:分布式编程工具。

在标准库中有一些库,如 drb,由多个文件构成。可以看到目录中有一个 drb.rb 文件,并且整个 drb 子目录包含了 drb 库的所有组件。

浏览 rubylibdir 子目录能够对 Ruby 提供的编程工具所能完成的任务有一个感性认识(也许一开始就是颠覆性的)。大多数程序员只使用了这些工具的子集,但这些巨大的编程库集合的子集也已经能够提供许多功能。

1.2.2 C 语言扩展目录(RbConfig::CONFIG[archdir]

通常 archdir 目录位于 rubylibdir 下的第一级目录中,它包含了特定架构的扩展和库文件。通常在目录中,这些文件都是以.so、.dll、.bundle(依赖于硬件和操作系统)为文件扩展名的。这些文件是 C 语言扩展:它们是二进制文件,是运行时可加载的文件,由 Ruby 的 C 语言扩展代码生成,并在 Ruby 安装过程中编译成为二进制格式。

如在 rubylibdir 目录中的 Ruby 语言程序文件,archdir 目录中的文件包含了能够加载到用户程序的标准库组件。(除此之外,还有一些 rbconfig 的扩展文件,这些扩展可以使用 irb 命令去发现它们。)这些文件不是用户可读的,但是 Ruby 的解释器可以读懂它们。从 Ruby 程序员的视角来说,由于它们都被编译为了二进制格式文件,所以所有的标准库使用都是一样的,不管它们是用 Ruby 编写的还是用 C 语言编写的。

安装在 archdir 目录的文件,每个平台安装的都不相同,它们依赖于其被编译的扩展。这个扩展又反过来取决于由个人请求编译的内容和 Ruby 能够编译的扩展所组成的代码库。

1.2.3 site_ruby(RbConfig::CONFIG[sitedir])vendor_ruby (RbConfig::CONFIG[vendordir])目录

在 Ruby 安装目录中包括一个名为 site_ruby 的子目录,它用于存储用户和系统管理员安装的第三方扩展和库文件。该目录中可能包括了用户所写的程序,还有一些从其他网站下载的工具包,以及 Ruby 库文件的存档。

site_ruby 目录包含 Ruby 语言和 C 语言的不同子目录(是RbConfig::CONFIG中不同的两个项,分别为 sitelibdir 和 sitearchdir),就这个意义而言,其与 Ruby 主安装目录并存在一个目录下。当用户请求一个扩展,Ruby 解释器检查 site_ruby 下的子目录,也同时检查主 rubylibdir 目录和主 archdir 目录。

与 site_ruby 目录并存的是 vendor_ruby 目录。第三方的扩展都安装于此。vendor_ruby 目录首次出现是在 Ruby1.9 中,从这两个目录中获得的包仍然在不断发展中。

1.2.4 gems 目录

RubyGems 实用工具是打包和发布 Ruby 库文件的标准方式。当用户安装 gems(被称为包)时,未绑定的库文件则会放置于 gems 目录。这个目录没有在配置数据结构(RbConfig::CONFIG)中列出,但是通常都和 site_ruby 在同一级目录。假如读者找到了 site_ruby,便可以看看 gems 目录中还安装了什么。在 1.4.5 节中将会了解更多关于 gems 的知识。

在这一节中,学到了 Ruby 调用扩展的机制和语义,这也是编写和安装扩展时需要用到的知识。

1.3 Ruby 扩展和编程库

本节的要点并不是关于 Ruby 标准库的参考。曾在引言中解释过,本书的目标不是编写一本 Ruby 语言的参考文档,而是教会读者使用 Ruby 语言并掌握它,并最终拓宽视野。

相应地,本节的目标是讲述扩展的工作方式,即如何使用 Ruby 运行这些扩展、它们之间技术实现的不同,并最终能让用户自己编写扩展和库文件的扩展架构。

随 Ruby 发布的扩展通常全部作为标准库来引用。标准库包括为不同项目和任务所提供的扩展,如数据库管理、网络、数学领域、XML 处理等。标准库精密的结构每次改变,哪怕只有一点,也都要随着 Ruby 新版本而发布。使用最多、最广泛的库,已经证明了其存在的价值,所以通常都趋于稳定。

使用扩展和库的关键是require方法,与之密切相关的是load方法。这些方法让读者可以在运行时加载扩展,包括自己编写的扩展。我们将通过加载内置的扩展来学习它们并拓展我们的视野。

1.3.1 加载外部文件和扩展

可以手动把程序存储在单一文件中,但如果有成百上千行或者成千上万行的代码,这将是一种负担而不是优势。尽管如此,可以将程序分解为能运行的不同文件。在 Ruby 中使用requireload方法将会使得这一过程变得容易。这里先阐述load的使用,它是两个方法中设计得最简单的一个。

{功能、扩展,还是库?!!}

用户在运行时加载到程序中的程序代码有许多不同的名称。功能(feature)是最为抽象的,很少听到过有人说“请求一个功能”(requiring a feature,这里使用require)这样特殊的使用方式。库(library)是更为具体和通用的。它意味着存在一组真实的易于编程的代码,并能够被加载调用。扩展(extension)可以被任意可加载的附加库所引用,但是在 Ruby 中这通常意味着它们不是用 Ruby 而是用 C 语言编写而成的。如果要说正在编写的是 Ruby 扩展,就意味着已经假定那是用 C 语言编写的。

要试一下这个例子,需要将程序分写在两个文件中。第一个文件,名为 loaddemo.rb,应该包含如下代码:

..\0.tif{}

当它遇到load方法的调用时,Ruby 就会加载第二个文件,这个文件名为 loadee.rb,包含如下代码:

..\0.tif{60%}

这两个文件应该放在同一个目录下(假定存在示例代码目录)。在命令行中运行 loaddemo.rb,可以看到如下输出结果:

..\0.tif{70%}

这个结果可以追溯到执行文件的某一行,以及执行的顺序。

为了在 loaddemo.rb 中加载名为 loadee.rb 的文件,将它作为参数:

..\页面提取自- Black_TWGR_e-Book2_June19-3.tif{30%}

如果将要加载的文件就在工作目录中,Ruby 将能够根据名称找到它。如果不是,Ruby 将在加载路径(load path)中查找它们。

1.3.2 加载位于默认加载路径中的文件

Ruby 解释器的加载路径是一系列目录,请求加载时,Ruby 会在这些目录中搜索文件。可以使用$:(美元符号及冒号)检查加载目录下的这些子目录的名称,看到的结果取决于用户所使用的平台。在 Mac OS X 上,一个典型的加载目录如下(这个示例包含了.rvm 的路径,它是 Ruby 版本管理器所决定的 Ruby 版本的路径):

E:\00在线编撰系统图书\830种以外的电子书\40503\17.png{}

在你的电脑中,“ruby-2.1.0”左边的部分可能会有所不同,如“/usr/local/lib/”,但是子目录的基本模式都是一样的。当加载一个文件时,Ruby 将会自上而下地在每一个子目录中搜索。

{注意} 当前的工作目录,通常使用一个点(.)表示,这不会包含在加载目录中。加载命令的作用如前面所介绍,这只是一个特例。

可以在load命令中使用代表上级目录的双点(..)符号导航到相对目录:

..\0.tif{30%}

注意,如果在程序运行中改变当前的目录,相对目录的引用也将会改变。

{注意} 记住load是一个方法,在程序文件中,只有 Ruby 遇到它的时候才会执行。Ruby 不会搜索整个文件去执行load命令。也就是说,当 Ruby 解释器遇到它的时候,它才会去寻找它要加载的文件。这意味着需要加载的文件名可以在运行时动态地决定。甚至可以在条件语句中包含一个load指令的调用,让它只有在条件为true的时候才会被执行。

同时也可以强制指定load搜索一个完全限定的文件路径,而不管加载路径的内容。

..\0.tif{80%}

当然,这比起使用加载目录或者相对路径来说兼容性会差一些,但是这可能会很有用处,尤其是如果拥有一个字符串变量,其中包含一段绝对路径并想要加载它的时候。

load命令总是会加载所请求的文件,不论这个文件是否已经加载过。假如一个文件在几次加载过程中发生改变,那么最新版本的文件将优先使用并覆盖之前加载的版本。尤其是在 irb 会话中,当在编辑器中修改一个文件时,想要立刻测试修改的效果,这将非常有用。

另一个加载文件的方法是require,它同样也搜索默认的加载路径中的目录。但是require有一些load不具有的特点。

1.3.3 请求功能

loadrequire最大的不同在于,require就算调用多次也不会重新加载已经加载过的文件。Ruby 会持续追踪已经被请求的那些文件而不会重复加载它们。

require比起load来说更为抽象。严格来说是不能请求一个文件的,而只能请求一个功能。一般来说,做到这一点甚至不用指定文件的扩展名。为了验证之前所述,将loaddemo.rb中的这一行进行修改,从

..\0.tif{30%}

修改为

..\0.tif{40%}

当运行 loaddemo.rb 时,即使提供了需要加载的文件的完整文件名,得到的结果也可能与之前相同。

通过把 loadee 看作一个“功能”而不是一个文件,require对用 Ruby 编写的扩展和使用 C 语言写成的扩展都用一样的方式。另外,.rb 扩展名的文件与其他扩展名为.so、.dll 或者.bundle 的文件使用方式也是一样的。

{指定工作目录!!}

require不能辨识出当前的工作目录(.)。用户可以显式地指定它,例如:

..\0.tif{40%}

或者可以使用数组添加运算符(<<),把当前目录添加到加载路径当中。

..\0.tif{18%}

这样,就不必在调用 require 的时候显式地指定工作目录了。

..\0.tif{40%}

也可以给require指定完全限定的路径,和使用load一样,把文件或者功能加载进来。读者也可以混合使用该规则,例如,即使是将静态路径与路径末端更为抽象的语法功能混合使用,以下语法也是可以正常运行的,例如:

..\0.tif{}

尽管load很有用,尤其是当加载多于一个文件的时候,但是require是一个日常使用的技术,用于请求 Ruby 扩展和库,不论是标准库还是第三方库。与加载 loadee 文件相比,请求标准库的功能要简单得多,只使用require就可以请求想要的任何库文件。之后,扩展中新的类和方法都可以使用了。下面的例子中,给出了请求前后在 irb 会话中的不同。

{:--}图像说明文字 ..\图\p22.tif{60%}

第一次调用scanf的时候失败并报错❶。但是在调用了require之后❷,没有编程人员的介入,"David Black"这个字符串对象也响应了scanf消息。(在这个例子中❸,使用空格为隐式分隔符,用于把原字符串抽取为两个连续的字符串)

1.3.4 require_relative 指令

require_relative是第三种加载文件的方式。这个指令会搜索相对于所在文件的目录来加载功能。因此在前一个例子中,可以这样:

..\0.tif{40%}

这样就可以不用把当前目录加入加载路径中。当需要导航到某个本地目录结构中的时候,require_relative是一个便捷的方式,例如:

..\0.tif{60%}

接下来将对随 Ruby 发布的命令行工具进行一次测验,以此总结这一章所学到的知识。

1.4 易用的 Ruby 工具和应用程序

安装 Ruby 后,就可以得到一组重要的命令行工具,它们被安装在配置信息 bindir 所指定的文件夹中,通常是/usr/local/bin、/usr/bin 或者/opt 同等的目录中。(可以使用require "rbconfig"去测试一下RbConfig::CONFIG["bindir"]返回的结果。)这些命令行工具具体是以下几个。

  • ruby:解释器。
  • irb:Ruby 交互式解释器。
  • rdocri:Ruby 文档工具。
  • rake:Ruby 的 make 工具,一套任务管理实用工具。
  • gem:一套 Ruby 库和应用程序包管理实用工具。
  • erb:一套模版系统。
  • testrb:一个用于测试框架的高级工具。

这一节将会讲述除erbtestrb之外的所有工具。它们在特定的条件下都很有用,但是不作为学习 Ruby 语言首要的目标和基础知识。

在这里读者可以不必记住本节的所有技术点,而是通读之后先留下感性认识。不久之后将会经常(尤其是一些命令行参数和ri实用工具)用到这些内容,随着使用频率的提高,对 Ruby 语言的理解会逐步加深。

1.4.1 解释器的命令行开关

从命令行启动 Ruby 解释器时,不仅可以提供程序的名称,也可以提供一个或多个命令行开关,正如在本章中所见那样。用户选择的开关将会通知解释器使用不同的特殊行为方式,抑或使用特殊的功能。

Ruby 有超过 20 个命令行开关。其中一些用得很少,而另外一些对于 Ruby 程序员来说使用频率较高。表 1-6 概括了最常用的一些 Ruby 命令行开关。

表 1-6 最常用的 Ruby 命令行开关

开 关 描 述 用 例
-c 不执行程序只检查程序文件的语法 ruby -c c2f.rb
-w 在程序执行过程中给出警告信息 ruby -w c2f.rb
-e 执行在命令行中引号内的代码 ruby -e 'puts "Code demo!"'
-l 行模式:在每一行输出后打印换行 ruby -le 'print "+ newline!"'
-rname 请求指定名字的扩展 ruby –rprofile
-v 显示 Ruby 版本信息,在详细模式(verbose mode)下执行程序 ruby –v
--version 显示 Ruby 版本信息 ruby –-version
-h 显示所有解释器开关的信息 ruby –h

接下来仔细看一看每一个开关的使用细节。

1.语法检查(-c

开关-c通知 Ruby 对一个或者多个文件的语法精确性进行代码检查,但不执行代码。它通常会和-w开关一起使用。

2.开启警告(-w

使用-w开关运行程序,解释器将会进入警告模式(warning mode)。这意味着用户将会看到比预期更多的警告输出,从而提示用户在程序中需要注意的地方,尽管这些不是语法错误,但都有文体上和逻辑上值得斟酌的地方。根据 Ruby 的设计:“你所编写的程序没有语法错误,但是它很怪异。你确定你所编写的程序吗?”甚至不用添加这个开关,Ruby 也会给出确定的警告,只是相对而言要少于完全警告模式。

3.执行字符串脚本(-E

开关-e会通知解释器,命令行中的引号内包含了 Ruby 的代码,应该去执行这段真实的代码,而不是执行文件中的代码。这样可以很方便地用于快速执行脚本代码,而不用把代码写在文件中再去执行文件。

例如,将自己的名字逆向地打印输出。在下面的例子中,添加执行开关(execute switch)可以快速地使用一条命令行工具实现这个功能:

{:--}..\0.tif{70%}

单引号中包含的内容是一个 Ruby 程序的整体(虽然很短)。假如想要运行多于一行的程序并使用-e开关,需要在小程序中使用文本换行符(回车):

{:--}..\0.tif{60%}

或者可以使用分号进行行分隔:

{:--}..\0.tif{}

{注意} 为什么在两行的reverse例子中,在程序代码的输出结果的两行之间会有一个空格呢?因为用键盘输入的一行是以一个换行符结束的,所以当反转输入的时候,新字符串就伴随新一行产生了。当请求 Ruby 操作和打印数据的时候,Ruby 是非常遵循文字本身的。

4.行模式运行(-L

开关-l将会产生一个效果:程序的每一次字符串输出都会被它自身的一行所替代,即使通常情况不是这样的。通常情况下,使用print打印输出不会自动以一个换行符结束,它与puts的使用不一样,但是使用了开关-l后,print也会以换行符结束。

下面使用温度转换器的程序作为例子来对比printputs的区别,用以确信程序不会在它们的输出中间插入额外的换行符(见 1.1.5 节)。可以使用-l 开关逆向实现这个效果,这甚至将致使print打印输出的每一行都在它们自己所属的那一行中。下面展示了差别:

{:--}..\0.tif{30%}

在这个例子中,使用-l的结果并不是期望的。但是这个例子展示出了开关的效果。

如果某一行已经存在换行符,使用-l开关会添加另一个换行符。通常-l开关是不常使用的,因为puts的功能已经足够达到按需添加换行符的需求,不过了解-l的用途还是有益的。

5.请求名称文件和扩展(-rname

开关-r用于调用require,并依赖其命令行参数,ruby -rscanf将会在运行开始时加载scanf。可以放置多个-r在单行的命令行参数中。

6.运行在详细模式之下(-v--verbose

使用-v运行程序将会执行两个任务:打印输出当前使用 Ruby 的版本信息,然后开启与-w开关相同的警告机制。最常使用-v的场景是找出 Ruby 的版本号:

{:--}..\0.tif{}

本例中使用的是 Ruby 2.1.0(补丁级别 0),于 2013 年 12 月 25 日发布,并基于 i686 机器编译,运行在 Mac OS X 系统上。这里因为没有程序需要运行,所以 Ruby 立刻就打印输出了版本信息。

7.打印 Ruby 版本信息(--version

这个开关可以让 Ruby 打印一次版本信息,然后退出。就算指定了程序文件名,也不会执行任何的代码。可以回忆一下-v开关在详细模式下打印版本信息并执行代码的过程。可以说,-v开关是悄悄地完成了打印版本和运行代码的模式,而-- version仅仅输出了版本 信息。

8.打印帮助信息(-h--help

这两个开关(-h--help)将会打印输出一个包含全部可用的命令行开关的列表,并概述它们的功能。

除了可以单独使用这些开关,也可以在 Ruby 调用的时候合并两个或者多个一起使用。

9.合并使用开关(-cw

之前已经介绍过-cw这样的合并使用方式了,它会检查文件的语法错误而不执行文件,当然也会给出警告:

{:--}..\0.tif{30%}

另一个很常用的合并使用的例子是-v-e,这将显示运行的 Ruby 的版本,同时运行单引号内的代码内容。有很多关于 Ruby 合并开关使用的讨论,如邮件列表和其他地方,人们使用它来验证相同的代码在不同版本之间的差异。例如,如果想要清晰地显示一个字符串调用方法start_with?在 Ruby 1.8.6 中不能运行而在 Ruby 2.0 中可以,那么可以分别运行这两个版本:

{:--}..\图\P25.tif{}

(当然,这要求系统中安装了两个版本)第一次运行(使用 1.8.6 版本)程序获得了undefined method 'start_with?'消息❶,这意味着执行了一个不存在的操作。但是使用 Ruby2.1.0 运行相同的代码段时,运行就正常了❷,Ruby 打印输出了true。这是一个非常便捷的方式去分享和提出关于 Ruby 版本切换时的问题。

现在,将讲述前面所提到的交互式 Ruby 解释器 irb,并进一步地了解它。这一部分在本章的一开头已经有过叙述,所以读者可能已经有所了解。如果没有,那么借此机会可以学习一下这个特殊且有用的 Ruby 工具。

{指定开关} 可以给 Ruby 提供分隔式的开关,例如:

{:--}..\0.tif{20%}

或者

{:--}..\0.tif{80%}

但是通常将它们合并在一起使用,正如正文中所讲述的例子。

1.4.2 走近 Ruby 的交互式解释器 irb

正如本书中所见,irb 是一个交互式的 Ruby 解释器,这意味着它不用处理文件,而是处理会话中所输入的代码。irb 是一个很好的工具,可以用于测试 Ruby 代码,同时也是一个学习 Ruby 的好工具。

在命令行中输入irb,就可以开启irb会话,irb将显示以下提示符:

{:--}..\0.tif{20%}

正如之前所述,还可以使用--simple-prompt选项使 irb 输出更为简单明了:

{:--}..\0.tif{40%}

irb 一旦启动,便可以输入 Ruby 命令。这里可以运行单次摄氏 - 华氏度转换程序。在本例中,irb 就像一个可装入口袋的计算器:它能计算任何输入并打印结果。因此不必使用print或者puts命令:

{:--}..\0.tif{40%}

如果想要知道一年有多少分钟(假如读者手边没有 Rent 乐队热门金曲的 CD[4]),就输入适当的乘法计算表达式:

{:--}..\页面提取自- Black_TWGR_e-Book2_June19-3.tif{30%}

当然,irb 也会处理输入的 Ruby 指令。比如,将天数、小时数、分钟数赋值给变量,然后将这些变量的值相乘,可以在 irb 中这样做:

{:--}..\0.tif{50%}

上面就是期望的计算结果。但是看一下上面条目的前 3 行,当输入days = 365的时候,irb 会相应输出365,为什么?

表达式days = 365是一个赋值表达式:将值365赋给了名为days的变量。赋值表达式最重要的任务就是将值赋给变量,因此可以在之后使用该变量。但是赋值表达式(days = 365这一整行)也有一个值,赋值表达式的值就是它右边的值。当 irb 发现任何表达式时,它就会输出这个表达式的值。因此,当 irb 发现days = 365时,它就输出365。这看起来有些输出过度,这是因为其处在 irb 之中,与在 irb 中输入2+2就看到了结果,而没有显式地使用print语句去打印结果是同样的道理。

同样,调用puts方法也有一个返回值,名为nil。假如在 irb 中输入一个puts语句,irb 将会严格地执行它,同时还会打印输出puts的返回值。

{:--}..\0.tif{40%}

这里有一种方法可以让 irb 不再喋喋不休,即使用-noecho参数。下面展示它如何使用:

{:--}..\0.tif{60%}

幸好有-noecho,附加的表达式不会回显其结果了。puts命令确实被执行了(可以看到输出"Hi"),但是puts的返回值(nil)被屏蔽了。

{中断 irb!!}

在 irb 中可能会卡在一个循环中,或者会话看起来像是失去响应(就好像是输入了左双引号而没有输入右双引号,或者类似的代码行)。重新回到控制的方法取决于系统本身,在最常见的系统中,使用 Ctrl+C 组合键可以达到这一目的。另一个方法是使用 Ctrl+Z 组合键。这是用户的系统中直接作用于 irb 上,最优、最通用的中断信息的方式。当然,如果任务被真的冻结,无响应,可以到进程或者任务管理器工具中终止 irb 进程。

要从 irb 中正常退出,输入exit即可。在许多系统中,Ctrl+D 组合键同样适用。

偶然情况下,会遇到 irb 出现问题(那是说遇到致命错误然后结束进程),而大多数时候它会捕获自己的错误,然后让用户继续操作。

一旦掌握了使用 irb 的窍门并用于打印输出一切的值,以及如何关闭它,就会发现 irb 是一个极为有用的工具(和玩具)。

Ruby 的源代码采用一种能自动生成文档的方式进行标记,然后通过一些工具解析和显示它们,这些工具是ri和 RDoc,下面就会讲述到。

1.4.3 ri和 RDoc

最早由 Dave Thomas 编写的ri(Ruby 索引)和 RDoc(Ruby 文档)是为 Ruby 程序提供文档的关系很紧密的一对工具。ri是一个命令行工具,而 RDoc 体系中包含了命令行工具rdocrirdoc是独立的程序,通过命令行来运行。(虽然用户也可以使用 Ruby 程序内所提供的工具,但这里暂时不讨论这方面的内容。)

RDoc 是一个文档系统。假如在程序文件(Ruby 或者 C)中使用规定的 RDoc 格式编写了注释,rdoc会扫描程序文件,并抽取这些注释,智能地(通过注释的内容进行索引)组织它们,最后创建出漂亮的格式化文档。在 Ruby 源码树以及许多 Ruby 安装目录的 Ruby 文件中,在许多的源码文件中可以看到 RDoc 标记,包括了 Ruby 文件和 C 语言文件。

ri与 RDoc 相互配合:它提供了一种方式用于查看 RDoc 萃取过和组织过的文档信息。具体来说(虽然不是唯一的,除非用户定制它),ri被用于组织和显示从 Ruby 源文件而来的 RDoc 的信息。在完全安装好 Ruby 的任何系统中,都可以使用一个简单的命令行工具ri获得关于 Ruby 语言的详细信息。

例如,下面是请求关于string对象的upcase方法的信息的方法:

{:--}..\0.tif{30%}

显示如下结果:

{:--}..\0.tif{}

{:--}..\0.tif{}

Stringupcase中间的散列标记(#),用于在ri命令中表明查找的实例方法,并与类方法区分。要查询类方法,可以使用分隔符::替换#。第 3 章将会讲述实例方法和类方法的区分。这里的要点是通过命令去处理大量的文档。

{注意} 默认情况下,运行ri会通过一个分页器(如 UNIX 中的more命令)输出结果。它会在输出的末端暂停,直到用户点击空格或者其他按键才会展示下一屏的信息,或者当所有的信息显示完毕就会完全退出。准确地说,在这个例子中,用户必须按下的按键因操作系统和分页器不同会有所变化。空格键、回车键、Esc 键、Ctrl+C 组合键、Ctrl+D 组合键和 Ctrl+Z 组合键都是很好的选择。如果想要使用ri而不进行分页显示,可以在命令行使用的时候加入-T开关(ri –Ttopic)。

下一节将会讲述 Ruby 命令行工具中的rake

1.4.4 任务管理实用工具:rake

正如其名字所代表的含义(由“Ruby make”而来),rake是一个极具make特性的任务管理实用工具。它是由已故的 Jim Weirich 编写完成的。同make一样,rake读取和执行定义在一个文件中的任务,这个文件名为 Rakefile。和make不同,rake必须使用 Ruby 语法定义任务。

代码清单 1-5 显示了一个 Rakefile。假如列表中的内容保存到一个称为 Rakefile 的文件,就能够在命令行中运行它:

{:--}..\0.tif{40%}

rake执行了admin命名空间中的clean_tmp任务。

代码清单 1-5 Rakefile 在admin命名空间中定义了clean_tmp任务

{:--}E:\00在线编撰系统图书\830种以外的电子书\40503\26.png{}

这里,rake定义任务的时候用到了许多本书中还没出现的 Ruby 技术,但其基本逻辑是非常简单的。

(1)循环进入/tmp 目录下的每一个目录条目❶。

(2)除非当前条目是一个文件,否则跳出当前的循环。提示一下,隐藏文件也不会被删除,因为列出目录的操作不会包括它们❷。

(3)提示删除文件❸。

(4)假如用户输入y(或者任何以y开头的字符串),则删除文件❹。

(5)假如用户输入q,中断循环,任务结束❺。

主要的编程逻辑是这样组成的:通过循环目录列表中的条目(参见“使用each遍历一个集合类型”),然后通过case语句进行条件判断并执行(这些技术将会在第 6 章中讲述)。

{使用each遍历一个集合类型!!}

表达式Dir["/tmp/*"].each do |f|调用了一个包含目录条目名称的数组的each方法。整个代码块从do开始并在end处结束(endDir使用统一缩进整齐排列),并对每数组中每一项执行一次。每一次执行,当前项都会绑定到参数f,这就是|f|语句的意义。在接下来的章节中,将会看到许多使用each的地方。在第 9 章讲述迭代器(iterator)的时候,将会对each进行详细的讲述。

上面任务定义中的命令desc提供了对任务描述的方式。这不仅在用户研究文件的时候可以派上用场,还可以用于在特定时间查看rake可以执行的所有任务。假如所处的目录中包含一个代码清单 1-5 中的Rakefile,便可以使用如下命令:

{:--}..\0.tif{25%}

可以看到所有定义过的任务列表。

{:--}..\0.tif{}

对于rake的命名空间和任务名称可以任意命名。甚至可以不需要一个命名空间,可以在顶级命名空间定义一个任务,如下所示:

{:--}..\0.tif{30%}

然后使用简单的名称调用它:

{:--}..\0.tif{25%}

但是,在任务中使用命名空间是个好主意,尤其是在用户定义的任务数量成倍增长的时候。可以定义命名空间至任意的深度,例如,下面的结构是合法的:

{:--}..\0.tif{35%}

定义好的任务是这样调用的:

{:--}..\0.tif{40%}

正如目录清理的例子所示,rake任务不会被限制在与 Ruby 编程相关的操作中。使用rake,用户可以随意使用 Ruby 语言以达到编写任何所需任务的目的。

下一个将要讲述的是gem命令,它使得安装第三方 Ruby 组件包变得非常容易。

1.4.5 使用gem命令安装组件包

RubyGems 库和实用工具集合包括了打包、安装 Ruby 库和应用程序的工具。这里不会覆盖创建 gem 的内容,只是先看一下 gem 安装程序和使用的方法。

使用gem安装一个 Ruby 的程序包通常是很简单的,在gem之后使用简单的install命令即可

{:--}..\0.tif{35%}

这个命令将会输出如下内容(这依赖于已经安装的 gem 和新安装的 gem 的依赖包):

{:--}..\0.tif{60%}

这些状态报告之后跟随着许多行信息,它展示了对于已经安装好的不同 gem 的ri和 RDoc 文档信息。(文档的安装是通过 RDoc 与正在处理的 gem 源文件关联,所以要耐心等待这个过程,它通常是 gem 安装中最长的过程。)

在 gem 安装的过程中,gem从 rubygem.org(www.rubygems.org)下载所需的 gem 文件。这些文件都是使用.gem 格式,被保存在用户 gems 目录下的缓存子目录中。当然也可以安装存放在本地磁盘或者其他媒介中的 gem 文件。指定文件的名称即可安装:

{:--}..\0.tif{80%}

假如指定安装的 gem 名称没有使用完全文件名(如 ruport),gem将会查找当前目录和 RubyGems 系统中的本地缓存。本地安装仍然会搜索远程的依赖包,除非使用-l(local)作为gem的命令行参数,它严格限制所有的操作都在本地进行。假如想要仅使用远程安装gem并包含依赖包,可以使用-rremote)参数。在大多数情况下,简单地使用“gem install包名”这样的方式即可(要卸载一个 gem 包,使用“gem uninstall包名”命令)。

一旦安装了一个 gem 包,就可以通过require方法使用它。

加载和使用 gem 包

此刻不会看到 gem 包位于初始化加载路径($:)之中,但仍然可以使用require请求它们。这里列出了怎样请求“hoe”(帮助用户打包自己的 gem 实用工具)的方式,并确保 Hoe gem 包已经安装:

{:--}..\0.tif{30%}

在这里,hoe 的关联目录将会出现在加载路径中,假如使用grep匹配“hoe”模式打印输出$:的值,如下:

{:--}..\0.tif{80%}

假如为了特殊的库安装了一个库的多个版本的 gem,然后想要强制使用其中一个版本而不是最常用的版本,也可以使用gem方法。(注意,这个方法和命令行工具中的gem不是一个工具。)在这个例子中,展示了如何强制使用非当前版本的 Hoe:

{:--}E:\00在线编撰系统图书\830种以外的电子书\40503\29.png{}

当然,更多时候都会使用最新 gem。只是 gem 系统给出了可以微调 gem 使用的工具,这也应该如用户所需。

关于 RubyGems 就讲述到这里,至此已经学完了 bin/目录的知识,接下来将进一步学习 Ruby 语言的核心部分。

1.5 小结

在本章中,看到了许多重要的 Ruby 语言基础知识,包括:

  • Ruby(编程语言)和 ruby(Ruby 解释器)的不同;
  • Ruby 变量的命名规范(稍后还会讨论并深入学习);
  • 基本 Ruby 运算符和内置结构;
  • 编写、存储、运行一个 Ruby 程序文件;
  • 键盘输入和屏幕输出;
  • 使用requireload操作 Ruby 库;
  • 解析 Ruby 安装目录;
  • 随 Ruby 发布的命令行工具。

读者现在拥有了一份关于 Ruby 工作原理和 Ruby 编程环境所需工具的蓝图。它包含了之前看到和练习过的重要 Ruby 技术。接下来,本书准备开始系统地介绍 Ruby。


[1] 生存工具箱(survival kit)通常是指语言教授者提供给外语初学者的语用套用语。——译者注

[2] 这里开关的作用如同 Linux 系统命令中的选项,如-v显示版本号,-h显示帮助。——译者注

[3] 可以在http://ruby-lang.org 找到 Ruby 最新版本的安装说明。

[4] Rent 乐队的热门金曲Seasons of Love的歌词中包含对一年有多少分钟的描述。——译者注

本文来自《Ruby 程序员修炼之道》(第 2 版)

这是一本深受好评的书。它不仅是一本纯 Ruby 的书,也不仅是一本纯 Rails 的书,而是一本为 Rails 程序员“优化”过的 Ruby 书。

本书从 Ruby 编程语言的基础开始一直讲到动态特性,其中包含大量的真实代码示例并附有详细的注解,对日常使用 Ruby 进行编程中会遇到的每个知识点都进行了清晰的讲解。本书的内容由浅入深,主要包含 Ruby 编程语言的语法、面向对象的特性、默认对象 self、控制流技术、常用的内置类、正则表达式、I/O 操作,最后用大量的篇幅讲述了 Ruby 中最值得关注的动态特性。

【预售 + 样章试读入口】http://www.epubit.com.cn/book/details/1850

No Reply at the moment.
You need to Sign in before reply, if you don't have an account, please Sign up first.