Ruby 简明教程

Ruby - Quick Guide

Ruby - Overview

Ruby 是一种纯粹的面向对象的编程语言。它于 1993 年由日本的 Yukihiro Matsumoto 创建。

Ruby is a pure object-oriented programming language. It was created in 1993 by Yukihiro Matsumoto of Japan.

您可以在 www.ruby-lang.org 上的 Ruby 邮件列表上找到 Yukihiro Matsumoto 的名字。Matsumoto 在 Ruby 社区中也被称为 Matz。

You can find the name Yukihiro Matsumoto on the Ruby mailing list at www.ruby-lang.org. Matsumoto is also known as Matz in the Ruby community.

Ruby is "A Programmer’s Best Friend".

Ruby is "A Programmer’s Best Friend".

Ruby 具有类似于 Smalltalk、Perl 和 Python 的特性。Perl、Python 和 Smalltalk 是脚本语言。Smalltalk 是一种真正的面向对象语言。Ruby 和 Smalltalk 一样,也是一种完美的面向对象语言。使用 Ruby 语法比使用 Smalltalk 语法更容易。

Ruby has features that are similar to those of Smalltalk, Perl, and Python. Perl, Python, and Smalltalk are scripting languages. Smalltalk is a true object-oriented language. Ruby, like Smalltalk, is a perfect object-oriented language. Using Ruby syntax is much easier than using Smalltalk syntax.

Features of Ruby

  1. Ruby is an open-source and is freely available on the Web, but it is subject to a license.

  2. Ruby is a general-purpose, interpreted programming language.

  3. Ruby is a true object-oriented programming language.

  4. Ruby is a server-side scripting language similar to Python and PERL.

  5. Ruby can be used to write Common Gateway Interface (CGI) scripts.

  6. Ruby can be embedded into Hypertext Markup Language (HTML).

  7. Ruby has a clean and easy syntax that allows a new developer to learn very quickly and easily.

  8. Ruby has similar syntax to that of many programming languages such as C++ and Perl.

  9. Ruby is very much scalable and big programs written in Ruby are easily maintainable.

  10. Ruby can be used for developing Internet and intranet applications.

  11. Ruby can be installed in Windows and POSIX environments.

  12. Ruby support many GUI tools such as Tcl/Tk, GTK, and OpenGL.

  13. Ruby can easily be connected to DB2, MySQL, Oracle, and Sybase.

  14. Ruby has a rich set of built-in functions, which can be used directly into Ruby scripts.

Tools You Will Need

要执行本教程中讨论的示例,您需要一台最新计算机,如带至少 2GB RAM 的英特尔酷睿 i3 或 i5(建议使用 4GB RAM)。您还需要以下软件 −

For performing the examples discussed in this tutorial, you will need a latest computer like Intel Core i3 or i5 with a minimum of 2GB of RAM (4GB of RAM recommended). You also will need the following software −

  1. Linux or Windows 95/98/2000/NT or Windows 7 operating system.

  2. Apache 1.3.19-5 Web server.

  3. Internet Explorer 5.0 or above Web browser.

  4. Ruby 1.8.5

本教程将提供使用 Ruby 创建 GUI、网络和 Web 应用程序所需的技能。它还会讨论扩展和嵌入 Ruby 应用程序。

This tutorial will provide the necessary skills to create GUI, networking, and Web applications using Ruby. It also will talk about extending and embedding Ruby applications.

What is Next?

下一章指导您获取 Ruby 及其文档。最后,它指导您如何安装 Ruby 以及如何准备环境以开发 Ruby 应用程序。

The next chapter guides you to where you can obtain Ruby and its documentation. Finally, it instructs you on how to install Ruby and prepare an environment to develop Ruby applications.

Ruby - Environment Setup

Local Environment Setup

如果您仍然希望为 Ruby 编程语言设置环境,那就让我们继续吧。本教程将教你有关环境设置的所有重要主题。我们建议你首先浏览以下主题,然后再进一步学习 −

If you are still willing to set up your environment for Ruby programming language, then let’s proceed. This tutorial will teach you all the important topics related to environment setup. We would recommend you to go through the following topics first and then proceed further −

  1. Ruby Installation on Linux/Unix − If you are planning to have your development environment on Linux/Unix Machine, then go through this chapter.

  2. Ruby Installation on Windows − If you are planning to have your development environment on Windows Machine, then go through this chapter.

  3. Ruby Command Line Options − This chapter list out all the command line options, which you can use along with Ruby interpreter.

  4. Ruby Environment Variables − This chapter has a list of all the important environment variables to be set to make Ruby Interpreter works.

要编写 Ruby 程序,需要一个编辑器——

To write your Ruby programs, you will need an editor −

  1. If you are working on Windows machine, then you can use any simple text editor like Notepad or Edit plus.

  2. VIM (Vi IMproved) is a very simple text editor. This is available on almost all Unix machines and now Windows as well. Otherwise, your can use your favorite vi editor to write Ruby programs.

  3. RubyWin is a Ruby Integrated Development Environment (IDE) for Windows.

  4. Ruby Development Environment (RDE) is also a very good IDE for windows users.

Interactive Ruby (IRb)

交互式 Ruby (IRb) 针对实验提供一个 shell。在 IRb shell 中,您可以立即逐行查看表达式结果。

Interactive Ruby (IRb) provides a shell for experimentation. Within the IRb shell, you can immediately view expression results, line by line.

此工具随 Ruby 安装一同提供,因此您无需执行额外操作即可使用 IRb。

This tool comes along with Ruby installation so you have nothing to do extra to have IRb working.

只需在命令提示符下键入 irb ,就会启动一个交互式 Ruby 会话,如下所示:

Just type irb at your command prompt and an Interactive Ruby Session will start as given below −

$irb
irb 0.6.1(99/09/16)
irb(main):001:0> def hello
irb(main):002:1> out = "Hello World"
irb(main):003:1> puts out
irb(main):004:1> end
nil
irb(main):005:0> hello
Hello World
nil
irb(main):006:0>

不必担心我们在此处所做的事情。您将在后续章节中了解所有这些步骤。

Do not worry about what we did here. You will learn all these steps in subsequent chapters.

What is Next?

我们假定现在您拥有一个可工作的 Ruby 环境,并且准备编写第一个 Ruby 程序。下一章将教您如何编写 Ruby 程序。

We assume now you have a working Ruby Environment and you are ready to write the first Ruby Program. The next chapter will teach you how to write Ruby programs.

Ruby - Syntax

让我们用 Ruby 编写一个简单的程序。所有 Ruby 文件都将具有扩展名 .rb 。因此,将以下源代码放入 test.rb 文件中。

Let us write a simple program in ruby. All ruby files will have extension .rb. So, put the following source code in a test.rb file.

#!/usr/bin/ruby -w

puts "Hello, Ruby!";

在这里,我们假设你的计算机在 /usr/bin 目录中有 Ruby 解释器。现在,尝试按如下所示运行此程序——

Here, we assumed that you have Ruby interpreter available in /usr/bin directory. Now, try to run this program as follows −

$ ruby test.rb

这会产生以下结果 −

This will produce the following result −

Hello, Ruby!

你已经看到了一个简单的 Ruby 程序,现在让我们了解一些与 Ruby 语法相关的基本概念。

You have seen a simple Ruby program, now let us see a few basic concepts related to Ruby Syntax.

Whitespace in Ruby Program

在 Ruby 代码中通常忽略空格字符(如空格和制表符),但当它们出现在字符串中时除外。但是,有时它们用于解释歧义的声明。在启用 -w 选项时,这种解释会产生警告。

Whitespace characters such as spaces and tabs are generally ignored in Ruby code, except when they appear in strings. Sometimes, however, they are used to interpret ambiguous statements. Interpretations of this sort produce warnings when the -w option is enabled.

Example

a + b is interpreted as a+b ( Here a is a local variable)
a  +b is interpreted as a(+b) ( Here a is a method call)

Line Endings in Ruby Program

Ruby 将分号和换行符解释为语句的结尾。但是,如果 Ruby 在行尾遇到运算符,例如 +、- 或反斜杠,则表示语句仍在继续。

Ruby interprets semicolons and newline characters as the ending of a statement. However, if Ruby encounters operators, such as +, −, or backslash at the end of a line, they indicate the continuation of a statement.

Ruby Identifiers

标识符是变量、常量和方法的名称。Ruby 标识符区分大小写。这意味着 Ram 和 RAM 是 Ruby 中的两个不同的标识符。

Identifiers are names of variables, constants, and methods. Ruby identifiers are case sensitive. It means Ram and RAM are two different identifiers in Ruby.

Ruby 标识符名称可能由字母数字字符和下划线字符(_)组成。

Ruby identifier names may consist of alphanumeric characters and the underscore character ( _ ).

Reserved Words

以下列表显示了 Ruby 中的保留字。这些保留字不能用作常量或变量名。不过,可将它们用作方法名。

The following list shows the reserved words in Ruby. These reserved words may not be used as constant or variable names. They can, however, be used as method names.

BEGIN

do

next

then

END

else

nil

true

alias

elsif

not

undef

and

end

or

unless

begin

ensure

redo

until

break

false

rescue

when

case

for

retry

while

class

if

return

while

def

in

self

FILE

defined?

module

super

LINE

Here Document in Ruby

“Here Document” 引用了如何从多行创建字符串。在 << 后,你可以指定一个字符串或一个标识符来终止该字符串字面值,并且当前行后所有行直到终止符的行都是该字符串的值。

"Here Document" refers to build strings from multiple lines. Following a << you can specify a string or an identifier to terminate the string literal, and all lines following the current line up to the terminator are the value of the string.

如果终止符带有引号,则引号的类型决定了面向行的字符串字面值类型。注意,<< 和终止符之间不能有空格。

If the terminator is quoted, the type of quotes determines the type of the line-oriented string literal. Notice there must be no space between << and the terminator.

这里有一些不同的示例 −

Here are different examples −

#!/usr/bin/ruby -w

print <<EOF
   This is the first way of creating
   here document ie. multiple line string.
EOF

print <<"EOF";                # same as above
   This is the second way of creating
   here document ie. multiple line string.
EOF

print <<`EOC`                 # execute commands
	echo hi there
	echo lo there
EOC

print <<"foo", <<"bar"  # you can stack them
	I said foo.
foo
	I said bar.
bar

这会产生以下结果 −

This will produce the following result −

   This is the first way of creating
   her document ie. multiple line string.
   This is the second way of creating
   her document ie. multiple line string.
hi there
lo there
      I said foo.
      I said bar.

Ruby BEGIN Statement

Syntax

BEGIN {
   code
}

声明在程序运行之前要调用的代码。

Declares code to be called before the program is run.

Example

#!/usr/bin/ruby

puts "This is main Ruby Program"

BEGIN {
   puts "Initializing Ruby Program"
}

这会产生以下结果 −

This will produce the following result −

Initializing Ruby Program
This is main Ruby Program

Ruby END Statement

Syntax

END {
   code
}

声明在程序结束时要调用的代码。

Declares code to be called at the end of the program.

Example

#!/usr/bin/ruby

puts "This is main Ruby Program"

END {
   puts "Terminating Ruby Program"
}
BEGIN {
   puts "Initializing Ruby Program"
}

这会产生以下结果 −

This will produce the following result −

Initializing Ruby Program
This is main Ruby Program
Terminating Ruby Program

Ruby Comments

注释将一行、一行的一部分或多行从 Ruby 解释器中隐藏。你可以在一行的开头使用井号字符 (#) −

A comment hides a line, part of a line, or several lines from the Ruby interpreter. You can use the hash character (#) at the beginning of a line −

# I am a comment. Just ignore me.

或者,注释可以在语句或表达式之后在同一行上 −

Or, a comment may be on the same line after a statement or expression −

name = "Madisetti" # This is again comment

您可以按如下方式注释多行 −

You can comment multiple lines as follows −

# This is a comment.
# This is a comment, too.
# This is a comment, too.
# I said that already.

这是另一种形式。此块注释使用 =begin/=end 将多行从解释器中隐藏 −

Here is another form. This block comment conceals several lines from the interpreter with =begin/=end −

=begin
This is a comment.
This is a comment, too.
This is a comment, too.
I said that already.
=end

Ruby - Classes and Objects

Ruby 是一个完美的面向对象编程语言。面向对象编程语言具有以下特点 −

Ruby is a perfect Object Oriented Programming Language. The features of the object-oriented programming language include −

  1. Data Encapsulation

  2. Data Abstraction

  3. Polymorphism

  4. Inheritance

这些特点已在章节 Object Oriented Ruby 中进行讨论。

These features have been discussed in the chapter Object Oriented Ruby.

面向对象程序涉及类和对象。类是单个对象根据其创建的蓝图。在面向对象术语中,我们说你的自行车是自行车类对象的一个实例。

An object-oriented program involves classes and objects. A class is the blueprint from which individual objects are created. In object-oriented terms, we say that your bicycle is an instance of the class of objects known as bicycles.

以任何车辆为例。它包含轮子、马力和燃油或油箱容量。这些特性形成了类 Vehicle 的数据成员。你可以借助这些特性来区分一辆车与另一辆车。

Take the example of any vehicle. It comprises wheels, horsepower, and fuel or gas tank capacity. These characteristics form the data members of the class Vehicle. You can differentiate one vehicle from the other with the help of these characteristics.

车辆还具有一些功能,例如停车、行驶和加速。即使这些功能也形成了类 Vehicle 的数据成员。因此,你可以将类定义为特性和功能的组合。

A vehicle can also have certain functions, such as halting, driving, and speeding. Even these functions form the data members of the class Vehicle. You can, therefore, define a class as a combination of characteristics and functions.

类 Vehicle 可以定义为 −

A class Vehicle can be defined as −

Class Vehicle {

   Number no_of_wheels
   Number horsepower
   Characters type_of_tank
   Number Capacity
   Function speeding {
   }

   Function driving {
   }

   Function halting {
   }
}

通过为这些数据成员指定不同的值,可以形成车辆类的多个实例。例如,飞机有三个轮子、1000 马力、燃油箱类型和 100 升容积。同样的,汽车有四个轮子、200 马力、汽油箱类型和 25 升容积。

By assigning different values to these data members, you can form several instances of the class Vehicle. For example, an airplane has three wheels, horsepower of 1,000, fuel as the type of tank, and a capacity of 100 liters. In the same way, a car has four wheels, horsepower of 200, gas as the type of tank, and a capacity of 25 liters.

Defining a Class in Ruby

要使用 Ruby 实现面向对象编程,首先需要学习如何在 Ruby 中创建对象和类。

To implement object-oriented programming by using Ruby, you need to first learn how to create objects and classes in Ruby.

Ruby 中的类总是以关键字 class 开始,后跟类的名称。名称始终应以大写字母开头。Customer 类可以显示为 −

A class in Ruby always starts with the keyword class followed by the name of the class. The name should always be in initial capitals. The class Customer can be displayed as −

class Customer
end

使用关键字 end 终止一个类。类中的所有数据成员都位于类定义和 end 关键字之间。

You terminate a class by using the keyword end. All the data members in the class are between the class definition and the end keyword.

Variables in a Ruby Class

Ruby 提供四种类型的变量 −

Ruby provides four types of variables −

  1. Local Variables − Local variables are the variables that are defined in a method. Local variables are not available outside the method. You will see more details about method in subsequent chapter. Local variables begin with a lowercase letter or _.

  2. Instance Variables − Instance variables are available across methods for any particular instance or object. That means that instance variables change from object to object. Instance variables are preceded by the at sign (@) followed by the variable name.

  3. Class Variables − Class variables are available across different objects. A class variable belongs to the class and is a characteristic of a class. They are preceded by the sign @@ and are followed by the variable name.

  4. Global Variables − Class variables are not available across classes. If you want to have a single variable, which is available across classes, you need to define a global variable. The global variables are always preceded by the dollar sign ($).

Example

使用类变量 @@no_of_customers,可以确定正在创建的对象数量。这能够计算客户数量。

Using the class variable @@no_of_customers, you can determine the number of objects that are being created. This enables in deriving the number of customers.

class Customer
   @@no_of_customers = 0
end

Creating Objects in Ruby using new Method

对象是类的实例。你现在将学习如何在 Ruby 中创建类的对象。可以使用类的 new 方法在 Ruby 中创建对象。

Objects are instances of the class. You will now learn how to create objects of a class in Ruby. You can create objects in Ruby by using the method new of the class.

new 方法是一种独特的类型方法,它在 Ruby 库中预定义。new 方法属于类方法。

The method new is a unique type of method, which is predefined in the Ruby library. The new method belongs to the class methods.

以下是创建 Customer 类对象的两个对象 cust1 和 cust2 的示例 −

Here is the example to create two objects cust1 and cust2 of the class Customer −

cust1 = Customer. new
cust2 = Customer. new

此处,cust1 和 cust2 是两个对象的名字。在等于号 (=) 之后编写对象名称,然后是类名称。然后,跟随点运算符和 new 关键字。

Here, cust1 and cust2 are the names of two objects. You write the object name followed by the equal to sign (=) after which the class name will follow. Then, the dot operator and the keyword new will follow.

Custom Method to Create Ruby Objects

可以将参数传递给 new 方法,这些参数可用于初始化类变量。

You can pass parameters to method new and those parameters can be used to initialize class variables.

当计划用参数声明 new 方法时,需要在创建类时声明 initialize 方法。

When you plan to declare the new method with parameters, you need to declare the method initialize at the time of the class creation.

initialize 方法是一种特殊类型的方法,当使用参数调用类的 new 方法时会执行该方法。

The initialize method is a special type of method, which will be executed when the new method of the class is called with parameters.

以下是创建初始化方法的示例 −

Here is the example to create initialize method −

class Customer
   @@no_of_customers = 0
   def initialize(id, name, addr)
      @cust_id = id
      @cust_name = name
      @cust_addr = addr
   end
end

在此示例中,使用 id, nameaddr 作为局部变量声明 initialize 方法。此处,def 和 end 用于定义 Ruby 方法 initialize。你将在后面的章节中了解有关方法的更多信息。

In this example, you declare the initialize method with id, name, and addr as local variables. Here, def and end are used to define a Ruby method initialize. You will learn more about methods in subsequent chapters.

在 initialize 方法中,您将这些局部变量传递到实例变量 @cust_id、@cust_name、@cust_addr 中。在此,局部变量包含传递到 new 方法的值。

In the initialize method, you pass on the values of these local variables to the instance variables @cust_id, @cust_name, and @cust_addr. Here local variables hold the values that are passed along with the new method.

现在,您可以按照如下方式来创建对象。

Now, you can create objects as follows −

cust1 = Customer.new("1", "John", "Wisdom Apartments, Ludhiya")
cust2 = Customer.new("2", "Poul", "New Empire road, Khandala")

Member Functions in Ruby Class

在 Ruby 中,函数称为方法。类中的每个方法以关键字 def 开始,后跟方法名称。

In Ruby, functions are called methods. Each method in a class starts with the keyword def followed by the method name.

方法名称始终首选 lowercase letters 。您使用关键字 end 在 Ruby 中结束一个方法。

The method name always preferred in lowercase letters. You end a method in Ruby by using the keyword end.

这里是一个定义 Ruby 方法的示例:

Here is the example to define a Ruby method −

class Sample
   def function
      statement 1
      statement 2
   end
end

在此,语句 1 和语句 2 是类 Sample 中方法函数主体的一部分。这些语句可以是任何有效的 Ruby 语句。例如,我们可以使用 puts 方法像下面这样打印 Hello Ruby:

Here, statement 1 and statement 2 are part of the body of the method function inside the class Sample. These statments could be any valid Ruby statement. For example we can put a method puts to print Hello Ruby as follows −

class Sample
   def hello
      puts "Hello Ruby!"
   end
end

现在,在以下示例中,创建一个 Sample 类的对象,并调用 hello 方法,然后查看结果:

Now in the following example, create one object of Sample class and call hello method and see the result −

#!/usr/bin/ruby

class Sample
   def hello
      puts "Hello Ruby!"
   end
end

# Now using above class to create objects
object = Sample. new
object.hello

这会产生以下结果 −

This will produce the following result −

Hello Ruby!

Simple Case Study

如果您想对类和对象做更多的练习,这里是一个案例研究。

Here is a case study if you want to do more practice with class and objects.

Ruby - Variables, Constants and Literals

变量是程序用来保存数据的内存位置。

Variables are the memory locations, which hold any data to be used by any program.

Ruby 支持五种类型的变量。在上一章你也已经简要地了解了这些变量。这五个类型的变量将在本节进行讲解。

There are five types of variables supported by Ruby. You already have gone through a small description of these variables in the previous chapter as well. These five types of variables are explained in this chapter.

Ruby Global Variables

全局变量以 $ 开头。未初始化的全局变量具有 nil 值,并且使用 -w 选项会产生警告。

Global variables begin with $. Uninitialized global variables have the value nil and produce warnings with the -w option.

对全局变量进行赋值会改变全局状态。不建议使用全局变量,它们会让程序晦涩难懂。

Assignment to global variables alters the global status. It is not recommended to use global variables. They make programs cryptic.

这里有一个展示全局变量使用方式的示例。

Here is an example showing the usage of global variable.

#!/usr/bin/ruby

$global_variable = 10
class Class1
   def print_global
      puts "Global variable in Class1 is #$global_variable"
   end
end
class Class2
   def print_global
      puts "Global variable in Class2 is #$global_variable"
   end
end

class1obj = Class1.new
class1obj.print_global
class2obj = Class2.new
class2obj.print_global

这里 $global_variable 是一个全局变量。这会产生以下结果:

Here $global_variable is a global variable. This will produce the following result −

NOTE - 在 Ruby 中,你可以在变量或常量的前面加上井号字符(#)来访问该变量或常量的值。

NOTE − In Ruby, you CAN access value of any variable or constant by putting a hash (#) character just before that variable or constant.

Global variable in Class1 is 10
Global variable in Class2 is 10

Ruby Instance Variables

实例变量以 @ 开头。未初始化的实例变量具有 nil 值,并且使用 -w 选项会产生警告。

Instance variables begin with @. Uninitialized instance variables have the value nil and produce warnings with the -w option.

这里有一个展示实例变量使用方式的示例。

Here is an example showing the usage of Instance Variables.

#!/usr/bin/ruby

class Customer
   def initialize(id, name, addr)
      @cust_id = id
      @cust_name = name
      @cust_addr = addr
   end
   def display_details()
      puts "Customer id #@cust_id"
      puts "Customer name #@cust_name"
      puts "Customer address #@cust_addr"
   end
end

# Create Objects
cust1 = Customer.new("1", "John", "Wisdom Apartments, Ludhiya")
cust2 = Customer.new("2", "Poul", "New Empire road, Khandala")

# Call Methods
cust1.display_details()
cust2.display_details()

这里,@cust_id、@cust_name 和 @cust_addr 是实例变量。这会产生以下结果:

Here, @cust_id, @cust_name and @cust_addr are instance variables. This will produce the following result −

Customer id 1
Customer name John
Customer address Wisdom Apartments, Ludhiya
Customer id 2
Customer name Poul
Customer address New Empire road, Khandala

Ruby Class Variables

类变量以 @@ 开头,并且必须在可以在方法定义中使用它们之前对其进行初始化。

Class variables begin with @@ and must be initialized before they can be used in method definitions.

引用未初始化的类变量会产生错误。类变量在定义了类变量的类或模块的后代中共享。

Referencing an uninitialized class variable produces an error. Class variables are shared among descendants of the class or module in which the class variables are defined.

覆盖类变量会使用 -w 选项产生警告。

Overriding class variables produce warnings with the -w option.

这里有一个展示类变量使用方式的示例:

Here is an example showing the usage of class variable −

#!/usr/bin/ruby

class Customer
   @@no_of_customers = 0
   def initialize(id, name, addr)
      @cust_id = id
      @cust_name = name
      @cust_addr = addr
   end
   def display_details()
      puts "Customer id #@cust_id"
      puts "Customer name #@cust_name"
      puts "Customer address #@cust_addr"
   end
   def total_no_of_customers()
      @@no_of_customers += 1
      puts "Total number of customers: #@@no_of_customers"
   end
end

# Create Objects
cust1 = Customer.new("1", "John", "Wisdom Apartments, Ludhiya")
cust2 = Customer.new("2", "Poul", "New Empire road, Khandala")

# Call Methods
cust1.total_no_of_customers()
cust2.total_no_of_customers()

此处 @@no_of_customers 是一个类变量。该类变量将生成以下结果:

Here @@no_of_customers is a class variable. This will produce the following result −

Total number of customers: 1
Total number of customers: 2

Ruby Local Variables

局部变量以小写字母或 _ 开头。局部变量作用域范围从类、模块、def 或 do 到相应的 end,或从代码块的花括号开头到结尾 {}。

Local variables begin with a lowercase letter or _. The scope of a local variable ranges from class, module, def, or do to the corresponding end or from a block’s opening brace to its close brace {}.

引用未初始化的局部变量时,将将其解释为一种对此方法的调用,此方法无参数。

When an uninitialized local variable is referenced, it is interpreted as a call to a method that has no arguments.

对未初始化局部变量的赋值还可用作变量声明。变量在当前作用域结束之前一直存在。在 Ruby 解析程序时确定局部变量的生命周期。

Assignment to uninitialized local variables also serves as variable declaration. The variables start to exist until the end of the current scope is reached. The lifetime of local variables is determined when Ruby parses the program.

在以上示例中,局部变量为 id、name 和 addr。

In the above example, local variables are id, name and addr.

Ruby Constants

常量以大写字母开头。可以在定义它们所在类的模块内访问类或模块中定义的常量,也可以全局访问定义在类或模块外部的常量。

Constants begin with an uppercase letter. Constants defined within a class or module can be accessed from within that class or module, and those defined outside a class or module can be accessed globally.

不可在方法内定义常量。引用未初始化的常量将导致错误。对已初始化的常量进行赋值将产生警告。

Constants may not be defined within methods. Referencing an uninitialized constant produces an error. Making an assignment to a constant that is already initialized produces a warning.

#!/usr/bin/ruby

class Example
   VAR1 = 100
   VAR2 = 200
   def show
      puts "Value of first Constant is #{VAR1}"
      puts "Value of second Constant is #{VAR2}"
   end
end

# Create Objects
object = Example.new()
object.show

此处 VAR1 和 VAR2 为常量。该类常量将生成以下结果:

Here VAR1 and VAR2 are constants. This will produce the following result −

Value of first Constant is 100
Value of second Constant is 200

Ruby Pseudo-Variables

它们是既表现为局部变量、又表现为常量形式的特殊变量。你无法为这些变量赋予任何值。

They are special variables that have the appearance of local variables but behave like constants. You cannot assign any value to these variables.

  1. self − The receiver object of the current method.

  2. true − Value representing true.

  3. false − Value representing false.

  4. nil − Value representing undefined.

  5. FILE − The name of the current source file.

  6. LINE − The current line number in the source file.

Ruby Basic Literals

Ruby 使用的文字规则简单直观。本节解释所有基本的 Ruby 文字。

The rules Ruby uses for literals are simple and intuitive. This section explains all basic Ruby Literals.

Integer Numbers

Ruby 支持整数。整数的范围可以从 -230 到 230-1 或从 -262 到 262-1。在此范围内的整数是 Fixnum 类的对象,而超出此范围的整数则存储在 Bignum 类的对象中。

Ruby supports integer numbers. An integer number can range from -230 to 230-1 or -262 to 262-1. Integers within this range are objects of class Fixnum and integers outside this range are stored in objects of class Bignum.

你可以使用可选的前置符号、可选的进制指示符(8 进制为 0,16 进制为 0x,2 进制为 0b),后跟用相应进制表示的一串数字来编写整数。连字符字符在数字字符串中将被忽略。

You write integers using an optional leading sign, an optional base indicator (0 for octal, 0x for hex, or 0b for binary), followed by a string of digits in the appropriate base. Underscore characters are ignored in the digit string.

你还可以获取与 ASCII 字符对应的整数值,或通过使其前置一个问号来转义序列。

You can also get the integer value, corresponding to an ASCII character or escape the sequence by preceding it with a question mark.

Example

123                  # Fixnum decimal
1_234                # Fixnum decimal with underline
-500                 # Negative Fixnum
0377                 # octal
0xff                 # hexadecimal
0b1011               # binary
?a                   # character code for 'a'
?\n                  # code for a newline (0x0a)
12345678901234567890 # Bignum

NOTE − 本教程中的单独一章解释了类和对象。

NOTE − Class and Objects are explained in a separate chapter of this tutorial.

Floating Numbers

Ruby 支持浮点数。它们也是数字,但带有关数。浮点数字是 Float 类的对象,可以采用以下任何值 −

Ruby supports floating numbers. They are also numbers but with decimals. Floating-point numbers are objects of class Float and can be any of the following −

Example

123.4                # floating point value
1.0e6                # scientific notation
4E20                 # dot not required
4e+20                # sign before exponential

String Literals

Ruby 字符串只是 8 位字节的序列,它们是 String 类的对象。双引号字符串允许替换和反斜杠表示法,但单引号字符串不允许替换,只允许对 \\ 和 \' 使用反斜杠表示法

Ruby strings are simply sequences of 8-bit bytes and they are objects of class String. Double-quoted strings allow substitution and backslash notation but single-quoted strings don’t allow substitution and allow backslash notation only for \\ and \'

Example

#!/usr/bin/ruby -w

puts 'escape using "\\"';
puts 'That\'s right';

这会产生以下结果 −

This will produce the following result −

escape using "\"
That's right

您可以使用序列 #{ expr } 将任何 Ruby 表达式的值替换到字符串中。这里,expr 可以是任何 Ruby 表达式。

You can substitute the value of any Ruby expression into a string using the sequence #{ expr }. Here, expr could be any ruby expression.

#!/usr/bin/ruby -w

puts "Multiplication Value : #{24*60*60}";

这会产生以下结果 −

This will produce the following result −

Multiplication Value : 86400

Backslash Notations

以下列出了 Ruby 支持的反斜杠表示法 −

Following is the list of Backslash notations supported by Ruby −

Notation

Character represented

\n

Newline (0x0a)

\r

Carriage return (0x0d)

\f

Formfeed (0x0c)

\b

Backspace (0x08)

\a

Bell (0x07)

\e

Escape (0x1b)

\s

Space (0x20)

\nnn

Octal notation (n being 0-7)

\xnn

Hexadecimal notation (n being 0-9, a-f, or A-F)

\cx, \C-x

Control-x

\M-x

Meta-x (c

0x80)

\M-\C-x

Meta-Control-x

\x

有关 Ruby 字符串的更多详细信息,请参阅 Ruby Strings

For more detail on Ruby Strings, go through Ruby Strings.

Ruby Arrays

Ruby 数组的字面意思是由在方括号之间放置逗号分隔的对象引用序列创建的。忽略结尾逗号。

Literals of Ruby Array are created by placing a comma-separated series of object references between the square brackets. A trailing comma is ignored.

Example

#!/usr/bin/ruby

ary = [  "fred", 10, 3.14, "This is a string", "last element", ]
ary.each do |i|
   puts i
end

这会产生以下结果 −

This will produce the following result −

fred
10
3.14
This is a string
last element

有关 Ruby 数组的更多详细信息,请参阅 Ruby Arrays

For more detail on Ruby Arrays, go through Ruby Arrays.

Ruby Hashes

字面 Ruby 哈希是通过将键值对列表放置在大括号内创建的,键和值之间用逗号或序列 ⇒ 隔开。忽略结尾逗号。

A literal Ruby Hash is created by placing a list of key/value pairs between braces, with either a comma or the sequence ⇒ between the key and the value. A trailing comma is ignored.

Example

#!/usr/bin/ruby

hsh = colors = { "red" => 0xf00, "green" => 0x0f0, "blue" => 0x00f }
hsh.each do |key, value|
   print key, " is ", value, "\n"
end

这会产生以下结果 −

This will produce the following result −

red is 3840
green is 240
blue is 15

有关 Ruby 哈希的更多详细信息,请参阅 Ruby Hashes

For more detail on Ruby Hashes, go through Ruby Hashes.

Ruby Ranges

范围表示一个区间,这是一个具有一定值和结尾的间隔。可以使用 s..e 和 s…​e 字面值或使用 Range.new 构建范围。

A Range represents an interval which is a set of values with a start and an end. Ranges may be constructed using the s..e and s…​e literals, or with Range.new.

使用 .. 构建的范围从头到尾(含)。使用 …​ 创建的那些范围不包括结尾值。作为一个迭代器时使用范围,范围返回序列中的每个值。

Ranges constructed using .. run from the start to the end inclusively. Those created using …​ exclude the end value. When used as an iterator, ranges return each value in the sequence.

范围 (1..5) 表示它包括 1、2、3、4、5 值,范围 (1…​5) 表示它包括 1、2、3、4 值。

A range (1..5) means it includes 1, 2, 3, 4, 5 values and a range (1…​5) means it includes 1, 2, 3, 4 values.

Example

#!/usr/bin/ruby

(10..15).each do |n|
   print n, ' '
end

这会产生以下结果 −

This will produce the following result −

10 11 12 13 14 15

有关 Ruby 范围的更多详细信息,请参阅 Ruby Ranges

For more detail on Ruby Ranges, go through Ruby Ranges.

Ruby - Operators

Ruby 支持丰富的运算符集,正如您对现代语言所期望的那样。大多数运算符实际上都是方法调用。例如,a + b 被解释为 a.+(b),其中变量 a 引用的对象中的 + 方法被调用,并且 b 作为其参数。

Ruby supports a rich set of operators, as you’d expect from a modern language. Most operators are actually method calls. For example, a + b is interpreted as a.+(b), where the + method in the object referred to by variable a is called with b as its argument.

对于每个运算符(+ - * / % ** & | ^ << >> && ||),都有一个相应的缩写赋值运算符形式(+= -= 等)。

For each operator (+ - * / % ** & | ^ << >> && ||), there is a corresponding form of abbreviated assignment operator (+= -= etc.).

Ruby Arithmetic Operators

假设变量 a 等于 10,变量 b 等于 20,则 −

Assume variable a holds 10 and variable b holds 20, then −

Operator

Description

Example

+

Addition − Adds values on either side of the operator.

a + b will give 30

Subtraction − Subtracts right hand operand from left hand operand.

a - b will give -10

*

Multiplication − Multiplies values on either side of the operator.

a * b will give 200

/

Division − Divides left hand operand by right hand operand.

b / a will give 2

%

Modulus − Divides left hand operand by right hand operand and returns remainder.

b % a will give 0

**

Exponent − Performs exponential (power) calculation on operators.

a**b will give 10 to the power 20

Ruby Comparison Operators

假设变量 a 等于 10,变量 b 等于 20,则 −

Assume variable a holds 10 and variable b holds 20, then −

Operator

Description

Example

==

Checks if the value of two operands are equal or not, if yes then condition becomes true.

(a == b) is not true.

!=

Checks if the value of two operands are equal or not, if values are not equal then condition becomes true.

(a != b) is true.

>

Checks if the value of left operand is greater than the value of right operand, if yes then condition becomes true.

(a > b) is not true.

<

Checks if the value of left operand is less than the value of right operand, if yes then condition becomes true.

(a < b) is true.

>=

Checks if the value of left operand is greater than or equal to the value of right operand, if yes then condition becomes true.

(a >= b) is not true.

Checks if the value of left operand is less than or equal to the value of right operand, if yes then condition becomes true.

(a ⇐ b) is true.

<⇒

Combined comparison operator. Returns 0 if first operand equals second, 1 if first operand is greater than the second and -1 if first operand is less than the second.

(a <⇒ b) returns -1.

===

Used to test equality within a when clause of a case statement.

(1…​10) === 5 returns true.

.eql?

True if the receiver and argument have both the same type and equal values.

1 == 1.0 returns true, but 1.eql?(1.0) is false.

equal?

True if the receiver and argument have the same object id.

if aObj is duplicate of bObj then aObj == bObj is true, a.equal?bObj is false but a.equal?aObj is true.

Ruby Assignment Operators

假设变量 a 等于 10,变量 b 等于 20,则 −

Assume variable a holds 10 and variable b holds 20, then −

Operator

Description

Example

=

Simple assignment operator, assigns values from right side operands to left side operand.

c = a + b will assign the value of a + b into c

+=

Add AND assignment operator, adds right operand to the left operand and assign the result to left operand.

c += a is equivalent to c = c + a

-=

Subtract AND assignment operator, subtracts right operand from the left operand and assign the result to left operand.

c -= a is equivalent to c = c - a

*=

Multiply AND assignment operator, multiplies right operand with the left operand and assign the result to left operand.

c *= a is equivalent to c = c * a

/=

Divide AND assignment operator, divides left operand with the right operand and assign the result to left operand.

c /= a is equivalent to c = c / a

%=

Modulus AND assignment operator, takes modulus using two operands and assign the result to left operand.

c %= a is equivalent to c = c % a

**=

Exponent AND assignment operator, performs exponential (power) calculation on operators and assign value to the left operand.

c *= a is equivalent to c = c * a

Ruby Parallel Assignment

Ruby 也支持变量的并行赋值。这允许使用单行 Ruby 代码初始化多个变量。例如 −

Ruby also supports the parallel assignment of variables. This enables multiple variables to be initialized with a single line of Ruby code. For example −

a = 10
b = 20
c = 30

使用并行赋值可以更快地声明 −

This may be more quickly declared using parallel assignment −

a, b, c = 10, 20, 30

并行赋值还可用于交换两个变量中的值 −

Parallel assignment is also useful for swapping the values held in two variables −

a, b = b, c

Ruby Bitwise Operators

按位运算符处理位并执行逐位运算。

Bitwise operator works on bits and performs bit by bit operation.

假设 a = 60;和 b = 13;现以二进制格式表示如下 −

Assume if a = 60; and b = 13; now in binary format they will be as follows −

 a    =  0011 1100
 b    =  0000 1101
 ------------------
 a&b  =  0000 1100
 a|b  =  0011 1101
 a^b  =  0011 0001
 ~a   =  1100 0011

Ruby 语言支持以下按位运算符。

The following Bitwise operators are supported by Ruby language.

Operator

Description

Example

&

Binary AND Operator copies a bit to the result if it exists in both operands.

(a & b) will give 12, which is 0000 1100

Binary OR Operator copies a bit if it exists in either operand.

(a

b) will give 61, which is 0011 1101

^

Binary XOR Operator copies the bit if it is set in one operand but not both.

(a ^ b) will give 49, which is 0011 0001

~

Binary Ones Complement Operator is unary and has the effect of 'flipping' bits.

(~a ) will give -61, which is 1100 0011 in 2’s complement form due to a signed binary number.

<<

Binary Left Shift Operator. The left operands value is moved left by the number of bits specified by the right operand.

a << 2 will give 240, which is 1111 0000

>>

Ruby Logical Operators

Ruby 语言支持以下逻辑运算符

The following logical operators are supported by Ruby language

假设变量 a 等于 10,变量 b 等于 20,则 −

Assume variable a holds 10 and variable b holds 20, then −

Operator

Description

Example

and

Called Logical AND operator. If both the operands are true, then the condition becomes true.

(a and b) is true.

or

Called Logical OR Operator. If any of the two operands are non zero, then the condition becomes true.

(a or b) is true.

&&

Called Logical AND operator. If both the operands are non zero, then the condition becomes true.

(a && b) is true.

Called Logical OR Operator. If any of the two operands are non zero, then the condition becomes true.

(a

b) is true.

!

Called Logical NOT Operator. Use to reverses the logical state of its operand. If a condition is true, then Logical NOT operator will make false.

!(a && b) is false.

not

Called Logical NOT Operator. Use to reverses the logical state of its operand. If a condition is true, then Logical NOT operator will make false.

Ruby Ternary Operator

还有一个称为三元运算符的运算符。它首先对一个表达式求值以获得真或假的值,然后根据求值结果执行两个给定语句中的一个。条件运算符具有以下语法:

There is one more operator called Ternary Operator. It first evaluates an expression for a true or false value and then executes one of the two given statements depending upon the result of the evaluation. The conditional operator has this syntax −

Operator

Description

Example

? :

Conditional Expression

If Condition is true ? Then value X : Otherwise value Y

Ruby Range Operators

Ruby 中的序列范围用于创建一系列连续值,包括一个起始值、一个结束值和介于两者之间的值范围。

Sequence ranges in Ruby are used to create a range of successive values - consisting of a start value, an end value, and a range of values in between.

在 Ruby 中,这些序列使用“..”和“···”范围运算符创建。两点形式创建一个包含范围,而三点形式创建一个排除指定高值的范围。

In Ruby, these sequences are created using the ".." and "…​" range operators. The two-dot form creates an inclusive range, while the three-dot form creates a range that excludes the specified high value.

Operator

Description

Example

..

Creates a range from start point to end point inclusive.

1..10 Creates a range from 1 to 10 inclusive.

…​

Creates a range from start point to end point exclusive.

1…​10 Creates a range from 1 to 9.

Ruby defined? Operators

defined? 是一个特殊运算符,它采用方法调用的形式来确定传入的表达式是否已定义。它返回表达式的描述字符串,如果表达式未定义,则返回 nil。

defined? is a special operator that takes the form of a method call to determine whether or not the passed expression is defined. It returns a description string of the expression, or nil if the expression isn’t defined.

defined? 运算符有以下各种使用方法

There are various usage of defined? Operator

Usage 1

defined? variable # True if variable is initialized

For Example

For Example

foo = 42
defined? foo    # => "local-variable"
defined? $_     # => "global-variable"
defined? bar    # => nil (undefined)

Usage 2

defined? method_call # True if a method is defined

For Example

For Example

defined? puts        # => "method"
defined? puts(bar)   # => nil (bar is not defined here)
defined? unpack      # => nil (not defined here)

Usage 3

# True if a method exists that can be called with super user
defined? super

For Example

For Example

defined? super     # => "super" (if it can be called)
defined? super     # => nil (if it cannot be)

Usage 4

defined? yield   # True if a code block has been passed

For Example

For Example

defined? yield    # => "yield" (if there is a block passed)
defined? yield    # => nil (if there is no block)

Ruby Dot "." and Double Colon "::" Operators

在模块方法名称前加上模块名称和一个句点来调用它,并且使用模块名称和两个冒号来引用常量。

You call a module method by preceding its name with the module’s name and a period, and you reference a constant using the module name and two colons.

:: 是一个一元运算符,它允许:类或模块内定义的常量、实例方法和类方法从类或模块外部的任何地方访问。

The :: is a unary operator that allows: constants, instance methods and class methods defined within a class or module, to be accessed from anywhere outside the class or module.

在 Ruby 中,类和方法也可以视为常量。

Remember in Ruby, classes and methods may be considered constants too.

你只需要为 :: Const_name 添加前缀,返回适当的类或模块对象。

You need to just prefix the :: Const_name with an expression that returns the appropriate class or module object.

如果没有使用前缀表达式,则默认使用主 Object 类。

If no prefix expression is used, the main Object class is used by default.

下面有两个示例 −

Here are two examples −

MR_COUNT = 0         # constant defined on main Object class
module Foo
   MR_COUNT = 0
   ::MR_COUNT = 1    # set global count to 1
   MR_COUNT = 2      # set local count to 2
end
puts MR_COUNT        # this is the global constant
puts Foo::MR_COUNT   # this is the local "Foo" constant

Second Example

Second Example

CONST = ' out there'
class Inside_one
   CONST = proc {' in there'}
   def where_is_my_CONST
      ::CONST + ' inside one'
   end
end
class Inside_two
   CONST = ' inside two'
   def where_is_my_CONST
      CONST
   end
end
puts Inside_one.new.where_is_my_CONST
puts Inside_two.new.where_is_my_CONST
puts Object::CONST + Inside_two::CONST
puts Inside_two::CONST + CONST
puts Inside_one::CONST
puts Inside_one::CONST.call + Inside_two::CONST

Ruby Operators Precedence

下表列出了从高到低优先级的运算符。

The following table lists all operators from highest precedence to lowest.

Method

Operator

Description

Yes

::

Constant resolution operator

Yes

[ ] [ ]=

Element reference, element set

Yes

**

Exponentiation (raise to the power)

Yes

! ~ + -

Not, complement, unary plus and minus (method names for the last two are +@ and -@)

Yes

* / %

Multiply, divide, and modulo

Yes

+ -

Addition and subtraction

Yes

>> <<

Right and left bitwise shift

Yes

&

Bitwise 'AND'

Yes

^

Bitwise exclusive `OR' and regular `OR'

Yes

⇐ < > >=

Comparison operators

Yes

<⇒ == === != =~ !~

Equality and pattern match operators (!= and !~ may not be defined as methods)

&&

Logical 'AND'

Logical 'OR'

.. …​

Range (inclusive and exclusive)

? :

Ternary if-then-else

= %= { /= -= +=

= &= >>= <⇐ *= &&=

= **=

Assignment

defined?

Check if specified symbol defined

not

Logical negation

or and

Logical composition

NOTE − 方法列包含“是”的运算符实际上是方法,因此可以被覆盖。

NOTE − Operators with a Yes in the method column are actually methods, and as such may be overridden.

Ruby - Comments

注释是 Ruby 代码中在运行时被忽略的注释行。单行注释以 # 字符开头,并且从 # 扩展到行的末尾,如下所示 −

Comments are lines of annotation within Ruby code that are ignored at runtime. A single line comment starts with # character and they extend from # to the end of the line as follows −

#!/usr/bin/ruby -w
# This is a single line comment.

puts "Hello, Ruby!"

执行后,以上程序产生以下结果 −

When executed, the above program produces the following result −

Hello, Ruby!

Ruby Multiline Comments

你可以使用 =begin=end 语法对多行进行注释,如下所示 −

You can comment multiple lines using =begin and =end syntax as follows −

#!/usr/bin/ruby -w

puts "Hello, Ruby!"

=begin
This is a multiline comment and con spwan as many lines as you
like. But =begin and =end should come in the first line only.
=end

执行后,以上程序产生以下结果 −

When executed, the above program produces the following result −

Hello, Ruby!

确保结尾注释足够远离代码,并且它们容易区分。如果一个块中存在多个结尾注释,请对齐它们。例如 −

Make sure trailing comments are far enough from the code and that they are easily distinguished. If more than one trailing comment exists in a block, align them. For example −

@counter      # keeps track times page has been hit
@siteCounter  # keeps track of times all pages have been hit

Ruby - if…​else, case, unless

Ruby 提供条件结构,这是现代语言中非常普遍的。在此,我们将解释 Ruby 中提供的所有条件语句和修饰符。

Ruby offers conditional structures that are pretty common to modern languages. Here, we will explain all the conditional statements and modifiers available in Ruby.

Ruby if…​else Statement

Syntax

if conditional [then]
   code...
[elsif conditional [then]
   code...]...
[else
   code...]
end

if 表达式用于条件执行。值 false 和 nil 为 false,其他所有值为 true。请注意,Ruby 使用 elsif,而不是 else if 或 elif。

if expressions are used for conditional execution. The values false and nil are false, and everything else are true. Notice Ruby uses elsif, not else if nor elif.

在条件为真时执行代码。如果条件不为真,则执行 else 子句中指定代码。

Executes code if the conditional is true. If the conditional is not true, code specified in the else clause is executed.

if 表达式的条件由保留字 then、换行符或分号与代码分隔。

An if expression’s conditional is separated from code by the reserved word then, a newline, or a semicolon.

Example

#!/usr/bin/ruby

x = 1
if x > 2
   puts "x is greater than 2"
elsif x <= 2 and x!=0
   puts "x is 1"
else
   puts "I can't guess the number"
end
x is 1

Ruby if modifier

Syntax

code if condition

在条件为真时执行代码。

Executes code if the conditional is true.

Example

#!/usr/bin/ruby

$debug = 1
print "debug\n" if $debug

这会产生以下结果 −

This will produce the following result −

debug

Ruby unless Statement

Syntax

unless conditional [then]
   code
[else
   code ]
end

在条件为假时执行代码。如果条件为真,则执行 else 子句中指定代码。

Executes code if conditional is false. If the conditional is true, code specified in the else clause is executed.

Example

#!/usr/bin/ruby

x = 1
unless x>=2
   puts "x is less than 2"
 else
   puts "x is greater than 2"
end

这会产生以下结果 −

This will produce the following result −

x is less than 2

Ruby unless modifier

Syntax

code unless conditional

在条件为假时执行代码。

Executes code if conditional is false.

Example

#!/usr/bin/ruby

$var =  1
print "1 -- Value is set\n" if $var
print "2 -- Value is set\n" unless $var

$var = false
print "3 -- Value is set\n" unless $var

这会产生以下结果 −

This will produce the following result −

1 -- Value is set
3 -- Value is set

Ruby case Statement

Syntax

case expression
[when expression [, expression ...] [then]
   code ]...
[else
   code ]
end

比较用例指定的表达式和使用 === 运算符指定的表达式,并执行与之匹配的 when 子句的代码。

Compares the expression specified by case and that specified by when using the === operator and executes the code of the when clause that matches.

用例指定的表达式作为左操作数进行评估。如果没有与之匹配的 when 子句,case 将执行 else 子句的代码。

The expression specified by the when clause is evaluated as the left operand. If no when clauses match, case executes the code of the else clause.

when 语句的表达式由保留字 then、换行符或分号与代码分隔。因此:

A when statement’s expression is separated from code by the reserved word then, a newline, or a semicolon. Thus −

case expr0
when expr1, expr2
   stmt1
when expr3, expr4
   stmt2
else
   stmt3
end

基本上类似于以下内容:

is basically similar to the following −

_tmp = expr0
if expr1 === _tmp || expr2 === _tmp
   stmt1
elsif expr3 === _tmp || expr4 === _tmp
   stmt2
else
   stmt3
end

Example

#!/usr/bin/ruby

$age =  5
case $age
when 0 .. 2
   puts "baby"
when 3 .. 6
   puts "little child"
when 7 .. 12
   puts "child"
when 13 .. 18
   puts "youth"
else
   puts "adult"
end

这会产生以下结果 −

This will produce the following result −

little child

Ruby - Loops

Ruby 中的循环用于按指定次数执行相同的代码块。本教程详细说明 Ruby 支持的所有循环语句。

Loops in Ruby are used to execute the same block of code a specified number of times. This chapter details all the loop statements supported by Ruby.

Ruby while Statement

Syntax

while conditional [do]
   code
end

执行代码,当条件为真时。while 循环的条件由保留字 do、换行符、反斜杠 \ 或分号 ; 与代码分开。

Executes code while conditional is true. A while loop’s conditional is separated from code by the reserved word do, a newline, backslash \, or a semicolon ;.

Example

#!/usr/bin/ruby

$i = 0
$num = 5

while $i < $num  do
   puts("Inside the loop i = #$i" )
   $i +=1
end

这会产生以下结果 −

This will produce the following result −

Inside the loop i = 0
Inside the loop i = 1
Inside the loop i = 2
Inside the loop i = 3
Inside the loop i = 4

Ruby while modifier

Syntax

code while condition

OR

begin
  code
end while conditional

执行代码,当条件为真时。

Executes code while conditional is true.

如果 while 修饰符后跟没有 rescue 或 ensure 子句的 begin 语句,则会在评估条件之前执行一次代码。

If a while modifier follows a begin statement with no rescue or ensure clauses, code is executed once before conditional is evaluated.

Example

#!/usr/bin/ruby

$i = 0
$num = 5
begin
   puts("Inside the loop i = #$i" )
   $i +=1
end while $i < $num

这会产生以下结果 −

This will produce the following result −

Inside the loop i = 0
Inside the loop i = 1
Inside the loop i = 2
Inside the loop i = 3
Inside the loop i = 4

Ruby until Statement

until conditional [do]
   code
end

执行代码,当条件为假时。until 语句的条件由保留字 do、换行符或分号与代码分开。

Executes code while conditional is false. An until statement’s conditional is separated from code by the reserved word do, a newline, or a semicolon.

Example

#!/usr/bin/ruby

$i = 0
$num = 5

until $i > $num  do
   puts("Inside the loop i = #$i" )
   $i +=1;
end

这会产生以下结果 −

This will produce the following result −

Inside the loop i = 0
Inside the loop i = 1
Inside the loop i = 2
Inside the loop i = 3
Inside the loop i = 4
Inside the loop i = 5

Ruby until modifier

Syntax

code until conditional

OR

begin
   code
end until conditional

执行代码,当条件为假时。

Executes code while conditional is false.

如果 until 修饰符后跟没有 rescue 或 ensure 子句的 begin 语句,则会在评估条件之前执行一次代码。

If an until modifier follows a begin statement with no rescue or ensure clauses, code is executed once before conditional is evaluated.

Example

#!/usr/bin/ruby

$i = 0
$num = 5
begin
   puts("Inside the loop i = #$i" )
   $i +=1;
end until $i > $num

这会产生以下结果 −

This will produce the following result −

Inside the loop i = 0
Inside the loop i = 1
Inside the loop i = 2
Inside the loop i = 3
Inside the loop i = 4
Inside the loop i = 5

Ruby for Statement

Syntax

for variable [, variable ...] in expression [do]
   code
end

针对表达式中的每个元素执行代码一次。

Executes code once for each element in expression.

Example

#!/usr/bin/ruby

for i in 0..5
   puts "Value of local variable is #{i}"
end

在这里,我们定义了范围 0..5。语句 for i in 0..5 将允许 i 取 0 到 5(包括 5)范围内的值。这将产生以下结果 −

Here, we have defined the range 0..5. The statement for i in 0..5 will allow i to take values in the range from 0 to 5 (including 5). This will produce the following result −

Value of local variable is 0
Value of local variable is 1
Value of local variable is 2
Value of local variable is 3
Value of local variable is 4
Value of local variable is 5

for…​in 循环与以下内容几乎完全等效 −

A for…​in loop is almost exactly equivalent to the following −

(expression).each do |variable[, variable...]| code end

但 for 循环不会为局部变量创建新作用域。for 循环的表达式由保留字 do、换行符或分号与代码分开。

except that a for loop doesn’t create a new scope for local variables. A for loop’s expression is separated from code by the reserved word do, a newline, or a semicolon.

Example

#!/usr/bin/ruby

(0..5).each do |i|
   puts "Value of local variable is #{i}"
end

这会产生以下结果 −

This will produce the following result −

Value of local variable is 0
Value of local variable is 1
Value of local variable is 2
Value of local variable is 3
Value of local variable is 4
Value of local variable is 5

Ruby break Statement

Syntax

break

终止最内部循环。如果在块内调用,则终止具有关联块的方法(方法返回 nil)。

Terminates the most internal loop. Terminates a method with an associated block if called within the block (with the method returning nil).

Example

#!/usr/bin/ruby

for i in 0..5
   if i > 2 then
      break
   end
   puts "Value of local variable is #{i}"
end

这会产生以下结果 −

This will produce the following result −

Value of local variable is 0
Value of local variable is 1
Value of local variable is 2

Ruby next Statement

Syntax

next

跳转到最内部循环的下一个迭代。如果在块内调用,则终止块的执行(yield 或调用返回 nil)。

Jumps to the next iteration of the most internal loop. Terminates execution of a block if called within a block (with yield or call returning nil).

Example

#!/usr/bin/ruby

for i in 0..5
   if i < 2 then
      next
   end
   puts "Value of local variable is #{i}"
end

这会产生以下结果 −

This will produce the following result −

Value of local variable is 2
Value of local variable is 3
Value of local variable is 4
Value of local variable is 5

Ruby redo Statement

Syntax

redo

重新启动最内部循环的此迭代,而不检查循环条件。如果在块中调用,则重新启动 yield 或调用。

Restarts this iteration of the most internal loop, without checking loop condition. Restarts yield or call if called within a block.

Example

#!/usr/bin/ruby

for i in 0..5
   if i < 2 then
      puts "Value of local variable is #{i}"
      redo
   end
end

这将产生以下结果,并进入无限循环 −

This will produce the following result and will go in an infinite loop −

Value of local variable is 0
Value of local variable is 0
............................

Ruby retry Statement

Syntax

retry

如果重试出现在 begin 表达式的救援子句中,则从 begin 体的开头重新开始。

If retry appears in rescue clause of begin expression, restart from the beginning of the begin body.

begin
   do_something # exception raised
rescue
   # handles error
   retry  # restart from beginning
end

如果重试出现在迭代器、块或 for 表达式的体中,则会重新开始迭代器调用的调用。对迭代器的参数会重新求值。

If retry appears in the iterator, the block, or the body of the for expression, restarts the invocation of the iterator call. Arguments to the iterator is re-evaluated.

for i in 1..5
   retry if some_condition # restart from i == 1
end

Example

#!/usr/bin/ruby
for i in 0..5
   retry if i > 2
puts "Value of local variable is #{i}"
end

这将产生以下结果,并进入无限循环 −

This will produce the following result and will go in an infinite loop −

Value of local variable is 1
Value of local variable is 2
Value of local variable is 1
Value of local variable is 2
Value of local variable is 1
Value of local variable is 2
............................

Ruby - Methods

Ruby 方法与任何其他编程语言中的函数非常类似。Ruby 方法用于将一个或多个可重复的语句捆绑到一个单元中。

Ruby methods are very similar to functions in any other programming language. Ruby methods are used to bundle one or more repeatable statements into a single unit.

方法名应以小写字母开头。如果您以大写字母开始方法名,Ruby 可能认为它是一个常量,因而可能会错误地解析该调用。

Method names should begin with a lowercase letter. If you begin a method name with an uppercase letter, Ruby might think that it is a constant and hence can parse the call incorrectly.

方法应该在调用之前定义,否则 Ruby 将引发一个有关未定义的方法调用的异常。

Methods should be defined before calling them, otherwise Ruby will raise an exception for undefined method invoking.

Syntax

def method_name [( [arg [= default]]...[, * arg [, &expr ]])]
   expr..
end

因此,您可以按照如下方式定义一个简单的方法−

So, you can define a simple method as follows −

def method_name
   expr..
end

您可以按如下方式表示一个接受参数的方法−

You can represent a method that accepts parameters like this −

def method_name (var1, var2)
   expr..
end

您可以为参数设置默认值,如果在调用方法时没有传递所需的参数,则将使用该默认值−

You can set default values for the parameters, which will be used if method is called without passing the required parameters −

def method_name (var1 = value1, var2 = value2)
   expr..
end

无论何时调用简单方法,都只需按照如下方式编写方法名−

Whenever you call the simple method, you write only the method name as follows −

method_name

然而,在使用参数调用方法时,请按照如下方式编写方法名和参数−

However, when you call a method with parameters, you write the method name along with the parameters, such as −

method_name 25, 30

使用带有参数的方法最重要的问题在于你无论何时调用这样的方法时,都需要记住参数的数目。例如,如果一个方法接收三个参数,而你只传递了两个,那么 Ruby 会显示一条错误信息。

The most important drawback to using methods with parameters is that you need to remember the number of parameters whenever you call such methods. For example, if a method accepts three parameters and you pass only two, then Ruby displays an error.

Example

#!/usr/bin/ruby

def test(a1 = "Ruby", a2 = "Perl")
   puts "The programming language is #{a1}"
   puts "The programming language is #{a2}"
end
test "C", "C++"
test

这会产生以下结果 −

This will produce the following result −

The programming language is C
The programming language is C++
The programming language is Ruby
The programming language is Perl

Return Values from Methods

Ruby 中的每一个方法默认返回一个值。这个返回值将是最后一条语句的值。例如 −

Every method in Ruby returns a value by default. This returned value will be the value of the last statement. For example −

def test
   i = 100
   j = 10
   k = 0
end

这个方法在被调用时,将返回最后声明的变量 k。

This method, when called, will return the last declared variable k.

Ruby return Statement

Ruby 中的 return 语句用于从 Ruby 方法返回一个或多个值。

The return statement in ruby is used to return one or more values from a Ruby Method.

Syntax

return [expr[`,' expr...]]

如果给出了超过两个表达式,则包含这些值的数组将是返回值。如果没有给出表达式,则 nil 将是返回值。

If more than two expressions are given, the array containing these values will be the return value. If no expression given, nil will be the return value.

Example

return

OR

return 12

OR

return 1,2,3

看一下这个示例 −

Have a look at this example −

#!/usr/bin/ruby

def test
   i = 100
   j = 200
   k = 300
return i, j, k
end
var = test
puts var

这会产生以下结果 −

This will produce the following result −

100
200
300

Variable Number of Parameters

假设你声明了一个需要两个参数的方法,无论何时你调用这个方法时,都需要传递两个参数。

Suppose you declare a method that takes two parameters, whenever you call this method, you need to pass two parameters along with it.

然而,Ruby 允许你声明可处理不定数量的参数的方法。让我们检查一个关于该内容的示例 −

However, Ruby allows you to declare methods that work with a variable number of parameters. Let us examine a sample of this −

#!/usr/bin/ruby

def sample (*test)
   puts "The number of parameters is #{test.length}"
   for i in 0...test.length
      puts "The parameters are #{test[i]}"
   end
end
sample "Zara", "6", "F"
sample "Mac", "36", "M", "MCA"

在这个代码中,你已经声明了一个方法 sample,它接收一个参数 test。然而,这个参数是一个可变参数。这意味着这个参数可以带入任意数量的变量。因此,上述代码将产生以下结果 −

In this code, you have declared a method sample that accepts one parameter test. However, this parameter is a variable parameter. This means that this parameter can take in any number of variables. So, the above code will produce the following result −

The number of parameters is 3
The parameters are Zara
The parameters are 6
The parameters are F
The number of parameters is 4
The parameters are Mac
The parameters are 36
The parameters are M
The parameters are MCA

Class Methods

当一个方法在类定义之外被定义时,这个方法默认被标记为 private。另一方面,在类定义中定义的方法默认被标记为 public。可以通过 Module 的 public 或 private 来更改默认可见性和方法的 private 标记。

When a method is defined outside of the class definition, the method is marked as private by default. On the other hand, the methods defined in the class definition are marked as public by default. The default visibility and the private mark of the methods can be changed by public or private of the Module.

无论何时你想访问类的某个方法时,首先需要实例化这个类。然后,你可以使用该对象来访问类的任何成员。

Whenever you want to access a method of a class, you first need to instantiate the class. Then, using the object, you can access any member of the class.

Ruby 为你提供了一种无需实例化类即可访问方法的方式。让我们看看一个类方法是如何声明和访问的 −

Ruby gives you a way to access a method without instantiating a class. Let us see how a class method is declared and accessed −

class Accounts
   def reading_charge
   end
   def Accounts.return_date
   end
end

看看 return_date 方法是如何声明的,它在类名后接一个点,后面再接方法名。你可以直接按以下方式访问这个类方法 −

See how the method return_date is declared. It is declared with the class name followed by a period, which is followed by the name of the method. You can access this class method directly as follows −

Accounts.return_date

要访问这个方法,不需要创建 Accounts 类的对象。

To access this method, you need not create objects of the class Accounts.

Ruby alias Statement

这为方法或全局变量提供一个别名。别名不能在方法主体中定义。即使方法被重写,别名也会保留方法的当前定义。

This gives alias to methods or global variables. Aliases cannot be defined within the method body. The alias of the method keeps the current definition of the method, even when methods are overridden.

禁止为编号全局变量($1,$2,…​)创建别名。重写内置全局变量可能会导致严重的问题。

Making aliases for the numbered global variables ($1, $2,…​) is prohibited. Overriding the built-in global variables may cause serious problems.

Syntax

alias method-name method-name
alias global-variable-name global-variable-name

Example

alias foo bar
alias $MATCH $&

在这里,我们为 bar 定义了 foo 别名,而 $MATCH 是 $& 的别名

Here we have defined foo alias for bar, and $MATCH is an alias for $&

Ruby undef Statement

这会取消方法的定义。undef 不会出现在方法主体中。

This cancels the method definition. An undef cannot appear in the method body.

通过使用 undef 和 alias,类的接口可以独立于超类进行修改,但请注意,self 对内部方法的调用可能会破坏程序。

By using undef and alias, the interface of the class can be modified independently from the superclass, but notice it may be broke programs by the internal method call to self.

Syntax

undef method-name

Example

要取消定义名为 bar 的方法,请执行以下操作:

To undefine a method called bar do the following −

undef bar

Ruby - Blocks

您已经了解 Ruby 如何定义方法,在该方法中您可以放置多条语句,然后调用该方法。与此类似,Ruby 有一个块的概念。

You have seen how Ruby defines methods where you can put number of statements and then you call that method. Similarly, Ruby has a concept of Block.

  1. A block consists of chunks of code.

  2. You assign a name to a block.

  3. The code in the block is always enclosed within braces ({}).

  4. A block is always invoked from a function with the same name as that of the block. This means that if you have a block with the name test, then you use the function test to invoke this block.

  5. You invoke a block by using the yield statement.

Syntax

block_name {
   statement1
   statement2
   ..........
}

在此,您将学习使用简单的 yield 语句调用块。您还将学习如何使用带有参数的 yield 语句调用块。您将使用两种类型的 yield 语句检查示例代码。

Here, you will learn to invoke a block by using a simple yield statement. You will also learn to use a yield statement with parameters for invoking a block. You will check the sample code with both types of yield statements.

The yield Statement

我们来看一个 yield 语句的示例 −

Let’s look at an example of the yield statement −

#!/usr/bin/ruby

def test
   puts "You are in the method"
   yield
   puts "You are again back to the method"
   yield
end
test {puts "You are in the block"}

这会产生以下结果 −

This will produce the following result −

You are in the method
You are in the block
You are again back to the method
You are in the block

您还可以使用 yield 语句传递参数。这里有一个示例 −

You also can pass parameters with the yield statement. Here is an example −

#!/usr/bin/ruby

def test
   yield 5
   puts "You are in the method test"
   yield 100
end
test {|i| puts "You are in the block #{i}"}

这会产生以下结果 −

This will produce the following result −

You are in the block 5
You are in the method test
You are in the block 100

在此,yield 语句后面写入了参数。您甚至可以传递多个参数。在块中,您将变量放在两个竖线(||)之间以接受参数。因此,在前面的代码中,yield 5 语句将值 5 作为参数传递给 test 块。

Here, the yield statement is written followed by parameters. You can even pass more than one parameter. In the block, you place a variable between two vertical lines (||) to accept the parameters. Therefore, in the preceding code, the yield 5 statement passes the value 5 as a parameter to the test block.

现在,看以下语句 −

Now, look at the following statement −

test {|i| puts "You are in the block #{i}"}

在此,值 5 在变量 i 中接收。现在,观察以下 puts 语句 −

Here, the value 5 is received in the variable i. Now, observe the following puts statement −

puts "You are in the block #{i}"

此 puts 语句的输出为 −

The output of this puts statement is −

You are in the block 5

如果您想要传递多个参数,则 yield 语句变为 −

If you want to pass more than one parameters, then the yield statement becomes −

yield a, b

并且该块为 −

and the block is −

test {|a, b| statement}

参数将用逗号分隔。

The parameters will be separated by commas.

Blocks and Methods

您已经了解块和方法如何相互关联。您通常使用 yield 语句从与块名称相同的调用块。 因此,您编写 −

You have seen how a block and a method can be associated with each other. You normally invoke a block by using the yield statement from a method that has the same name as that of the block. Therefore, you write −

#!/usr/bin/ruby

def test
   yield
end
test{ puts "Hello world"}

这个示例是实现块的最简单方法。您使用 yield 语句来调用测试块。

This example is the simplest way to implement a block. You call the test block by using the yield statement.

但如果一个方法的最后一个参数之前带有 &,则可以将一个块传递给此方法,并且该块将被分配给最后一个参数。如果 * 和 & 都存在于参数列表中,则 & 应出现在后面。

But if the last argument of a method is preceded by &, then you can pass a block to this method and this block will be assigned to the last parameter. In case both * and & are present in the argument list, & should come later.

#!/usr/bin/ruby

def test(&block)
   block.call
end
test { puts "Hello World!"}

这会产生以下结果 −

This will produce the following result −

Hello World!

BEGIN and END Blocks

每个 Ruby 源文件都可以声明代码块以在文件加载时运行(BEGIN 块)并在程序执行结束后运行(END 块)。

Every Ruby source file can declare blocks of code to be run as the file is being loaded (the BEGIN blocks) and after the program has finished executing (the END blocks).

#!/usr/bin/ruby

BEGIN {
   # BEGIN block code
   puts "BEGIN code block"
}

END {
   # END block code
   puts "END code block"
}
   # MAIN block code
puts "MAIN code block"

一个程序可能包含多个 BEGIN 和 END 块。BEGIN 块按照遇到的顺序执行。END 块按照反向顺序执行。执行时,上述示例产生以下结果:

A program may include multiple BEGIN and END blocks. BEGIN blocks are executed in the order they are encountered. END blocks are executed in reverse order. When executed, the above program produces the following result −

BEGIN code block
MAIN code block
END code block

Ruby - Modules and Mixins

模块是将方法、类和常量分组在一起的一种方式。模块给您带来了两大好处:

Modules are a way of grouping together methods, classes, and constants. Modules give you two major benefits.

  1. Modules provide a namespace and prevent name clashes.

  2. Modules implement the mixin facility.

模块定义了一个名称空间,一个沙盒,您可以在其中使用您的方法和常量,而不用担心被其他方法和常量踩到。

Modules define a namespace, a sandbox in which your methods and constants can play without having to worry about being stepped on by other methods and constants.

Syntax

module Identifier
   statement1
   statement2
   ...........
end

模块常量的命名方式与类常量相同,以大写字母开头。方法定义看起来也很相似:模块方法的定义方式与类方法相同。

Module constants are named just like class constants, with an initial uppercase letter. The method definitions look similar, too: Module methods are defined just like class methods.

与类方法一样,您可以通过在方法名称前面加上模块名称和一个句点来调用模块方法,并且可以使用模块名称和两个冒号来引用常量。

As with class methods, you call a module method by preceding its name with the module’s name and a period, and you reference a constant using the module name and two colons.

Example

#!/usr/bin/ruby

# Module defined in trig.rb file

module Trig
   PI = 3.141592654
   def Trig.sin(x)
   # ..
   end
   def Trig.cos(x)
   # ..
   end
end

我们可以使用相同的函数名称但不同的功能来定义另一个模块:

We can define one more module with the same function name but different functionality −

#!/usr/bin/ruby

# Module defined in moral.rb file

module Moral
   VERY_BAD = 0
   BAD = 1
   def Moral.sin(badness)
   # ...
   end
end

与类方法一样,在模块中定义方法时,您需要指定模块名称,后跟一个点,然后再指定方法名称。

Like class methods, whenever you define a method in a module, you specify the module name followed by a dot and then the method name.

Ruby require Statement

require 语句类似于 C 和 C++ 的 include 语句,以及 Java 的 import 语句。如果第三方程序想要使用任何已定义的模块,它只需使用 Ruby require 语句加载模块文件即可:

The require statement is similar to the include statement of C and C++ and the import statement of Java. If a third program wants to use any defined module, it can simply load the module files using the Ruby require statement −

Syntax

require filename

这里,不需要给文件名称加上 .rb 扩展名。

Here, it is not required to give .rb extension along with a file name.

Example

$LOAD_PATH << '.'

require 'trig.rb'
require 'moral'

y = Trig.sin(Trig::PI/4)
wrongdoing = Moral.sin(Moral::VERY_BAD)

在这里,我们使用 $LOAD_PATH << '.' 使 Ruby 了解到包含的文件必须在当前目录中搜索。如果您不想使用 $LOAD_PATH,则可以使用 require_relative 从相对目录包含文件。

Here we are using $LOAD_PATH << '.' to make Ruby aware that included files must be searched in the current directory. If you do not want to use $LOAD_PATH then you can use require_relative to include files from a relative directory.

IMPORTANT - 在此,两个文件都包含相同的函数名称。因此,这将在包含到调用程序时导致代码歧义,但模块避免了这种代码歧义,我们能够使用模块名称调用适当的函数。

IMPORTANT − Here, both the files contain the same function name. So, this will result in code ambiguity while including in calling program but modules avoid this code ambiguity and we are able to call appropriate function using module name.

Ruby include Statement

您可以将模块嵌入到类中。要将模块嵌入到类中,您可以在类中使用 include 语句:

You can embed a module in a class. To embed a module in a class, you use the include statement in the class −

Syntax

include modulename

如果模块在一个单独的文件中定义,那么在将模块嵌入到类中之前需要使用 require 语句包含该文件。

If a module is defined in a separate file, then it is required to include that file using require statement before embedding module in a class.

Example

考虑 support.rb 文件中编写的以下模块。

Consider the following module written in support.rb file.

module Week
   FIRST_DAY = "Sunday"
   def Week.weeks_in_month
      puts "You have four weeks in a month"
   end
   def Week.weeks_in_year
      puts "You have 52 weeks in a year"
   end
end

现在,您可以按如下所示在课堂中包含这个模块 −

Now, you can include this module in a class as follows −

#!/usr/bin/ruby
$LOAD_PATH << '.'
require "support"

class Decade
include Week
   no_of_yrs = 10
   def no_of_months
      puts Week::FIRST_DAY
      number = 10*12
      puts number
   end
end
d1 = Decade.new
puts Week::FIRST_DAY
Week.weeks_in_month
Week.weeks_in_year
d1.no_of_months

这会产生以下结果 −

This will produce the following result −

Sunday
You have four weeks in a month
You have 52 weeks in a year
Sunday
120

Mixins in Ruby

在通读本节之前,我们假定您了解面向对象的概念。

Before going through this section, we assume you have the knowledge of Object Oriented Concepts.

当一个类可以从多个父类中继承特征时,该类应当展示多重继承。

When a class can inherit features from more than one parent class, the class is supposed to show multiple inheritance.

Ruby 不直接支持多重继承,但 Ruby 模块还有另一个美妙之处。总的来说,它们极大地消除了对多重继承的需求,提供了被称为混合的功能。

Ruby does not support multiple inheritance directly but Ruby Modules have another wonderful use. At a stroke, they pretty much eliminate the need for multiple inheritance, providing a facility called a mixin.

混合为您提供了一种受控良好的方式来对类添加功能。但是,当混合中的代码开始与使用它的类中的代码进行交互时,它们的真正威力才会显现出来。

Mixins give you a wonderfully controlled way of adding functionality to classes. However, their true power comes out when the code in the mixin starts to interact with code in the class that uses it.

让我们研究以下示例代码来了解混合 −

Let us examine the following sample code to gain an understand of mixin −

module A
   def a1
   end
   def a2
   end
end
module B
   def b1
   end
   def b2
   end
end

class Sample
include A
include B
   def s1
   end
end

samp = Sample.new
samp.a1
samp.a2
samp.b1
samp.b2
samp.s1

模块 A 包含方法 a1 和 a2。模块 B 包含方法 b1 和 b2。类 Sample 包含模块 A 和 B。类 Sample 可以访问所有四个方法,即 a1、a2、b1 和 b2。因此,您可以看到,类 Sample 从两个模块均继承。因此,您可以说类 Sample 展示了多重继承或混合。

Module A consists of the methods a1 and a2. Module B consists of the methods b1 and b2. The class Sample includes both modules A and B. The class Sample can access all four methods, namely, a1, a2, b1, and b2. Therefore, you can see that the class Sample inherits from both the modules. Thus, you can say the class Sample shows multiple inheritance or a mixin.

Ruby - Strings

Ruby 中的 String 对象保存并处理一个或多个字节的任意序列,通常表示表示人类语言的字符。

A String object in Ruby holds and manipulates an arbitrary sequence of one or more bytes, typically representing characters that represent human language.

最简单的字符串文字用单引号(撇号字符)括起来。引号中的文本是该字符串的值——

The simplest string literals are enclosed in single quotes (the apostrophe character). The text within the quote marks is the value of the string −

'This is a simple Ruby string literal'

如果您需要将一个撇号放在一个单引号字符串文字中,请在前面加上一个反斜杠,这样 Ruby 解释器就不会认为它是终止字符串——

If you need to place an apostrophe within a single-quoted string literal, precede it with a backslash, so that the Ruby interpreter does not think that it terminates the string −

'Won\'t you read O\'Reilly\'s book?'

反斜杠还可以用来转义另一个反斜杠,这样第二个反斜杠就不会被解释为转义字符。

The backslash also works to escape another backslash, so that the second backslash is not itself interpreted as an escape character.

以下是 Ruby 的与字符串相关的特性。

Following are the string-related features of Ruby.

Expression Substitution

表达式替换是将任何 Ruby 表达式的值嵌入到一个字符串中的一种方式,使用 #{ 和 } ——

Expression substitution is a means of embedding the value of any Ruby expression into a string using #{ and } −

#!/usr/bin/ruby

x, y, z = 12, 36, 72
puts "The value of x is #{ x }."
puts "The sum of x and y is #{ x + y }."
puts "The average was #{ (x + y + z)/3 }."

这会产生以下结果 −

This will produce the following result −

The value of x is 12.
The sum of x and y is 48.
The average was 40.

General Delimited Strings

使用常规定界字符串,您可以在一对匹配的任意定界符(例如 !、(、{、< 等)内创建字符串,前面加上一个百分号字符 (%)。Q、q 和 x 具有特殊含义。常规定界字符串可以是——

With general delimited strings, you can create strings inside a pair of matching though arbitrary delimiter characters, e.g., !, (, {, <, etc., preceded by a percent character (%). Q, q, and x have special meanings. General delimited strings can be −

%{Ruby is fun.}  equivalent to "Ruby is fun."
%Q{ Ruby is fun. } equivalent to " Ruby is fun. "
%q[Ruby is fun.]  equivalent to a single-quoted string
%x!ls! equivalent to back tick command output `ls`

Escape Characters

Character Encoding

Ruby 的默认字符集是 ASCII,其字符可以用单个字节表示。如果您使用 UTF-8 或其他现代字符集,则字符可用一个至四个字节表示。

The default character set for Ruby is ASCII, whose characters may be represented by single bytes. If you use UTF-8, or another modern character set, characters may be represented in one to four bytes.

您可以在程序的开头使用 $KCODE 来更改字符集,如下所示——

You can change your character set using $KCODE at the beginning of your program, like this −

$KCODE = 'u'

String Built-in Methods

我们需要一个 String 对象的实例来调用 String 方法。以下是创建 String 对象实例的方法——

We need to have an instance of String object to call a String method. Following is the way to create an instance of String object −

new [String.new(str = "")]

这将返回一个包含 str 副本的新字符串对象。现在,使用 str 对象,我们可以使用任何可用的实例方法。例如——

This will return a new string object containing a copy of str. Now, using str object, we can all use any available instance methods. For example −

#!/usr/bin/ruby

myStr = String.new("THIS IS TEST")
foo = myStr.downcase

puts "#{foo}"

这会产生以下结果 −

This will produce the following result −

this is test

String unpack Directives

Example

尝试以下示例来解包各种数据。

Try the following example to unpack various data.

"abc \0\0abc \0\0".unpack('A6Z6')   #=> ["abc", "abc "]
"abc \0\0".unpack('a3a3')           #=> ["abc", " \000\000"]
"abc \0abc \0".unpack('Z*Z*')       #=> ["abc ", "abc "]
"aa".unpack('b8B8')                 #=> ["10000110", "01100001"]
"aaa".unpack('h2H2c')               #=> ["16", "61", 97]
"\xfe\xff\xfe\xff".unpack('sS')     #=> [-2, 65534]
"now = 20is".unpack('M*')           #=> ["now is"]
"whole".unpack('xax2aX2aX1aX2a')    #=> ["h", "e", "l", "l", "o"]

Ruby - Arrays

Ruby 数组是有序、整数索引的任何对象的集合。数组中的每个元素都与一个索引关联并通过索引引用。

Ruby arrays are ordered, integer-indexed collections of any object. Each element in an array is associated with and referred to by an index.

数组索引从 0 开始,就像在 C 或 Java 中一样。负索引假定相对于数组的末尾——也就是说,索引 -1 表示数组的最后一个元素,-2 是数组中的倒数第二个元素,依此类推。

Array indexing starts at 0, as in C or Java. A negative index is assumed relative to the end of the array --- that is, an index of -1 indicates the last element of the array, -2 is the next to last element in the array, and so on.

Ruby 数组可以包含对象,例如字符串、整数、Fixnum、哈希、符号,甚至是其他数组对象。Ruby 数组不像其他语言中的数组那样严格。在向 Ruby 数组中添加元素时,它们会自动增长。

Ruby arrays can hold objects such as String, Integer, Fixnum, Hash, Symbol, even other Array objects. Ruby arrays are not as rigid as arrays in other languages. Ruby arrays grow automatically while adding elements to them.

Creating Arrays

创建或初始化数组有很多方法。一种方法是使用 new 类方法:

There are many ways to create or initialize an array. One way is with the new class method −

names = Array.new

您能在创建数组时设置数组的大小:

You can set the size of an array at the time of creating array −

names = Array.new(20)

数组名称现在的尺寸或长度是 20 个元素。您可以使用 size 或 length 方法返回数组的大小:

The array names now has a size or length of 20 elements. You can return the size of an array with either the size or length methods −

#!/usr/bin/ruby

names = Array.new(20)
puts names.size  # This returns 20
puts names.length # This also returns 20

这会产生以下结果 −

This will produce the following result −

20
20

您可以按照如下方式给数组中的每个元素赋一个值:

You can assign a value to each element in the array as follows −

#!/usr/bin/ruby

names = Array.new(4, "mac")
puts "#{names}"

这会产生以下结果 −

This will produce the following result −

["mac", "mac", "mac", "mac"]

您还可以在 new 中使用一个块,为每个元素填充块求值的内容:

You can also use a block with new, populating each element with what the block evaluates to −

#!/usr/bin/ruby

nums = Array.new(10) { |e| e = e * 2 }
puts "#{nums}"

这会产生以下结果 −

This will produce the following result −

[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

Array 还有另一种方法 []。它的工作原理如下:

There is another method of Array, []. It works like this −

nums = Array.[](1, 2, 3, 4,5)

数组创建的另一种形式如下:

One more form of array creation is as follows −

nums = Array[1, 2, 3, 4,5]

核心 Ruby 中提供的 Kernel 模块具有一个 Array 方法,该方法只接受一个参数。在此,该方法将一个范围作为参数来创建一个由数字组成的数组——

The Kernel module available in core Ruby has an Array method, which only accepts a single argument. Here, the method takes a range as an argument to create an array of digits −

#!/usr/bin/ruby

digits = Array(0..9)
puts "#{digits}"

这会产生以下结果 −

This will produce the following result −

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Array Built-in Methods

我们需要一个 Array 对象的实例来调用 Array 方法。如我们所见,以下是创建 Array 对象实例的方法——

We need to have an instance of Array object to call an Array method. As we have seen, following is the way to create an instance of Array object −

Array.[](...) [or] Array[...] [or] [...]

这将返回一个由给定对象填充的新数组。现在,使用创建的对象,我们可以调用任何可用的实例方法。例如——

This will return a new array populated with the given objects. Now, using the created object, we can call any available instance methods. For example −

#!/usr/bin/ruby

digits = Array(0..9)
num = digits.at(6)
puts "#{num}"

这会产生以下结果 −

This will produce the following result −

6

Array pack Directives

Example

尝试以下示例来打包各种数据。

Try the following example to pack various data.

a = [ "a", "b", "c" ]
n = [ 65, 66, 67 ]
puts a.pack("A3A3A3")   #=> "a  b  c  "
puts a.pack("a3a3a3")   #=> "a\000\000b\000\000c\000\000"
puts n.pack("ccc")      #=> "ABC"

这会产生以下结果 −

This will produce the following result −

a  b  c
abc
ABC

Ruby - Hashes

哈希是键值对的集合,如下所示:“员工”⇒“工资”。它类似于数组,不同之处在于,索引通过任意对象类型的键完成,而不是整数索引。

A Hash is a collection of key-value pairs like this: "employee" = > "salary". It is similar to an Array, except that indexing is done via arbitrary keys of any object type, not an integer index.

通过键或值遍历哈希时的顺序可能看起来是任意的,并且通常不会按插入顺序排列。如果尝试通过不存在的键访问哈希,则该方法将返回 nil。

The order in which you traverse a hash by either key or value may seem arbitrary and will generally not be in the insertion order. If you attempt to access a hash with a key that does not exist, the method will return nil.

Creating Hashes

与数组一样,有许多方法可以创建哈希。你可以使用 new 类方法创建空哈希 −

As with arrays, there is a variety of ways to create hashes. You can create an empty hash with the new class method −

months = Hash.new

还可以使用 new 创建具有默认值的哈希,而默认值只是 nil −

You can also use new to create a hash with a default value, which is otherwise just nil −

months = Hash.new( "month" )

or

months = Hash.new "month"

当使用具有默认值的哈希访问任何键时,如果键或值不存在,访问哈希将返回默认值 −

When you access any key in a hash that has a default value, if the key or value doesn’t exist, accessing the hash will return the default value −

#!/usr/bin/ruby

months = Hash.new( "month" )

puts "#{months[0]}"
puts "#{months[72]}"

这会产生以下结果 −

This will produce the following result −

month
month
#!/usr/bin/ruby

H = Hash["a" => 100, "b" => 200]

puts "#{H['a']}"
puts "#{H['b']}"

这会产生以下结果 −

This will produce the following result −

100
200

可以使用任何 Ruby 对象作为键或值,甚至是数组,所以以下示例是有效的 −

You can use any Ruby object as a key or value, even an array, so the following example is a valid one −

[1,"jan"] => "January"

Hash Built-in Methods

我们需要一个 Hash 对象的实例来调用 Hash 方法。正如我们所看到的,以下是创建 Hash 对象实例的方法 −

We need to have an instance of Hash object to call a Hash method. As we have seen, following is the way to create an instance of Hash object −

Hash[[key =>|, value]* ] or

Hash.new [or] Hash.new(obj) [or]
Hash.new { |hash, key| block }

这将返回一个填充给定对象的新哈希。现在使用创建的对象,我们可以调用任何可用的实例方法。例如 −

This will return a new hash populated with the given objects. Now using the created object, we can call any available instance methods. For example −

#!/usr/bin/ruby

$, = ", "
months = Hash.new( "month" )
months = {"1" => "January", "2" => "February"}

keys = months.keys
puts "#{keys}"

这会产生以下结果 −

This will produce the following result −

["1", "2"]

以下是公共 hash 方法(假设 hash 是数组对象) −

Following are the public hash methods (assuming hash is an array object) −

Sr.No.

Methods & Description

1

hash == other_hash Tests whether two hashes are equal, based on whether they have the same number of key-value pairs, and whether the key-value pairs match the corresponding pair in each hash.

2

hash.[key] Using a key, references a value from hash. If the key is not found, returns a default value.

3

hash.[key] = value Associates the value given by value with the key given by key.

4

hash.clear Removes all key-value pairs from hash.

5

hash.default(key = nil) Returns the default value for hash, nil if not set by default=. ([] returns a default value if the key does not exist in hash.)

6

hash.default = obj Sets a default value for hash.

7

hash.default_proc Returns a block if hash was created by a block.

8

hash.delete(key) [or] *array.delete(key) {

key

block }* Deletes a key-value pair from hash by key. If block is used, returns the result of a block if pair is not found. Compare delete_if.

9

*hash.delete_if {

key,value

block }* Deletes a key-value pair from hash for every pair the block evaluates to true.

10

*hash.each {

key,value

block }* Iterates over hash, calling the block once for each key, passing the key-value as a two-element array.

11

*hash.each_key {

key

block }* Iterates over hash, calling the block once for each key, passing key as a parameter.

12

*hash.each_key {

key_value_array

block }* Iterates over hash, calling the block once for each key, passing the key and value as parameters.

13

*hash.each_key {

value

block }* Iterates over hash, calling the block once for each key, passing value as a parameter.

14

hash.empty? Tests whether hash is empty (contains no key-value pairs), returning true or false.

15

hash.fetch(key [, default] ) [or] *hash.fetch(key) {

key

block }* Returns a value from hash for the given key. If the key can’t be found, and there are no other arguments, it raises an IndexError exception; if default is given, it is returned; if the optional block is specified, its result is returned.

16

hash.has_key?(key) [or] hash.include?(key) [or] hash.key?(key) [or] hash.member?(key) Tests whether a given key is present in hash, returning true or false.

17

hash.has_value?(value) Tests whether hash contains the given value.

18

hash.index(value) Returns the key for the given value in hash, nil if no matching value is found.

19

hash.indexes(keys) Returns a new array consisting of values for the given key(s). Will insert the default value for keys that are not found. This method is deprecated. Use select.

20

hash.indices(keys) Returns a new array consisting of values for the given key(s). Will insert the default value for keys that are not found. This method is deprecated. Use select.

21

hash.inspect Returns a pretty print string version of hash.

22

hash.invert Creates a new hash, inverting keys and values from hash; that is, in the new hash, the keys from hash become values and values become keys.

23

hash.keys Creates a new array with keys from hash.

24

hash.length Returns the size or length of hash as an integer.

25

hash.merge(other_hash) [or] *hash.merge(other_hash) {

key, oldval, newval

block }* Returns a new hash containing the contents of hash and other_hash, overwriting pairs in hash with duplicate keys with those from other_hash.

26

hash.merge!(other_hash) [or] *hash.merge!(other_hash) {

key, oldval, newval

block }* Same as merge, but changes are done in place.

27

hash.rehash Rebuilds hash based on the current values for each key. If values have changed since they were inserted, this method reindexes hash.

28

*hash.reject {

key, value

block }* Creates a new hash for every pair the block evaluates to true

29

*hash.reject! {

key, value

block }* Same as reject, but changes are made in place.

30

hash.replace(other_hash) Replaces the contents of hash with the contents of other_hash.

31

*hash.select {

key, value

block }* Returns a new array consisting of key-value pairs from hash for which the block returns true.

32

hash.shift Removes a key-value pair from hash, returning it as a two-element array.

33

hash.size Returns the size or length of hash as an integer.

34

hash.sort Converts hash to a two-dimensional array containing arrays of key-value pairs, then sorts it as an array.

35

hash.store(key, value) Stores a key-value pair in hash.

36

hash.to_a Creates a two-dimensional array from hash. Each key/value pair is converted to an array, and all these arrays are stored in a containing array.

37

hash.to_hash Returns hash (self).

38

hash.to_s Converts hash to an array, then converts that array to a string.

39

hash.update(other_hash) [or] *hash.update(other_hash) {

key, oldval, newval

block}* Returns a new hash containing the contents of hash and other_hash, overwriting pairs in hash with duplicate keys with those from other_hash.

40

hash.value?(value) Tests whether hash contains the given value.

41

hash.values Returns a new array containing all the values of hash.

42

hash.values_at(obj, …​) Returns a new array containing the values from hash that are associated with the given key or keys.

Ruby - Date & Time

Time 类在 Ruby 中表示日期和时间。它是操作系统提供的时间和日期功能之上的一层薄应用。在您的系统上,此类可能无法表示 1970 年之前或 2038 年之后的日期。

The Time class represents dates and times in Ruby. It is a thin layer over the system date and time functionality provided by the operating system. This class may be unable on your system to represent dates before 1970 or after 2038.

本章使您熟悉所有最需要的日期和时间概念。

This chapter makes you familiar with all the most wanted concepts of date and time.

Getting Current Date and Time

以下是获取当前日期和时间的简单示例 -

Following is the simple example to get current date and time −

#!/usr/bin/ruby -w

time1 = Time.new
puts "Current Time : " + time1.inspect

# Time.now is a synonym:
time2 = Time.now
puts "Current Time : " + time2.inspect

这会产生以下结果 −

This will produce the following result −

Current Time : Mon Jun 02 12:02:39 -0700 2008
Current Time : Mon Jun 02 12:02:39 -0700 2008

Getting Components of a Date & Time

我们可以使用 Time 对象获取日期和时间的各种组成部分。以下是显示相同的示例 -

We can use Time object to get various components of date and time. Following is the example showing the same −

#!/usr/bin/ruby -w

time = Time.new

# Components of a Time
puts "Current Time : " + time.inspect
puts time.year    # => Year of the date
puts time.month   # => Month of the date (1 to 12)
puts time.day     # => Day of the date (1 to 31 )
puts time.wday    # => 0: Day of week: 0 is Sunday
puts time.yday    # => 365: Day of year
puts time.hour    # => 23: 24-hour clock
puts time.min     # => 59
puts time.sec     # => 59
puts time.usec    # => 999999: microseconds
puts time.zone    # => "UTC": timezone name

这会产生以下结果 −

This will produce the following result −

Current Time : Mon Jun 02 12:03:08 -0700 2008
2008
6
2
1
154
12
3
8
247476
UTC

Time.utc, Time.gm and Time.local Functions

可以使用这两个函数以标准格式设置日期,如下所示 -

These two functions can be used to format date in a standard format as follows −

# July 8, 2008
Time.local(2008, 7, 8)
# July 8, 2008, 09:10am, local time
Time.local(2008, 7, 8, 9, 10)
# July 8, 2008, 09:10 UTC
Time.utc(2008, 7, 8, 9, 10)
# July 8, 2008, 09:10:11 GMT (same as UTC)
Time.gm(2008, 7, 8, 9, 10, 11)

以下是将所有组件获取为以下格式的数组的示例 -

Following is the example to get all the components in an array in the following format −

[sec,min,hour,day,month,year,wday,yday,isdst,zone]

尝试以下操作 -

Try the following −

#!/usr/bin/ruby -w

time = Time.new
values = time.to_a
p values

这将生成以下结果 -

This will generate the following result −

[26, 10, 12, 2, 6, 2008, 1, 154, false, "MST"]

此数组可以传递给 Time.utc 或 Time.local 函数以获取不同格式的日期,如下所示 -

This array could be passed to Time.utc or Time.local functions to get different format of dates as follows −

#!/usr/bin/ruby -w

time = Time.new
values = time.to_a
puts Time.utc(*values)

这将生成以下结果 -

This will generate the following result −

Mon Jun 02 12:15:36 UTC 2008

以下是获取时间的方式:表示为自平台相关历元起的秒数

Following is the way to get time represented internally as seconds since the (platform-dependent) epoch −

# Returns number of seconds since epoch
time = Time.now.to_i

# Convert number of seconds into Time object.
Time.at(time)

# Returns second since epoch which includes microseconds
time = Time.now.to_f

Timezones and Daylight Savings Time

可以使用 Time 对象来获取所有与时区和夏令时相关的信息,如下所示 -

You can use a Time object to get all the information related to Timezones and daylight savings as follows −

time = Time.new

# Here is the interpretation
time.zone       # => "UTC": return the timezone
time.utc_offset # => 0: UTC is 0 seconds offset from UTC
time.zone       # => "PST" (or whatever your timezone is)
time.isdst      # => false: If UTC does not have DST.
time.utc?       # => true: if t is in UTC time zone
time.localtime  # Convert to local timezone.
time.gmtime     # Convert back to UTC.
time.getlocal   # Return a new Time object in local zone
time.getutc     # Return a new Time object in UTC

Formatting Times and Dates

有多种格式化日期和时间的方法。这里是一个显示一些示例的示例 -

There are various ways to format date and time. Here is one example showing a few −

#!/usr/bin/ruby -w

time = Time.new
puts time.to_s
puts time.ctime
puts time.localtime
puts time.strftime("%Y-%m-%d %H:%M:%S")

这会产生以下结果 −

This will produce the following result −

Mon Jun 02 12:35:19 -0700 2008
Mon Jun  2 12:35:19 2008
Mon Jun 02 12:35:19 -0700 2008
2008-06-02 12:35:19

Time Formatting Directives

以下表格中的这些指令与方法 Time.strftime 一起使用。

These directives in the following table are used with the method Time.strftime.

Sr.No.

Directive & Description

1

%a The abbreviated weekday name (Sun).

2

%A The full weekday name (Sunday).

3

%b The abbreviated month name (Jan).

4

%B The full month name (January).

5

%c The preferred local date and time representation.

6

%d Day of the month (01 to 31).

7

%H Hour of the day, 24-hour clock (00 to 23).

8

%I Hour of the day, 12-hour clock (01 to 12).

9

%j Day of the year (001 to 366).

10

%m Month of the year (01 to 12).

11

%M Minute of the hour (00 to 59).

12

%p Meridian indicator (AM or PM).

13

%S Second of the minute (00 to 60).

14

%U Week number of the current year, starting with the first Sunday as the first day of the first week (00 to 53).

15

%W Week number of the current year, starting with the first Monday as the first day of the first week (00 to 53).

16

%w Day of the week (Sunday is 0, 0 to 6).

17

%x Preferred representation for the date alone, no time.

18

%X Preferred representation for the time alone, no date.

19

%y Year without a century (00 to 99).

20

%Y Year with century.

21

%Z Time zone name.

22

%% Literal % character.

Time Arithmetic

你可以使用以下方法对时间执行简单的算术运算:

You can perform simple arithmetic with time as follows −

now = Time.now          # Current time
puts now

past = now - 10         # 10 seconds ago. Time - number => Time
puts past

future = now + 10  # 10 seconds from now Time + number => Time
puts future

diff = future - past     # => 10  Time - Time => number of seconds
puts diff

这会产生以下结果 −

This will produce the following result −

Thu Aug 01 20:57:05 -0700 2013
Thu Aug 01 20:56:55 -0700 2013
Thu Aug 01 20:57:15 -0700 2013
20.0

Ruby - Ranges

范围无所不在:从 1 月到 12 月、从 0 到 9、从第 50 行到第 67 行,等等。Ruby 支持范围,并允许我们以多种方式使用范围 −

Ranges occur everywhere: January to December, 0 to 9, lines 50 through 67, and so on. Ruby supports ranges and allows us to use ranges in a variety of ways −

  1. Ranges as Sequences

  2. Ranges as Conditions

  3. Ranges as Intervals

Ranges as Sequences

第一个也是最自然的范围用法是用来表述序列。序列有起点、终点和生成序列中连续值的方法。

The first and perhaps the most natural use of ranges is to express a sequence. Sequences have a start point, an end point, and a way to produce successive values in the sequence.

Ruby 使用 ''..''''…​'' 范围运算符创建这些序列。两点形式创建一个包括范围,而三点形式创建一个不包括指定高值的范围。

Ruby creates these sequences using the ''..'' and ''…​'' range operators. The two-dot form creates an inclusive range, while the three-dot form creates a range that excludes the specified high value.

(1..5)        #==> 1, 2, 3, 4, 5
(1...5)       #==> 1, 2, 3, 4
('a'..'d')    #==> 'a', 'b', 'c', 'd'

1..100 序列作为一个范围对象保存,该对象包含对两个 Fixnum 对象的引用。如果需要,您可以使用 to_a 方法将范围转换成列表。尝试以下示例 −

The sequence 1..100 is held as a Range object containing references to two Fixnum objects. If you need to, you can convert a range to a list using the to_a method. Try the following example −

#!/usr/bin/ruby

$, =", "   # Array value separator
range1 = (1..10).to_a
range2 = ('bar'..'bat').to_a

puts "#{range1}"
puts "#{range2}"

这会产生以下结果 −

This will produce the following result −

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
["bar", "bas", "bat"]

范围实现的方法让您以多种方式对其进行迭代并测试其内容 −

Ranges implement methods that let you iterate over them and test their contents in a variety of ways −

#!/usr/bin/ruby

# Assume a range
digits = 0..9

puts digits.include?(5)
ret = digits.min
puts "Min value is #{ret}"

ret = digits.max
puts "Max value is #{ret}"

ret = digits.reject {|i| i < 5 }
puts "Rejected values are #{ret}"

digits.each do |digit|
   puts "In Loop #{digit}"
end

这会产生以下结果 −

This will produce the following result −

true
Min value is 0
Max value is 9
Rejected values are 5, 6, 7, 8, 9
In Loop 0
In Loop 1
In Loop 2
In Loop 3
In Loop 4
In Loop 5
In Loop 6
In Loop 7
In Loop 8
In Loop 9

Ranges as Conditions

范围也可以用作条件表达式。例如,以下代码片段从标准输入打印出一组行,其中每组中的第一行包含单词 start,最后一行包含单词 ends −

Ranges may also be used as conditional expressions. For example, the following code fragment prints sets of lines from the standard input, where the first line in each set contains the word start and the last line the word ends −

while gets
   print if /start/../end/
end

范围可以用于 case 语句中 −

Ranges can be used in case statements −

#!/usr/bin/ruby

score = 70

result = case score
   when 0..40 then "Fail"
   when 41..60 then "Pass"
   when 61..70 then "Pass with Merit"
   when 71..100 then "Pass with Distinction"
   else "Invalid Score"
end

puts result

这会产生以下结果 −

This will produce the following result −

Pass with Merit

Ranges as Intervals

多功能范围的最后一种用途是作为区间测试:查看某个值是否落在范围表示的区间内。这是使用 === 即 case 相等运算符来完成的。

A final use of the versatile range is as an interval test: seeing if some value falls within the interval represented by the range. This is done using ===, the case equality operator.

#!/usr/bin/ruby

if ((1..10) === 5)
   puts "5 lies in (1..10)"
end

if (('a'..'j') === 'c')
   puts "c lies in ('a'..'j')"
end

if (('a'..'j') === 'z')
   puts "z lies in ('a'..'j')"
end

这会产生以下结果 −

This will produce the following result −

5 lies in (1..10)
c lies in ('a'..'j')

Ruby - Iterators

迭代器只不过是集合支持的方法。存储一组数据成员的对象称为集合。在 Ruby 中,数组和哈希可以称为集合。

Iterators are nothing but methods supported by collections. Objects that store a group of data members are called collections. In Ruby, arrays and hashes can be termed collections.

迭代器依次返回集合的所有元素。这里我们将讨论两个迭代器,each 和 collect。让我们详细了解一下它们。

Iterators return all the elements of a collection, one after the other. We will be discussing two iterators here, each and collect. Let’s look at these in detail.

Ruby each Iterator

each 迭代器返回数组或哈希的所有元素。

The each iterator returns all the elements of an array or a hash.

Syntax

collection.each do |variable|
   code
end

为集合中的每个元素执行代码。此处,集合可以是一个数组或一个 ruby 哈希。

Executes code for each element in collection. Here, collection could be an array or a ruby hash.

Example

#!/usr/bin/ruby

ary = [1,2,3,4,5]
ary.each do |i|
   puts i
end

这会产生以下结果 −

This will produce the following result −

1
2
3
4
5

你总是将 each 迭代器与一个块关联起来。它一次向块返回数组的每个值。该值存储在变量 i 中,然后显示在屏幕上。

You always associate the each iterator with a block. It returns each value of the array, one by one, to the block. The value is stored in the variable i and then displayed on the screen.

Ruby collect Iterator

collect 迭代器返回集合的所有元素。

The collect iterator returns all the elements of a collection.

Syntax

collection = collection.collect

collect 方法不一定总是与一个块相关联。collect 方法返回整个集合,无论它是一个数组还是哈希。

The collect method need not always be associated with a block. The collect method returns the entire collection, regardless of whether it is an array or a hash.

Example

#!/usr/bin/ruby

a = [1,2,3,4,5]
b = Array.new
b = a.collect
puts b

这会产生以下结果 −

This will produce the following result −

1
2
3
4
5

NOTE − collect 方法不是在数组之间进行复制的正确方法。还有另一种称为 clone 的方法,应该使用该方法将一个数组复制到另一个数组中。

NOTE − The collect method is not the right way to do copying between arrays. There is another method called a clone, which should be used to copy one array into another array.

通常在你希望使用每个值来获取新数组时使用 collect 方法。例如,此代码生成包含 a 中每个值的 10 倍的数组 b。

You normally use the collect method when you want to do something with each of the values to get the new array. For example, this code produces an array b containing 10 times each value in a.

#!/usr/bin/ruby

a = [1,2,3,4,5]
b = a.collect{|x| 10*x}
puts b

这会产生以下结果 −

This will produce the following result −

10
20
30
40
50

Ruby - File I/O

Ruby 提供了在内核模块中实现的整套 I/O 相关方法。所有 I/O 方法都派生自类 IO。

Ruby provides a whole set of I/O-related methods implemented in the Kernel module. All the I/O methods are derived from the class IO.

类 IO 提供所有基本方法,如 read、write、gets、puts、readline、getc 和 printf。

The class IO provides all the basic methods, such as read, write, gets, puts, readline, getc, and printf.

本章将介绍 Ruby 中可用的所有基本 I/O 函数。有关更多函数,请参阅 Ruby 类 IO。

This chapter will cover all the basic I/O functions available in Ruby. For more functions, please refer to Ruby Class IO.

The puts Statement

在前面的章节中,您已将值分配给变量,然后使用 puts 语句打印输出。

In the previous chapters, you have assigned values to variables and then printed the output using puts statement.

puts 语句指示程序显示存储在变量中的值。这会在每行末尾添加一行。

The puts statement instructs the program to display the value stored in the variable. This will add a new line at the end of each line it writes.

Example

#!/usr/bin/ruby

val1 = "This is variable one"
val2 = "This is variable two"
puts val1
puts val2

这会产生以下结果 −

This will produce the following result −

This is variable one
This is variable two

The gets Statement

gets 语句可用于从称为 STDIN 的标准屏幕接受用户的任何输入。

The gets statement can be used to take any input from the user from standard screen called STDIN.

Example

以下代码向您展示如何使用 gets 语句。此代码将提示用户输入值,该值将存储在变量 val 中,最后将打印在 STDOUT 中。

The following code shows you how to use the gets statement. This code will prompt the user to enter a value, which will be stored in a variable val and finally will be printed on STDOUT.

#!/usr/bin/ruby

puts "Enter a value :"
val = gets
puts val

这会产生以下结果 −

This will produce the following result −

Enter a value :
This is entered value
This is entered value

The putc Statement

与将整个字符串输出到屏幕上的 puts 语句不同, putc 语句可用于一次输出一个字符。

Unlike the puts statement, which outputs the entire string onto the screen, the putc statement can be used to output one character at a time.

Example

以下代码的输出仅为字符 H −

The output of the following code is just the character H −

#!/usr/bin/ruby

str = "Hello Ruby!"
putc str

这会产生以下结果 −

This will produce the following result −

H

The print Statement

print 语句与 puts 语句相似。唯一的区别是 puts 语句在打印内容后转到下一行,而 print 语句会将光标定位在同一行。

The print statement is similar to the puts statement. The only difference is that the puts statement goes to the next line after printing the contents, whereas with the print statement the cursor is positioned on the same line.

Example

#!/usr/bin/ruby

print "Hello World"
print "Good Morning"

这会产生以下结果 −

This will produce the following result −

Hello WorldGood Morning

Opening and Closing Files

到目前为止,你一直在对标准输入和输出进行读写。接下来,我们将了解如何使用实际数据文件。

Until now, you have been reading and writing to the standard input and output. Now, we will see how to play with actual data files.

The File.new Method

可根据模式字符串,使用 File.new 方法创建 File 对象以进行读、写或两者兼而有之的操作。最后,可使用 File.close 方法关闭该文件。

You can create a File object using File.new method for reading, writing, or both, according to the mode string. Finally, you can use File.close method to close that file.

Syntax

aFile = File.new("filename", "mode")
   # ... process the file
aFile.close

The File.open Method

可使用 File.open 方法创建一个新文件对象并将该文件对象指定给一个文件。但是,在 File.open 和 File.new 方法之间有一个区别。区别在于 File.open 方法可以与一个代码块关联,但你不能使用 File.new 方法执行相同的操作。

You can use File.open method to create a new file object and assign that file object to a file. However, there is one difference in between File.open and File.new methods. The difference is that the File.open method can be associated with a block, whereas you cannot do the same using the File.new method.

File.open("filename", "mode") do |aFile|
   # ... process the file
end

Reading and Writing Files

我们用于“简单”I/O 的相同方法可用于所有文件对象。因此, gets 从标准输入读取一行,而 aFile.gets 从文件对象 aFile 中读取一行。

The same methods that we’ve been using for 'simple' I/O are available for all file objects. So, gets reads a line from standard input, and aFile.gets reads a line from the file object aFile.

不过,I/O 对象提供了另外一组访问方法,使我们的生活更轻松。

However, I/O objects provides additional set of access methods to make our lives easier.

The sysread Method

可使用 sysread 方法读取文件的内容。使用 sysread 方法时,你可以在任何模式下打开文件。例如 −

You can use the method sysread to read the contents of a file. You can open the file in any of the modes when using the method sysread. For example −

以下为输入文本文件 −

Following is the input text file −

This is a simple text file for testing purpose.

现在,我们尝试读取此文件 −

Now let’s try to read this file −

#!/usr/bin/ruby

aFile = File.new("input.txt", "r")
if aFile
   content = aFile.sysread(20)
   puts content
else
   puts "Unable to open file!"
end

此语句将输出文件的前 20 个字符。文件指针现在将置于文件中的第 21 个字符。

This statement will output the first 20 characters of the file. The file pointer will now be placed at the 21st character in the file.

The syswrite Method

可使用 syswrite 方法向文件中写入内容。使用 syswrite 方法时,需要以写入模式打开文件。例如 −

You can use the method syswrite to write the contents into a file. You need to open the file in write mode when using the method syswrite. For example −

#!/usr/bin/ruby

aFile = File.new("input.txt", "r+")
if aFile
   aFile.syswrite("ABCDEF")
else
   puts "Unable to open file!"
end

此语句将“ABCDEF”写入文件。

This statement will write "ABCDEF" into the file.

The each_byte Method

此方法属于 File 类。each_byte 方法始终与一个代码块相关联。考虑以下代码示例 −

This method belongs to the class File. The method each_byte is always associated with a block. Consider the following code sample −

#!/usr/bin/ruby

aFile = File.new("input.txt", "r+")
if aFile
   aFile.syswrite("ABCDEF")
   aFile.each_byte {|ch| putc ch; putc ?. }
else
   puts "Unable to open file!"
end

字符逐个传递给变量 ch,然后按如下方式显示在屏幕上 −

Characters are passed one by one to the variable ch and then displayed on the screen as follows −

s. .a. .s.i.m.p.l.e. .t.e.x.t. .f.i.l.e. .f.o.r. .t.e.s.t.i.n.g. .p.u.r.p.o.s.e...
.
.

The IO.readlines Method

File 类是 IO 类的子类。IO 类也有一些方法可用于操作文件。

The class File is a subclass of the class IO. The class IO also has some methods, which can be used to manipulate files.

IO 类方法之一是 IO.readlines。此方法按行返回文件的内容。以下代码显示了方法 IO.readlines 的用法 −

One of the IO class methods is IO.readlines. This method returns the contents of the file line by line. The following code displays the use of the method IO.readlines −

#!/usr/bin/ruby

arr = IO.readlines("input.txt")
puts arr[0]
puts arr[1]

在此代码中,变量 arr 是一个数组。文件 input.txt 的每一行将成为数组 arr 中的一个元素。因此, arr[0] 将包含第一行,而 arr[1] 将包含文件的第二行。

In this code, the variable arr is an array. Each line of the file input.txt will be an element in the array arr. Therefore, arr[0] will contain the first line, whereas arr[1] will contain the second line of the file.

The IO.foreach Method

此方法也行行返回输出。foreach 方法和 readlines 方法之间的区别在于 foreach 方法具有区块。但与 readlines 方法不同的是,foreach 方法不会返回数组。例如 −

This method also returns output line by line. The difference between the method foreach and the method readlines is that the method foreach is associated with a block. However, unlike the method readlines, the method foreach does not return an array. For example −

#!/usr/bin/ruby

IO.foreach("input.txt"){|block| puts block}

此代码将文件 test 的内容逐行传递给变量 block,然后在屏幕上显示输出。

This code will pass the contents of the file test line by line to the variable block, and then the output will be displayed on the screen.

Renaming and Deleting Files

你可以使用重命名和删除方法富有编程性地用 Ruby 重命名和删除文件。

You can rename and delete files programmatically with Ruby with the rename and delete methods.

以下是重命名一个现有文件 test1.txt 的示例 −

Following is the example to rename an existing file test1.txt −

#!/usr/bin/ruby

# Rename a file from test1.txt to test2.txt
File.rename( "test1.txt", "test2.txt" )

以下是删除一个现有文件 test2.txt 的示例 −

Following is the example to delete an existing file test2.txt −

#!/usr/bin/ruby

# Delete file test2.txt
File.delete("test2.txt")

File Modes and Ownership

使用 chmod 方法带有掩码,来更改文件的模式或权限/访问列表 −

Use the chmod method with a mask to change the mode or permissions/access list of a file −

以下是更改一个现有文件 test.txt 的模式为掩码值的一个示例 −

Following is the example to change mode of an existing file test.txt to a mask value −

#!/usr/bin/ruby

file = File.new( "test.txt", "w" )
file.chmod( 0755 )

File Inquiries

以下命令测试在打开一个文件之前它是否存在 −

The following command tests whether a file exists before opening it −

#!/usr/bin/ruby

File.open("file.rb") if File::exists?( "file.rb" )

以下命令查询文件是否确实是一个文件 −

The following command inquire whether the file is really a file −

#!/usr/bin/ruby

# This returns either true or false
File.file?( "text.txt" )

以下命令找出给定文件名是否是一个目录 −

The following command finds out if the given file name is a directory −

#!/usr/bin/ruby

# a directory
File::directory?( "/usr/local/bin" ) # => true

# a file
File::directory?( "file.rb" ) # => false

以下命令找出文件是否可读、可写或可执行 −

The following command finds whether the file is readable, writable or executable −

#!/usr/bin/ruby

File.readable?( "test.txt" )   # => true
File.writable?( "test.txt" )   # => true
File.executable?( "test.txt" ) # => false

以下命令找出文件大小是否为零 −

The following command finds whether the file has zero size or not −

#!/usr/bin/ruby

File.zero?( "test.txt" )      # => true

以下命令返回文件的大小 −

The following command returns size of the file −

#!/usr/bin/ruby

File.size?( "text.txt" )     # => 1002

以下命令可用于找出文件类型 −

The following command can be used to find out a type of file −

#!/usr/bin/ruby

File::ftype( "test.txt" )     # => file

ftype 方法通过返回以下项之一来识别文件类型 − file、directory、characterSpecial、blockSpecial、fifo、link、socket 或 unknown。

The ftype method identifies the type of the file by returning one of the following − file, directory, characterSpecial, blockSpecial, fifo, link, socket, or unknown.

以下命令可用于查找文件何时被创建、修改或最后一次访问 −

The following command can be used to find when a file was created, modified, or last accessed −

#!/usr/bin/ruby

File::ctime( "test.txt" ) # => Fri May 09 10:06:37 -0700 2008
File::mtime( "text.txt" ) # => Fri May 09 10:44:44 -0700 2008
File::atime( "text.txt" ) # => Fri May 09 10:45:01 -0700 2008

Directories in Ruby

所有文件都包含在各个目录中,而 Ruby 处理这些目录也不成问题。File 类可以处理文件,Dir 类可以处理目录。

All files are contained within various directories, and Ruby has no problem handling these too. Whereas the File class handles files, directories are handled with the Dir class.

Navigating Through Directories

要在 Ruby 程序中更改目录,请按如下方式使用 Dir.chdir。此示例将当前目录更改为 /usr/bin。

To change directory within a Ruby program, use Dir.chdir as follows. This example changes the current directory to /usr/bin.

Dir.chdir("/usr/bin")

你可以使用 Dir.pwd 找出当前目录 −

You can find out what the current directory is with Dir.pwd −

puts Dir.pwd # This will return something like /usr/bin

你可以使用 Dir.entries 在特定目录内获取文件和目录的列表——

You can get a list of the files and directories within a specific directory using Dir.entries −

puts Dir.entries("/usr/bin").join(' ')

Dir.entries 返回一个数组,其中包含指定目录内的所有条目。Dir.foreach 提供相同的功能——

Dir.entries returns an array with all the entries within the specified directory. Dir.foreach provides the same feature −

Dir.foreach("/usr/bin") do |entry|
   puts entry
end

获取目录列表的更简洁的方法是使用 Dir 类的数组方法——

An even more concise way of getting directory listings is by using Dir’s class array method −

Dir["/usr/bin/*"]

Creating a Directory

Dir.mkdir 可用于创建目录——

The Dir.mkdir can be used to create directories −

Dir.mkdir("mynewdir")

你还可以使用 mkdir 设置新目录(不是已存在的目录)的权限——

You can also set permissions on a new directory (not one that already exists) with mkdir −

NOTE ——掩码 755 设置权限所有者、组、世界 [任何人] 为 rwxr-xr-x,其中 r = 读,w = 写,x = 执行。

NOTE − The mask 755 sets permissions owner, group, world [anyone] to rwxr-xr-x where r = read, w = write, and x = execute.

Dir.mkdir( "mynewdir", 755 )

Deleting a Directory

Dir.delete 可用于删除目录。Dir.unlink 和 Dir.rmdir 执行完全相同的功能,并且为了方便而提供。

The Dir.delete can be used to delete a directory. The Dir.unlink and Dir.rmdir performs exactly the same function and are provided for convenience.

Dir.delete("testdir")

Creating Files & Temporary Directories

临时文件是指在程序执行期间短暂创建的文件,但不是信息的永久存储。

Temporary files are those that might be created briefly during a program’s execution but aren’t a permanent store of information.

Dir.tmpdir 提供当前系统上临时目录的路径,虽然该方法默认不可用。要使 Dir.tmpdir 可用,需要使用 require 'tmpdir'。

Dir.tmpdir provides the path to the temporary directory on the current system, although the method is not available by default. To make Dir.tmpdir available it’s necessary to use require 'tmpdir'.

你可以将 Dir.tmpdir 与 File.join 一起使用,以创建一个与平台无关的临时文件——

You can use Dir.tmpdir with File.join to create a platform-independent temporary file −

require 'tmpdir'
   tempfilename = File.join(Dir.tmpdir, "tingtong")
   tempfile = File.new(tempfilename, "w")
   tempfile.puts "This is a temporary file"
   tempfile.close
   File.delete(tempfilename)

此代码创建一个临时文件,向其中写入数据并删除它。Ruby 的标准库还包括一个名为 Tempfile 的库,它可以为你创建临时文件——

This code creates a temporary file, writes data to it, and deletes it. Ruby’s standard library also includes a library called Tempfile that can create temporary files for you −

require 'tempfile'
   f = Tempfile.new('tingtong')
   f.puts "Hello"
   puts f.path
   f.close

Built-in Functions

以下是处理文件和目录的 Ruby 内置函数——

Here are the ruby built-in functions to process files and directories −

Ruby - Exceptions

执行和异常总是同时发生。如果您打开一个不存在的文件,则如果没有正确处理此情况,那么您的程序将被认为质量很差。

The execution and the exception always go together. If you are opening a file, which does not exist, then if you did not handle this situation properly, then your program is considered to be of bad quality.

如果出现异常,程序将停止。因此,异常被用于处理执行程序期间可能发生的各种错误,并采取适当操作,而不是完全停止程序。

The program stops if an exception occurs. So exceptions are used to handle various type of errors, which may occur during a program execution and take appropriate action instead of halting program completely.

Ruby 提供了一个很好的机制来处理异常。我们将可能引发异常的代码放在一个 begin/end 块中,并使用 rescue 子句告诉 Ruby 我们想要处理的异常类型。

Ruby provide a nice mechanism to handle exceptions. We enclose the code that could raise an exception in a begin/end block and use rescue clauses to tell Ruby the types of exceptions we want to handle.

Syntax

begin
# -
rescue OneTypeOfException
# -
rescue AnotherTypeOfException
# -
else
# Other exceptions
ensure
# Always will be executed
end

从 begin 到 rescue 的所有内容都受到保护。如果在此代码块执行期间发生异常,控制将传递到 rescue 和 end 之间的块。

Everything from begin to rescue is protected. If an exception occurs during the execution of this block of code, control is passed to the block between rescue and end.

对于 begin 块中的每个 rescue 子句,Ruby 将引发的异常与每个参数逐个进行比较。如果 rescue 子句中命名的异常与当前抛出异常的类型相同,或者该异常的超类匹配,则匹配将成功。

For each rescue clause in the begin block, Ruby compares the raised Exception against each of the parameters in turn. The match will succeed if the exception named in the rescue clause is the same as the type of the currently thrown exception, or is a superclass of that exception.

万一异常与任何指定的错误类型都不匹配,我们可以在所有 rescue 子句之后使用一个 else 子句。

In an event that an exception does not match any of the error types specified, we are allowed to use an else clause after all the rescue clauses.

Example

#!/usr/bin/ruby

begin
   file = open("/unexistant_file")
   if file
      puts "File opened successfully"
   end
rescue
      file = STDIN
end
print file, "==", STDIN, "\n"

这将生成以下结果。您可以看到 STDIN 被替换为 file,因为 open 失败。

This will produce the following result. You can see that STDIN is substituted to file because open failed.

#<IO:0xb7d16f84>==#<IO:0xb7d16f84>

Using retry Statement

您可以使用 rescue 块捕获异常,然后使用 retry 语句从头开始执行 begin 块。

You can capture an exception using rescue block and then use retry statement to execute begin block from the beginning.

Syntax

begin
   # Exceptions raised by this code will
   # be caught by the following rescue clause
rescue
   # This block will capture all types of exceptions
   retry  # This will move control to the beginning of begin
end

Example

#!/usr/bin/ruby

begin
   file = open("/unexistant_file")
   if file
      puts "File opened successfully"
   end
rescue
   fname = "existant_file"
   retry
end

以下是流程 −

The following is the flow of the process −

  1. An exception occurred at open.

  2. Went to rescue. fname was re-assigned.

  3. By retry went to the beginning of the begin.

  4. This time file opens successfully.

  5. Continued the essential process.

NOTE − 请注意,如果重新替换的文件不存在,此示例代码会无限次重试。如果您对异常过程使用 retry,请小心。

NOTE − Notice that if the file of re-substituted name does not exist this example code retries infinitely. Be careful if you use retry for an exception process.

Using raise Statement

您可以使用 raise 语句引发异常。以下方法在每次调用时都会引发异常。它会打印其第二个消息。

You can use raise statement to raise an exception. The following method raises an exception whenever it’s called. It’s second message will be printed.

Syntax

raise

OR

raise "Error Message"

OR

raise ExceptionType, "Error Message"

OR

raise ExceptionType, "Error Message" condition

第一种形式只重新引发当前异常(如果不存在当前异常,则重新引发 RuntimeError)。这用于需要在传递异常之前拦截异常的异常处理程序中。

The first form simply re-raises the current exception (or a RuntimeError if there is no current exception). This is used in exception handlers that need to intercept an exception before passing it on.

第二种形式创建一个新的 RuntimeError 异常,将其消息设置为给定的字符串。然后在调用堆栈中引发此异常。

The second form creates a new RuntimeError exception, setting its message to the given string. This exception is then raised up the call stack.

第三种形式使用第一个参数创建一个异常,然后将关联消息设置为第二个参数。

The third form uses the first argument to create an exception and then sets the associated message to the second argument.

第四种形式类似于第三种形式,但您可以添加任何条件语句(如 unless)以引发异常。

The fourth form is similar to the third form but you can add any conditional statement like unless to raise an exception.

Example

#!/usr/bin/ruby

begin
   puts 'I am before the raise.'
   raise 'An error has occurred.'
   puts 'I am after the raise.'
rescue
   puts 'I am rescued.'
end
puts 'I am after the begin block.'

这会产生以下结果 −

This will produce the following result −

I am before the raise.
I am rescued.
I am after the begin block.

另一个展示 raise 用法的示例 −

One more example showing the usage of raise −

#!/usr/bin/ruby

begin
   raise 'A test exception.'
rescue Exception => e
   puts e.message
   puts e.backtrace.inspect
end

这会产生以下结果 −

This will produce the following result −

A test exception.
["main.rb:4"]

Using ensure Statement

有时候,你需要保证在代码块结束时完成一些处理,无论是否引发异常。例如,你可能在进入该代码块时打开了文件,你需要确保在该块退出时将其关闭。

Sometimes, you need to guarantee that some processing is done at the end of a block of code, regardless of whether an exception was raised. For example, you may have a file open on entry to the block and you need to make sure it gets closed as the block exits.

ensure 子句就是用来这么做的。ensure 位于最后一个 rescue 子句之后,并且包含一个将在块终止时始终执行的代码块。无论该块正常退出,是引发和捕获了一个异常,还是被未捕获的异常终止,ensure 块都将被运行。

The ensure clause does just this. ensure goes after the last rescue clause and contains a chunk of code that will always be executed as the block terminates. It doesn’t matter if the block exits normally, if it raises and rescues an exception, or if it is terminated by an uncaught exception, the ensure block will get run.

Syntax

begin
   #.. process
   #..raise exception
rescue
   #.. handle error
ensure
   #.. finally ensure execution
   #.. This will always execute.
end

Example

begin
   raise 'A test exception.'
rescue Exception => e
   puts e.message
   puts e.backtrace.inspect
ensure
   puts "Ensuring execution"
end

这会产生以下结果 −

This will produce the following result −

A test exception.
["main.rb:4"]
Ensuring execution

Using else Statement

如果存在 else 子句,则它位于 rescue 子句之后、任何 ensure 之前。

If the else clause is present, it goes after the rescue clauses and before any ensure.

else 子句的主体只在代码主体没有引发任何异常时执行。

The body of an else clause is executed only if no exceptions are raised by the main body of code.

Syntax

begin
   #.. process
   #..raise exception
rescue
   # .. handle error
else
   #.. executes if there is no exception
ensure
   #.. finally ensure execution
   #.. This will always execute.
end

Example

begin
   # raise 'A test exception.'
   puts "I'm not raising exception"
rescue Exception => e
   puts e.message
   puts e.backtrace.inspect
else
   puts "Congratulations-- no errors!"
ensure
   puts "Ensuring execution"
end

这会产生以下结果 −

This will produce the following result −

I'm not raising exception
Congratulations-- no errors!
Ensuring execution

引发的错误消息可以用 $! 变量捕获。

Raised error message can be captured using $! variable.

Catch and Throw

虽然 raise 和 rescue 的异常机制非常适合在出现问题时放弃执行,但在正常处理过程中有时也可以从一些深度嵌套的构造中跳出。这就是 catch 和 throw 派上用场的时候。

While the exception mechanism of raise and rescue is great for abandoning the execution when things go wrong, it’s sometimes nice to be able to jump out of some deeply nested construct during normal processing. This is where catch and throw come in handy.

catch 定义了一个用给定名称(可以是符号或字符串)标记的块。该块被正常执行,直到遇到 throw。

The catch defines a block that is labeled with the given name (which may be a Symbol or a String). The block is executed normally until a throw is encountered.

Syntax

throw :lablename
#.. this will not be executed
catch :lablename do
#.. matching catch will be executed after a throw is encountered.
end

OR

throw :lablename condition
#.. this will not be executed
catch :lablename do
#.. matching catch will be executed after a throw is encountered.
end

Example

以下示例在一个请求中输入了“!”来使用 throw 终止与用户的交互。

The following example uses a throw to terminate interaction with the user if '!' is typed in response to any prompt.

def promptAndGet(prompt)
   print prompt
   res = readline.chomp
   throw :quitRequested if res == "!"
   return res
end

catch :quitRequested do
   name = promptAndGet("Name: ")
   age = promptAndGet("Age: ")
   sex = promptAndGet("Sex: ")
   # ..
   # process information
end
promptAndGet("Name:")

你应该在你的机器上尝试上述程序,因为它需要手动交互。这将产生以下结果 −

You should try the above program on your machine because it needs manual interaction. This will produce the following result −

Name: Ruby on Rails
Age: 3
Sex: !
Name:Just Ruby

Class Exception

Ruby 的标准类和模块会引发异常。所有的异常类都形成一个层次结构,其中类 Exception 位于顶部。下一级包含七种不同的类型 −

Ruby’s standard classes and modules raise exceptions. All the exception classes form a hierarchy, with the class Exception at the top. The next level contains seven different types −

  1. Interrupt

  2. NoMemoryError

  3. SignalException

  4. ScriptError

  5. StandardError

  6. SystemExit

在此级别还有另一个异常 Fatal ,但 Ruby 解释器仅在内部使用它。

There is one other exception at this level, Fatal, but the Ruby interpreter only uses this internally.

ScriptError 和 StandardError 都有许多子类,但我们不必在这里深入细节。重要的是,如果我们创建自己的异常类,它们需要是类 Exception 或其子代的子类。

Both ScriptError and StandardError have a number of subclasses, but we do not need to go into the details here. The important thing is that if we create our own exception classes, they need to be subclasses of either class Exception or one of its descendants.

我们来看一个示例 −

Let’s look at an example −

class FileSaveError < StandardError
   attr_reader :reason
   def initialize(reason)
      @reason = reason
   end
end

现在,看一看下面的示例,它将使用此异常 −

Now, look at the following example, which will use this exception −

File.open(path, "w") do |file|
begin
   # Write out the data ...
rescue
   # Something went wrong!
   raise FileSaveError.new($!)
end
end

此处的重要行是 raise FileSaveError.new($!)。我们调用 raise 来发出异常已发生信号,向其传递 FileSaveError 的一个新实例,原因是该特定异常导致数据的写入失败。

The important line here is raise FileSaveError.new($!). We call raise to signal that an exception has occurred, passing it a new instance of FileSaveError, with the reason being that specific exception caused the writing of the data to fail.

Ruby - Object Oriented

Ruby 是一种纯面向对象语言,在 Ruby 中一切都被视为对象。Ruby 中的每个值都是一个对象,即使是最原始的东西:字符串、数字甚至是 true 和 false。即使类本身也是 Object 类的一个实例的对象。本章将带你了解与面向对象 Ruby 相关的全部主要功能。

Ruby is a pure object-oriented language and everything appears to Ruby as an object. Every value in Ruby is an object, even the most primitive things: strings, numbers and even true and false. Even a class itself is an object that is an instance of the Class class. This chapter will take you through all the major functionalities related to Object Oriented Ruby.

类用于指定对象的格式,它将数据表示和用于操作数据的函数组合在一个整洁的包中。类中的数据和方法称为类的成员。

A class is used to specify the form of an object and it combines data representation and methods for manipulating that data into one neat package. The data and methods within a class are called members of the class.

Ruby Class Definition

当你定义一个类时,你就定义了一个数据类型的蓝图。这实际上并不定义任何数据,但它确实定义了类名意味着什么,即类的对象将由什么组成,以及可以在这样的对象上执行什么操作。

When you define a class, you define a blueprint for a data type. This doesn’t actually define any data, but it does define what the class name means, that is, what an object of the class will consist of and what operations can be performed on such an object.

类定义以关键字 class 开始,随后是 class name ,并用 end 分隔。例如,我们使用关键字 class 定义 Box 类,如下所示:

A class definition starts with the keyword class followed by the class name and is delimited with an end. For example, we defined the Box class using the keyword class as follows −

class Box
   code
end

名称必须以大写字母开头,并且根据惯例,包含多个单词的名称会将每个单词的首字母大写并连接在一起,并且没有分隔符(驼峰式命名法)。

The name must begin with a capital letter and by convention names that contain more than one word are run together with each word capitalized and no separating characters (CamelCase).

Define Ruby Objects

一个类为对象提供蓝图,因此一个对象基本上是从一个类中创建的。我们使用 new 关键字声明类的对象。以下语句声明类的两个对象 −

A class provides the blueprints for objects, so basically an object is created from a class. We declare objects of a class using new keyword. Following statements declare two objects of class Box −

box1 = Box.new
box2 = Box.new

The initialize Method

initialize method 是一个标准 Ruby 类方法,并且作用方式几乎与 constructor 在其他面向对象编程语言中的作用方式相同。如果你想要在创建对象的时候对一些类变量进行初始化,initialize 方法是很有用的。此方法可以接受一个参数列表,并且像任何其他 ruby 方法一样,它会以 def 关键字为前导,如下所示 −

The initialize method is a standard Ruby class method and works almost same way as constructor works in other object oriented programming languages. The initialize method is useful when you want to initialize some class variables at the time of object creation. This method may take a list of parameters and like any other ruby method it would be preceded by def keyword as shown below −

class Box
   def initialize(w,h)
      @width, @height = w, h
   end
end

The instance Variables

instance variables 是一种类属性,一旦使用类创建了对象,它们就会成为对象的属性。每个对象的属性都单独分配,并且不与其他对象共享值。它们在类中使用 @ 运算符进行访问,但是要在类外部访问它们,我们使用 public 方法,它们称为 accessor methods 。如果我们采用上面定义的类 Box ,那么 @width 和 @height 就是类 Box 的实例变量。

The instance variables are kind of class attributes and they become properties of objects once objects are created using the class. Every object’s attributes are assigned individually and share no value with other objects. They are accessed using the @ operator within the class but to access them outside of the class we use public methods, which are called accessor methods. If we take the above defined class Box then @width and @height are instance variables for the class Box.

class Box
   def initialize(w,h)
      # assign instance variables
      @width, @height = w, h
   end
end

The accessor & setter Methods

要让变量在类外部可用,它们必须在 accessor methods 内进行定义,这些访问器方法也称为 getter 方法。以下示例显示了访问器方法的用法 −

To make the variables available from outside the class, they must be defined within accessor methods, these accessor methods are also known as a getter methods. Following example shows the usage of accessor methods −

#!/usr/bin/ruby -w

# define a class
class Box
   # constructor method
   def initialize(w,h)
      @width, @height = w, h
   end

   # accessor methods
   def printWidth
      @width
   end

   def printHeight
      @height
   end
end

# create an object
box = Box.new(10, 20)

# use accessor methods
x = box.printWidth()
y = box.printHeight()

puts "Width of the box is : #{x}"
puts "Height of the box is : #{y}"

执行上述代码后,将生成以下结果 −

When the above code is executed, it produces the following result −

Width of the box is : 10
Height of the box is : 20

与用于访问变量值的访问器方法类似,Ruby 提供了一种使用 setter methods 从类外部设置这些变量值的方法,如下定义 −

Similar to accessor methods, which are used to access the value of the variables, Ruby provides a way to set the values of those variables from outside of the class using setter methods, which are defined as below −

#!/usr/bin/ruby -w

# define a class
class Box
   # constructor method
   def initialize(w,h)
      @width, @height = w, h
   end

   # accessor methods
   def getWidth
      @width
   end
   def getHeight
      @height
   end

   # setter methods
   def setWidth=(value)
      @width = value
   end
   def setHeight=(value)
      @height = value
   end
end

# create an object
box = Box.new(10, 20)

# use setter methods
box.setWidth = 30
box.setHeight = 50

# use accessor methods
x = box.getWidth()
y = box.getHeight()

puts "Width of the box is : #{x}"
puts "Height of the box is : #{y}"

执行上述代码后,将生成以下结果 −

When the above code is executed, it produces the following result −

Width of the box is : 30
Height of the box is : 50

The instance Methods

instance methods 的定义方式与我们使用 def 关键字定义任何其他方法的方式相同,并且它们只能使用类实例来使用,如下所示。它们的功能并不仅限于访问实例变量,还可以根据你的要求执行更多操作。

The instance methods are also defined in the same way as we define any other method using def keyword and they can be used using a class instance only as shown below. Their functionality is not limited to access the instance variables, but also they can do a lot more as per your requirement.

#!/usr/bin/ruby -w

# define a class
class Box
   # constructor method
   def initialize(w,h)
      @width, @height = w, h
   end
   # instance method
   def getArea
      @width * @height
   end
end

# create an object
box = Box.new(10, 20)

# call instance methods
a = box.getArea()
puts "Area of the box is : #{a}"

执行上述代码后,将生成以下结果 −

When the above code is executed, it produces the following result −

Area of the box is : 200

The class Methods and Variables

class variables 是一个变量,它在类的所有实例中共享。换句话说,该变量只有一个实例,它由对象实例访问。类变量使用两个 @ 字符(@@)作为前缀。必须在类定义中初始化类变量,如下所示。

The class variables is a variable, which is shared between all instances of a class. In other words, there is one instance of the variable and it is accessed by object instances. Class variables are prefixed with two @ characters (@@). A class variable must be initialized within the class definition as shown below.

类方法使用 def self.methodname() 定义,它以 end 定界符结尾,并且会使用类名作为 classname.methodname 调用,如下例所示 −

A class method is defined using def self.methodname(), which ends with end delimiter and would be called using the class name as classname.methodname as shown in the following example −

#!/usr/bin/ruby -w

class Box
   # Initialize our class variables
   @@count = 0
   def initialize(w,h)
      # assign instance avriables
      @width, @height = w, h

      @@count += 1
   end

   def self.printCount()
      puts "Box count is : #@@count"
   end
end

# create two object
box1 = Box.new(10, 20)
box2 = Box.new(30, 100)

# call class method to print box count
Box.printCount()

执行上述代码后,将生成以下结果 −

When the above code is executed, it produces the following result −

Box count is : 2

The to_s Method

你定义的任何类都应具有 to_s 实例方法以返回对象的字符串表示形式。以下是一个简单的示例,用于表示 Box 对象的宽度和高度 −

Any class you define should have a to_s instance method to return a string representation of the object. Following is a simple example to represent a Box object in terms of width and height −

#!/usr/bin/ruby -w

class Box
   # constructor method
   def initialize(w,h)
      @width, @height = w, h
   end
   # define to_s method
   def to_s
      "(w:#@width,h:#@height)"  # string formatting of the object.
   end
end

# create an object
box = Box.new(10, 20)

# to_s method will be called in reference of string automatically.
puts "String representation of box is : #{box}"

执行上述代码后,将生成以下结果 −

When the above code is executed, it produces the following result −

String representation of box is : (w:10,h:20)

Access Control

Ruby 在实例方法级别提供三级保护,它们可能是 public, private, or protected 。Ruby 对实例和类变量不适用任何访问控制。

Ruby gives you three levels of protection at instance methods level, which may be public, private, or protected. Ruby does not apply any access control over instance and class variables.

  1. Public Methods − Public methods can be called by anyone. Methods are public by default except for initialize, which is always private.

  2. Private Methods − Private methods cannot be accessed, or even viewed from outside the class. Only the class methods can access private members.

  3. Protected Methods − A protected method can be invoked only by objects of the defining class and its subclasses. Access is kept within the family.

以下是一个简单的示例,展示了所有三个访问修饰符的语法 −

Following is a simple example to show the syntax of all the three access modifiers −

#!/usr/bin/ruby -w

# define a class
class Box
   # constructor method
   def initialize(w,h)
      @width, @height = w, h
   end

   # instance method by default it is public
   def getArea
      getWidth() * getHeight
   end

   # define private accessor methods
   def getWidth
      @width
   end
   def getHeight
      @height
   end
   # make them private
   private :getWidth, :getHeight

   # instance method to print area
   def printArea
      @area = getWidth() * getHeight
      puts "Big box area is : #@area"
   end
   # make it protected
   protected :printArea
end

# create an object
box = Box.new(10, 20)

# call instance methods
a = box.getArea()
puts "Area of the box is : #{a}"

# try to call protected or methods
box.printArea()

当执行以上代码时,它会产生以下结果。在此,第一个方法成功调用,但第二个方法出现了问题。

When the above code is executed, it produces the following result. Here, first method is called successfully but second method gave a problem.

Area of the box is : 200
test.rb:42: protected method `printArea' called for #
<Box:0xb7f11280 @height = 20, @width = 10> (NoMethodError)

Class Inheritance

面向对象编程中最重要的概念之一是继承。继承允许我们在另一个类的基础上定义一个类,这使创建和维护应用程序变得更加容易。

One of the most important concepts in object-oriented programming is that of inheritance. Inheritance allows us to define a class in terms of another class, which makes it easier to create and maintain an application.

继承还提供了重用代码功能和快速实现时间的机会,但不幸的是,Ruby 不支持多层次继承,但 Ruby 支持 mixins 。混合就像多重继承的一种专门实现,其中仅继承接口部分。

Inheritance also provides an opportunity to reuse the code functionality and fast implementation time but unfortunately Ruby does not support multiple levels of inheritances but Ruby supports mixins. A mixin is like a specialized implementation of multiple inheritance in which only the interface portion is inherited.

在创建一个类时,程序员可以指定新类应该继承现有类的成员,而不是编写全新的数据成员和成员函数。这个现有的类被称为 base class or superclass ,新类被称为 derived class or sub-class

When creating a class, instead of writing completely new data members and member functions, the programmer can designate that the new class should inherit the members of an existing class. This existing class is called the base class or superclass, and the new class is referred to as the derived class or sub-class.

Ruby 也支持子类化的概念,即继承,以下示例解释了这个概念。扩展类的语法很简单。只需添加 < 字符和超类的名称到你的类语句中即可。例如,以下定义一个类 BigBox 作为 Box 的子类 −

Ruby also supports the concept of subclassing, i.e., inheritance and following example explains the concept. The syntax for extending a class is simple. Just add a < character and the name of the superclass to your class statement. For example, following define a class BigBox as a subclass of Box −

#!/usr/bin/ruby -w

# define a class
class Box
   # constructor method
   def initialize(w,h)
      @width, @height = w, h
   end
   # instance method
   def getArea
      @width * @height
   end
end

# define a subclass
class BigBox < Box

   # add a new instance method
   def printArea
      @area = @width * @height
      puts "Big box area is : #@area"
   end
end

# create an object
box = BigBox.new(10, 20)

# print the area
box.printArea()

执行上述代码后,将生成以下结果 −

When the above code is executed, it produces the following result −

Big box area is : 200

Methods Overriding

尽管可以在派生类中添加新功能,但有时您想要更改父类中已定义的方法的行为。您可以通过保持方法名称相同并覆盖方法的功能来做到这一点,如下例所示 -

Though you can add new functionality in a derived class, but sometimes you would like to change the behavior of already defined method in a parent class. You can do so simply by keeping the method name same and overriding the functionality of the method as shown below in the example −

#!/usr/bin/ruby -w

# define a class
class Box
   # constructor method
   def initialize(w,h)
      @width, @height = w, h
   end
   # instance method
   def getArea
      @width * @height
   end
end

# define a subclass
class BigBox < Box

   # change existing getArea method as follows
   def getArea
      @area = @width * @height
      puts "Big box area is : #@area"
   end
end

# create an object
box = BigBox.new(10, 20)

# print the area using overriden method.
box.getArea()

Operator Overloading

我们希望 + 运算符使用 + 执行两个 Box 对象的向量加法,* 运算符根据一个标量乘以一个 Box 的宽度和高度,一元 - 运算符对 Box 的宽度和高度求反。下面是定义了数学运算符的 Box 类的版本 -

We’d like the + operator to perform vector addition of two Box objects using +, the * operator to multiply a Box width and height by a scalar, and the unary - operator to do negate the width and height of the Box. Here is a version of the Box class with mathematical operators defined −

class Box
   def initialize(w,h)     # Initialize the width and height
      @width,@height = w, h
   end

   def +(other)       # Define + to do vector addition
      Box.new(@width + other.width, @height + other.height)
   end

   def -@           # Define unary minus to negate width and height
      Box.new(-@width, -@height)
   end

   def *(scalar)           # To perform scalar multiplication
      Box.new(@width*scalar, @height*scalar)
   end
end

Freezing Objects

有时,我们希望阻止对对象的更改。Object 中的 freeze 方法允许我们做到这一点,有效地将对象变成一个常量。可以通过调用 Object.freeze 来冻结任何对象。冻结的对象可能不会被修改:您不能更改其实例变量。

Sometimes, we want to prevent an object from being changed. The freeze method in Object allows us to do this, effectively turning an object into a constant. Any object can be frozen by invoking Object.freeze. A frozen object may not be modified: you can’t change its instance variables.

您可以使用 Object.frozen? 方法检查给定对象是否已冻结,该方法在对象被冻结的情况下返回 true,否则返回 false 值。以下示例阐明了概念 -

You can check if a given object is already frozen or not using Object.frozen? method, which returns true in case the object is frozen otherwise a false value is return. Following example clears the concept −

#!/usr/bin/ruby -w

# define a class
class Box
   # constructor method
   def initialize(w,h)
      @width, @height = w, h
   end

   # accessor methods
   def getWidth
      @width
   end
   def getHeight
      @height
   end

   # setter methods
   def setWidth=(value)
      @width = value
   end
   def setHeight=(value)
      @height = value
   end
end

# create an object
box = Box.new(10, 20)

# let us freez this object
box.freeze
if( box.frozen? )
   puts "Box object is frozen object"
else
   puts "Box object is normal object"
end

# now try using setter methods
box.setWidth = 30
box.setHeight = 50

# use accessor methods
x = box.getWidth()
y = box.getHeight()

puts "Width of the box is : #{x}"
puts "Height of the box is : #{y}"

执行上述代码后,将生成以下结果 −

When the above code is executed, it produces the following result −

Box object is frozen object
test.rb:20:in `setWidth=': can't modify frozen object (TypeError)
   from test.rb:39

Class Constants

您可以通过将直接数字或字符串值分配给一个变量来定义类中的常量,而该变量不用 @ 或 @@ 定义。约定上,我们将常量名称保持为大写。

You can define a constant inside a class by assigning a direct numeric or string value to a variable, which is defined without using either @ or @@. By convention, we keep constant names in upper case.

一旦定义了常量,您就不能更改它,但是您可以直接在类中访问常量,就像访问变量一样,但是如果您想在类外访问常量,则必须像下面示例中所示使用 classname::constant

Once a constant is defined, you cannot change its value but you can access a constant directly inside a class much like a variable but if you want to access a constant outside of the class then you would have to use classname::constant as shown in the below example.

#!/usr/bin/ruby -w

# define a class
class Box
   BOX_COMPANY = "TATA Inc"
   BOXWEIGHT = 10
   # constructor method
   def initialize(w,h)
      @width, @height = w, h
   end
   # instance method
   def getArea
      @width * @height
   end
end

# create an object
box = Box.new(10, 20)

# call instance methods
a = box.getArea()
puts "Area of the box is : #{a}"
puts Box::BOX_COMPANY
puts "Box weight is: #{Box::BOXWEIGHT}"

执行上述代码后,将生成以下结果 −

When the above code is executed, it produces the following result −

Area of the box is : 200
TATA Inc
Box weight is: 10

类常量可被继承并且可以像实例方法一样被覆盖。

Class constants are inherited and can be overridden like instance methods.

Create Object Using Allocate

当您想在不调用其构造函数 initialize (即使用 new 方法)的情况下创建对象时,可能会出现这种情况,在这种情况下,您可以调用 allocate,它将为您创建一个未初始化的对象,如下例所示 -

There may be a situation when you want to create an object without calling its constructor initialize i.e. using new method, in such case you can call allocate, which will create an uninitialized object for you as in the following example −

#!/usr/bin/ruby -w

# define a class
class Box
   attr_accessor :width, :height

   # constructor method
   def initialize(w,h)
      @width, @height = w, h
   end

   # instance method
   def getArea
      @width * @height
   end
end

# create an object using new
box1 = Box.new(10, 20)

# create another object using allocate
box2 = Box.allocate

# call instance method using box1
a = box1.getArea()
puts "Area of the box is : #{a}"

# call instance method using box2
a = box2.getArea()
puts "Area of the box is : #{a}"

执行上述代码后,将生成以下结果 −

When the above code is executed, it produces the following result −

Area of the box is : 200
test.rb:14: warning: instance variable @width not initialized
test.rb:14: warning: instance variable @height not initialized
test.rb:14:in `getArea': undefined method `*'
   for nil:NilClass (NoMethodError) from test.rb:29

Class Information

如果类定义是可执行代码,这意味着它们在某个对象的上下文中执行:self 必须引用某些东西。让我们找出是什么。

If class definitions are executable code, this implies that they execute in the context of some object: self must reference something. Let’s find out what it is.

#!/usr/bin/ruby -w

class Box
   # print class information
   puts "Type of self = #{self.type}"
   puts "Name of self = #{self.name}"
end

执行上述代码后,将生成以下结果 −

When the above code is executed, it produces the following result −

Type of self = Class
Name of self = Box

这意味着类定义以该类为当前对象执行。这意味着在执行方法定义期间将可以使用元类及其超类中的方法。

This means that a class definition is executed with that class as the current object. This means that methods in the metaclass and its superclasses will be available during the execution of the method definition.

Ruby - Regular Expressions

正则表达式是一个特殊的字符序列,它帮助您使用保存在模式中的特殊语法匹配或找到其他字符串或一组字符串。

A regular expression is a special sequence of characters that helps you match or find other strings or sets of strings using a specialized syntax held in a pattern.

正则表达式文字是在斜杠之间或在任意分隔符之后加 %r 的模式,如下所示 −

A regular expression literal is a pattern between slashes or between arbitrary delimiters followed by %r as follows −

Syntax

/pattern/
/pattern/im    # option can be specified
%r!/usr/local! # general delimited regular expression

Example

#!/usr/bin/ruby

line1 = "Cats are smarter than dogs";
line2 = "Dogs also like meat";

if ( line1 =~ /Cats(.*)/ )
   puts "Line1 contains Cats"
end
if ( line2 =~ /Cats(.*)/ )
   puts "Line2 contains  Dogs"
end

这会产生以下结果 −

This will produce the following result −

Line1 contains Cats

Regular-Expression Modifiers

正则表达式文字可能包括一个可选修饰符来控制匹配的各个方面。修饰符在第二个斜杠字符之后指定,如前所示,可用以下字符中的一个表示 −

Regular expression literals may include an optional modifier to control various aspects of matching. The modifier is specified after the second slash character, as shown previously and may be represented by one of these characters −

Sr.No.

Modifier & Description

1

i Ignores case when matching text.

2

o Performs #{} interpolations only once, the first time the regexp literal is evaluated.

3

x Ignores whitespace and allows comments in regular expressions.

4

m Matches multiple lines, recognizing newlines as normal characters.

5

u,e,s,n Interprets the regexp as Unicode (UTF-8), EUC, SJIS, or ASCII. If none of these modifiers is specified, the regular expression is assumed to use the source encoding.

与使用 %Q 分隔的字符串文字类似,Ruby 允许你使用 %r 开始你的正则表达式,然后跟上你选择的限定符。当你要描述的模式包含大量你不想要转义的正斜杠字符时,此方法非常有用 −

Like string literals delimited with %Q, Ruby allows you to begin your regular expressions with %r followed by a delimiter of your choice. This is useful when the pattern you are describing contains a lot of forward slash characters that you don’t want to escape −

# Following matches a single slash character, no escape required
%r|/|

# Flag characters are allowed with this syntax, too
%r[</(.*)>]i

Regular-Expression Patterns

除了控制字符 (+ ? . * ^ $ ( ) [ ] { } | \) ,所有字符都匹配自身。你可以在控制字符前面加上反斜杠对其进行转义。

Except for control characters, (+ ? . * ^ $ ( ) [ ] { } | \), all characters match themselves. You can escape a control character by preceding it with a backslash.

Regular-Expression Examples

Search and Replace

一些最重要的使用正则表达式的 String 方法是 subgsub ,以及它们的原地变体 sub!gsub!

Some of the most important String methods that use regular expressions are sub and gsub, and their in-place variants sub! and gsub!.

所有这些方法都使用 Regexp 模式执行搜索并替换操作。 subsub! 替换模式的首次出现, gsubgsub! 替换所有出现。

All of these methods perform a search-and-replace operation using a Regexp pattern. The sub & sub! replaces the first occurrence of the pattern and gsub & gsub! replaces all occurrences.

subgsub 返回一个新字符串,保持原始字符串不变,而 sub!gsub! 修改了调用它们的字符串。

The sub and gsub returns a new string, leaving the original unmodified where as sub! and gsub! modify the string on which they are called.

以下示例 −

Following is the example −

#!/usr/bin/ruby

phone = "2004-959-559 #This is Phone Number"

# Delete Ruby-style comments
phone = phone.sub!(/#.*$/, "")
puts "Phone Num : #{phone}"

# Remove anything other than digits
phone = phone.gsub!(/\D/, "")
puts "Phone Num : #{phone}"

这会产生以下结果 −

This will produce the following result −

Phone Num : 2004-959-559
Phone Num : 2004959559

以下示例 −

Following is another example −

#!/usr/bin/ruby

text = "rails are rails, really good Ruby on Rails"

# Change "rails" to "Rails" throughout
text.gsub!("rails", "Rails")

# Capitalize the word "Rails" throughout
text.gsub!(/\brails\b/, "Rails")
puts "#{text}"

这会产生以下结果 −

This will produce the following result −

Rails are Rails, really good Ruby on Rails

Ruby/DBI Tutorial

本章教你如何使用 Ruby 访问数据库。Ruby DBI 模块为 Ruby 脚本提供了一个与 Perl DBI 模块类似的数据库独立接口。

This chapter teaches you how to access a database using Ruby. The Ruby DBI module provides a database-independent interface for Ruby scripts similar to that of the Perl DBI module.

DBI 代表 Ruby 的数据库独立接口,这意味着 DBI 在 Ruby 代码和底层数据库之间提供了一个抽象层,让你可以非常轻松地切换数据库实现。它定义了一组方法、变量和约定,它们提供了一个一致的数据库接口,与实际使用的数据库无关。

DBI stands for Database Independent Interface for Ruby, which means DBI provides an abstraction layer between the Ruby code and the underlying database, allowing you to switch database implementations really easily. It defines a set of methods, variables, and conventions that provide a consistent database interface, independent of the actual database being used.

DBI 可以与以下内容交互 −

DBI can interface with the following −

  1. ADO (ActiveX Data Objects)

  2. DB2

  3. Frontbase

  4. mSQL

  5. MySQL

  6. ODBC

  7. Oracle

  8. OCI8 (Oracle)

  9. PostgreSQL

  10. Proxy/Server

  11. SQLite

  12. SQLRelay

Architecture of a DBI Application

DBI 独立于后端可用的任何数据库。无论你使用的是 Oracle、MySQL 还是 Informix 等,都可以使用 DBI。这从以下架构图中可以看出。

DBI is independent of any database available in the backend. You can use DBI whether you are working with Oracle, MySQL or Informix, etc. This is clear from the following architecture diagram.

ruby dbi

Ruby DBI 的一般架构使用两层 −

The general architecture for Ruby DBI uses two layers −

  1. The database interface (DBI) layer. This layer is database independent and provides a set of common access methods that are used the same way regardless of the type of database server with which you’re communicating.

  2. The database driver (DBD) layer. This layer is database dependent; different drivers provide access to different database engines. There is one driver for MySQL, another for PostgreSQL, another for InterBase, another for Oracle, and so forth. Each driver interprets requests from the DBI layer and maps them onto requests appropriate for a given type of database server.

Prerequisites

如果你想要编写 Ruby 脚本来访问 MySQL 数据库,则需要安装 Ruby MySQL 模块。

If you want to write Ruby scripts to access MySQL databases, you’ll need to have the Ruby MySQL module installed.

如上所述,该模块充当 DBD,并且可以从 https://www.tmtm.org/en/mysql/ruby/ 下载

This module acts as a DBD as explained above and can be downloaded from https://www.tmtm.org/en/mysql/ruby/

Obtaining and Installing Ruby/DBI

您可以使用 Ruby Gems 软件包管理器安装 Ruby DBI:

You can install ruby DBI using the Ruby Gems packaging manager:

gem install dbi

在开始此安装之前,请确保您拥有 root 权限。现在,按照以下步骤操作:

Before starting this installation make sure you have the root privilege. Now, follow the steps given below −

Step 1

$ tar zxf dbi-0.2.0.tar.gz

Step 2

进入发行版目录 dbi-0.2.0 nd,并使用该目录中的 setup.rb 脚本对其进行配置。最通用的配置命令如下所示,config 参数后不跟任何参数。此命令将发行版配置为默认安装所有驱动程序。

Go in distribution directory dbi-0.2.0 nd configure it using the setup.rb script in that directory. The most general configuration command looks like this, with no arguments following the config argument. This command configures the distribution to install all drivers by default.

$ ruby setup.rb config

要更具体,请提供一个 --with 选项,列出您想要使用的发行版的特定部分。例如,要仅配置主 DBI 模块和 MySQL DBD 级别驱动程序,请发出以下命令:

To be more specific, provide a --with option that lists the particular parts of the distribution you want to use. For example, to configure only the main DBI module and the MySQL DBD-level driver, issue the following command −

$ ruby setup.rb config --with = dbi,dbd_mysql

Step 3

最后一步是使用以下命令构建驱动程序并将其安装:

Final step is to build the driver and install it using the following commands −

$ ruby setup.rb setup
$ ruby setup.rb install

Database Connection

假设我们要使用 MySQL 数据库,在连接到数据库之前确保以下内容:

Assuming we are going to work with MySQL database, before connecting to a database make sure of the following −

  1. You have created a database TESTDB.

  2. You have created EMPLOYEE in TESTDB.

  3. This table is having fields FIRST_NAME, LAST_NAME, AGE, SEX, and INCOME.

  4. User ID "testuser" and password "test123" are set to access TESTDB.

  5. Ruby Module DBI is installed properly on your machine.

  6. You have gone through MySQL tutorial to understand MySQL Basics.

以下是连接到 MySQL 数据库“TESTDB”的示例

Following is the example of connecting with MySQL database "TESTDB"

#!/usr/bin/ruby -w

require "dbi"

begin
   # connect to the MySQL server
   dbh = DBI.connect("DBI:Mysql:TESTDB:localhost", "testuser", "test123")
   # get server version string and display it
   row = dbh.select_one("SELECT VERSION()")
   puts "Server version: " + row[0]
rescue DBI::DatabaseError => e
   puts "An error occurred"
   puts "Error code:    #{e.err}"
   puts "Error message: #{e.errstr}"
ensure
   # disconnect from server
   dbh.disconnect if dbh
end

在运行此脚本时,它会在我们的 Linux 计算机上产生以下结果。

While running this script, it produces the following result at our Linux machine.

Server version: 5.0.45

如果已与数据源建立连接,则返回数据库句柄并将其保存在 dbh 中以供进一步使用,否则 dbh 将设置为 nil 值,而 e.err 和 e::errstr 将分别返回错误代码和错误字符串。

If a connection is established with the data source, then a Database Handle is returned and saved into dbh for further use otherwise dbh is set to nil value and e.err and e::errstr return error code and an error string respectively.

最后,在退出之前,请确保已关闭数据库连接且已释放资源。

Finally, before coming out it, ensure that database connection is closed and resources are released.

INSERT Operation

当您想要将记录创建到数据库表中时,需要 INSERT 操作。

INSERT operation is required when you want to create your records into a database table.

一旦建立了数据库连接,我们就可以使用 do 方法或 prepareexecute 方法在数据库表中创建表或记录。

Once a database connection is established, we are ready to create tables or records into the database tables using do method or prepare and execute method.

Using do Statement

可以通过调用 do 数据库句柄方法发出不返回行的语句。此方法采用语句字符串参数,并返回受语句影响的行数。

Statements that do not return rows can be issued by invoking the do database handle method. This method takes a statement string argument and returns a count of the number of rows affected by the statement.

dbh.do("DROP TABLE IF EXISTS EMPLOYEE")
dbh.do("CREATE TABLE EMPLOYEE (
   FIRST_NAME  CHAR(20) NOT NULL,
   LAST_NAME  CHAR(20),
   AGE INT,
   SEX CHAR(1),
   INCOME FLOAT )" );

同样,您可以执行 SQL INSERT 语句,以在 EMPLOYEE 表中创建记录。

Similarly, you can execute the SQL INSERT statement to create a record into the EMPLOYEE table.

#!/usr/bin/ruby -w

require "dbi"

begin
   # connect to the MySQL server
   dbh = DBI.connect("DBI:Mysql:TESTDB:localhost", "testuser", "test123")
   dbh.do( "INSERT INTO EMPLOYEE(FIRST_NAME, LAST_NAME, AGE, SEX, INCOME)
      VALUES ('Mac', 'Mohan', 20, 'M', 2000)" )
   puts "Record has been created"
   dbh.commit
rescue DBI::DatabaseError => e
   puts "An error occurred"
   puts "Error code:    #{e.err}"
   puts "Error message: #{e.errstr}"
   dbh.rollback
ensure
   # disconnect from server
   dbh.disconnect if dbh
end

Using prepare and execute

您可以使用 DBI 类的 prepare 和 execute 方法通过 Ruby 代码执行 SQL 语句。

You can use prepare and execute methods of DBI class to execute the SQL statement through Ruby code.

记录创建采用以下步骤:

Record creation takes the following steps −

  1. Preparing SQL statement with INSERT statement. This will be done using the prepare method.

  2. Executing SQL query to select all the results from the database. This will be done using the execute method.

  3. Releasing Statement handle. This will be done using finish API

  4. If everything goes fine, then commit this operation otherwise you can rollback the complete transaction.

下面是使用这两种方法的语法 −

Following is the syntax to use these two methods −

sth = dbh.prepare(statement)
sth.execute
   ... zero or more SQL operations ...
sth.finish

这两种方法可用于将 bind 值传递给 SQL 语句。可能出现以下情况:要输入的值未提前给出。在这种情况下,将使用绑定值。问号 ( ? ) 用于替换实际值,然后通过 execute() API 传递实际值。

These two methods can be used to pass bind values to SQL statements. There may be a case when values to be entered is not given in advance. In such a case, binding values are used. A question mark (?) is used in place of actual values and then actual values are passed through execute() API.

以下是 EMPLOYEE 表中创建两条记录的示例 −

Following is the example to create two records in the EMPLOYEE table −

#!/usr/bin/ruby -w

require "dbi"

begin
   # connect to the MySQL server
   dbh = DBI.connect("DBI:Mysql:TESTDB:localhost", "testuser", "test123")
   sth = dbh.prepare( "INSERT INTO EMPLOYEE(FIRST_NAME, LAST_NAME, AGE, SEX, INCOME)
      VALUES (?, ?, ?, ?, ?)" )
   sth.execute('John', 'Poul', 25, 'M', 2300)
   sth.execute('Zara', 'Ali', 17, 'F', 1000)
   sth.finish
   dbh.commit
   puts "Record has been created"
rescue DBI::DatabaseError => e
   puts "An error occurred"
   puts "Error code:    #{e.err}"
   puts "Error message: #{e.errstr}"
   dbh.rollback
ensure
   # disconnect from server
   dbh.disconnect if dbh
end

如果一次有多个 INSERT,那么先准备一条语句,然后在循环中多次执行它比每次通过循环来调用 do 更有效率。

If there are multiple INSERTs at a time, then preparing a statement first and then executing it multiple times within a loop is more efficient than invoking do each time through the loop.

READ Operation

在任何数据库上进行 READ 操作意味着从数据库中获取一些有用的信息。

READ Operation on any database means to fetch some useful information from the database.

一旦建立我们的数据库连接,我们就可以对该数据库进行查询。我们可以使用 do 方法或 prepareexecute 方法从数据库表中获取值。

Once our database connection is established, we are ready to make a query into this database. We can use either do method or prepare and execute methods to fetch values from a database table.

记录获取需要以下步骤 −

Record fetching takes following steps −

  1. Preparing SQL query based on required conditions. This will be done using the prepare method.

  2. Executing SQL query to select all the results from the database. This will be done using the execute method.

  3. Fetching all the results one by one and printing those results. This will be done using the fetch method.

  4. Releasing Statement handle. This will be done using the finish method.

以下是在 EMPLOYEE 表中查询薪水超过 1000 的所有记录的过程。

Following is the procedure to query all the records from EMPLOYEE table having salary more than 1000.

#!/usr/bin/ruby -w

require "dbi"

begin
   # connect to the MySQL server
   dbh = DBI.connect("DBI:Mysql:TESTDB:localhost", "testuser", "test123")
   sth = dbh.prepare("SELECT * FROM EMPLOYEE WHERE INCOME > ?")
   sth.execute(1000)

   sth.fetch do |row|
   printf "First Name: %s, Last Name : %s\n", row[0], row[1]
   printf "Age: %d, Sex : %s\n", row[2], row[3]
   printf "Salary :%d \n\n", row[4]
end
   sth.finish
rescue DBI::DatabaseError => e
   puts "An error occurred"
   puts "Error code:    #{e.err}"
   puts "Error message: #{e.errstr}"
ensure
   # disconnect from server
   dbh.disconnect if dbh
end

这会产生以下结果 −

This will produce the following result −

First Name: Mac, Last Name : Mohan
Age: 20, Sex : M
Salary :2000

First Name: John, Last Name : Poul
Age: 25, Sex : M
Salary :2300

还有更简单的快捷方式来从数据库中获取记录。如果感兴趣,请浏览 Fetching the Result ,否则继续下一部分。

There are more short cut methods to fetch records from the database. If you are interested then go through the Fetching the Result otherwise proceed to the next section.

Update Operation

对任何数据库上的 UPDATE 操作意味着更新数据库中已有的一个或多个记录。以下是在 SEX 为 'M' 的情况下更新所有记录的过程。在这里,我们将所有男性的 AGE 增加一年。这需要三个步骤 −

UPDATE Operation on any database means to update one or more records, which are already available in the database. Following is the procedure to update all the records having SEX as 'M'. Here, we will increase AGE of all the males by one year. This will take three steps −

  1. Preparing SQL query based on required conditions. This will be done using the prepare method.

  2. Executing SQL query to select all the results from the database. This will be done using the execute method.

  3. Releasing Statement handle. This will be done using the finish method.

  4. If everything goes fine then commit this operation otherwise you can rollback the complete transaction.

#!/usr/bin/ruby -w

require "dbi"

begin
   # connect to the MySQL server
   dbh = DBI.connect("DBI:Mysql:TESTDB:localhost", "testuser", "test123")
   sth = dbh.prepare("UPDATE EMPLOYEE SET AGE = AGE + 1 WHERE SEX = ?")
   sth.execute('M')
   sth.finish
   dbh.commit
rescue DBI::DatabaseError => e
   puts "An error occurred"
   puts "Error code:    #{e.err}"
   puts "Error message: #{e.errstr}"
   dbh.rollback
ensure
   # disconnect from server
   dbh.disconnect if dbh
end

DELETE Operation

当您想从数据库中删除某些记录时,需要 DELETE 操作。以下是删除 EMPLOYEE 表中 AGE 大于 20 的所有记录的过程。此操作将执行以下步骤。

DELETE operation is required when you want to delete some records from your database. Following is the procedure to delete all the records from EMPLOYEE where AGE is more than 20. This operation will take following steps.

  1. Preparing SQL query based on required conditions. This will be done using the prepare method.

  2. Executing SQL query to delete required records from the database. This will be done using the execute method.

  3. Releasing Statement handle. This will be done using the finish method.

  4. If everything goes fine then commit this operation otherwise you can rollback the complete transaction.

#!/usr/bin/ruby -w

require "dbi"

begin
   # connect to the MySQL server
   dbh = DBI.connect("DBI:Mysql:TESTDB:localhost", "testuser", "test123")
   sth = dbh.prepare("DELETE FROM EMPLOYEE WHERE AGE > ?")
   sth.execute(20)
   sth.finish
   dbh.commit
rescue DBI::DatabaseError => e
   puts "An error occurred"
   puts "Error code:    #{e.err}"
   puts "Error message: #{e.errstr}"
   dbh.rollback
ensure
   # disconnect from server
   dbh.disconnect if dbh
end

Performing Transactions

事务是一种确保数据一致性的机制。事务应当有以下四种属性 −

Transactions are a mechanism that ensures data consistency. Transactions should have the following four properties −

  1. Atomicity − Either a transaction completes or nothing happens at all.

  2. Consistency − A transaction must start in a consistent state and leave the system is a consistent state.

  3. Isolation − Intermediate results of a transaction are not visible outside the current transaction.

  4. Durability − Once a transaction was committed, the effects are persistent, even after a system failure.

DBI 提供两种方法来提交或回滚交易。还有一个名为事务的方法,可用于实施事务。有两种简单方法来实施事务 -

The DBI provides two methods to either commit or rollback a transaction. There is one more method called transaction which can be used to implement transactions. There are two simple approaches to implement transactions −

Approach I

第一种方法使用 DBI 的 commit 和 rollback 方法来明确提交或取消事务 -

The first approach uses DBI’s commit and rollback methods to explicitly commit or cancel the transaction −

dbh['AutoCommit'] = false # Set auto commit to false.
begin
   dbh.do("UPDATE EMPLOYEE SET AGE = AGE+1 WHERE FIRST_NAME = 'John'")
   dbh.do("UPDATE EMPLOYEE SET AGE = AGE+1 WHERE FIRST_NAME = 'Zara'")
   dbh.commit
rescue
   puts "transaction failed"
   dbh.rollback
end
dbh['AutoCommit'] = true

Approach II

第二种方法使用事务方法。这更简单,因为它采用包含构成事务的语句的代码块。事务方法执行代码块,然后根据代码块是否成功或失败,自动调用 commit 或 rollback -

The second approach uses the transaction method. This is simpler, because it takes a code block containing the statements that make up the transaction. The transaction method executes the block, then invokes commit or rollback automatically, depending on whether the block succeeds or fails −

dbh['AutoCommit'] = false # Set auto commit to false.
dbh.transaction do |dbh|
   dbh.do("UPDATE EMPLOYEE SET AGE = AGE+1 WHERE FIRST_NAME = 'John'")
   dbh.do("UPDATE EMPLOYEE SET AGE = AGE+1 WHERE FIRST_NAME = 'Zara'")
end
dbh['AutoCommit'] = true

COMMIT Operation

提交是一项操作,它向数据库发出一个绿色信号,以最终确定所做的更改,并且在此操作之后,任何更改都将无法还原。

Commit is the operation, which gives a green signal to database to finalize the changes, and after this operation, no change can be reverted back.

下面是一个调用 commit 方法的简单示例。

Here is a simple example to call the commit method.

dbh.commit

ROLLBACK Operation

如果你对一项或多项更改不满意,并且希望完全撤销这些更改,那么请使用 rollback 方法。

If you are not satisfied with one or more of the changes and you want to revert back those changes completely, then use the rollback method.

下面是一个调用 rollback 方法的简单示例。

Here is a simple example to call the rollback method.

dbh.rollback

Disconnecting Database

要断开数据库连接,请使用断开连接 API。

To disconnect Database connection, use disconnect API.

dbh.disconnect

如果用户使用断开连接方法关闭与数据库的连接,DBI 会回滚所有未完成的事务。但是,与其依赖于 DBI 的任何实施细节,你的应用程序最好明确调用 commit 或 rollback。

If the connection to a database is closed by the user with the disconnect method, any outstanding transactions are rolled back by the DBI. However, instead of depending on any of DBI’s implementation details, your application would be better off calling the commit or rollback explicitly.

Handling Errors

有许多错误来源。一些示例是已执行 SQL 语句中的语法错误、连接故障或对已取消或完成的语句句柄调用获取方法。

There are many sources of errors. A few examples are a syntax error in an executed SQL statement, a connection failure, or calling the fetch method for an already canceled or finished statement handle.

如果 DBI 方法失败,DBI 会引发异常。DBI 方法可能会引发任何几种类型的异常,但最重要的两个异常类是 DBI::InterfaceError 和 DBI::DatabaseError。

If a DBI method fails, DBI raises an exception. DBI methods may raise any of several types of exception but the two most important exception classes are DBI::InterfaceError and DBI::DatabaseError.

这些类的异常对象有三个属性,分别名为 err、errstr 和 state,表示错误号、描述性错误字符串和标准错误代码。这些属性的解释如下 -

Exception objects of these classes have three attributes named err, errstr, and state, which represent the error number, a descriptive error string, and a standard error code. The attributes are explained below −

  1. err − Returns an integer representation of the occurred error or nil if this is not supported by the DBD.The Oracle DBD for example returns the numerical part of an ORA-XXXX error message.

  2. errstr − Returns a string representation of the occurred error.

  3. state − Returns the SQLSTATE code of the occurred error.The SQLSTATE is a five-character-long string. Most DBDs do not support this and return nil instead.

你一定在大多数示例中看到了上面的代码 -

You have seen following code above in most of the examples −

rescue DBI::DatabaseError => e
   puts "An error occurred"
   puts "Error code:    #{e.err}"
   puts "Error message: #{e.errstr}"
   dbh.rollback
ensure
   # disconnect from server
   dbh.disconnect if dbh
end

要获取有关你的脚本在执行时正在进行的操作的调试信息,你可以启用跟踪。要做到这一点,你必须首先加载 dbi/trace 模块,然后调用控制跟踪模式和输出目的地的 trace 方法 -

To get debugging information about what your script is doing as it executes, you can enable tracing. To do this, you must first load the dbi/trace module and then call the trace method that controls the trace mode and output destination −

require "dbi/trace"
..............

trace(mode, destination)

模式值可以是 0(关闭)、1、2 或 3,目标应该是 IO 对象。默认值分别是 2 和 STDERR。

The mode value may be 0 (off), 1, 2, or 3, and the destination should be an IO object. The default values are 2 and STDERR, respectively.

Code Blocks with Methods

有一些方法可以创建句柄。这些方法可以通过代码块来调用。使用代码块和方法一起的优点是,它们将句柄作为其参数传递给代码块,并在代码块终止时自动清除句柄。以下有几个理解这个概念的示例。

There are some methods that create handles. These methods can be invoked with a code block. The advantage of using code block along with methods is that they provide the handle to the code block as its parameter and automatically cleans up the handle when the block terminates. There are few examples to understand the concept.

  1. DBI.connect − This method generates a database handle and it is recommended to call disconnect at the end of the block to disconnect the database.

  2. dbh.prepare − This method generates a statement handle and it is recommended to finish at the end of the block. Within the block, you must invoke execute method to execute the statement.

  3. dbh.execute − This method is similar except we don’t need to invoke execute within the block. The statement handle is automatically executed.

Example 1

DBI.connect 可以获取代码块,向数据库句柄传递代码块,然后在代码块的末尾自动断开与句柄的连接,如下所示。

DBI.connect can take a code block, passes the database handle to it, and automatically disconnects the handle at the end of the block as follows.

dbh = DBI.connect("DBI:Mysql:TESTDB:localhost", "testuser", "test123") do |dbh|

Example 2

dbh.prepare 可以获取代码块,向语句句柄传递代码块,然后在代码块的末尾自动调用完成方法,如下所示。

dbh.prepare can take a code block, passes the statement handle to it, and automatically calls finish at the end of the block as follows.

dbh.prepare("SHOW DATABASES") do |sth|
   sth.execute
   puts "Databases: " + sth.fetch_all.join(", ")
end

Example 3

dbh.execute 可以获取代码块,向语句句柄传递代码块,然后在代码块的末尾自动调用完成方法,如下所示 −

dbh.execute can take a code block, passes the statement handle to it, and automatically calls finish at the end of the block as follows −

dbh.execute("SHOW DATABASES") do |sth|
   puts "Databases: " + sth.fetch_all.join(", ")
end

DBI 事务方法还会获取上面描述的代码块。

DBI transaction method also takes a code block which has been described in above.

Driver-specific Functions and Attributes

使用 DBI,数据库驱动程序可以提供其他特定于数据库的功能,用户可以通过任何句柄对象的 func 方法调用这些函数。

The DBI lets the database drivers provide additional database-specific functions, which can be called by the user through the func method of any Handle object.

支持特定于驱动程序的属性,可以使用 []=[] 方法设置或获取这些属性。

Driver-specific attributes are supported and can be set or gotten using the []= or [] methods.

Example

#!/usr/bin/ruby

require "dbi"
begin
   # connect to the MySQL server
   dbh = DBI.connect("DBI:Mysql:TESTDB:localhost", "testuser", "test123")
   puts dbh.func(:client_info)
   puts dbh.func(:client_version)
   puts dbh.func(:host_info)
   puts dbh.func(:proto_info)
   puts dbh.func(:server_info)
   puts dbh.func(:thread_id)
   puts dbh.func(:stat)
rescue DBI::DatabaseError => e
   puts "An error occurred"
   puts "Error code:    #{e.err}"
   puts "Error message: #{e.errstr}"
ensure
   dbh.disconnect if dbh
end

这会产生以下结果 −

This will produce the following result −

5.0.45
50045
Localhost via UNIX socket
10
5.0.45
150621
Uptime: 384981  Threads: 1  Questions: 1101078  Slow queries: 4 \
Opens: 324  Flush tables: 1  Open tables: 64  \
Queries per second avg: 2.860

Ruby Web Applications - CGI Programming

Ruby 是一种通用语言;它根本不能准确地称为 Web 语言。尽管如此,Web 应用程序和通用 Web 工具仍然是 Ruby 最常见的用途之一。

Ruby is a general-purpose language; it can’t properly be called a web language at all. Even so, web applications and web tools in general are among the most common uses of Ruby.

您不仅可以使用 Ruby 编写自己的 SMTP 服务器、FTP 守护程序或 Web 服务器,还可以将 Ruby 用于更常用的任务,例如 CGI 编程或作为 PHP 的替代方案。

Not only can you write your own SMTP server, FTP daemon, or Web server in Ruby, but you can also use Ruby for more usual tasks such as CGI programming or as a replacement for PHP.

请花几分钟时间参阅 CGI Programming 教程,以更详细地了解 CGI 编程。

Please spend few minutes with CGI Programming Tutorial for more detail on CGI Programming.

Writing CGI Scripts

最基本的 Ruby CGI 脚本如下所示 −

The most basic Ruby CGI script looks like this −

#!/usr/bin/ruby

puts "HTTP/1.0 200 OK"
puts "Content-type: text/html\n\n"
puts "<html><body>This is a test</body></html>"

如果您调用此脚本 test.cgi 并以正确的权限将其上传到基于 Unix 的 Web 托管服务提供商,您可以将其用作 CGI 脚本。

If you call this script test.cgi and uploaded it to a Unix-based Web hosting provider with the right permissions, you could use it as a CGI script.

例如,如果您有一个托管在 Linux Web 托管服务提供商上的名为 https://www.example.com/ 的网站,并且您已将 test.cgi 上传到主目录并授予它执行权限,那么访问 https://www.example.com/test.cgi 时应该会返回一个显示 This is a test 的 HTML 网页。

For example, if you have the Web site https://www.example.com/ hosted with a Linux Web hosting provider and you upload test.cgi to the main directory and give it execute permissions, then visiting https://www.example.com/test.cgi should return an HTML page saying This is a test.

在此,当 Web 浏览器请求 test.cgi 时,Web 服务器会在网站上查找 test.cgi,然后使用 Ruby 解释器执行它。此 Ruby 脚本会返回一个基本的 HTTP 头,然后返回一个基本的 HTML 文档。

Here when test.cgi is requested from a Web browser, the Web server looks for test.cgi on the Web site, and then executes it using the Ruby interpreter. The Ruby script returns a basic HTTP header and then returns a basic HTML document.

Using cgi.rb

Ruby 自带一个名为 cgi 的特殊库,它支持比之前的 CGI 脚本更复杂的操作。

Ruby comes with a special library called cgi that enables more sophisticated interactions than those with the preceding CGI script.

让我们创建一个使用 cgi 的基本 CGI 脚本 −

Let’s create a basic CGI script that uses cgi −

#!/usr/bin/ruby

require 'cgi'
cgi = CGI.new

puts cgi.header
puts "<html><body>This is a test</body></html>"

此处,您创建了一个 CGI 对象并使用它为您打印头行。

Here, you created a CGI object and used it to print the header line for you.

Form Processing

使用类 CGI 使您可以通过两种方式访问 HTML 查询参数。假设我们提供了一个 URL /cgi-bin/test.cgi?FirstName = Zara&LastName = Ali。

Using class CGI gives you access to HTML query parameters in two ways. Suppose we are given a URL of /cgi-bin/test.cgi?FirstName = Zara&LastName = Ali.

按照以下方法直接使用 CGI#[] 访问 FirstName 和 LastName 参数:

You can access the parameters FirstName and LastName using CGI#[] directly as follows −

#!/usr/bin/ruby

require 'cgi'
cgi = CGI.new
cgi['FirstName'] # =>  ["Zara"]
cgi['LastName']  # =>  ["Ali"]

访问这些表单变量还有另一种方法。以下代码会给你一个所有键值对的哈希值:

There is another way to access these form variables. This code will give you a hash of all the key and values −

#!/usr/bin/ruby

require 'cgi'
cgi = CGI.new
h = cgi.params  # =>  {"FirstName"=>["Zara"],"LastName"=>["Ali"]}
h['FirstName']  # =>  ["Zara"]
h['LastName']   # =>  ["Ali"]

以下代码用于检索所有键:

Following is the code to retrieve all the keys −

#!/usr/bin/ruby

require 'cgi'
cgi = CGI.new
cgi.keys         # =>  ["FirstName", "LastName"]

如果表单包含具有相同名称的多个字段,则相应的键值对将作为数组返回给脚本。[] 访问器仅返回这些值中的第一个。对 params 方法的结果进行索引用以获取所有键值对。

If a form contains multiple fields with the same name, the corresponding values will be returned to the script as an array. The [] accessor returns just the first of these.index the result of the params method to get them all.

在此示例中,假设表单有三个名为“姓名”的字段,我们输入了三个姓名“Zara”、“Huma”和“Nuha”:

In this example, assume the form has three fields called "name" and we entered three names "Zara", "Huma" and "Nuha" −

#!/usr/bin/ruby

require 'cgi'
cgi = CGI.new
cgi['name']        # => "Zara"
cgi.params['name'] # => ["Zara", "Huma", "Nuha"]
cgi.keys           # => ["name"]
cgi.params         # => {"name"=>["Zara", "Huma", "Nuha"]}

Note - Ruby 将自动处理 GET 和 POST 方法。这两个不同的方法没有单独的处理。

Note − Ruby will take care of GET and POST methods automatically. There is no separate treatment for these two different methods.

可以发送正确数据的相关但基本的表单具有如下 HTML 代码:

An associated, but basic, form that could send the correct data would have the HTML code like so −

<html>
   <body>
      <form method = "POST" action = "http://www.example.com/test.cgi">
         First Name :<input type = "text" name = "FirstName" value = "" />
         <br />
         Last Name :<input type = "text" name = "LastName" value = "" />
         <input type = "submit" value = "Submit Data" />
      </form>
   </body>
</html>

Creating Forms and HTML

CGI 包含大量用于创建 HTML 的方法。每个标签都有一个方法。为了启用这些方法,你必须通过调用 CGI.new 创建一个 CGI 对象。

CGI contains a huge number of methods used to create HTML. You will find one method per tag. In order to enable these methods, you must create a CGI object by calling CGI.new.

为了简化标签嵌套,这些方法将内容用作代码块。代码块应该返回一个字符串,该字符串将用作标签的内容。例如:

To make tag nesting easier, these methods take their content as code blocks. The code blocks should return a String, which will be used as the content for the tag. For example −

#!/usr/bin/ruby

require "cgi"
cgi = CGI.new("html4")
cgi.out {
   cgi.html {
      cgi.head { "\n"+cgi.title{"This Is a Test"} } +
      cgi.body { "\n"+
         cgi.form {"\n"+
            cgi.hr +
            cgi.h1 { "A Form: " } + "\n"+
            cgi.textarea("get_text") +"\n"+
            cgi.br +
            cgi.submit
         }
      }
   }
}

NOTE - CGI 类的 form 方法可以接受一个方法参数,该参数将设置提交表单时要使用的 HTTP 方法 (GET、POST 等)。本示例中使用的默认值为 POST。

NOTE − The form method of the CGI class can accept a method parameter, which will set the HTTP method ( GET, POST, and so on…​) to be used on form submittal. The default, used in this example, is POST.

这会产生以下结果 −

This will produce the following result −

Content-Type: text/html
Content-Length: 302

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Final//EN">

<HTML>
   <HEAD>
      <TITLE>This Is a Test</TITLE>
   </HEAD>
   <BODY>
      <FORM METHOD = "post" ENCTYPE = "application/x-www-form-urlencoded">
         <HR>
         <H1>A Form: </H1>
         <TEXTAREA COLS = "70" NAME = "get_text" ROWS = "10"></TEXTAREA>
         <BR>
         <INPUT TYPE = "submit">
      </FORM>
   </BODY>
</HTML>

Quoting Strings

在处理 URL 和 HTML 代码时,你必须小心引用某些字符。例如,斜杠字符 (/) 在 URL 中具有特殊含义,因此当它不是路径名的一部分时,必须 escaped

When dealing with URLs and HTML code, you must be careful to quote certain characters. For instance, a slash character ( / ) has special meaning in a URL, so it must be escaped if it’s not part of the pathname.

例如,URL 查询部分中的任何 / 都将转换为字符串 %2F,并且你必须将其转换回 / 才能使用它。空格和与号也是特殊字符。为了解决此问题,CGI 提供了例程 CGI.escapeCGI.unescape

For example, any / in the query portion of the URL will be translated to the string %2F and must be translated back to a / for you to use it. Space and ampersand are also special characters. To handle this, CGI provides the routines CGI.escape and CGI.unescape.

#!/usr/bin/ruby

require 'cgi'
puts CGI.escape(Zara Ali/A Sweet & Sour Girl")

这会产生以下结果 −

This will produce the following result −

Zara+Ali%2FA Sweet+%26+Sour+Girl")
#!/usr/bin/ruby

require 'cgi'
puts CGI.escapeHTML('<h1>Zara Ali/A Sweet & Sour Girl</h1>')

这会产生以下结果 −

This will produce the following result −

&lt;h1&gt;Zara Ali/A Sweet & Sour Girl&lt;/h1&gt;'

Useful Methods in CGI Class

以下列出了 CGI 类相关方法:

Here is the list of methods related to CGI class −

  1. The Ruby CGI − Methods related to Standard CGI library.

Cookies and Sessions

我们在不同的部分中解释了这两个概念。请遵循以下部分:

We have explained these two concepts in different sections. Please follow the sections −

  1. The Ruby CGI Cookies − How to handle CGI Cookies.

  2. The Ruby CGI Sessions − How to manage CGI sessions.

Web Hosting Servers

你可以在网上查看以下主题,以在基于 Unix 的服务器上托管你的网站:

You could check the following topic on the internet to host your website on a Unix-based Server −

Sending Email using Ruby - SMTP

简单邮件传输协议 (SMTP) 是一种协议,处理在邮件服务器之间发送和路由电子邮件。

Simple Mail Transfer Protocol (SMTP) is a protocol, which handles sending e-mail and routing e-mail between mail servers.

Ruby 提供 Net::SMTP 类用于简单邮件传输协议 (SMTP) 客户端连接,并提供了两个类方法 new 和 start。

Ruby provides Net::SMTP class for Simple Mail Transfer Protocol (SMTP) client-side connection and provides two class methods new and start.

  1. The new takes two parameters − The server name defaulting to localhost. The port number defaulting to the well-known port 25.

  2. The start method takes these parameters − The server − IP name of the SMTP server, defaulting to localhost. The port − Port number, defaulting to 25. The domain − Domain of the mail sender, defaulting to ENV["HOSTNAME"]. The account − Username, default is nil. The password − User password, defaulting to nil. The authtype − Authorization type, defaulting to cram_md5.

一个 SMTP 对象具有一个实例方法 sendmail,它通常用于发送邮件。它采用三个参数 -

An SMTP object has an instance method called sendmail, which will typically be used to do the work of mailing a message. It takes three parameters −

  1. The source − A string or array or anything with an each iterator returning one string at a time.

  2. The sender − A string that will appear in the from field of the email.

  3. The recipients − A string or an array of strings representing the recipients' addressee(s).

Example

下面是使用 Ruby 脚本发送一封电子邮件的简单方法,试试吧 -

Here is a simple way to send one email using Ruby script. Try it once −

require 'net/smtp'

message = <<MESSAGE_END
From: Private Person <me@fromdomain.com>
To: A Test User <test@todomain.com>
Subject: SMTP e-mail test

This is a test e-mail message.
MESSAGE_END

Net::SMTP.start('localhost') do |smtp|
  smtp.send_message message, 'me@fromdomain.com', 'test@todomain.com'
end

这里,你使用文档将基本电子邮件放入消息中,并注意正确设置标头格式。电子邮件需要一个 FromToSubject 标头,并用空行与电子邮件正文分开。

Here, you have placed a basic e-mail in message, using a document, taking care to format the headers correctly. E-mails require a From, To, and Subject header, separated from the body of the e-mail with a blank line.

要发送邮件,你可以使用 Net::SMTP 连接到本地计算机上的 SMTP 服务器,然后将 send_message 方法与邮件、发件人地址和目标地址一起用作参数(尽管发件人和收件人地址都在电子邮件本身中,但这些地址并不总是用于路由邮件)。

To send the mail you use Net::SMTP to connect to the SMTP server on the local machine and then use the send_message method along with the message, the from address, and the destination address as parameters (even though the from and to addresses are within the e-mail itself, these aren’t always used to route mail).

如果你不打算在计算机上运行 SMTP 服务器,你可以使用 Net::SMTP 与远程 SMTP 服务器进行通信。除非你正在使用 Web 邮件服务(例如 Hotmail 或 Yahoo! Mail),你的电子邮件提供商会向你提供发件服务器详细信息,你可以将其提供给 Net::SMTP,具体如下 -

If you’re not running an SMTP server on your machine, you can use the Net::SMTP to communicate with a remote SMTP server. Unless you’re using a webmail service (such as Hotmail or Yahoo! Mail), your e-mail provider will have provided you with outgoing mail server details that you can supply to Net::SMTP, as follows −

Net::SMTP.start('mail.your-domain.com')

此代码行将连接到 mail.your-domain.com 的端口 25 上的 SMTP 服务器,而不会使用任何用户名或密码。不过,如果你需要,你可以指定端口号和其他详细信息。例如 -

This line of code connects to the SMTP server on port 25 of mail.your-domain.com without using any username or password. If you need to, though, you can specify port number and other details. For example −

Net::SMTP.start('mail.your-domain.com',
                25,
                'localhost',
                'username', 'password' :plain)

此示例使用用户名和明文格式的密码连接到 mail.your-domain.com 上的 SMTP 服务器。它将客户端的主机名标识为 localhost。

This example connects to the SMTP server at mail.your-domain.com using a username and password in plain text format. It identifies the client’s hostname as localhost.

Sending an HTML e-mail using Ruby

如果你使用 Ruby 发送文本消息,那么所有内容都将被视为简单文本。即使你在文本消息中包含 HTML 标签,它也会显示为简单文本,HTML 标签不会根据 HTML 语法进行格式化。但是,Ruby Net::SMTP 提供了将 HTML 消息作为实际 HTML 消息发送的选项。

When you send a text message using Ruby then all the content will be treated as simple text. Even if you will include HTML tags in a text message, it will be displayed as simple text and HTML tags will not be formatted according to HTML syntax. But Ruby Net::SMTP provides option to send an HTML message as actual HTML message.

发送邮件时,你可以指定一个 Mime 版本、内容类型和字符集来发送 HTML 邮件。

While sending an email message you can specify a Mime version, content type and character set to send an HTML email.

Example

以下是将 HTML 内容作为电子邮件发送的示例,试试吧 -

Following is the example to send HTML content as an email. Try it once −

require 'net/smtp'

message = <<MESSAGE_END
From: Private Person <me@fromdomain.com>
To: A Test User <test@todomain.com>
MIME-Version: 1.0
Content-type: text/html
Subject: SMTP e-mail test

This is an e-mail message to be sent in HTML format

<b>This is HTML message.</b>
<h1>This is headline.</h1>
MESSAGE_END

Net::SMTP.start('localhost') do |smtp|
   smtp.send_message message, 'me@fromdomain.com', 'test@todomain.com'
end

Sending Attachments as an e-mail

要发送包含混合内容的电子邮件,需要将 Content-type 标头设置为 multipart/mixed 。然后,可以在 boundaries 中指定文本和附件部分。

To send an email with mixed content requires to set Content-type header to multipart/mixed. Then text and attachment sections can be specified within boundaries.

一个边界的开头是两个连字符后跟一个唯一数字,它不能出现在电子邮件的消息部分。表示电子邮件的最终部分的最终边界也必须以两个连字符结束。

A boundary is started with two hyphens followed by a unique number, which cannot appear in the message part of the email. A final boundary denoting the email’s final section must also end with two hyphens.

附加文件应使用 pack("m") 函数进行编码,以便在传输之前进行 base64 编码。

Attached files should be encoded with the pack("m") function to have base64 encoding before transmission.

Example

以下是发送文件 /tmp/test.txt 作为附件的示例。

Following is the example, which will send a file /tmp/test.txt as an attachment.

require 'net/smtp'

filename = "/tmp/test.txt"
# Read a file and encode it into base64 format
filecontent = File.read(filename)
encodedcontent = [filecontent].pack("m")   # base64

marker = "AUNIQUEMARKER"
body = <<EOF
This is a test email to send an attachement.
EOF

# Define the main headers.
part1 = <<EOF
From: Private Person <me@fromdomain.net>
To: A Test User <test@todmain.com>
Subject: Sending Attachement
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary = #{marker}
--#{marker}
EOF

# Define the message action
part2 = <<EOF
Content-Type: text/plain
Content-Transfer-Encoding:8bit

#{body}
--#{marker}
EOF

# Define the attachment section
part3 = <<EOF
Content-Type: multipart/mixed; name = \"#{filename}\"
Content-Transfer-Encoding:base64
Content-Disposition: attachment; filename = "#{filename}"

#{encodedcontent}
--#{marker}--
EOF

mailtext = part1 + part2 + part3

# Let's put our code in safe area
begin
   Net::SMTP.start('localhost') do |smtp|
      smtp.sendmail(mailtext, 'me@fromdomain.net', ['test@todmain.com'])
   end
rescue Exception => e
   print "Exception occured: " + e
end

NOTE − 您可以在数组内部指定多个目标,但是它们应该以逗号分隔。

NOTE − You can specify multiple destinations inside the array but they should be separated by comma.

Ruby - Socket Programming

Ruby 提供了两个级别的网络服务访问权限。在低级别,您可以访问底层操作系统中的基本套接字支持,这使您可以为面向连接和无连接协议实现客户端和服务器。

Ruby provides two levels of access to network services. At a low level, you can access the basic socket support in the underlying operating system, which allows you to implement clients and servers for both connection-oriented and connectionless protocols.

Ruby 还具有提供对特定应用程序级网络协议(例如 FTP、HTTP 等)的高级访问权限的库。

Ruby also has libraries that provide higher-level access to specific application-level network protocols, such as FTP, HTTP, and so on.

本章将帮助您了解网络中最著名的概念 - 套接字编程。

This chapter gives you an understanding on most famous concept in Networking − Socket Programming.

What are Sockets?

套接字是双向通信通道的端点。套接字可以在进程内进行通信,也可以在同一电脑上的进程之间或不同区域中的进程之间进行通信。

Sockets are the endpoints of a bidirectional communications channel. Sockets may communicate within a process, between processes on the same machine, or between processes on different continents.

套接字可以通过多种不同的信道类型进行实现:Unix 域套接字、TCP、UDP 等。套接字提供用于处理常见传输的特定类,以及用于处理其余部分的通用接口。

Sockets may be implemented over a number of different channel types: Unix domain sockets, TCP, UDP, and so on. The socket provides specific classes for handling the common transports as well as a generic interface for handling the rest.

套接字具有自己的词汇:

Sockets have their own vocabulary −

Sr.No.

Term & Description

1

domain The family of protocols that will be used as the transport mechanism. These values are constants such as PF_INET, PF_UNIX, PF_X25, and so on.

2

type The type of communications between the two endpoints, typically SOCK_STREAM for connection-oriented protocols and SOCK_DGRAM for connectionless protocols.

3

protocol Typically zero, this may be used to identify a variant of a protocol within a domain and type.

4

hostname The identifier of a network interface − A string, which can be a host name, a dotted-quad address, or an IPV6 address in colon (and possibly dot) notation A string "<broadcast>", which specifies an INADDR_BROADCAST address. A zero-length string, which specifies INADDR_ANY, or An Integer, interpreted as a binary address in host byte order.

5

port Each server listens for clients calling on one or more ports. A port may be a Fixnum port number, a string containing a port number, or the name of a service.

A Simple Client

这里我们将编写一个非常简单的客户端程序,它将打开到给定端口和给定主机的连接。Ruby 类 TCPSocket 提供 open 函数来打开这样的套接字。

Here we will write a very simple client program, which will open a connection to a given port and given host. Ruby class TCPSocket provides open function to open such a socket.

TCPSocket.open(hosname, port ) 在端口上向 hostname 打开一个 TCP 连接。

The TCPSocket.open(hosname, port ) opens a TCP connection to hostname on the port.

一旦您打开了一个套接字,就可以像任何 IO 对象一样从中读取。完成后,请记住关闭它,就像您关闭文件一样。

Once you have a socket open, you can read from it like any IO object. When done, remember to close it, as you would close a file.

以下代码是一个非常简单的客户端,它连接到给定的主机和端口,从套接字读取任何可用数据,然后退出 −

The following code is a very simple client that connects to a given host and port, reads any available data from the socket, and then exits −

require 'socket'        # Sockets are in standard library

hostname = 'localhost'
port = 2000

s = TCPSocket.open(hostname, port)

while line = s.gets     # Read lines from the socket
   puts line.chop       # And print with platform line terminator
end
s.close                 # Close the socket when done

A Simple Server

要编写 Internet 服务器,我们使用 TCPServer 类。TCPServer 对象是 TCPSocket 对象的工厂。

To write Internet servers, we use the TCPServer class. A TCPServer object is a factory for TCPSocket objects.

现在,调用 TCPServer.open(hostname, port 函数来指定服务的端口,并创建一个 TCPServer 对象。

Now call TCPServer.open(hostname, port function to specify a port for your service and create a TCPServer object.

接下来,调用返回的 TCPServer 对象的 accept 方法。此方法会一直等到客户端连接至您指定的端口,然后返回一个 TCPSocket 对象,该对象表示与该客户端的连接。

Next, call the accept method of the returned TCPServer object. This method waits until a client connects to the port you specified, and then returns a TCPSocket object that represents the connection to that client.

require 'socket'                 # Get sockets from stdlib

server = TCPServer.open(2000)    # Socket to listen on port 2000
loop {                           # Servers run forever
   client = server.accept        # Wait for a client to connect
   client.puts(Time.now.ctime)   # Send the time to the client
   client.puts "Closing the connection. Bye!"
   client.close                  # Disconnect from the client
}

现在,在后台运行此服务器,然后运行上述客户端查看结果。

Now, run this server in background and then run the above client to see the result.

Multi-Client TCP Servers

互联网上的大多数服务器都设计为一次处理大量客户端。

Most servers on the Internet are designed to deal with large numbers of clients at any one time.

Ruby 的 Thread 类简单创建多线程序。该程序可以接受请求,并立即创建一个新的执行线程来处理该连接,同时允许主程序等待更多连接−

Ruby’s Thread class makes it easy to create a multithreaded server.one that accepts requests and immediately creates a new thread of execution to process the connection while allowing the main program to await more connections −

require 'socket'                 # Get sockets from stdlib

server = TCPServer.open(2000)    # Socket to listen on port 2000
loop {                           # Servers run forever
   Thread.start(server.accept) do |client|
   client.puts(Time.now.ctime)   # Send the time to the client
   client.puts "Closing the connection. Bye!"
   client.close                  # Disconnect from the client
   end
}

在此示例中,您有一个永久循环,当 server.accept 响应时,会创建一个新线程,并立即启动它来处理刚刚接受的连接,方法是用连接对象传递到该线程中。然而,主程序会立即循环返回等待新连接。

In this example, you have a permanent loop, and when server.accept responds, a new thread is created and started immediately to handle the connection that has just been accepted, using the connection object passed into the thread. However, the main program immediately loops back and awaits new connections.

按照此方式使用 Ruby 线程意味着该代码是可移植的,并且可以在 Linux、OS X 和 Windows 上以相同方式运行。

Using Ruby threads in this way means the code is portable and will run in the same way on Linux, OS X, and Windows.

A Tiny Web Browser

我们可以使用 socket 库来实现任何因特网协议。例如,下面是一个获取网页内容的代码−

We can use the socket library to implement any Internet protocol. Here, for example, is a code to fetch the content of a web page −

require 'socket'

host = 'www.tutorialspoint.com'     # The web server
port = 80                           # Default HTTP port
path = "/index.htm"                 # The file we want

# This is the HTTP request we send to fetch a file
request = "GET #{path} HTTP/1.0\r\n\r\n"

socket = TCPSocket.open(host,port)  # Connect to server
socket.print(request)               # Send request
response = socket.read              # Read complete response
# Split response at first blank line into headers and body
headers,body = response.split("\r\n\r\n", 2)
print body                          # And display it

要实现类似的 Web 客户端,您可以使用预先构建的库(例如 Net::HTTP )来使用 HTTP。下方的代码的作用等同于之前的代码−

To implement the similar web client, you can use a pre-built library like Net::HTTP for working with HTTP. Here is the code that does the equivalent of the previous code −

require 'net/http'                  # The library we need
host = 'www.tutorialspoint.com'     # The web server
path = '/index.htm'                 # The file we want

http = Net::HTTP.new(host)          # Create a connection
headers, body = http.get(path)      # Request the file
if headers.code == "200"            # Check the status code
   print body
else
   puts "#{headers.code} #{headers.message}"
end

请查看类似的库以配合使用 FTP、SMTP、POP 和 IMAP 协议。

Please check similar libraries to work with FTP, SMTP, POP, and IMAP protocols.

Further Readings

我们为您提供了有关套接字编程的快速入门指导。这是一个大课题,因此推荐您查看 Ruby Socket Library and Class Methods 以查找更多详细信息。

We have given you a quick start on Socket Programming. It is a big subject, so it is recommended that you go through Ruby Socket Library and Class Methods to find more details.

Ruby - XML, XSLT and XPath Tutorial

What is XML?

可扩展标记语言 (XML) 是一种类似 HTML 或 SGML 的标记语言。它由万维网联盟推荐,并作为一个开放标准提供。

The Extensible Markup Language (XML) is a markup language much like HTML or SGML. This is recommended by the World Wide Web Consortium and available as an open standard.

XML 是一种便携式开源语言,允许程序员开发应用程序,无论操作系统和/或开发语言是什么,其他应用程序都可以读取这些应用程序。

XML is a portable, open source language that allows programmers to develop applications that can be read by other applications, regardless of operating system and/or developmental language.

XML 极其适用于追踪少量至中等数量的数据,而不需要基于 SQL 的后端。

XML is extremely useful for keeping track of small to medium amounts of data without requiring a SQL-based backbone.

XML Parser Architectures and APIs

对于 XML 解析器,有两种不同的类型 −

There are two different flavors available for XML parsers −

  1. SAX-like (Stream interfaces) − Here you register callbacks for events of interest and then let the parser proceed through the document. This is useful when your documents are large or you have memory limitations, it parses the file as it reads it from disk, and the entire file is never stored in memory.

  2. DOM-like (Object tree interfaces) − This is World Wide Web Consortium recommendation wherein the entire file is read into memory and stored in a hierarchical (tree-based) form to represent all the features of an XML document.

在处理大型文件时,SAX 显然无法像 DOM 那样快速处理信息。另一方面,专属使用 DOM 会真正耗尽你的资源,尤其是在大量的小型文件中使用时。

SAX obviously can’t process information as fast as DOM can when working with large files. On the other hand, using DOM exclusively can really kill your resources, especially if used on a lot of small files.

SAX 是只读的,而 DOM 允许对 XML 文件进行更改。由于这两个不同的 API 实际上是互补的,因此你没有理由不能将它们都用于大型项目。

SAX is read-only, while DOM allows changes to the XML file. Since these two different APIs literally complement each other there is no reason why you can’t use them both for large projects.

Parsing and Creating XML using Ruby

处理 XML 最常见的方法是使用 Sean Russell 的 REXML 库。自 2002 年以来,REXML 已成为标准 Ruby 发行版的一部分。

The most common way to manipulate XML is with the REXML library by Sean Russell. Since 2002, REXML has been part of the standard Ruby distribution.

REXML 是一个符合 XML 1.0 标准的纯 Ruby XML 处理器。它是一个无需验证的处理器,通过了所有 OASIS 无需验证的一致性测试。

REXML is a pure-Ruby XML processor conforming to the XML 1.0 standard. It is a non-validating processor, passing all of the OASIS non-validating conformance tests.

REXML 解析器相对于其他可用解析器具有以下优势 −

REXML parser has the following advantages over other available parsers −

  1. It is written 100 percent in Ruby.

  2. It can be used for both SAX and DOM parsing.

  3. It is lightweight, less than 2000 lines of code.

  4. Methods and classes are really easy-to-understand.

  5. SAX2-based API and Full XPath support.

  6. Shipped with Ruby installation and no separate installation is required.

对于所有我们的 XML 代码示例,让我们使用一个简单的 XML 文件作为输入 −

For all our XML code examples, let’s use a simple XML file as an input −

<collection shelf = "New Arrivals">
   <movie title = "Enemy Behind">
      <type>War, Thriller</type>
      <format>DVD</format>
      <year>2003</year>
      <rating>PG</rating>
      <stars>10</stars>
      <description>Talk about a US-Japan war</description>
   </movie>
   <movie title = "Transformers">
      <type>Anime, Science Fiction</type>
      <format>DVD</format>
      <year>1989</year>
      <rating>R</rating>
      <stars>8</stars>
      <description>A schientific fiction</description>
   </movie>
   <movie title = "Trigun">
      <type>Anime, Action</type>
      <format>DVD</format>
      <episodes>4</episodes>
      <rating>PG</rating>
      <stars>10</stars>
      <description>Vash the Stampede!</description>
   </movie>
   <movie title = "Ishtar">
      <type>Comedy</type>
      <format>VHS</format>
      <rating>PG</rating>
      <stars>2</stars>
      <description>Viewable boredom</description>
   </movie>
</collection>

DOM-like Parsing

让我们首先以树状的方式解析我们的 XML 数据。我们首先需要 rexml/document 库;通常,我们引入 REXML 以方便地导入到顶级命名空间。

Let’s first parse our XML data in tree fashion. We begin by requiring the rexml/document library; often we do an include REXML to import into the top-level namespace for convenience.

#!/usr/bin/ruby -w

require 'rexml/document'
include REXML

xmlfile = File.new("movies.xml")
xmldoc = Document.new(xmlfile)

# Now get the root element
root = xmldoc.root
puts "Root element : " + root.attributes["shelf"]

# This will output all the movie titles.
xmldoc.elements.each("collection/movie"){
   |e| puts "Movie Title : " + e.attributes["title"]
}

# This will output all the movie types.
xmldoc.elements.each("collection/movie/type") {
   |e| puts "Movie Type : " + e.text
}

# This will output all the movie description.
xmldoc.elements.each("collection/movie/description") {
   |e| puts "Movie Description : " + e.text
}

这会产生以下结果 −

This will produce the following result −

Root element : New Arrivals
Movie Title : Enemy Behind
Movie Title : Transformers
Movie Title : Trigun
Movie Title : Ishtar
Movie Type : War, Thriller
Movie Type : Anime, Science Fiction
Movie Type : Anime, Action
Movie Type : Comedy
Movie Description : Talk about a US-Japan war
Movie Description : A schientific fiction
Movie Description : Vash the Stampede!
Movie Description : Viewable boredom

SAX-like Parsing

要以面向流的方式处理相同的数据,movies.xml,文件,我们将定义一个侦听器类,其方法将成为解析器回调的目标。

To process the same data, movies.xml, file in a stream-oriented way we will define a listener class whose methods will be the target of callbacks from the parser.

NOTE −不建议对小文件使用类似 SAX 的解析,这仅仅是一个演示示例。

NOTE − It is not suggested to use SAX-like parsing for a small file, this is just for a demo example.

#!/usr/bin/ruby -w

require 'rexml/document'
require 'rexml/streamlistener'
include REXML

class MyListener
   include REXML::StreamListener
   def tag_start(*args)
      puts "tag_start: #{args.map {|x| x.inspect}.join(', ')}"
   end

   def text(data)
      return if data =~ /^\w*$/     # whitespace only
      abbrev = data[0..40] + (data.length > 40 ? "..." : "")
      puts "  text   :   #{abbrev.inspect}"
   end
end

list = MyListener.new
xmlfile = File.new("movies.xml")
Document.parse_stream(xmlfile, list)

这会产生以下结果 −

This will produce the following result −

tag_start: "collection", {"shelf"=>"New Arrivals"}
tag_start: "movie", {"title"=>"Enemy Behind"}
tag_start: "type", {}
   text   :   "War, Thriller"
tag_start: "format", {}
tag_start: "year", {}
tag_start: "rating", {}
tag_start: "stars", {}
tag_start: "description", {}
   text   :   "Talk about a US-Japan war"
tag_start: "movie", {"title"=>"Transformers"}
tag_start: "type", {}
   text   :   "Anime, Science Fiction"
tag_start: "format", {}
tag_start: "year", {}
tag_start: "rating", {}
tag_start: "stars", {}
tag_start: "description", {}
   text   :   "A schientific fiction"
tag_start: "movie", {"title"=>"Trigun"}
tag_start: "type", {}
   text   :   "Anime, Action"
tag_start: "format", {}
tag_start: "episodes", {}
tag_start: "rating", {}
tag_start: "stars", {}
tag_start: "description", {}
   text   :   "Vash the Stampede!"
tag_start: "movie", {"title"=>"Ishtar"}
tag_start: "type", {}
tag_start: "format", {}
tag_start: "rating", {}
tag_start: "stars", {}
tag_start: "description", {}
   text   :   "Viewable boredom"

XPath and Ruby

查看 XML 的另一种方式是 XPath。这是一种描述如何在 XML 文档中找到特定元素和属性的伪语言,将该文档视为逻辑有序树。

An alternative way to view XML is XPath. This is a kind of pseudo-language that describes how to locate specific elements and attributes in an XML document, treating that document as a logical ordered tree.

REXML 通过 XPath 类支持 XPath。它假定基于树的解析(文档对象模型),如上所述。

REXML has XPath support via the XPath class. It assumes tree-based parsing (document object model) as we have seen above.

#!/usr/bin/ruby -w

require 'rexml/document'
include REXML

xmlfile = File.new("movies.xml")
xmldoc = Document.new(xmlfile)

# Info for the first movie found
movie = XPath.first(xmldoc, "//movie")
p movie

# Print out all the movie types
XPath.each(xmldoc, "//type") { |e| puts e.text }

# Get an array of all of the movie formats.
names = XPath.match(xmldoc, "//format").map {|x| x.text }
p names

这会产生以下结果 −

This will produce the following result −

<movie title = 'Enemy Behind'> ... </>
War, Thriller
Anime, Science Fiction
Anime, Action
Comedy
["DVD", "DVD", "DVD", "VHS"]

XSLT and Ruby

Ruby 可使用两种 XSLT 解析器。这里对每种解析器做了简要说明。

There are two XSLT parsers available that Ruby can use. A brief description of each is given here.

Ruby-Sablotron

该解析器由 Masayoshi Takahashi 编写和维护。这主要针对 Linux 操作系统编写,需要以下库 −

This parser is written and maintained by Masayoshi Takahashi. This is written primarily for Linux OS and requires the following libraries −

  1. Sablot

  2. Iconv

  3. Expat

你可以在 Ruby-Sablotron 找到此模块。

You can find this module at Ruby-Sablotron.

XSLT4R

XSLT4R 由 Michael Neumann 编写,可在 XML 下的库部分中的 RAA 中找到。XSLT4R 使用一个简单的命令行界面,不过也可以在第三方应用程序中使用它来转换 XML 文档。

XSLT4R is written by Michael Neumann and can be found at the RAA in the Library section under XML. XSLT4R uses a simple commandline interface, though it can alternatively be used within a third-party application to transform an XML document.

XSLT4R 需要 XMLScan 才能运行,该扫描器包含在 XSLT4R 归档文件中,并且也是一个 100% Ruby 模块。可以使用标准 Ruby 安装方法安装这些模块(即,ruby install.rb)。

XSLT4R needs XMLScan to operate, which is included within the XSLT4R archive and which is also a 100 percent Ruby module. These modules can be installed using standard Ruby installation method (i.e., ruby install.rb).

XSLT4R 具有以下语法 −

XSLT4R has the following syntax −

ruby xslt.rb stylesheet.xsl document.xml [arguments]

如果你想在应用程序中使用 XSLT4R,你可以包括 XSLT 并输入所需的 parameters。以下为示例 −

If you want to use XSLT4R from within an application, you can include XSLT and input the parameters you need. Here is the example −

require "xslt"

stylesheet = File.readlines("stylesheet.xsl").to_s
xml_doc = File.readlines("document.xml").to_s
arguments = { 'image_dir' => '/....' }
sheet = XSLT::Stylesheet.new( stylesheet, arguments )

# output to StdOut
sheet.apply( xml_doc )

# output to 'str'
str = ""
sheet.output = [ str ]
sheet.apply( xml_doc )

Further Reading

  1. For a complete detail on REXML Parser, please refer to standard documentation for REXML Parser Documentation.

  2. You can download XSLT4R from RAA Repository.

Web Services with Ruby - SOAP4R

What is SOAP?

简单对象访问协议(SOAP)是一个基于 XML,通常(但并非必须)基于 HTTP 的跨平台、与语言无关的 RPC 协议。

The Simple Object Access Protocol (SOAP), is a cross-platform and language-independent RPC protocol based on XML and, usually (but not necessarily) HTTP.

它使用 XML 对进行远程过程调用的信息进行编码,并使用 HTTP 通过网络将该信息从客户端传输到服务器,反之亦然。

It uses XML to encode the information that makes the remote procedure call, and HTTP to transport that information across a network from clients to servers and vice versa.

与 COM、CORBA 等其他技术相比,SOAP 具有若干优势:例如,它的部署和调试成本相对较低、具有可扩展性和易用性,并且为不同的语言和平台存在多种实现。

SOAP has several advantages over other technologies like COM, CORBA etc: for example, its relatively cheap deployment and debugging costs, its extensibility and ease-of-use, and the existence of several implementations for different languages and platforms.

请参阅我们的简单教程 SOAP 以详细了解它。

Please refer to our simple tutorial SOAP to understand it in detail.

本小节让你熟悉 Ruby 的 SOAP 实现(SOAP4R)。这是一本基础教程,因此如果你需要更深入了解,你需要参考其他资源。

This chapter makes you familiar with the SOAP implementation for Ruby (SOAP4R). This is a basic tutorial, so if you need a deep detail, you would need to refer other resources.

Installing SOAP4R

SOAP4R 是由 Hiroshi Nakamura 开发的 Ruby 的 SOAP 实现,可从以下位置下载:

SOAP4R is the SOAP implementation for Ruby developed by Hiroshi Nakamura and can be downloaded from −

NOTE − 已经安装此组件的可能性很大。

NOTE − There may be a great chance that you already have installed this component.

Download SOAP

如果您知道 gem 实用的功能,可以使用以下命令安装SOAP4R及其相关包。

If you are aware of gem utility then you can use the following command to install SOAP4R and related packages.

$ gem install soap4r --include-dependencies

如果您在Windows上工作,则需要从上述位置下载一个zip文件,并且需要使用标准安装方法通过运行ruby install.rb来安装它。

If you are working on Windows, then you need to download a zipped file from the above location and need to install it using the standard installation method by running ruby install.rb.

Writing SOAP4R Servers

SOAP4R支持两种不同类型的服务器−

SOAP4R supports two different types of servers −

  1. CGI/FastCGI based (SOAP::RPC::CGIStub)

  2. Standalone (SOAP::RPC:StandaloneServer)

本章提供了有关编写独立服务器的详细信息。在编写SOAP服务器中涉及以下步骤。

This chapter gives detail on writing a stand alone server. The following steps are involved in writing a SOAP server.

Step 1 - Inherit SOAP::RPC::StandaloneServer Class

要实施您自己的独立服务器,您需要编写一个新类,该类将成为SOAP::StandaloneServer的子类,如下所示:−

To implement your own stand alone server you need to write a new class, which will be child of SOAP::StandaloneServer as follows −

class MyServer < SOAP::RPC::StandaloneServer
  ...............
end

NOTE -如果您想编写基于FastCGI的服务器,则需要将SOAP::RPC::CGIStub作为父类,其余过程将保持不变。

NOTE − If you want to write a FastCGI based server then you need to take SOAP::RPC::CGIStub as parent class, rest of the procedure will remain the same.

Step 2 - Define Handler Methods

第二步是编写您希望向外界公开的Web服务方法。

Second step is to write your Web Services methods, which you would like to expose to the outside world.

它们可以作为简单的Ruby方法编写。例如,我们编写两个方法,将两个数字相加并除以两个数字−

They can be written as simple Ruby methods. For example, let’s write two methods to add two numbers and divide two numbers −

class MyServer < SOAP::RPC::StandaloneServer
   ...............

   # Handler methods
   def add(a, b)
      return a + b
   end
   def div(a, b)
      return a / b
   end
end

Step 3 - Expose Handler Methods

下一步是将我们定义的方法添加到服务器中。初始化方法用于使用以下两种方法之一公开服务方法:−

Next step is to add our defined methods to our server. The initialize method is used to expose service methods with one of the two following methods −

class MyServer < SOAP::RPC::StandaloneServer
   def initialize(*args)
      add_method(receiver, methodName, *paramArg)
   end
end

以下是参数说明 −

Here is the description of the parameters −

Sr.No.

Parameter & Description

1

receiver The object that contains the methodName method. You define the service methods in the same class as the methodDef method, this parameter is self.

2

methodName The name of the method that is called due to an RPC request.

3

paramArg Specifies, when given, the parameter names and parameter modes.

要理解inout或out参数的用法,请考虑以下服务方法,该服务方法采用两个参数(inParam和inoutParam),返回一个普通返回值(retVal)以及两个其他参数:inoutParam和outParam−

To understand the usage of inout or out parameters, consider the following service method that takes two parameters (inParam and inoutParam), returns one normal return value (retVal) and two further parameters: inoutParam and outParam −

def aMeth(inParam, inoutParam)
   retVal = inParam + inoutParam
   outParam = inParam . inoutParam
   inoutParam = inParam * inoutParam
   return retVal, inoutParam, outParam
end

现在,我们可以按以下方式公开此方法:−

Now, we can expose this method as follows −

add_method(self, 'aMeth', [
   %w(in inParam),
   %w(inout inoutParam),
   %w(out outParam),
   %w(retval return)
])

Step 4 - Start the Server

最后一步是通过实例化一个派生类的实例并调用 start 方法来启动服务器。

The final step is to start your server by instantiating one instance of the derived class and calling start method.

myServer = MyServer.new('ServerName', 'urn:ruby:ServiceName', hostname, port)

myServer.start

以下是所需参数的说明:−

Here is the description of required parameters −

Sr.No.

Parameter & Description

1

ServerName A server name, you can give what you like most.

2

urn:ruby:ServiceName Here urn:ruby is constant but you can give a unique ServiceName name for this server.

3

hostname Specifies the hostname on which this server will listen.

4

port An available port number to be used for the web service.

Example

现在,使用上述步骤,让我们编写一个独立服务器 −

Now, using the above steps, let us write one standalone server −

require "soap/rpc/standaloneserver"

begin
   class MyServer < SOAP::RPC::StandaloneServer

      # Expose our services
      def initialize(*args)
         add_method(self, 'add', 'a', 'b')
         add_method(self, 'div', 'a', 'b')
      end

      # Handler methods
      def add(a, b)
         return a + b
      end
      def div(a, b)
         return a / b
      end
end
   server = MyServer.new("MyServer",
            'urn:ruby:calculation', 'localhost', 8080)
   trap('INT){
      server.shutdown
   }
   server.start
rescue => err
   puts err.message
end

在执行时,此服务器应用程序在 localhost 上启动一个独立 SOAP 服务器,并侦听端口 8080 上的请求。它公开了一种服务方法,即加法和除法,该方法接受两个参数并返回结果。

When executed, this server application starts a standalone SOAP server on localhost and listens for requests on port 8080. It exposes one service methods, add and div, which takes two parameters and return the result.

现在,您可以按如下方式在后台运行此服务器 −

Now, you can run this server in background as follows −

$ ruby MyServer.rb&

Writing SOAP4R Clients

SOAP::RPC::Driver 类提供对编写 SOAP 客户端应用程序的支持。本章介绍此类,并根据一个应用程序演示其使用。

The SOAP::RPC::Driver class provides support for writing SOAP client applications. This chapter describes this class and demonstrate its usage on the basis of an application.

以下是调用 SOAP 服务所需的最基本信息 −

Following is the bare minimum information you would need to call a SOAP service −

  1. The URL of the SOAP service (SOAP Endpoint URL).

  2. The namespace of the service methods (Method Namespace URI).

  3. The names of the service methods and their parameters.

现在,我们将编写一个 SOAP 客户端,它将调用在上一个示例中定义的服务方法,即加法和除法。

Now, we will write a SOAP client which would call service methods defined in above example, named add and div.

以下是创建 SOAP 客户端的主要步骤。

Here are the main steps to create a SOAP client.

Step 1 - Create a SOAP Driver Instance

我们通过调用 new 方法按照如下方式创建 SOAP::RPC::Driver 的一个实例 −

We create an instance of SOAP::RPC::Driver by calling its new method as follows −

SOAP::RPC::Driver.new(endPoint, nameSpace, soapAction)

以下是所需参数的说明:−

Here is the description of required parameters −

Sr.No.

Parameter & Description

1

endPoint URL of the SOAP server to connect with.

2

nameSpace The namespace to use for all RPCs done with this SOAP::RPC::Driver object.

3

soapAction A value for the SOAPAction field of the HTTP header. If nil this defaults to the empty string "".

Step 2 - Add Service Methods

若要向 SOAP::RPC::Driver 添加 SOAP 服务方法,我们可以使用 SOAP::RPC::Driver 实例调用如下方法 −

To add a SOAP service method to a SOAP::RPC::Driver we can call the following method using SOAP::RPC::Driver instance −

driver.add_method(name, *paramArg)

以下是参数说明 −

Here is the description of the parameters −

Sr.No.

Parameter & Description

1

name The name of the remote web service method.

2

paramArg Specifies the names of the remote procedures' parameters.

Step 3 - Invoke SOAP service

最后一步是使用 SOAP::RPC::Driver 实例调用 SOAP 服务,如下所示 −

The final step is to invoice SOAP service using SOAP::RPC::Driver instance as follows −

result = driver.serviceMethod(paramArg...)

在其中,serviceMethod 为实际的 Web 服务方法,paramArg…​​ 是需在服务方法中传递的参数列表。

Here serviceMethod is the actual web service method and paramArg…​ is the list parameters required to pass in the service method.

Example

根据上述步骤,我们编写 SOAP 客户端,如下所示:

Based on the above steps, we will write a SOAP client as follows −

#!/usr/bin/ruby -w

require 'soap/rpc/driver'

NAMESPACE = 'urn:ruby:calculation'
URL = 'http://localhost:8080/'

begin
   driver = SOAP::RPC::Driver.new(URL, NAMESPACE)

   # Add remote sevice methods
   driver.add_method('add', 'a', 'b')

   # Call remote service methods
   puts driver.add(20, 30)
rescue => err
   puts err.message
end

Further Readings

我刚刚解释了 Web 服务与 Ruby 的基本概念。如果您希望进一步深入了解,可以访问以下链接,以查找更多有关 Web Services with Ruby 的详细信息。

I have explained you just very basic concepts of Web Services with Ruby. If you want to drill down it further, then there is following link to find more details on Web Services with Ruby.

Ruby - Tk Guide

Introduction

Ruby 的标准图形用户界面 (GUI) 为 Tk。Tk 起初是 John Ousterhout 开发的 Tcl 脚本语言的 GUI。

The standard graphical user interface (GUI) for Ruby is Tk. Tk started out as the GUI for the Tcl scripting language developed by John Ousterhout.

Tk 拥有独特的识别标记,它是唯一跨平台的 GUI。Tk 可以运行在 Windows、Mac 和 Linux 上,并在每个操作系统上都提供原生观感。

Tk has the unique distinction of being the only cross-platform GUI. Tk runs on Windows, Mac, and Linux and provides a native look-and-feel on each operating system.

基于 Tk 的应用程序的基本组件被称为窗口小部件。该组件有时也称为窗口,因为在 Tk 中,“窗口”和“窗口小部件”经常可以互换使用。

The basic component of a Tk-based application is called a widget. A component is also sometimes called a window, since, in Tk, "window" and "widget" are often used interchangeably.

Tk 应用程序遵循一个窗口小部件层次结构,其中可以在另一个窗口小部件内放置任意数量的窗口小部件,并且这些窗口小部件又可以在另一个窗口小部件内放置,依此类推。Tk 程序中的主窗口小部件被称为根窗口小部件,可以通过制作 TkRoot 类的一个新实例来创建它。

Tk applications follow a widget hierarchy where any number of widgets may be placed within another widget, and those widgets within another widget, ad infinitum. The main widget in a Tk program is referred to as the root widget and can be created by making a new instance of the TkRoot class.

  1. Most Tk-based applications follow the same cycle: create the widgets, place them in the interface, and finally, bind the events associated with each widget to a method.

  2. There are three geometry managers; place, grid and pack that are responsible for controlling the size and location of each of the widgets in the interface.

Installation

Ruby Tk 绑定与 Ruby 一起分发,但 Tk 是一个单独的安装程序。Windows 用户可以从 ActiveState’s ActiveTcl 下载一个单次点击的 Tk 安装程序。

The Ruby Tk bindings are distributed with Ruby but Tk is a separate installation. Windows users can download a single click Tk installation from ActiveState’s ActiveTcl.

Mac 和 Linux 用户可能不需要安装它,因为存在很大几率 Tk 已经与操作系统一同安装了,但如果没有安装,你可以从 Tcl Developer Xchange 下载预构建软件包或获取源代码。

Mac and Linux users may not need to install it because there is a great chance that its already installed along with OS but if not, you can download prebuilt packages or get the source from the Tcl Developer Xchange.

Simple Tk Application

Ruby/Tk 程序有一个典型的结构,即创建主窗口或 root 窗口(TkRoot 的一个实例),向其中添加窗口小部件以构建用户界面,然后通过调用 Tk.mainloop 启动主事件循环。

A typical structure for Ruby/Tk programs is to create the main or root window (an instance of TkRoot), add widgets to it to build up the user interface, and then start the main event loop by calling * Tk.mainloop*.

面向 Ruby/Tk 的传统 Hello, World! 示例看起来像这样 −

The traditional Hello, World! example for Ruby/Tk looks something like this −

require 'tk'

root = TkRoot.new { title "Hello, World!" }
TkLabel.new(root) do
   text 'Hello, World!'
   pack { padx 15 ; pady 15; side 'left' }
end
Tk.mainloop

在这里,在加载 tk 扩展模块之后,我们使用 TkRoot.new 创建了一个根级别框架。然后我们制作了一个 TkLabel 窗口小部件作为根框架的一个子级,同时为该标签设置几个选项。最后,我们打包根框架并进入主 GUI 事件循环。

Here, after loading the tk extension module, we create a root-level frame using TkRoot.new. We then make a TkLabel widget as a child of the root frame, setting several options for the label. Finally, we pack the root frame and enter the main GUI event loop.

如果你运行这个脚本,将会产生以下结果 −

If you would run this script, it would produce the following result −

ruby tk1

Ruby/Tk Widget Classes

有一系列 Ruby/Tk 类,它们可用于使用 Ruby/Tk 创建所需 GUI。

There is a list of various Ruby/Tk classes, which can be used to create a desired GUI using Ruby/Tk.

  1. TkFrame Creates and manipulates frame widgets.

  2. TkButton Creates and manipulates button widgets.

  3. TkLabel Creates and manipulates label widgets.

  4. TkEntry Creates and manipulates entry widgets.

  5. TkCheckButton Creates and manipulates checkbutton widgets.

  6. TkRadioButton Creates and manipulates radiobutton widgets.

  7. TkListbox Creates and manipulates listbox widgets.

  8. TkComboBox Creates and manipulates listbox widgets.

  9. TkMenu Creates and manipulates menu widgets.

  10. TkMenubutton Creates and manipulates menubutton widgets.

  11. Tk.messageBox Creates and manipulates a message dialog.

  12. TkScrollbar Creates and manipulates scrollbar widgets.

  13. TkCanvas Creates and manipulates canvas widgets.

  14. TkScale Creates and manipulates scale widgets.

  15. TkText Creates and manipulates text widgets.

  16. TkToplevel Creates and manipulates toplevel widgets.

  17. TkSpinbox Creates and manipulates Spinbox widgets.

  18. TkProgressBar Creates and manipulates Progress Bar widgets.

  19. Dialog Box Creates and manipulates Dialog Box widgets.

  20. Tk::Tile::Notebook Display several windows in limited space with notebook metaphor.

  21. Tk::Tile::Paned Displays a number of subwindows, stacked either vertically or horizontally.

  22. Tk::Tile::Separator Displays a horizontal or vertical separator bar.

  23. Ruby/Tk Font, Colors and Images Understanding Ruby/Tk Fonts, Colors and Images

Standard Configuration Options

所有控件都具有若干不同的配置选项,这些选项通常用于控制它们的显示方式或行为方式。有哪些可用选项当然取决于控件类。

All widgets have a number of different configuration options, which generally control how they are displayed or how they behave. The options that are available depend upon the widget class of course.

以下列出了所有标准配置选项,这些选项可适用于任何 Ruby/Tk 控件。

Here is a list of all the standard configuration options, which could be applicable to any Ruby/Tk widget.

Ruby/Tk Geometry Management

几何管理处理根据要求对不同控件进行定位。Tk 中的几何管理依赖于主控件和从控件的概念。

Geometry Management deals with positioning different widgets as per requirement. Geometry management in Tk relies on the concept of master and slave widgets.

主控件是一个控件,通常是顶级窗口或框架,它将包含其它控件,即从控件。你可以认为几何管理器控制了主控件,并决定在其中显示什么。

A master is a widget, typically a top-level window or a frame, which will contain other widgets, which are called slaves. You can think of a geometry manager as taking control of the master widget, and deciding what will be displayed within.

几何管理器将询问每个从控件的自然大小,或理想的显示大小。然后,它获取该信息,并将其与程序在要求几何管理器管理该特定从控件时提供的任何参数结合在一起。

The geometry manager will ask each slave widget for its natural size, or how large it would ideally like to be displayed. It then takes that information and combines it with any parameters provided by the program when it asks the geometry manager to manage that particular slave widget.

有三个几何管理器,即 place、grid 和 pack,它们负责控制接口中每个控件的大小和位置。

There are three geometry managers place, grid and pack that are responsible for controlling the size and location of each of the widgets in the interface.

  1. grid Geometry manager that arranges widgets in a grid.

  2. pack Geometry manager that packs around edges of cavity.

  3. place Geometry manager for fixed or rubber-sheet placement.

Ruby/Tk Event Handling

Ruby/Tk 支持事件循环,它会从操作系统接收事件。这些事件包括按钮按下、击键、鼠标移动、窗口大小调整等。

Ruby/Tk supports event loop, which receives events from the operating system. These are things like button presses, keystrokes, mouse movement, window resizing, and so on.

Ruby/Tk 可用于管理此事件循环。它会弄清楚该事件适用于哪个控件(用户是否单击了此按钮?如果按下了键,哪个文本框具有焦点?),并相应地分派事件。单个控件知道如何响应事件,因此例如,当鼠标移动到按钮上时,按钮可能会变色,而在鼠标离开时恢复原色。

Ruby/Tk takes care of managing this event loop for you. It will figure out what widget the event applies to (did the user click on this button? if a key was pressed, which textbox had the focus?), and dispatch it accordingly. Individual widgets know how to respond to events, so for example a button might change color when the mouse moves over it, and revert back when the mouse leaves.

在更高的层面上,Ruby/Tk 会在你的程序中调用回调,以表明控件发生了重大事件。对于这两种情况,你可以提供一个代码块或一个 Ruby Proc 对象,以指定应用程序如何响应该事件或回调。

At a higher level, Ruby/Tk invokes callbacks in your program to indicate that something significant happened to a widget For either case, you can provide a code block or a Ruby Proc object that specifies how the application responds to the event or callback.

让我们了解如何使用 bind 方法将基本窗口系统事件与处理它们的 Ruby 过程关联起来。bind 的最简单形式使其输入一个指示事件名称的字符串和一个 Tk 用于处理该事件的代码块。

Let’s take a look at how to use the bind method to associate basic window system events with the Ruby procedures that handle them. The simplest form of bind takes as its inputs a string indicating the event name and a code block that Tk uses to handle the event.

例如,要捕获控件上的第一个鼠标按钮的 ButtonRelease 事件,你可以编写 −

For example, to catch the ButtonRelease event for the first mouse button on some widget, you’d write −

someWidget.bind('ButtonRelease-1') {
   ....code block to handle this event...
}

事件名称可以包括其他修饰符和详细信息。修饰符是一个字符串,如 Shift、Control 或 Alt,指示按下了其中一个修饰键。

An event name can include additional modifiers and details. A modifier is a string like Shift, Control or Alt, indicating that one of the modifier keys was pressed.

因此,例如,要捕获在用户按住 Ctrl 键并单击鼠标右键时生成的事件。

So, for example, to catch the event that’s generated when the user holds down the Ctrl key and clicks the right mouse button.

someWidget.bind('Control-ButtonPress-3', proc { puts "Ouch!" })

当用户激活它们时,许多 Ruby/Tk 控件都会触发回调,并且你可以使用 command 回调指定在这种情况下调用某个代码块或过程。如前所述,你可以在创建控件时指定 command 回调过程 −

Many Ruby/Tk widgets can trigger callbacks when the user activates them, and you can use the command callback to specify that a certain code block or procedure is invoked when that happens. As seen earlier, you can specify the command callback procedure when you create the widget −

helpButton = TkButton.new(buttonFrame) {
   text "Help"
   command proc { showHelp }
}

或者,你可以在稍后使用控件的 command 方法分配它 −

Or you can assign it later, using the widget’s command method −

helpButton.command proc { showHelp }

由于 command 方法接受过程或代码块,因此你还可以编写上一段代码示例,如下所示 −

Since the command method accepts either procedures or code blocks, you could also write the previous code example as −

helpButton = TkButton.new(buttonFrame) {
   text "Help"
   command { showHelp }
}

The configure Method

配置方法可用于设置和检索任何小组件配置值。例如,如需更改按钮的宽度,你可以随时按如下方式调用配置方法:

The configure method can be used to set and retrieve any widget configuration values. For example, to change the width of a button you can call configure method any time as follows −

require "tk"

button = TkButton.new {
   text 'Hello World!'
   pack
}
button.configure('activebackground', 'blue')
Tk.mainloop

如需获取当前小组件的值,只需按如下方式提供该值,而无需提供实际值:

To get the value for a current widget, just supply it without a value as follows −

color = button.configure('activebackground')

你还可以不提供任何选项来调用配置,这样便会提供所有选项及其值的列表。

You can also call configure without any options at all, which will give you a listing of all options and their values.

The cget Method

对于单纯检索选项的值,配置会返回比你通常所需信息还多的信息。cget 方法仅返回当前值。

For simply retrieving the value of an option, configure returns more information than you generally want. The cget method returns just the current value.

color = button.cget('activebackground')

Ruby - LDAP Tutorial

Ruby/LDAP 是 Ruby 的一个扩展库。它提供了一些 LDAP 库的接口,如 OpenLDAP、UMich LDAP、Netscape SDK、ActiveDirectory。

Ruby/LDAP is an extension library for Ruby. It provides the interface to some LDAP libraries like OpenLDAP, UMich LDAP, Netscape SDK, ActiveDirectory.

应用程序开发的通用 API 在 RFC1823 中描述,并由 Ruby/LDAP 支持。

The common API for application development is described in RFC1823 and is supported by Ruby/LDAP.

Ruby/LDAP Installation

您可以从 SOURCEFORGE.NET 下载并安装完整的 Ruby/LDAP 程序包。

You can download and install a complete Ruby/LDAP package from SOURCEFORGE.NET.

在安装 Ruby/LDAP 之前,请确保您具有以下组件:

Before installing Ruby/LDAP, make sure you have the following components −

  1. Ruby 1.8.x (at least 1.8.2 if you want to use ldap/control).

  2. OpenLDAP, Netscape SDK, Windows 2003 or Windows XP.

现在,您可以使用标准 Ruby 安装方法。在开始之前,如果您想查看 extconf.rb 的可用选项,请使用“--help”选项运行它。

Now, you can use standard Ruby Installation method. Before starting, if you’d like to see the available options for extconf.rb, run it with '--help' option.

$ ruby extconf.rb [--with-openldap1|--with-openldap2| \
                   --with-netscape|--with-wldap32]
$ make
$ make install

NOTE − 如果您在 Windows 上构建软件,您可能需要使用 nmake 而不是 make。

NOTE − If you’re building the software on Windows, you may need to use nmake instead of make.

Establish LDAP Connection

这是一个两步过程:

This is a two-step process −

Step 1 − Create Connection Object

以下是创建与 LDAP 目录连接的语法。

Following is the syntax to create a connection to a LDAP directory.

LDAP::Conn.new(host = 'localhost', port = LDAP_PORT)
  1. host − This is the host ID running LDAP directory. We will take it as localhost.

  2. port − This is the port being used for LDAP service. Standard LDAP ports are 636 and 389. Make sure which port is being used at your server otherwise you can use LDAP::LDAP_PORT.

此调用将返回与服务器、主机在端口 port 上的新 LDAP::Conn 连接。

This call returns a new LDAP::Conn connection to the server, host, on port port.

Step 2 − Binding

这里是我们通常指定将在会话剩余部分中使用的用户名和密码。

This is where we usually specify the username and password we will use for the rest of the session.

以下是绑定一个 LDAP 连接的语法,使用 DN, dn ,凭据, pwd ,和绑定方法, method

Following is the syntax to bind an LDAP connection, using the DN, dn, the credential, pwd, and the bind method, method

conn.bind(dn = nil, password = nil, method = LDAP::LDAP_AUTH_SIMPLE)do
....
end

您可以使用相同的方法而没有代码块。在这种情况下,您需要如下显式解绑连接 −

You can use the same method without a code block. In this case, you would need to unbind the connection explicitly as follows −

conn.bind(dn = nil, password = nil, method = LDAP::LDAP_AUTH_SIMPLE)
....
conn.unbind

如果提供了代码块,则 self 被传递给该块。

If a code block is given, self is yielded to the block.

我们现在可以在 bind 方法的块(bind 和 unbind 之间)中执行搜索、添加、修改或删除操作,前提是我们有适当的权限。

We can now perform search, add, modify or delete operations inside the block of the bind method (between bind and unbind), provided we have the proper permissions.

Example

假设我们正在本地服务器上工作,让我们将适当的主机、域、用户 ID 和密码等组合在一起。

Assuming we are working on a local server, let’s put things together with appropriate host, domain, user id and password, etc.

#/usr/bin/ruby -w

require 'ldap'

$HOST =    'localhost'
$PORT =    LDAP::LDAP_PORT
$SSLPORT = LDAP::LDAPS_PORT

conn = LDAP::Conn.new($HOST, $PORT)
conn.bind('cn = root, dc = localhost, dc = localdomain','secret')
....
conn.unbind

Adding an LDAP Entry

添加 LDPA 条目是一个两步过程 −

Adding an LDPA entry is a two step process −

Step 1 − Creating LDAP::Mod object

我们需要将 LDAP::Mod 对象传递给 conn.add 方法以创建条目。以下是创建 LDAP::Mod 对象的简单语法 −

We need LDAP::Mod object pass to conn.add method to create an entry. Here is a simple syntax to create LDAP::Mod object −

Mod.new(mod_type, attr, vals)
  1. mod_type − One or more option LDAP_MOD_ADD, LDAP_MOD_REPLACE or LDAP_MOD_DELETE.

  2. attr − should be the name of the attribute on which to operate.

  3. vals − is an array of values pertaining to attr. If vals contains binary data, mod_type should be logically OR’ed (|) with LDAP_MOD_BVALUES.

此调用返回 LDAP::Mod 对象,可以将其传递给 LDAP::Conn 类中的方法,例如 Conn#add、Conn#add_ext、Conn#modify 和 Conn#modify_ext。

This call returns LDAP::Mod object, which can be passed to methods in the LDAP::Conn class, such as Conn#add, Conn#add_ext, Conn#modify and Conn#modify_ext.

Step 2 − Calling conn.add Method

一旦我们准备好 LDAP::Mod 对象,就可以调用 conn.add 方法来创建条目。下面是调用此方法的语法 −

Once we are ready with LDAP::Mod object, we can call conn.add method to create an entry. Here is a syntax to call this method −

conn.add(dn, attrs)

此方法添加了一个条目,带有 DN、dn 和属性 attrs。此处,attrs 应是 LDAP::Mod 对象的数组或属性/值数组对的哈希。

This method adds an entry with the DN, dn, and the attributes, attrs. Here, attrs should be either an array of LDAP::Mod objects or a hash of attribute/value array pairs.

Example

下面是一个完整示例,它将创建两个目录条目 −

Here is a complete example, which will create two directory entries −

#/usr/bin/ruby -w

require 'ldap'

$HOST =    'localhost'
$PORT =    LDAP::LDAP_PORT
$SSLPORT = LDAP::LDAPS_PORT

conn = LDAP::Conn.new($HOST, $PORT)
conn.bind('cn = root, dc = localhost, dc = localdomain','secret')

conn.perror("bind")
entry1 = [
   LDAP.mod(LDAP::LDAP_MOD_ADD,'objectclass',['top','domain']),
   LDAP.mod(LDAP::LDAP_MOD_ADD,'o',['TTSKY.NET']),
   LDAP.mod(LDAP::LDAP_MOD_ADD,'dc',['localhost']),
]

entry2 = [
   LDAP.mod(LDAP::LDAP_MOD_ADD,'objectclass',['top','person']),
   LDAP.mod(LDAP::LDAP_MOD_ADD, 'cn', ['Zara Ali']),
   LDAP.mod(LDAP::LDAP_MOD_ADD | LDAP::LDAP_MOD_BVALUES, 'sn',
                     ['ttate','ALI', "zero\000zero"]),
]

begin
   conn.add("dc = localhost, dc = localdomain", entry1)
   conn.add("cn = Zara Ali, dc = localhost, dc =  localdomain", entry2)
rescue LDAP::ResultError
   conn.perror("add")
   exit
end
conn.perror("add")
conn.unbind

Modifying an LDAP Entry

修改条目类似于添加条目。只需使用属性调用 modify 方法来修改,而不是用 add。下面是 modify 方法的简单语法。

Modifying an entry is similar to adding one. Just call the modify method instead of add with the attributes to modify. Here is a simple syntax of modify method.

conn.modify(dn, mods)

此方法使用 DN、dn 和属性 mods 修改条目。此处,mods 应是 LDAP::Mod 对象的数组或属性/值数组对的哈希。

This method modifies an entry with the DN, dn, and the attributes, mods. Here, mods should be either an array of LDAP::Mod objects or a hash of attribute/value array pairs.

Example

为了修改上一节中添加的条目的姓氏,我们可以编写 −

To modify the surname of the entry, which we added in the previous section, we would write −

#/usr/bin/ruby -w

require 'ldap'

$HOST =    'localhost'
$PORT =    LDAP::LDAP_PORT
$SSLPORT = LDAP::LDAPS_PORT

conn = LDAP::Conn.new($HOST, $PORT)
conn.bind('cn = root, dc = localhost, dc = localdomain','secret')

conn.perror("bind")
entry1 = [
   LDAP.mod(LDAP::LDAP_MOD_REPLACE, 'sn', ['Mohtashim']),
]

begin
   conn.modify("cn = Zara Ali, dc = localhost, dc = localdomain", entry1)
rescue LDAP::ResultError
   conn.perror("modify")
   exit
end
conn.perror("modify")
conn.unbind

Deleting an LDAP Entry

要删除条目,请使用 distinguished name 作为参数来调用 delete 方法。下面是 delete 方法的简单语法。

To delete an entry, call the delete method with the distinguished name as parameter. Here is a simple syntax of delete method.

conn.delete(dn)

此方法使用 DN、dn 删除条目。

This method deletes an entry with the DN, dn.

Example

要删除上一节中添加的 Zara Mohtashim 条目,我们可以编写 −

To delete Zara Mohtashim entry, which we added in the previous section, we would write −

#/usr/bin/ruby -w

require 'ldap'

$HOST =    'localhost'
$PORT =    LDAP::LDAP_PORT
$SSLPORT = LDAP::LDAPS_PORT

conn = LDAP::Conn.new($HOST, $PORT)
conn.bind('cn = root, dc = localhost, dc = localdomain','secret')

conn.perror("bind")
begin
   conn.delete("cn = Zara-Mohtashim, dc = localhost, dc = localdomain")
rescue LDAP::ResultError
   conn.perror("delete")
   exit
end
conn.perror("delete")
conn.unbind

Modifying the Distinguished Name

无法使用 modify 方法修改条目的 distinguished name。可以使用 modrdn 方法来代替。以下是 modrdn 方法的简单语法 −

It’s not possible to modify the distinguished name of an entry with the modify method. Instead, use the modrdn method. Here is simple syntax of modrdn method −

conn.modrdn(dn, new_rdn, delete_old_rdn)

此方法使用 DN、dn 修改条目的 RDN,为其提供新的 RDN、new_rdn。如果 delete_old_rdn 为 true,则旧的 RDN 值将从条目中删除。

This method modifies the RDN of the entry with DN, dn, giving it the new RDN, new_rdn. If delete_old_rdn is true, the old RDN value will be deleted from the entry.

Example

假设我们有以下条目 −

Suppose we have the following entry −

dn: cn = Zara Ali,dc = localhost,dc = localdomain
cn: Zara Ali
sn: Ali
objectclass: person

然后,我们可以使用以下代码修改它的 distinguished name −

Then, we can modify its distinguished name with the following code −

#/usr/bin/ruby -w

require 'ldap'

$HOST =    'localhost'
$PORT =    LDAP::LDAP_PORT
$SSLPORT = LDAP::LDAPS_PORT

conn = LDAP::Conn.new($HOST, $PORT)
conn.bind('cn = root, dc = localhost, dc = localdomain','secret')

conn.perror("bind")
begin
   conn.modrdn("cn = Zara Ali, dc = localhost, dc = localdomain", "cn = Zara Mohtashim", true)
rescue LDAP::ResultError
   conn.perror("modrdn")
   exit
end
conn.perror("modrdn")
conn.unbind

要在 LDAP 目录中执行搜索,请使用包含三种不同搜索模式之一的 search 方法 −

To perform a search on a LDAP directory, use the search method with one of the three different search modes −

  1. LDAP_SCOPE_BASEM − Search only the base node.

  2. LDAP_SCOPE_ONELEVEL − Search all children of the base node.

  3. LDAP_SCOPE_SUBTREE − Search the whole subtree including the base node.

Example

在这里,我们将搜索 cn = localhost,cn = localdomain 项的整个子树以查找人员对象 -

Here, we are going to search the whole subtree of entry dc = localhost, dc = localdomain for person objects −

#/usr/bin/ruby -w

require 'ldap'

$HOST =    'localhost'
$PORT =    LDAP::LDAP_PORT
$SSLPORT = LDAP::LDAPS_PORT

base = 'dc = localhost,dc = localdomain'
scope = LDAP::LDAP_SCOPE_SUBTREE
filter = '(objectclass = person)'
attrs = ['sn', 'cn']

conn = LDAP::Conn.new($HOST, $PORT)
conn.bind('cn = root, dc = localhost, dc = localdomain','secret')

conn.perror("bind")
begin
   conn.search(base, scope, filter, attrs) { |entry|
      # print distinguished name
      p entry.dn
      # print all attribute names
      p entry.attrs
      # print values of attribute 'sn'
      p entry.vals('sn')
      # print entry as Hash
      p entry.to_hash
   }
rescue LDAP::ResultError
   conn.perror("search")
   exit
end
conn.perror("search")
conn.unbind

这会对每个匹配项调用提供的代码块,其中 LDAP 项由 LDAP::Entry 类的实例表示。使用 search 的最后一个参数,您可以指定感兴趣的属性,省略所有其他属性。如果您在这里传递 nil,则会返回所有属性,与关系数据库中的“SELECT *”相同。

This invokes the given code block for each matching entry where the LDAP entry is represented by an instance of the LDAP::Entry class. With the last parameter of search, you can specify the attributes in which you are interested, omitting all others. If you pass nil here, all attributes are returned same as "SELECT *" in relational databases.

LDAP::Entry 类的 dn 方法(get_dn 的别名)返回项的专有名称,使用 to_hash 方法,您可以获得其属性的哈希表示形式(包括专有名称)。若要获取项属性的列表,请使用 attrs 方法(get_attributes 的别名)。此外,要获取一个特定属性值的列表,请使用 vals 方法(get_values 的别名)。

The dn method (alias for get_dn) of the LDAP::Entry class returns the distinguished name of the entry, and with the to_hash method, you can get a hash representation of its attributes (including the distinguished name). To get a list of an entry’s attributes, use the attrs method (alias for get_attributes). Also, to get the list of one specific attribute’s values, use the vals method (alias for get_values).

Handling Errors

Ruby/LDAP 定义了两种不同的异常类 -

Ruby/LDAP defines two different exception classes −

  1. In case of an error, the new, bind or unbind methods raise an LDAP::Error exception.

  2. In case of add, modify, delete or searching an LDAP directory raise an LDAP::ResultError.

Further Reading

有关 LDAP 方法的完整详细信息,请参阅 LDAP Documentation 的标准文档。

For complete details on LDAP methods, please refer to the standard documentation for LDAP Documentation.

Ruby - Multithreading

传统程序有一个单一执行线程,直到程序终止之前,组成程序的语句或指令将按顺序执行。

Traditional programs have a single thread of execution the statements or instructions that comprise the program are executed sequentially until the program terminates.

多线程程序有多个执行线程。在每个线程中,语句按顺序执行,但是这些线程本身可以并行执行,例如在多核 CPU 上。通常在单 CPU 计算机中,多个线程实际上并不会并行执行,但是可以通过交错执行线程来模拟并行性。

A multithreaded program has more than one thread of execution. Within each thread, statements are executed sequentially, but the threads themselves may be executed in parallel on a multicore CPU, for example. Often on a single CPU machine, multiple threads are not actually executed in parallel, but parallelism is simulated by interleaving the execution of the threads.

利用 Thread 类,Ruby 非常便于编写多线程程序。Ruby 线程是一种轻量且高效的方式,在您的代码中实现并发。

Ruby makes it easy to write multi-threaded programs with the Thread class. Ruby threads are a lightweight and efficient way to achieve concurrency in your code.

Creating Ruby Threads

若要启动一个新线程,只需将一个块与对 Thread.new 的调用关联即可。将创建一个新线程来执行块中代码,原始线程会立即从 Thread.new 返回,并使用下一条语句恢复执行 −

To start a new thread, just associate a block with a call to Thread.new. A new thread will be created to execute the code in the block, and the original thread will return from Thread.new immediately and resume execution with the next statement −

# Thread #1 is running here
Thread.new {
   # Thread #2 runs this code
}
# Thread #1 runs this code

Example

这是一个示例,演示如何使用多线程 Ruby 程序。

Here is an example, which shows how we can use multi-threaded Ruby program.

#!/usr/bin/ruby

def func1
   i = 0
   while i<=2
      puts "func1 at: #{Time.now}"
      sleep(2)
      i = i+1
   end
end

def func2
   j = 0
   while j<=2
      puts "func2 at: #{Time.now}"
      sleep(1)
      j = j+1
   end
end

puts "Started At #{Time.now}"
t1 = Thread.new{func1()}
t2 = Thread.new{func2()}
t1.join
t2.join
puts "End at #{Time.now}"

这将生成以下结果:

This will produce following result −

Started At Wed May 14 08:21:54 -0700 2008
func1 at: Wed May 14 08:21:54 -0700 2008
func2 at: Wed May 14 08:21:54 -0700 2008
func2 at: Wed May 14 08:21:55 -0700 2008
func1 at: Wed May 14 08:21:56 -0700 2008
func2 at: Wed May 14 08:21:56 -0700 2008
func1 at: Wed May 14 08:21:58 -0700 2008
End at Wed May 14 08:22:00 -0700 2008

Thread Lifecycle

使用 Thread.new 创建新的线程。您还可以使用同义词 Thread.start 和 Thread.fork。

A new threads are created with Thread.new. You can also use the synonyms Thread.start and Thread.fork.

在创建线程后无需启动该线程,当 CPU 资源可用时,它将自动开始运行。

There is no need to start a thread after creating it, it begins running automatically when CPU resources become available.

Thread 类定义了一些方法来查询和操作正在运行的线程。线程会运行与 Thread.new 的调用关联的块中的代码,然后停止运行。

The Thread class defines a number of methods to query and manipulate the thread while it is running. A thread runs the code in the block associated with the call to Thread.new and then it stops running.

该块中最后一个表达式的值为线程值,可以通过调用 Thread 对象的 value 方法获取该值。如果线程已经运行完成,则 value 会立即返回线程值。否则,value 方法会阻塞,在线程完成之前不会返回。

The value of the last expression in that block is the value of the thread, and can be obtained by calling the value method of the Thread object. If the thread has run to completion, then the value returns the thread’s value right away. Otherwise, the value method blocks and does not return until the thread has completed.

类方法 Thread.current 返回表示当前线程的 Thread 对象。这样,线程可以对自己进行操作。类方法 Thread.main 返回表示主线程的 Thread 对象。这是在启动 Ruby 程序时开始运行的初始执行线程。

The class method Thread.current returns the Thread object that represents the current thread. This allows threads to manipulate themselves. The class method Thread.main returns the Thread object that represents the main thread. This is the initial thread of execution that began when the Ruby program was started.

您可以通过调用线程的 Thread.join 方法来等待特定线程完成。调用线程将阻塞,直到给定线程完成。

You can wait for a particular thread to finish by calling that thread’s Thread.join method. The calling thread will block until the given thread is finished.

Threads and Exceptions

如果在主线程中引发了一个异常,并且在任何地方都没有处理该异常,则 Ruby 解释器将打印一条消息并退出。在主线程之外的线程中,未处理的异常会导致该线程停止运行。

If an exception is raised in the main thread, and is not handled anywhere, the Ruby interpreter prints a message and exits. In threads, other than the main thread, unhandled exceptions cause the thread to stop running.

如果一个线程由于未处理的异常而 t 退出,而另一个线程 s 调用 t.join 或 t.value,那么 t 中发生的异常将在线程 s 中引发。

If a thread t exits because of an unhandled exception, and another thread s calls t.join or t.value, then the exception that occurred in t is raised in the thread s.

如果 Thread.abort_on_exception 为 false,即默认条件,一个未处理的异常会简单地终止当前线程,而其他所有线程继续运行。

If Thread.abort_on_exception is false, the default condition, an unhandled exception simply kills the current thread and all the rest continue to run.

如果您希望任何线程中的任何未处理异常导致解释器退出,请将类方法 Thread.abort_on_exception 设置为 true。

If you would like any unhandled exception in any thread to cause the interpreter to exit, set the class method Thread.abort_on_exception to true.

t = Thread.new { ... }
t.abort_on_exception = true

Thread Variables

当创建一个线程时,该线程通常可以访问范围内的任何变量。一个线程块中的局部变量属于该线程,并且它们不受共享。

A thread can normally access any variables that are in scope when the thread is created. Variables local to the block of a thread are local to the thread, and are not shared.

Thread 类提供一个特殊功能,它允许按名称创建和访问局部变量。您可以简单地将线程对象视为散列表,使用 []= 来写入元素,并使用 [] 来读取元素。

Thread class features a special facility that allows thread-local variables to be created and accessed by name. You simply treat the thread object as if it were a Hash, writing to elements using []= and reading them back using [].

在此示例中,每个线程都会使用键 mycount 在一个局部变量中记录变量 count 的当前值。

In this example, each thread records the current value of the variable count in a threadlocal variable with the key mycount.

#!/usr/bin/ruby

count = 0
arr = []

10.times do |i|
   arr[i] = Thread.new {
      sleep(rand(0)/10.0)
      Thread.current["mycount"] = count
      count += 1
   }
end

arr.each {|t| t.join; print t["mycount"], ", " }
puts "count = #{count}"

这会产生以下结果:

This produces the following result −

8, 0, 3, 7, 2, 1, 6, 5, 4, 9, count = 10

主线程等待子线程执行完成,然后打印出每个线程捕获的 count 值。

The main thread waits for the subthreads to finish and then prints out the value of count captured by each.

Thread Priorities

影响线程调度的第一个因素是线程优先级:高优先级的线程会先于低优先级的线程被调度。更确切地说,当没有更高优先级的线程等待运行时,线程才能获得 CPU 时间。

The first factor that affects the thread scheduling is the thread priority: high-priority threads are scheduled before low-priority threads. More precisely, a thread will only get CPU time if there are no higher-priority threads waiting to run.

可以使用 priority = 和 priority 设置和查询 Ruby Thread 对象的优先级。新创建的线程的优先级与创建它的线程的优先级相同。主线程的初始优先级为 0。

You can set and query the priority of a Ruby Thread object with priority = and priority. A newly created thread starts at the same priority as the thread that created it. The main thread starts off at priority 0.

无法在开始运行之前设置线程的优先级。然而,线程可以在其执行的第一个操作中提升或降低其自身的优先级。

There is no way to set the priority of a thread before it starts running. A thread can, however, raise or lower its own priority as the first action it takes.

Thread Exclusion

如果两个线程共享对相同数据的访问权,并且其中至少有一个线程修改了该数据,则你必须特别注意以确保没有线程可能会看到不一致状态下的数据。这称为线程互斥。

If two threads share access to the same data, and at least one of the threads modifies that data, you must take special care to ensure that no thread can ever see the data in an inconsistent state. This is called thread exclusion.

Mutex 是一个类,它实现了针对某个共享资源进行互斥访问的简单信号量锁。也就是说,在给定的时间,只有一个线程可能持有该锁。其他线程可以选择在队列中等待锁可用,或者仅仅选择立即获取一个指示锁不可用的错误。

Mutex is a class that implements a simple semaphore lock for mutually exclusive access to some shared resource. That is, only one thread may hold the lock at a given time. Other threads may choose to wait in line for the lock to become available, or may simply choose to get an immediate error indicating that the lock is not available.

通过将对共享数据的全部访问权限置于互斥锁的控制之下,我们可以确保一致性和原子操作。我们尝试两个示例,第一个是没有互斥锁的,第二个是有互斥锁的 −

By placing all accesses to the shared data under control of a mutex, we ensure consistency and atomic operation. Let’s try to examples, first one without mutax and second one with mutax −

Example without Mutax

#!/usr/bin/ruby
require 'thread'

count1 = count2 = 0
difference = 0
counter = Thread.new do
   loop do
      count1 += 1
      count2 += 1
   end
end
spy = Thread.new do
   loop do
      difference += (count1 - count2).abs
   end
end
sleep 1
puts "count1 :  #{count1}"
puts "count2 :  #{count2}"
puts "difference : #{difference}"

这会产生以下结果 −

This will produce the following result −

count1 :  1583766
count2 :  1583766
difference : 0
#!/usr/bin/ruby
require 'thread'
mutex = Mutex.new

count1 = count2 = 0
difference = 0
counter = Thread.new do
   loop do
      mutex.synchronize do
         count1 += 1
         count2 += 1
      end
   end
end
spy = Thread.new do
   loop do
      mutex.synchronize do
         difference += (count1 - count2).abs
      end
   end
end
sleep 1
mutex.lock
puts "count1 :  #{count1}"
puts "count2 :  #{count2}"
puts "difference : #{difference}"

这会产生以下结果 −

This will produce the following result −

count1 :  696591
count2 :  696591
difference : 0

Handling Deadlock

当我们开始使用 Mutex 对象进行线程互斥时,我们必须小心避免死锁。当所有线程都在等待获得另一个线程持有的资源时,就会发生死锁。由于所有线程都处于阻塞状态,因此它们无法释放所持有的锁。并且由于它们无法释放这些锁,因此没有其他线程可以获得这些锁。

When we start using Mutex objects for thread exclusion we must be careful to avoid deadlock. Deadlock is the condition that occurs when all threads are waiting to acquire a resource held by another thread. Because all threads are blocked, they cannot release the locks they hold. And because they cannot release the locks, no other thread can acquire those locks.

这是条件变量发挥作用的地方。条件变量只是一个与资源关联并用于保护特定互斥锁的信号量。当你需要不可用的资源时,你会等待条件变量。该操作将释放对相应互斥锁的锁。当其他一些线程发出资源可用信号时,原始线程会退出等待并在临界区域同时重新获得锁。

This is where condition variables come into picture. A condition variable is simply a semaphore that is associated with a resource and is used within the protection of a particular mutex. When you need a resource that’s unavailable, you wait on a condition variable. That action releases the lock on the corresponding mutex. When some other thread signals that the resource is available, the original thread comes off the wait and simultaneously regains the lock on the critical region.

Example

#!/usr/bin/ruby
require 'thread'
mutex = Mutex.new

cv = ConditionVariable.new
a = Thread.new {
   mutex.synchronize {
      puts "A: I have critical section, but will wait for cv"
      cv.wait(mutex)
      puts "A: I have critical section again! I rule!"
   }
}

puts "(Later, back at the ranch...)"

b = Thread.new {
   mutex.synchronize {
      puts "B: Now I am critical, but am done with cv"
      cv.signal
      puts "B: I am still critical, finishing up"
   }
}
a.join
b.join

这会产生以下结果 −

This will produce the following result −

A: I have critical section, but will wait for cv
(Later, back at the ranch...)
B: Now I am critical, but am done with cv
B: I am still critical, finishing up
A: I have critical section again! I rule!

Thread States

有 5 个可能的返回值与下表所示的 5 种可能的状态相对应。status 方法返回线程的状态。

There are five possible return values corresponding to the five possible states as shown in the following table. The status method returns the state of the thread.

Thread state

Return value

Runnable

run

Sleeping

Sleeping

Aborting

aborting

Terminated normally

false

Terminated with exception

nil

Thread Class Methods

Thread 类提供了以下方法,它们适用于程序中所有可用的线程。这些方法将被调用,使用 Thread 类名如下 −

Following methods are provided by Thread class and they are applicable to all the threads available in the program. These methods will be called as using Thread class name as follows −

Thread.abort_on_exception = true

Thread Instance Methods

这些方法适用于线程的实例。这些方法将被调用,使用 Thread 的实例如下 −

These methods are applicable to an instance of a thread. These methods will be called as using an instance of a Thread as follows −

#!/usr/bin/ruby

thr = Thread.new do   # Calling a class method new
   puts "In second thread"
   raise "Raise exception"
end
thr.join   # Calling an instance method join

Ruby - Built-in Functions

由于 Kernel 模块是由 Object 类包含的,因此其方法在 Ruby 程序的任何地方都可用。它们可以在没有接收者(函数形式)的情况下被调用。因此,它们通常被称为函数。

Since the Kernel module is included by Object class, its methods are available everywhere in the Ruby program. They can be called without a receiver (functional form). Therefore, they are often called functions.

Functions for Numbers

这里有一个与数字相关的内置函数列表。它们应该被如下使用 −

Here is a list of Built-in Functions related to number. They should be used as follows −

#!/usr/bin/ruby

num = 12.40
puts num.floor      # 12
puts num + 10       # 22.40
puts num.integer?   # false  as num is a float.

这会产生以下结果 −

This will produce the following result −

12
22.4
false

Functions for Float

Functions for Math

Conversion Field Specifier

函数 sprintf( fmt[, arg…​]) 和 format( fmt[, arg…​]) 返回一个字符串,其中 arg 根据 fmt 进行格式化。格式化规范与 C 编程语言中 sprintf 的规范基本相同。fmt 中的转换说明符(% 后跟转换字段说明符)替换为对应参数的格式化字符串。

The function sprintf( fmt[, arg…​]) and format( fmt[, arg…​]) returns a string in which arg is formatted according to fmt. Formatting specifications are essentially the same as those for sprintf in the C programming language. Conversion specifiers (% followed by conversion field specifier) in fmt are replaced by formatted string of corresponding argument.