3.4.5 Ruby

Ruby也是一门通用编程语言,由松本行弘(Yukihiro Matsumoto)发明,并在1996年达到了1.0版。它的主要特征包括开放类定义(open class)、混合器(mixin)和code block等。

对于Ruby语言的学习,我推荐OReilly出版的《The Ruby Programming Language》,其作者之一正是松本行弘。

Ruby在服务器端的编程基于Rack,它是Ruby的一个规范,定义了服务器跟应用程序交互的接口,跟Python的WSGI类似。同样地,这里“服务器”是指接受客户端(如浏览器)HTTP请求的程序,而“应用程序”是指(由你编写的)响应HTTP请求的程序。Ruby所有的Web编程框架都基于Rack,包括Rails在内。因此了解、掌握它十分必要1

一个Rack的“Hello,world”程序如下:

require 'rack'

app = proc do |env|
  ['200', {'Content-Type' => 'text/html'}, ['<p>Hello, Rack!</p>']]
end

Rack::Handler::WEBrick.run(app, :Port => 8090, :Host => '0.0.0.0')

其中,app就是我们的Rack应用程序。按照Rack规范:

  • Rack应用程序可以是任何具有“call”方法的对象。在这里它是一个proc。
  • 它带有一个参数,env,包含了与CGI环境变量类似的输入。
  • 它返回一个数组,包含三个元素,分别是HTTP状态代码,应答头(response header)以及消息主体(message body)。

要运行这个程序首先要安装rack(一个Ruby Gem,包含用于构建Rack程序的辅助工具,相当于Python的wsgiref包):

gem install rack

然后在命令行执行(假设程序保存在文件hello.rb中):

ruby hello.rb

在浏览器中访问http://localhost:8090/,即可看到结果。

与Python WSGI相同,Rack也支持中间件(middleware)。一个简单的例子如下(包含三个文件,文件名在首行注释):

# app.rb
class App
  def call(env)
    ['200', {'Content-Type' => 'text/html'}, ['<p>Hello, Rack!</p>']]
  end
end
# middleware.rb
class Middleware
  def initialize(app)
    @app = app
  end

  def call(env)
    status, headers, body = @app.call(env)
    body.each {|line| line.upcase! }
    return [status, headers, body]
  end
end
# config.ru
require './app'
require './middleware'

use Middleware
run App.new

我们已经说过“Rack应用程序可以是任何具有“call”方法的对象”。在这个例子中,它是一个App类的对象。

中间件也是一个Rack应用程序,但是它“含有”另一个Rack应用程序。Rack服务器调用中间件,传递给它一个env参数,并接受它返回的状态代码、应答头部和消息主体。这样,中间件可以检查、修改env,并把它传递给“内含”的Rack应用程序。它还可以检查、修改内含的Rack应用程序的返回结果,并返回给上级服务器(也可能是另一个中间件!)。在这个例子中,我们把内部App返回的消息主体字符都转化成大写。

虽然简单,但这是一个产品级的配置:我们使用了config.ru来组装我们的Rack应用程序和中间件。config.ru是Rack应用程序的标准配置:它指定了Rack应用程序(通过run)及中间件(通过use),把它们组装在一起,形成了一个完整的Rack应用。

要运行这个应用程序,在命令行上执行:

rackup -p 8090 -o 0.0.0.0

然后就可以在浏览器中通过http://localhost:8090/访问它。

关于Rack的更多信息,可参考:https://github.com/rack/rack

关于Ruby的Web编程框架,除了著名的Ruby on Rails,还有Sinatra(一个小型轻量级框架)等,它们都基于Rack。

1. 限于篇幅,这里对Rack只做了简单的介绍。若想进一步了解Rack,请看我的博文Ruby Rack及其应用(上)

results matching ""

    No results matching ""