2006-11-15

翻译:Appendix A:Introduction to Ruby

关键字: Ruby
在另外一个BLOG力写了,就不COPY来了,转个地址来。。
http://www.1steam.cn/1ster/?action=show&id=4

附录A:Ruby简介
原文:Agile_Web_Development_With_Rails-Beta-Appendix A
翻译:Ysl.1ster..!(http://www.1ster.cn)
来源:http://www.1steam.cn/1ster/
欢迎转载,转载时请请注明来源。


ruby是个相当简单的语言,虽然话是这么说,但是像在这样一个附录里就把它讲清楚基本上是不可能的。我们只是尽量的多讲解一些书中用到的例子,以使你有着更清晰的认识。本篇中很多内容来源于另外一本书《Programming Ruby》的第二章内容。

A.1 Ruby是面向对象的语言
在Ruby中,你所接触的一切都是对象,你操作的是对象,你的操作结果仍然是对象。
当你写OO语言时,你所看到的是真实世界的模型,例如,在这种处理方式下,你会发现,事物的类别也是需要描述的。在网上商店中,商品条目也是一种类别。在 Ruby中,你可以使用class来描述任何一个类别。class是状态(比如quantity 和the product_id)和操作这些状态的方法(例如一个计算价格总数的方法)的聚合物。在下面我将给你展示如何来创建classes。
一旦你定义了一些classes,你可能就会想着怎样给他们创建实例。例如,在前面的store系统中,你已经把Fred买书和Wilma买PDF的实例 (instances)区分开了。注意这里的我们用object来替代instances,后面我们都是用object这个词了。
我们通过调用构造器(constructor)来创建对象(Objects),什么是构造器呢?它是一个和class密切相关的一个比较特殊的方法。标准的构造器是new()。所以给你一个class,它就是LineItem,你怎么来创建它的对象呢,看下面:
line_item_one = LineItem.new
line_item_one.quantity = 1
line_item_one.sku = “AUTO_B_00”

line_item_two = LineItem.new
line_item_two.quantity = 2
line_item_two.sku = “RUBY_P_00”
这两个实例来自同一个class,但是却各自有着自己独特的个性。准确的讲,每个都有他们自己的状态,内在实例变量。我们的每一个Line item,都可以拥有一个实例变量来表现自己拥有的书籍本数。
在每个class中,你可以定义实例方法(methods),每个方法都有一定的功能,自然,这些方法在class内都是可以访问的;至于在class外面能不能访问,还要取决于权限条件。这些方法可以用来读取变量的状态。
方法是通过发送消息给object来调用的。发送的这个消息包含方法名,以及这个方法所需要的参数。当一个object收到一条消息时,就在自己的class中寻找与之匹配的方法。
方法和消息到底是怎么联系的,这个逻辑停起来貌似蛮复杂的,其实不然,他们都很自然,让你感到很优雅,让我们来看看一些方法调用的例子吧:

* “dave”.length
* line_item_one.quantity
* -1942.abs
* cart.add_line_item(next_purchase)

这里,圆点(.)之前的部分成为接收者,圆点之后的名子就是需要调用的方法。第一行是取得一个字符串(string)的长度(4)。第二个例子是取得 line item这个对象的数量(quantity)。第三个例子是计算一个整数的绝对值。最后一行展示的是添加以个条目到购物车中。

A2、Ruby的命名原则
本地变量,方法参数以及方法名全部应该以小写字母或者小划线开头,例如:order,line_items以及xr2000都是正确的。实例变量则以” at”符号(@)开头,比如@quantity 和 @product_id都是正确的。Ruby习惯用下划线分割多词名称的方法和变量名(所以line_item就比 lineItem要好)。
Class名,module名以及常量名则应该用大写字母开头的字符。按约定,他们使用大写字母,而不是使用下划线,来区别一个名称的开头字母。Class名看上去差不多是这样的:Object,PurchaseOrder,和 LineItem。

Rails 使用了大量的符号(symbol),符号看上去和变量名差不多,只是它们都是以冒号(:)开头的,比如下面这几个都是正确的符号,:action, :line_items,and :id。你可以认为符号就是可以神奇的变为常量的字符串。另外,你可以认为冒号的意思是:“叫..名字”,所以呢,:id就可以理解为:“一个叫做id的东西”。
Rails使用符号来标识事物,特别的是,可以在命名方法参数或者在散列(hash)中寻找值的时候使用它们作为关键字(key)。例如:
redirect_to :action => “edit”, :id => params[:id]

A.3 方法

让我们来写一个方法用来返回一个问候,一个私人的问候,我们待会会调用这个方法。

def say_goodnight(name)
result = “Good night, ” + name
return result
end

1. Time for bed…
puts say_goodnight(“Mary-Ellen”)
puts say_goodnight(“John-Boy”)

你不需要在每行后面添加分号(;),你要做的只是让每句单独成行就可以了。Ruby的注释是以从#字符开开始直到这行结束;缩进排列也不是必须的,自然,你缩进两个字符也是个好习惯。
方法以关键字def来定义,接着是方法名(上面的例子中就是say_goodbye)和方法所需要的参数。Ruby也不需要使用弯括号(大括号)来包含函数体(类),你只需要在主题结束的地方标记一个end就可以了。方法体的第一行连接字符串’Good night’和参数name,并把结果赋给自变量result。下一行返回这个结果给调用者(caller)。注意我们并没有申明result这个自变量,我们给他赋值的时候他就可用了。
已经定义了方法,我们接着调用了两次,在两种情况下,我们都那这个结果传给方法puts(),这个方法会把他的参数打印到控制台上,并且换行,我们把这个存贮为hello.rb,我们就可以按照下面的方式来运行它了:
work> ruby hello.rb
Good night, Mary-Ellen
Good night, John-Boy

前面的puts say_goodnight(“John-Boy”)这行包含了两个方法调用,一个是say_goodnight();另外一个是puts().为什么一个方法的参数放在括号中,另外一个方法调用的参数却没有放在括号中呢?我们来做个测试,大家看下面的两行是相同的:
puts say_goodnight(“John-Boy”)
puts(say_goodnight(“John-Boy”))

在Rails应用程序中,你将发现很多在表达式中的调用使用括号;而在另外一些看上去像命令或者申明的则不推荐使用括号。
这个例子也展示了一些Ruby的字符串对象,一种创建字符串的方法是使用string literals;创建的字符串放在单引号或者双引号之间,自然这两种方式是不相同的,在Ruby中处理这两种申明也是不一样的。在单引号中的字符串, Ruby做的工作很少,仅仅出去仅有的几个例外,Ruby认为你放在单引号中的值就是字符串值。
在双引号中,Ruby做了很多的工作。首先查看里面的以反斜杠开始的词,并把它们替换成它们的二进制值,最常碰到的是\n(代表新行的开始),\n强制断行。
Ruby接着处理表达式解析,在字符串中,#{expression}格式的将被它的值替代,我们可以按照这个格式来重写我们前面的方法。

def say_goodnight(name)
result = “Good night, #{name}”
return result
end
puts say_goodnight(‘Pa’)

当Ruby构造这个字符串时,它会取得当前的name值,并把他替换在字符串中。其他的复杂表达式也可以包含在#{...}中。这里我们调用capitalize( )方法,来定义这些字符串以大写字母开头的形式输出我们的参数。

def say_goodnight(name)
result = “Good night, #{name.capitalize}”
return result
end
puts say_goodnight(‘uncle’)

最后我们来优化下这个方法,在Ruby中,返回的就是是最后的表达式的值,所以我们可以去掉临时变量,直接返回值。

def say_goodnight(name) “Good night, #{name.capitalize}”
end
puts say_goodnight(‘ma’)

A.4 类

下面是Ruby的类的定义方式:
class Order < ActiveRecord::Base
- has_many :line_items
- def self.find_all_unpaid
- find(:all, ‘paid = 0’)
- end
-
- def total sum = 0
- line_items.each {|li| sum += li.total}
- end
- end

类定义以关键字class开头,接着是类的名字(必须是以大写字母开头),这里的Order就是定义为Basa类的子类,并且符合ActiveRecord模型。
Rails 大量应用类级别的申明(class-level declarations),这里的has_many就是在Active Record中定义的一个方法。在这里在定义Order类时被调用,通常这类方法都是类级别的调用,所以我们称为declarations。
在class内部,你可以定义类方法,和实例方法,带上前缀self.(像上面的第五行)就是类的方法,这类方法是可以被类调用的。这样的化,我们在任何地方使用下面的方式来进行:
to_collect = Order.find_all_unpaid
正规的方法定义都是实例方法,(例如上面例子中的第九行定义的total),它们在使用在对象实例上的,在下面的例子中,一个变量order源自Order类,我们在前面类的定义中定义total( )方法。
>puts “The total is #{order.total}”

注意find_all_unpaid( ) 和 total( )这两个方法之间的不同点。第一个方法不需要指定为某个实际的order。所以我们把它定义为类级别的,然后就可以通过类来调用它了。第二个方法针对某个具体的order,所以我们定义它为实例变量,然后就可以在指定的对象上调用它。

类的对象用实例变量来保存它们的状态,这些变量的名字全部以@开头,对实例的方法都是可以读取的。每个对象取得它征集的实例变量。
class Greeter
def initialize(name) @name = name
end
def say(phrase) puts ”#{phrase}, #{@name}”
end
end
g1 = Greeter.new(“Fred”)
g2 = Greeter.new(“Wilma”)
g1.say(“Hello”) #=> Hello, Fred
g2.say(“Hi”) #=> Hi, Wilma

实例变量对于外界来说是不能直接访问的,要使得它们可以被操作,只有写方法来读取。
class Greeter
def initialize(name)
@name = name
end
def name
@name
end
def name=(new_name) @name = new_name
end
end

g = Greeter.new(“Barney”)
puts g.name #=> Barney
g.name = “Betty”
puts g.name #=> Betty

Ruby提供便捷的方式来使用这些方法(这对那些讨厌写那些getters 和setters的人来说真是个好消息)。
class Greeter
attr_accessor :name # create reader and writer methods
attr_reader :greeting # create reader only
attr_writer :age # create writer only

私有和保护
一个类的实例方法默认是公共的,谁都可以调用它们,你可能想重写这些方法使得它们只对指定的对象开放。
class MyClass
def m1 # this method is public
end

protected
def m2 # this method is protected
end

private
def m3 # this method is private
end
end

这个private标识暗示限制条件,private方法只能在同一个实例内进行调用;Protected方法则可以在同以个实例中和同一个父类的其他子类可以调用。

A.5 模块
模块看上去和classes很像,它也可以拥有方法,常量,甚至其他的模块或者类定义;但是很类不同的是,你不能创建基于 模块的对象。
使用Modules有两个目的。第一,它们相当于namespace,使你定义的方法名不会和其他的地方定义的方法名字冲突。第二,使你可以在类之间共享函数阿(如果一个类混在模里),module的实例方法变成可用的,就像你在类中定义的一样。多个类可以混合在同一个module中,共享module的功能函数二不需要使用继承。你也可以混合多个module到一个类中。
Rails使用modules来实现helper的方法,它自动的混合 helper的方法到程序的视图模板,举个例子,如果你想写以个helper方法,使得你在视图层可以通过store控制器来调用它,你就可以在 app/helpers目录下的store_helper.rb中定义一下这个module。
module StoreHelper
def capitalize_words(string)
string.gsub(/\b\w/) { $&.upcase }
end
end

A.6 数组和散列
Ruby 的数组和散列都是按照索引来标识的。两者都可以存贮对象,按照key去访问。在arrays中,其key是整数;而在hashes中则支持韧任何对象为 key。数组和散列的存在都是增加新元素。对于数组来说,其访问更加容易;但是散列的弹性很大。数组和散列都可以存贮不同类型的对象,例如你可以在数组中包含整数,字符串,浮点数等等。
你可以在中括号中包含的元素来创建数组,给出一个数组对象,你可以在〔〕中包含来取得每个元素,就像下面这个例子表现的一样,Ruby数组的开始标识是0。
a = [ 1, ‘cat’, 3.14 ] # array with three elements
a0 # access the first element (1)
a2 = nil # set the third element

1. array now [ 1, ‘cat’, nil ]

你可能已经注意到了,我们在这里使用了以个比较特殊的值nil,在很多语言里,nil(null)的意思是:不是对象。在Ruby中,不是这个意思,nil是一个对象,和其他对象一样,它的意思是不表现上什么。
数组对象经常使用到的方法是<<,这个方法的意思是附加值给它的接收者。
ages = []
for person in @people
ages << person.age
end
Ruby有一个快捷的方式来创建数组,如下:
a = [ ‘ant’, ‘bee’, ‘cat’, ‘dog’, ‘elk’ ]

1. this is the same: #这两个是相同的
a = %w{ ant bee cat dog elk }

Ruby中,hash和数字相似,一个hash使用大括号而不是使用中括号。每对实体必须提供两个对象:一个是key,一个是值。例如,你可能想把乐器和它们的部件联系起来。
inst_section = {
:cello => ‘string’,
:clarinet => ‘woodwind’,
:drum => ‘percussion’,
:oboe => ‘woodwind’,
:trumpet => ‘brass’,
:violin => ‘string’
}
=> 符号左边的是key,相应的,其右边的就是其对应的值,一个实例中的key必须是惟一的,就像上面一样,你不能定义两个实体给:drum。key和值都可以是任意的,你可以使用数组中的值来作为散列的hash的值。在Rails中,散列常常使用符号作为key,许多rails的散列都是精心设计的,这样你在取值的时候不仅可以字符串,还可以使用符号来作为key。
和数组一样,散列也是用〔〕来索引值。
inst_section[:oboe] #=> ‘woodwind’
inst_section[:cello] #=> ‘string’
inst_section[:bassoon] #=> nil
最后一行表示,当饮用散列中一个不存在的key时,将返回nil,按照约定,在条件语句中,nil代表false。

散列和参数列表
在方法调用时,你可以传递hashes过去作为参数,但是只有当hash 是最后一个参数时,Ruby才允许你忽略括号。rails对这种方式很敏感,下面的代码段展示的就是把一个 两个元素的 hash值传给方法redirect_to( ),事实上,你可以忽略这样的情况,仅仅把它认为是Ruby的关键参数。
redirect_to :action => ‘show’, :id => product.id

A.7 流程控制

在Ruby语言中,基本的控制结构都有,例如IF语句、WHILE循环,JAVA,C和Perl等语言可能会因为少写一个括号而导致错误产生,Ruby则不一样,它使用end关键字来标识循环体的结束。

if count > 10
puts “Try again”
elsif tries == 3
puts “You lose”
else
puts “Enter a number”
end
Similarly, while statements are terminated with end.
while weight < 100 and num_pallets <= 30
pallet = next_pallet()
weight += pallet.weight
num_pallets += 1
end

如果if或者while的语句只有一句语句,则修改起来很是方便,只要把它写在if和while后面就可以了。例如:
puts “Danger, Will Robinson” if radiation > 3000

A.8 正则表达式
正则表达式是一种特殊的字符格式用来匹配字符串。在Ruby中,你可以按照这样的方式创建正则表达式,/pattern/ 或者 %r{pattern}。
例如,你可能想写一个正则表达式来匹配这样一个字符串:包含Perl或者Python,那就可以写出这样的表达式:/Perl|Python/。
前划线是正字表达式的限定符,表达式包含我们想匹配的两个字符串,并且用垂直线(|)分割。这个竖线的意思是:不是左边的,就是右边的。在我们说的这个例子中就是不是Perl就是Python。在模式中,你可以像在算法中使用括号一样来使用括号,所以你也可以重写前面的那个模式:/P(erl|ython) /。语言中使用=~来验证某个字符串是不是符合模式。
if line = /P(erl|ython)/
  puts “There seems to be a perturbation in the force”
end
你也可以在模式中申明字符重复,/ab+c/这个模式匹配的是一个字符串包含一个a,后面紧接着一个或者多个b,最后是一个c。如果把他修改成这样的,/abc/,就匹配一个a,零或者多个b,最后是一个c.
Ruby的正则表达式是个很深奥又复杂的主题,这小节的内容只是个皮毛,详细的资料请参考专业的书籍。

A.9 块和迭代

代码块是一堆放在括号或者do….end之间的代码,按照习惯,如果只有一行,则放在括号里;而如果有多行的话,则习惯放在do…end之间。
{ puts “Hello” } # this is a block

do          ###
  lub.enroll(person) # and so is this
  person.socialize  #
end          ###

只有在方法调用以后才可以出现代码块,把块的开始放在方法调用的结尾,例如,在下面的代码中,块包含 puts “Hi”和方法greet是相关的。
greet { puts “Hi” }
如果一个方法有自己的参数,则把这些参数放在块之前。
verbose_greet(“Dave”, “loyal customer”) { puts “Hi” }

一个方法可以使用yield语句来调用一次或者多次调用代码块。你可以认为yield是一个访问外部的一种调用。你可以通过给yield赋值的办法来给代码块传递参数。在代码块中,你用竖线(|)分割你需要接收的参数。

代码块在Ruby中大量存在,他们常常用来和迭代器进行关联,迭代器是一种可以把返回的值元素放在诸如数组等容器中的方法。
animals = %w( ant bee cat dog elk ) # create an array(创建一个数组)
animals.each {|animal| puts animal } # iterate over the contents(迭代)

每个整数N都有实现了一个times()方法,用他来调用N次这个块。
3.times { print “Ho! ” } #=> Ho! Ho! Ho!

A.10 异常
异常也是对象,(是Exception或者它子类的类)。raise方法产生一个异常,这样就会打断当前的程序流程。所以,Ruby反响搜索这个这个函数的调用者,并把这个异常挂起。
捕获异常的方法是把可能产生异常的代码放在begin和 end之间,并且使用关键字rescue进行捕获。
begin
content = load_blog_data(file_name)
rescue BlogDataNotFound
STDERR.puts “File #{file_name} not found”
rescue BlogDataFormatError
STDERR.puts “Invalid blog data in #{file_name}”
rescue Exception => exc
STDERR.puts “General error loading #{file_name}: #{exc.message}”
end

A.11 Marshaling对象
Ruby可以把一个对象转换成字节流,并把它存在应用程序外。这个过程叫做marshaling.这样保存的对象可以在以后被其它的程实例读取(或者其它的程序),并且把原来的进行备份。
当你使用marshaling的时候可能出现两个问题,第一,一些对象不能被保存,如果将要被保存的对象包括其它程序调用,方法体,IO实例等,或者单独的一个对象,或者当你保存一个匿名的类或者方法时,一个TypeError错误就会产生。
第二,当你载入一个marshaled对象时,Ruby需要知道这个对象的定义(或者包含的所有对象)

Rails 使用marshaling来存储session 数据,如果你想让Rails自动加载这个类,可能会因为某些东西没有定义而是的不能认识session,所以可以在你的controller使用 model来定义所有要加载的集合。这样就可以加载必须的类使得其能正才工作。

A.12 交互式 Ruby
irb是一个交互式的运行Ruby的工具。irb也就是一个Ruby的shell,在这里你可以使用命令行、编辑器、和作业控制。你在命令行中运行irb。一旦它开始运行,只需要敲入Ruby代码,irb会返回给你表达式的运算结果。
% irb
irb(main):001:0> def sum(n1, n2)
irb(main):002:1> n1 + n2
irb(main):003:1> end
=> nil
irb(main):004:0> sum(3, 4)
=> 7
irb(main):005:0> sum(“cat”, “dog”)
=> “catdog

你可以在Rails程序中使用irb,这样你就可以方便的对你写得方法进行校验,(这样可能会影响到数据库)。另外,你可以设置Rails的环境让他自动完成,这样比起手工实现scripts/console要好的多,具体的请参考239页。

A.13 Ruby 惯用法
Ruby是一门很重视惯用法的语言,在网上有很多这方面的的好资料,下面是一些:
? http://www.glue.umd.edu/billtj/ruby.html
? http://www.rubygarden.org/faq
? http://en.wikipedia.org/wiki/Ruby_programming_language
? http://www.zenspider.com/Languages/Ruby/QuickRef.html

下面列举几个在本书中使用到的惯用法。
empty! 和 empty? 方法
Ruby的方法名可以以一个感叹号(!)或者一个问号(?)来结束,使用感叹号的方法要修改接收者本身,或者具有破坏性,则用一个感叹号结尾,简单,直觉。问号的则表示返回的是真或者假。

a || b
   这个表达式的意思是计算a的值,如果a不是false或者nil,则返回a;相对的,则返回b的值。这个通过的方法常常是用来表示:如果第一个值不存在的话,则返回一个默认值。

a ||= b
   这个表示的是一种缩写方式,其意思是:a op= b 和 a = a op b是一样的意思,对于其它的运算符都是通用的,例如:
count += 1          # same as count = count + 1
price ×= discount      # price = price × discount
count ||= 0         # count = count || 0

所以, 当count不存在的时候count ||= 0 会返回一个0。

obj = self.new(这个惯用法是比较高级的,我们在这里略微介绍下)
通常,一个类的方法需要创建它本身的实例:
class Person < ActiveRecord::Base
 def self.for_dave
  Person.new(:name => ‘Dave’)
 end
end
上面的这个可以正常工作,返回一个Person对象。但是以后,有人可能会继承我们的这个类。
class Employee < Person
 # ..
end
dave = Employee.for_dave # returns a Person
这个for_dave( ) 方法会返回一个Person的对象,所以这就是使用Employee.for_dave返回的?使用self.new则会返回一个Employee的对象。

require File.dirname(FILE) + ’/../test_helper’
Ruby的require method加载一个外部的资源到我们的程序中。这个常常被用来加载我们承袭所需要的类库。通常,Ruby在LOAD_PATH下寻找需要加载的文件。
有时候我们想指定加载一些File,我们可以给出这个文件的绝对路径,但是问题是,我们不能确定我们的用户是不是按照我们自己的路径来安装程序的,也不晓得它们的实际路径。
但是一旦程序安装好了,它的路径也就确定了,我们可以使用变量 FILE来取得安装目录,然后再加上需要包含的目标文件路径,就是绝对的路径了。。

A.14 RDoc文档
RDoc 是针对Ruby源代码的闻到文档系统,就像JAVA中的JavaDoc,RDoc会为以大堆的代码生成HTML文档,它会自动的在代码中获取信息,但是和 JavaDoc不同的是,就算代码中没有包含一个注释,它也可以一份很不错的文档,所以写RDOC和写代码一样,并不痛苦,你可以在16章看到它的详细用法。
RDoc使用Ruby的文档库,具体依赖你的Ruby是怎么安装的,你可以使用ri命令来读取这个文档。
dave> ri String.capitalize
String#capitalize
  str.capitalize => new_str

  Returns a copy of str with the first character converted to
  uppercase and the remainder to lowercase.

"hello".capitalize #=> "Hello"
"HELLO".capitalize #=> "Hello"
"123ABC".capitalize #=> "123abc"

如果你是使用RubyGems安装的Rails,你可以运行gem_server,然后打开http://localhost:8808.文档API。
rake appdoc命令为Rails工程创建HTML文档,并把它保存在doc/app目录下。

========附录A完毕============
--------2006.11.13---------
评论
codemyth 2006-11-19
那本ruby的镐头书,<programing ruby>在ruby的help中就有chm版本的,好长阿
daoger 2006-11-16
不错!正在学习中。
iceskysl
搜索本博客
我的相册
6e4b1028-c838-4ae2-9808-316c7a60e315-thumb
111
共 1 张
最近加入圈子
存档
最新评论