Programming Ruby

The Pragmatic Programmer's Guide

上一章 < 目录 ^
下一章>

Ruby a和 Web



Ruby对互联网来说并不陌生,不只是因为你可以用它来写SMTP服务器,FTP服务器,或者web服务器,你还可以用它来完成通常的任务,比如CGI编程或者作为PHP的一个替代品。

编写 CGI 脚本

你可以用Ruby轻松的编写CGI脚本,为了让Ruby脚本产生HTML输出,你只需要这样做:

#!/usr/bin/env ruby
print "HTTP/1.0 200 OK\r\n"
print "Content-type: text/html\r\n\r\n"
print "<html><body>Hello World!</body></html>\r\n"

你可以用Ruby的正则表达式来解析输入字符串,查询环境变量,检查标记,填充模板,转义(escape )特殊字符,格式化HTML,然后输出。

或者,你可以用类CGI。

 

使用cgi.rb

类CGI主要来帮助你更方便的编写CGI脚本,使用它,你可以操纵表单(form),cookies和环境变量,维护有状态的session等等。关于它的文档在第497页,但是我们在这里要先看一下它大体的功能。

 

引用(Quoting

当处理URL和HTML的时候,我们必须注意对一些特殊字符的引用。比如,一个斜线 (``/'') 在URL里有特殊意义,如果它不是URL的路径名称的一部分,那么它必须被转义(escaped)。也就是说,它在URL中将被转换为 ``%2F'',而且反过来你要想使用它,必须把它再转换为斜线。空格和and符号("&")也是特殊字符。为了处理这样的情况,CGI提供了方法 CGI.escapeCGI.unescape

require 'cgi'
puts CGI.escape( "Nicholas Payton/Trumpet & Flugel Horn" )
结果:
Nicholas+Payton%2FTrumpet+%26+Flugel+Horn

类似的,你也许需要对HTML中的特殊字符进行转义(escape ):

require 'cgi'
puts CGI.escapeHTML( '<a href="/mp3">Click Here</a>' )
结果:
&lt;a href=&quot;/mp3&quot;&gt;Click Here&lt;/a&gt;

而且,你也可以只对HTML的一部分标记进行转义,而不是全部:

require 'cgi'
puts CGI.escapeElement('<hr><a href="/mp3">Click Here</a><br>','A')
produces:
<hr>&lt;a href=&quot;/mp3&quot;&gt;Click Here&lt;/a&gt;<br>

这里又有A标记被转义了,其它标记都没有变化。

每个这样的转义方法都有一个un-开头的版本,来恢复这个转义之前的字符串。

表单(Forms)

使用CGI类你有两种方法访问HTML传过来的参数。比如我们有一个URL/cgi-bin/lookup?player=Miles%20Davis&year=1958,你可以用CGI#[ ]方法直接访问player和year变量:

require 'cgi'
cgi = CGI.new
cgi['player'] ? ["Miles Davis"]
cgi['year'] ? ["1958"]

或者,你可以把所有参数放入一个Hash中,再从这个哈希查询参数。

require 'cgi'
cgi = CGI.new
h = cgi.params
h['player'] ? ["Miles Davis"]

创建窗体(Forms)和 HTML

CGI类包含了很多产生HTML的方法,每个HTML标记都有一个方法。为了使用这些方法,你首先要创建一个CGI对象,这可以通过CGI.new方法。这个方法可以接收一个参数表示HTML的级别。比如,我们这里用了html3。

为了使得这些标记嵌套方便,这些方法都接受一个block作为来处理内容,这个block返回一个字符串,这也是这个tag的内容。比如下面的例子,我们加入了一些换行是为了能适应屏幕,能使得它被很好的显示而已。

require "cgi"
cgi = CGI.new("html3")  # add HTML generation methods
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
      }
    }
  }
}
produces:
Content-Type: text/html
Content-Length: 302
 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 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 NAME="get_text" ROWS="10" COLS="70"></TEXTAREA>      
<BR><INPUT TYPE="submit"></FORM></BODY></HTML>
 

这段代码将产生一个HTML文档,主题(title)为“This Is a Test”,然后一个水平线,然后是一个header,一个测试的输入框,一个提交按钮。当接收到这个提交的页面,你会得到一个变量名为get_text,其值为你输入的文本。

Cookies

你可以通过使用cookies在客户机上存放各种有用的信息,你可以创建一个有名字的cookie,并且设定一个指定的值。为了将这个cookie发送到浏览器,你需要在CGI#out中设置cookie 头(header)。

require "cgi"
cookie = CGI::Cookie.new("rubyweb", "CustID=123", "Part=ABC");
cgi = CGI.new("html3")
cgi.out( "cookie" => [cookie] ){
  cgi.html{
    "\nHTML content here"
  }
}
produces:
Content-Type: text/html
Content-Length: 86
Set-Cookie: rubyweb=CustID%3D123&Part%3DABC; path=

 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML>       
HTML content here</HTML>
   

当用户下次访问这个页面时,你可以取得cookie CustIDPart,然后将它们显示在HTML输出中。

require "cgi"
cgi = CGI.new("html3")
cgi.out{
  cgi.html{
    cgi.pre{
      cookie = cgi.cookies["rubyweb"]
        "\nCookies are\n" + cookie.value.join("\n")
    }
  }
}
produces:
Content-Type: text/html
Content-Length: 111


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML><PRE>       
Cookies are       
CustID=123       
Part=ABC</PRE></HTML>
  

Sessions

cookies需要我们进行一些人为处理后才变得有用,我们真正需要的是一个session:web客户端的持久性的状态。session在Ruby中用 CGI::Session处理,它也使用了cookie,但是提供了一个高层次的接口。

 
require "cgi"        
require "cgi/session"
 
cgi = CGI.new("html3")        
sess = CGI::Session.new( cgi, "session_key" => "rubyweb",        
                          "session_id"  => "9650",        
                          "new_session" => true,        
                          "prefix" => "web-session.")        
sess["CustID"] = 123        
sess["Part"] = "ABC"
 
cgi.out{        
  cgi.html{        
    "\nHTML content here"        
  }        
}
    

这将给用户rubyweb发送一个cookie,它的值为9650。它也会在服务器的上建立一个文件$TMP/web-session.9650,以“键-值”(key-value)的方式保存着CustID和Part的值。

当这个用户又访问这个网站时,你需要一个参数指明session id。在这个例子里应该是rubyweb=9650,然后,你就可以取得所有和这个sesion相关的数据了。

require "cgi"
require "cgi/session"

cgi = CGI.new("html3")         
sess = CGI::Session.new( cgi, "session_key" => "rubyweb",         
                               "prefix" => "web-session.")         
cgi.out{         
  cgi.html{         
    "\nCustomer #{sess['CustID']} orders an #{sess['Part']}"         
  }         
}
 

将Ruby嵌入HTML

到目前为止我们已经看过了用Ruby创建HTML文件和显示它,下面我们来看看如何将Ruby嵌入HTML文档中。

有好几个包支持我们将Ruby嵌入其它文档中,特别是HTML页面,一般来说,这叫做eRuby,它的实现也有好几种,比如eruby和erb。下面的部分将介绍eruby。

将Ruby脚本嵌入HTML非常有用,我们不仅能像ASP,JSP,PHP一样使用它,而且还能发挥Ruby的力量。

使用eruby

eruby 简单来说就像一个过滤器,对于输入的文件中的普通HTML内容,它将不会做什么处理,而对下面的格式的代码进行特殊处理:

 

表达式 意义
<% ruby code %> 将定界符内的Ruby代码转换为它的结果输出
<%= ruby expression %> 输出这个表达式的值到HTML中
<%# ruby code %> 代码注释,这些内容将被忽略。对测试来说可能有用。

调用eruby的方法如下:

eruby [
            options
            ] [
            document
            ]

如果document忽略,则eruby将从标准输入读取。eruby的命令行参数如下:

 
Option Description
-d, --debug 设置$DEBUG 为 true.
-K kcode 指定不同的编码系统(coding system 参见137页)
-M mode 指定运行模式,为下面之一:
f 过滤模式
c CGI 模式(将错误打印为HTML, 设置$SAFE=1)
n NPH-CGI 模式(打印额外的头部为HTTP,设置$SAFE=1)
-n, --noheader 禁止CGI头部( header)输出
-v, --verbose 允许详细(verbose )模式
--version 打印出版本信息并退出程序

让我们来看一些简单的例子,假如我们给eruby运行时候的输入为下面的内容:

This text is <% a = 100; puts "#{a}% Live!" %>

eruby 将把<%和%>之间的Ruby代码换成它执行后的值然后显示出来。
This text is 100% Live!

使用 <%= 的形式,直接将后面的表达式的值输出,比如,我们的输入如下:

<%a = 100%>This text is almost <%=a%> degrees! Cool!

将=a替换为a的实际值,如下:
This text is almost 100 degrees! Cool!

当然,你也可以将Ruby嵌入更复杂的文档,比如HTML。

 

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<title>eruby example</title>
</head>
<body>
<h1>Enumeration</h1>
<ul>
<%(1..10).each do|i|%>
  <li>number <%=i%></li>
<%end%>
</ul>
<h1>Environment variables</h1>
<table>
<%ENV.keys.sort.each do |key|%>
  <tr>
    <th><%=key%></th><td><%=ENV[key]%></td>
  </tr>
<%end%>
</table>
</body>
</html>

在 Apache中安装eruby

你可以安装一个使用eRuby的Apache服务器来自动解析嵌入Ruby的文档,就像PHP那样。你可以将嵌入Ruby的文档命名为以`.rhtml'' 为后缀,然后设置web服务器来调用eruby来解析这些文件输出想要的HTML。

为了在Apache中使用eruby,需要以下几步。

 

就是如此,你可以在HTML文档中加入嵌入的Ruby脚本来动态地创建表单和内容。但也请你阅读一下从497页开始的CGI库。

提高性能

你可以使用Ruby为web编写CGI程序,但是,如同其它CGI程序一样,默认得设置下,对每个cgi页面的请求都将产生一个新的Ruby进程,这将占用服务器很多的资源,而且容易引起性能的下降。Apache web服务器通过使用可装载的模块来解决这个问题。

一般来说,这些模块都会动态的被载入,而且会成为Web服务器进程的一部分,也就是说不需要每次有CGI请求进来都产生一个Ruby解释器程序,Web服务器就充当了解释器的角色。

所以我们会用到mod_ruby,它会将Ruby解释器连接(links)到Web服务器,关于它的具体信息可以看它的发布程序附带的README文件。

一旦你安装配置好之后,你就可以像原来一样运行Ruby脚本,不同的是现在地速度应该是快很多了。


上一章< 目录^
下一章>

Extracted from the book "Programming Ruby - The Pragmatic Programmer's Guide"
Copyright © 2001 by Addison Wesley Longman, Inc. This material may be distributed only subject to the terms and conditions set forth in the Open Publication License, v1.0 or later (the latest version is presently available at http://www.opencontent.org/openpub/)).

Distribution of substantively modified versions of this document is prohibited without the explicit permission of the copyright holder.

Distribution of the work or derivative of the work in any standard (paper) book form is prohibited unless prior permission is obtained from the copyright holder.