在 Forge - Ruby 入门

作者:Reuven M. Lerner

大约十年前,当我在纽约工作时,我的朋友向我展示了一些让我震惊的东西——一个实际上在 Web 浏览器内部运行的程序,无需按提交按钮。它简洁、有趣且看起来像是一次重大的范式转变。我们都对这种新的“Java”语言及其小程序将对 Web 开发意味着什么感到兴奋。尽管我们不太清楚它会在哪里或如何结束,但在一段时间内我们只谈论这个。

在那之后的十年里,许多不同的技术被炒作为“下一个最好的东西”或“你需要用来制作更好的网站的工具”。 事实上,我们不断受到关于开发软件的更新、更好、更快和更便宜方法的轰炸。 其中一些承诺已经实现,但通常会伴随权衡。 例如,在 Zope 中开发 Web 应用程序确实非常容易——一旦你克服了学习曲线。 Web 服务很好,直到你开始处理跨不同平台的复杂数据结构。

你可以想象我当时的惊讶,当我开始看到另一种“最佳新方法”即将出现时——但这一个是由我尊敬的人吹捧的,他们通常不会这么快屈服于炒作。 我当然说的是“Ruby on Rails”,一个用于创建和部署 Web 应用程序的面向对象的系统。 几个月来,我一直在阅读 Rails 有多么精彩,以及它如何使 Web 开发变得非常简单。

我一直想尝试 Ruby 作为一种语言,而 Rails 的发展给了我一个这样做的机会。 本月,我们将初步了解 Ruby,研究使用基本 Ruby 语言和库创建 Web 应用程序的简单方法。 在我的下一篇文章中,我们将研究 Rails,看看它与其他更成熟的框架相比如何。

什么是 Ruby?

Ruby 是一种开源编程语言,最初由日本程序员松本行弘(Yukihiro Matsumoto),又名 Matz 开发。 Ruby 最初于 1995 年末发布,这使得它比许多人想象的要古老。 日本以外的人们花了一些时间才发现并使用 Ruby,部分原因是缺乏文档。 Dave Thomas 的书 Programming Ruby 第一版(参见在线资源)提供了对该语言的扎实介绍,以及其类库的参考指南,为其提供了所需的公关推动力。 “Pickaxe book”的第二版(众所周知)现在已经可用。

Ruby 被设计为一种“面向对象的脚本语言”,它确实感觉像是 Perl 和 Smalltalk 的混合体。 它假设你理解面向对象编程,并且可能不是初学者学习的好语言。 但是,如果你熟悉对象和 Perl,那么你可以很快学会用 Ruby 做很多事情。

这是一个简单的 “Hello, world” Ruby 程序

#!/usr/bin/env ruby

print "Hello, world\n"

第一行确保我们运行 Ruby 解释器,无论它可能在我们路径中的哪个位置。 正如你可能期望的那样,第二行打印"Hello, world"后跟一个换行符。 像 Python 而不像 Perl,Ruby 代码行的末尾不需要分号。

现在我们已经创建了一个简单的命令行程序,是时候创建一个等效的 CGI 程序了。 CGI 程序可以在所有类型的 Web 服务器之间移植。 尽管不是特别快或智能,但它们易于编写,并且是涉足语言的 Web 开发方面的好方法。

在 Ruby 的情况下,最简单的 CGI 程序将类似于上面的代码。 毕竟,CGI 规范告诉我们,写入标准输出的任何内容都会发送到用户的 Web 浏览器。 只要我们在文本之前发送 Content-type 标头,我们就可以毫不费力地将其制作为 CGI 程序

#!/usr/bin/env ruby

# HTTP response headers, including double newline
print "Content-type: text/plain\n\n"

# Contents
print "Hello, world\n"

果然,将上面的程序命名为 hello.rb,将其放在我的 Web 服务器的 cgi-bin 目录中,并将我的 Web 浏览器指向 https:///cgi-bin/hello.rb 产生了"Hello, world"浏览器中的消息。

使用 Ruby 库

包含的 Ruby 库中的 CGI 对象提供了理解 Web 功能的方法,从 HTML 格式化到 cookie 和参数。 例如,这是我们的“Hello, world”程序的新版本,它使用内置功能编写

#!/usr/bin/env ruby
# *-ruby-*-

require 'cgi'

# Create an instance of CGI, with HTML 4 output
cgi = CGI.new("html4")

# Send the following to the CGI object's output
cgi.out {
  cgi.html {

# Produce a header
cgi.head { cgi.title { "This is a title" }
} +

# Produce a body
cgi.body {
  cgi.h1 { "This is a headline" } +
    cgi.p { "Hello, world." }
}
  }
}

正如你所看到的,即使输出在很大程度上是相同的,代码现在看起来也大不相同。 我们所做的是从显式的 print 语句切换到在我们的 CGI 对象上调用的方法,并添加了标题和头条新闻。

当我们使用 CGI.new 创建 CGI 对象时,我们可以传递一个参数,指示我们希望拥有的 HTML 兼容级别。 除非你有充分的理由不这样做,否则瞄准最高级别的兼容性,即 HTML4,是一个好主意。

请注意输出是如何从 cgi.out 开始,作为一组代码块运行的,每个代码块都应返回一个文本字符串。 因此,cgi.h1 和 cgi.p 被组合在一起——使用 + 运算符,就像在 Python 或 Java 中一样——并被馈送到 cgi.body。 cgi.head 和 cgi.body 也被连接在一起并馈送到 cgi.html。 这种层次结构模仿最终的文档输出格式,使其易于理解和使用此功能。

当 CGI 程序处理来自用户的参数时,它们会变得更有趣。 我们可以使用 CGI.params 方法获取参数

#!/usr/bin/env ruby
# *-ruby-*-

require 'cgi'

# Create an instance of CGI
cgi = CGI.new("html4")

# Get our first name
firstname = cgi.params['firstname']
if (firstname.empty?)
  firstname = '(No firstname)'
end

# Get our last name
lastname = cgi.params['lastname']
if (lastname.empty?)
  lastname = '(No lastname)'
end

# Send some output to the end user
cgi.out {

  cgi.html {

# Produce a header
cgi.head { cgi.title { "This is a title" }
} +

# Produce a body
cgi.body {
  cgi.h1 { "This is a headline" } +
    cgi.p { "Hello, #{firstname} #{lastname}." }
}
  }
}

此代码与其前身之间有两个基本区别。 首先,我们现在定义了两个变量 firstname 和 lastname,然后我们将它们打印给用户。 这些变量是根据传递给程序的参数值定义的,可以通过 GET 请求中的 URL 或 POST 请求的正文传递。 我们在 firstname 和 lastname 上都使用 empty? 方法来检查它们是否为空,如果为空,则为它们分配一个默认值。 最后,我们在双引号字符串中使用 Ruby 的 #{expression} 语法来显示用户的名字和姓氏。

WEBrick

以上是我们可能从简单的 CGI 程序中期望得到的——易于编写、易于使用且执行速度慢。 如果我们的程序变得更加复杂,我们必须处理我们可能更愿意忽略的新问题,例如个性化。

幸运的是,Ruby 自带了自己的 HTTP 服务器,称为 WEBrick,它在某些方面类似于 AOLserver 或 mod_perl。 如果你对更直接的 mod_perl 等效物感兴趣,还有 mod_ruby,它在 Apache 下运行。 要在端口 8000 上启动基本 HTTP 服务器,查看与 Apache 相同的静态文档,请使用以下代码

#!/usr/bin/env ruby
# *-ruby-*-

require 'webrick'
include WEBrick

# Create an HTTP server
s = HTTPServer.new(
  :Port            => 8000,
  :DocumentRoot    => "/usr/local/apache/htdocs/"
)

# When the server gets a control-C, kill it
trap("INT"){ s.shutdown }

# Start the server
s.start

这里有几点需要注意。 首先,没有太多代码。 你指示 WEBrick 应侦听的端口,告诉它文件位于何处,然后启动它。

在我们启动服务器之前,我们必须确保可以轻松停止它。 为此,我们调用 trap,指示我们想要捕获 SIGINT(即 Ctrl-C),并且在收到该信号时应调用 s.shutdown。

如果你将上面的程序放在名为 server.rb 的文件中并执行它,你应该有一个功能齐全的 HTTP 服务器在你的系统上运行。 创建 Web 服务器从未如此简单。

Ruby Servlets

当然,没有人会为了速度或提供静态文档而运行 WEBrick 而不是 Apache。 相反,当你想创建自定义行为时,WEBrick 会大放异彩。 在精神和术语上,WEBrick servlet 和 Java servlet 之间有相当多的重叠。 基本思想是相同的:定义一个新类,然后将该类的实例附加到特定的 URL。 例如,如果我们想创建一个 servlet 来打印一天中的时间,我们可以创建以下内容

#!/sw/bin/ruby
require 'webrick'
include WEBrick

# ---------------------------------------------
# Define a new class
class CurrentTimeServlet
  < WEBrick::HTTPServlet::AbstractServlet

  def do_GET(request, response)
    response['Content-Type'] = 'text/plain'
    response.status = 200
    response.body = Time.now.to_s + "\n"
  end
end

# ----------------------------------------------
# Create an HTTP server
s = HTTPServer.new(
  :Port            => 8000,
  :DocumentRoot    => "/usr/local/apache/htdocs/"
)

s.mount("/time", CurrentTimeServlet)

# When the server gets a control-C, kill it
trap("INT"){ s.shutdown }

# Start the server
s.start

我们的一个文件包含 CurrentTimeServlet 的类定义和启动 WEBrick 的命令。 这不是创建 servlet 的最优雅风格,你通常希望将每个 servlet 放在自己的文件中。 也就是说,Ruby 使在任何可能最适合的地方定义或重新定义类和方法变得容易和方便。 这是 Ruby 中让我想起 Perl 的功能之一:该语言在编写代码时为你提供了很大的灵活性,但希望你足够负责以避免搞砸。

我们将我们的 servlet CurrentTimeServlet 定义为 WEBrick::HTTPServlet::AbstractServlet 的子类,使其成为一个简单的 servlet。 然后,我们定义 do_GET 方法以及 do_POST 方法(如果你愿意),它会获取请求和响应对象。 如果你编写过 Java servlet,那么这对你来说应该很熟悉。 我们设置响应的内容类型、响应的状态代码 (200),甚至响应的正文,只需几行简单的代码。 就是这样; 我们的 servlet 已经定义好并准备就绪。 剩下要做的就是将 servlet 连接到 URL

s.mount("/time", CurrentTimeServlet)

如果需要,我们可以在初始化 servlet 时将参数传递给它。 s.mount 的前两个参数之后的任何内容都会被发送

s.mount("/time", CurrentTimeServlet, 'a parameter')
结论

我们可以在如此少的代码行中完成这么多工作,这很令人惊奇吗? 也许——尽管类似的功能当然也存在于其他语言中。 例如,Perl 程序员可以从 CPAN 下载 HTTP::Server::Simple 并执行许多相同的操作。 如果我真的有兴趣修改 HTTP 服务器的行为以执行有趣的事情,我可能会首先考虑使用 mod_perl 或 AOLserver,因为考虑到性能和灵活性。

也就是说,WEBrick 非常容易运行,并且易于创建基于 HTTP 的自定义行为。 例如,我可以想象使用它来处理 Web 服务,因为 Ruby 带来的灵活性,或者测试用 Rails 编写的应用程序。

而且,尽管人们正在使用 Ruby 和 WEBrick 进行普通的 Web 开发,但大多数兴奋点似乎都在特定的 Rails 框架上,而不是 Ruby 或 WEBrick 本身。 在我的下一篇文章中,我们将开始探索 Rails——如何安装它,如何使用它开发应用程序,以及它与其他开源应用程序框架相比如何。

本文的资源: /article/8397

Reuven M. Lerner,一位长期的 Web/数据库顾问和开发人员,现在是西北大学学习科学专业的 graduate student(研究生)。 他的 Weblog 在 altneuland.lerner.co.il,你可以通过 reuven@lerner.co.il 联系他。

加载 Disqus 评论