添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
< 不要删除 此文本,因为它是在浏览器中运行时生成的“主要”标题列表的占位符>

本教程介绍如何使用 Oracle 安装 Ruby/Rails,以及如何开发高效且功能强大的 Ruby/Rails 应用程序。您将获得为企业创建强大的 Ruby/Rails 应用程序所需的知识。

大约 1 个小时

Ruby on Rails 是一个免费的 Web 应用程序框架,旨在使我们能够更加快捷地创建数据库驱动的 Web 站点,并从一开始就提供主要代码框架(结构)。Ruby on Rails 通常缩写为 Rails RoR ,它是一个用 Ruby 编程语言编写的开源项目,使用 Rails 框架的应用程序是用模型-视图-控制器设计模式开发的。

RoR 在 J2EE 和 PHP 编程人员中备受推崇。当您了解 RoR 的优势后,它对 J2EE 和 PHP 编程人员的吸引力具有重要作用。首先,它使用任何自视甚高的设计模式都很钦佩的严格的模型-视图-控制器体系结构 — 这可以解释它为什么吸引了大量 J2EE 开发人员。其次,使用 Rails 可以轻松地构建基本系统 — 这对 PHP 开发人员具有吸引力。

RoR 为 MVC 提供了令人满意的解决方案。模型不仅仅是数据;它执行适用于该数据的所有业务规则。模型既可充当网关守卫又可充当数据存储。视图通常基于模型中的数据生成一个用户界面。尽管视图可能会通过各种方式向用户显示输入的数据,但视图本身从不处理传入的数据。数据显示之后,视图的工作就完成了。控制器接收来自外界的事件(通常是用户输入),与模型进行交互,并向用户显示相应的视图。

RoR 将 ActiveRecord 作为对象关系映射 (ORM) 层提供,以便连接数据库和操作数据。ActiveRecord 严格遵循标准的 ORM 模型:表映射到类,行映射到对象,列映射到对象属性。

创建 Rails 应用程序时,将在根目录下生成以下目录和文件:app、components、config、db、doc、lib、log、public、Rakefile、README、script、test、tmp 和 vendor。

Ruby 是一种动态的开源编程语言,重点关注简单性和工作效率。其简洁的语法易于阅读、便于编写。要了解有关 Ruby 语言的更多信息,参见 附录:Ruby 入门

要获得更多信息,您可以访问 Ruby on Rails OTN 论坛。

您将在本节中使用以下命令:

OCI8.new (userid, password, dbname = nil, privilege = nil):

通过 userid 和 password 连接到 Oracle。dbname 是 Net8 的连接字符串。如果您需要 DBA 权限,则将 privilege 设置为 :SYSDBA 或 :SYSOPER。

OCI8#logoff

断开与 Oracle 的连接。将回滚未提交的事务。

OCIError 包含 Oracle 的错误代码的异常类。您可以通过 OCIError#message 获得错误消息,通过 OCIError#code 获得错误代码。

创建 ruby 脚本过程中的第一个任务是创建到数据库的连接。执行以下步骤:

如果连接成功,您会看到上面的消息;如果连接失败,您将看到一条错误消息。

connect.rb 文件的内容如下:

# connect.rb: Create connections to Oracle
require 'config.rb'
begin
# login as normal user
conn1 = OCI8.new(DB_USER, DB_PASSWORD, DB_SERVER)
puts "login as normal user succeeded."
conn1.logoff
puts "logoff succeeded."
puts
# login as DBA
conn2 = OCI8.new('sys', 'oracle', DB_SERVER, :SYSDBA)
puts "login with DBA privilege succeeded."
conn2.logoff
puts "logoff succeeded."
rescue OCIError
# display the error message
puts "OCIError occured!"
puts "Code: " + $!.code.to_s
puts "Desc: " + $!.message
puts '-'*80

本教程的其余脚本中将包括以下命令:

require 'config.rb'

#create connection
conn = OCI8.new(DB_USER, DB_PASSWORD, DB_SERVER)

# operations on conn goes here
#log out
conn.logoff

fetch.rb 文件的内容如下:

#fetch.rb: Fetch data from database
require 'config.rb'
# Create a connection to Oracle
conn = OCI8.new(DB_USER, DB_PASSWORD, DB_SERVER)
# parse and exec the statement
cursor = conn.parse("select * from regions")
cursor.exec
# output column names
puts cursor.getColNames.join(",")
# output rows
while r = cursor.fetch
puts r.join(",")
end
# close the cursor and logoff
cursor.close
conn.logoff
puts '-'*80

还可以通过另一种方式编写更针对于 ruby 的相同的代码。在终端窗口中,通过执行以下命令来执行 fetch_r.rb 脚本:

ruby fetch_r.rb
The output is shown in the screenshot. 

fetch_r.rb 文件的内容如下:

# fetch_r.rb: Fetch in a more ruby-like way
require 'config.rb'
# Create a connection to Oracle
conn = OCI8.new(DB_USER, DB_PASSWORD, DB_SERVER)
# Fetch and display the rows in a block
nrow = conn.exec("select * from regions") do |r|
puts r.join(",")
end
# Display count of rows
puts ' '*30 + nrow.to_s + " rows were fetched."
conn.logoff
puts '-'*80

在获取许多行时,预先获取有助于改善性能。在终端窗口中,通过执行以下命令来执行 prefetch.rb 脚本:

ruby prefetch.rb
The output is shown in the screenshot. 

prefetch.rb 文件的内容如下:

# prefetch.rb: Prefetch data
require 'config.rb'
SELECT_STATEMENT = "select a.first_name, a.last_name, b.first_name, b.last_name, a.salary - b.salary salarydiff from employees a, employees b where b.id > a.id"
# Create a connection to Oracle
conn = OCI8.new(DB_USER, DB_PASSWORD, DB_SERVER)
# Execute statement with different prefetch sizes
[nil,100].each do |prefetchrows|
cursor = conn.parse(SELECT_STATEMENT)
if prefetchrows
cursor.prefetch_rows = prefetchrows
else
prefetchrows = "default"
end
puts "Executing with prefetch rows = #{prefetchrows}"
time1 = Time.now
cursor.exec
while r = cursor.fetch()
# puts r.join(",")
end
# Display count of rows
puts ' '*30 + cursor.row_count.to_s + " rows were fetched."
time2 = Time.now
puts "Time cost: " + (time2-time1).to_s
puts
conn.logoff
puts '-'*80
OCI8::Cursor#bind_param(key, val, type = nil, length = nil)

显式绑定变量。当 key 为数字时,它通过位置绑定(从 1 开始)。当 key 为字符串时,它通过占位符名称绑定。

OCI8#exec (sql, *bindvars) or OCI8::Cursor#exec(*bindvars)

也可以通过 bindvars 绑定变量。

OCI8::Cursor#[key] 获取/设置绑定变量的值。

要在本示例中使用绑定变量,执行以下步骤。

bind.rb 文件的内容如下:

# bind.rb: How to bind variables
require 'config.rb'
# Create a connection to Oracle
conn = OCI8.new(DB_USER, DB_PASSWORD, DB_SERVER)
# Prepare the data, also display what's hard-coded statement
conn.exec("DELETE FROM test_bind")
conn.exec("INSERT INTO test_bind VALUES(1, 'Unknown')")
conn.exec("INSERT INTO test_bind VALUES(2, 'Unknown')")
conn.exec("INSERT INTO test_bind VALUES(3, 'Unknown')")
# Now update the data using bind variables.
cursor = conn.parse("UPDATE test_bind SET name = :name WHERE id = :id")
cursor.bind_param(1,nil,String,100) # Or: cursor.bind_param(1, ' '*100)
cursor.bind_param(2,Fixnum) # Or: cursor.bind_param(2, id)
id = 1
['China', 'Korea', 'Japan'].each { |country|
cursor[1] = country
cursor[2] = id
cursor.exec
id = id + 1
}
# Fetch back the updated data
conn.exec("SELECT * FROM test_bind").fetch do |row|
puts row.join(',')
end
conn.logoff
puts '-'*80

要使用绑定变量测试性能改善,通过执行以下命令来执行 bind_perf_test.rb 脚本:

ruby bind_perf_test.rb
The output is shown in the screenshot. 

bind_perf_test.rb 文件的内容如下:

# bind_perf_test.rb: Performance test for binding variables
require 'config.rb'
# Create a connection to Oracle
conn = OCI8.new(DB_USER, DB_PASSWORD, DB_SERVER)
# Not using bind varables.
puts "Fetching result, not using bind variables:"
time1 = Time.now
(100..200).each { |id|
cursor = conn.parse("SELECT first_name FROM employees WHERE id = #{id}")
cursor.exec
#puts id.to_s + " --> " + cursor.fetch[0]
}
time2 = Time.now
puts "Time cost: " + (time2-time1).to_s
puts
# Using bind varables.
puts "Fetching result, using bind variables:"
time1 = Time.now
cursor = conn.parse("SELECT first_name FROM employees WHERE id = :id")
cursor.bind_param(1, Fixnum)
(100..200).each { |id|
cursor[1] = id
cursor.exec
#puts id.to_s + " --> " + cursor.fetch[0]
}
time2 = Time.now
puts "Time cost: " + (time2-time1).to_s
# End of the test
conn.logoff
puts '-'*80

定义数据类型

您将在本节中使用以下命令:

OCI8::Cursor#define(pos, type, length = nil)

在分析和执行过程中使用该方法。pos 从 1 开始。当 type 为 String 时使用 length。

您可以显式指明获取的值的数据类型。要在本示例中定义数据类型,执行以下步骤。

# Prepare the data conn.exec("DELETE FROM test_define"); conn.exec("INSERT INTO test_define VALUES(1,'Scott Tiger', SYSDATE, SYSTIMESTAMP)") # Define to fetch Date and Time cursor = conn.parse("SELECT name,birthdate,lastvisit FROM test_define WHERE id = :id") cursor.bind_param(1, 1) cursor.define(1, String, 100) cursor.define(2, Date) cursor.define(3, Time) cursor.exec while r = cursor.fetch puts r.join("\n") # Define to fetch Date and Time as String #conn.exec("ALTER SESSION SET nls_territory='TAIWAN' nls_language='TRADITIONAL CHINESE'") cursor = conn.parse("SELECT name,birthdate,lastvisit FROM test_define WHERE id = :id") cursor.bind_param(1, 1) cursor.define(1, String, 100) cursor.define(2, String, 100) cursor.define(3, String, 100) cursor.exec while r = cursor.fetch puts r.join("\n") conn.logoff puts '-'*80 OCI8#autocommit

获取/设置自动提交模式的状态。默认值是 false(记住:注销时不回滚未提交的事务)。如果为 true,每次执行 insert/update/delete 语句时都自动提交事务。

OCI8#commit() 提交事务。 OCI8#rollback() 回滚事务。

要了解如何管理事务,执行以下步骤。

在终端窗口中,通过执行以下命令来执行 transaction.rb 脚本:

ruby transaction.rb

该脚本使用 conn1 连接更新一行。在 Oracle 中,新数据在提交前仅在原始数据库会话中可见。其输出显示在屏幕截图中。

在这种情况下,conn2 连接不知道 conn1 的未提交事务发生了什么。

transaction.rb 文件的内容如下:

# transaction.rb: How to use transactions
require 'config.rb'
# Create connections to Oracle
conn1 = OCI8.new(DB_USER, DB_PASSWORD, DB_SERVER)
conn2 = OCI8.new(DB_USER, DB_PASSWORD, DB_SERVER)
conn1.exec("DELETE FROM test_transaction")
conn1.exec("INSERT INTO test_transaction VALUES(1, 'old value')")
conn1.commit
#conn1.autocommit = true
puts "OCI8.autocommit = " + conn1.autocommit.to_s
puts "conn1 updated the name to 'something new'";
conn1.exec("UPDATE test_transaction SET name = 'something new' WHERE id = 1");
#conn1.commit
puts "conn2 got the name as '" +
conn2.exec('SELECT name FROM test_transaction WHERE id = 1').fetch[0] + "'"
conn1.logoff
conn2.logoff
puts '-'*80

更新 transaction.rb 脚本,取消对 conn1.autocommit = true 的注释,然后保存文件。

使用以下命令再次执行脚本:

ruby transaction.rb

现在,conn2 知道哪些内容是新增的。其输出显示在屏幕截图中。

单独提交每一行会额外增加服务器的负载。您可以比较单独提交每行与在事务结束后提交之间的性能差异。要测试差别,使用以下命令执行 trans_perf_test.rb 脚本:

ruby trans_perf_test.rb

注意,第二个数字比第一个数字小。其输出显示在屏幕截图中。

trans_perf_test.rb 文件的内容如下:

# trans_perf_test.rb: Performance test for transactions
require 'config.rb'
# Create a connection to Oracle
conn = OCI8.new(DB_USER, DB_PASSWORD, DB_SERVER)
conn.exec("DELETE FROM test_transaction")
conn.commit
# Commit on each time
conn.autocommit = true
time1 = Time.now
300.times{
conn.exec("INSERT INTO test_transaction VALUES(1, 'something')")
}
time2 = Time.now
# Commit on the last step
conn.autocommit = false # It's the default
time3 = Time.now
300.times{
conn.exec("INSERT INTO test_transaction VALUES(1, 'something')")
}
conn.commit
time4 = Time.now
# Output the comparation
puts "Time cost of each-time commit(sec): " + (time2-time1).to_s
puts "Time cost of one-time commit(sec) : " + (time4-time3).to_s
conn.logoff
puts '-'*80

使用 PL/SQL

PL/SQL 是 Oracle 对 SQL 的过程语言扩展。PL/SQL 存储过程和函数存储在数据库中,因此其访问速度非常快。使用 PL/SQL 存储过程允许所有数据库应用程序重用逻辑,无论应用程序以何种方式访问数据库。许多与数据相关的操作在 PL/SQL 中的执行速度比将数据提取到一个程序中(例如,Ruby)然后再进行处理的速度快。

您将在本节中使用以下命令:

DBMS_UTILITY 和 DBMS_OUTPUT

Oracle 存储程序包。TO_CHAR 是一个内置函数。

要在 Ruby 脚本中调用 PL/SQL 过程和函数,执行以下步骤。

plsql.rb 文件的内容如下:

# plsql.rb: Call PL/SQL procedures and functions
require 'config.rb'
# Create a connection to Oracle
conn = OCI8.new(DB_USER, DB_PASSWORD, DB_SERVER)
puts 'Get version information from Oracle:'
cursor = conn.parse("BEGIN DBMS_UTILITY.db_version(:ver, :comp); END;")
cursor.exec(' '*50,' '*50)
puts "Oracle DB Version: " + cursor[1]
puts "Oracle DB Compatibility: " + cursor[2]
puts
puts 'Call TO_CHAR function:'
cursor = conn.parse("BEGIN :str := TO_CHAR(:num, 'FM0999'); END;")
cursor.exec('ABCD', 123)
puts "TO_CHAR input: " + cursor[2].to_s
puts "TO_CHAR output: " + cursor[1]
puts
puts 'Get DBMS_OUTPUT:'
conn.exec("BEGIN DBMS_OUTPUT.ENABLE(NULL); END;")
conn.exec("BEGIN DBMS_OUTPUT.put_line('Hello world!'); END;")
conn.exec("BEGIN DBMS_OUTPUT.put_line('Can you see me?'); END;")
cursor = conn.parse("BEGIN DBMS_OUTPUT.get_line(:line, :status); END;")
cursor.bind_param(':line', nil, String, 255)
cursor.bind_param(':status',Fixnum)
while true
cursor.exec
break if cursor[':status'] == 1
puts cursor[':line']
end
puts '-'*80

使用 LOB:存储/检索图像

Oracle 字符大对象 (CLOB) 和二进制大对象 (BLOB) 列(以及 PL/SQL 变量)可以包含大容量 (GB) 的字符和二进制数据。您将在本节中使用以下命令:

OCI8::BLOB#available

检查 BLOB 是否可用。要使用 BLOB,您首先需要插入 EMPTY_BLOB()。

OCI8::BLOB#read(size = nil) 从 BLOB 读取最大的字节大小,如果大小省略,则读取到文件结束处。 OCI8::BLOB#write(string) 将给定的字符串写入到 BLOB。

要创建一个小型应用程序以将图像加载并显示到数据库,执行以下步骤。

脚本文件所在的文件夹中有一个 PNG 文件。创建一个名为 download 的文件夹。在终端窗口中,通过执行以下命令来执行 blob.rb 脚本:

mkdir download
ruby blob.rb

其输出显示在屏幕截图中。

blob.rb 文件的内容如下:

#blob.rb: Save and Load BLOB
require 'config.rb'
# Create a connection to Oracle
conn = OCI8.new(DB_USER, DB_PASSWORD, DB_SERVER)
conn.exec("DELETE FROM test_blob")
# Must insert a EMPTY_BLOB before save real data
cursor = conn.parse("INSERT INTO test_blob VALUES(:name, EMPTY_BLOB())")
Dir["*.png"].each do |fname|
cursor.exec(fname)
end
# Save BLOB into Oracle
conn.exec("SELECT name,image FROM test_blob") do |name, image|
puts "uploading file: " + name
File.open(name, 'r') do |f|
image.write(f.read)
image.size = f.pos
end
end
# Load BLOB from Oracle
conn.exec("SELECT name,image FROM test_blob") do |name, image|
puts "downloading file: " + name
File.open("download/"+name, 'w') do |f|
f.write(image.read)
end
end
# End
conn.logoff
puts '-'*80

xml.rb 文件的内容如下:

# xml.rb: Generate a xml document based on relational data
require 'config.rb'
# Create a connection to Oracle
conn = OCI8.new(DB_USER, DB_PASSWORD, DB_SERVER)
sql = "SELECT DBMS_XMLGEN.getxml('
SELECT dept.name,dept.id,
CURSOR(
SELECT emp.first_name,emp.last_name,emp.phone_number
FROM employees emp
WHERE emp.department_id=dept.id
) AS employees
FROM departments dept
WHERE dept.id in (20,110)'
) AS xml
FROM dual"
# Fetch as CLOB
puts conn.exec(sql).fetch[0].read
conn.logoff
puts '-'*80

使用 ActiveRecord

ActiveRecord 连接业务对象和数据库表以创建持久的域模型,其中的逻辑和数据位于一个包装中。它是对象关系映射 (ORM) 设计模式的一个实现。要在本示例中使用 ActiveRecord,执行以下步骤。

activerecord.rb 文件的内容如下:

# activerecord.rb: Using ActiveRecord, the ORM module
require 'config.rb'
require 'rubygems'
require 'active_record'
# Establish a connection to Oracle
ActiveRecord::Base.establish_connection(
:adapter => "oracle_enhanced",
:database => DB_SERVER,
:username => DB_USER,
:password => DB_PASSWORD )
# Define the Classes, they are mapped to table countries and regions
class Country < ActiveRecord::Base
belongs_to :region
end
class Region < ActiveRecord::Base
has_many :countries
end
# Enjoy the automatic Object-Relation Mapping
cty = Country.find('CN')
puts 'CN refers to [' + cty.name + ']'
puts '[' + cty.name + '] is in [' + cty.region.name + ']'
puts
rgn = Region.find(cty.region.id)
puts 'Countries in the same region with [' + cty.name + ']'
rgn.countries.each { |country| puts ' - ' + country.name }
puts '-'*80

使用迁移和结构构建 Ruby on Rails 应用程序

在本教程的其余部分,您将使用以下术语:

一个用于构建其他 Ruby 程序的 Ruby 程序。在 Rails 中,Rake 用于执行一系列任务,如 db:migrate、db:schema:dump、db:schema:load、db:test:prepare 和 db:test:purge。它在作用域和目的方面与常见的 Linux 设计工具类似。 一个 Ruby 类,代表您应用程序中的一个重要对象,通过 ActiveRecord 的 ORM 链接到您的数据库表。 开发人员可以在 Ruby(而非数据描述语言 (DDL))中创建、修改和删除其数据库对象。 通过创建、编辑、查看和删除 ActiveRecord 类反映的条目,为您的数据提供一个简单接口。结构生成后,它将包含控制器文件(确定您应用程序的用户最终转向哪些页面)和视图文件(呈现应用程序用户将看到的页面)。

要使用迁移和结构构建一个 Rails 应用程序,执行以下步骤。

您可以查看该应用程序。打开一个浏览器窗口,输入以下 URL。单击 New Comic 创建一条记录,以确保该应用程序正常运行。

http://localhost:3000/comics

输入 comic 信息,然后单击 Create

comic 创建成功。单击 Back

用 Ruby on Rails 建立数据关系模型并构建应用程序

教程这一节将有助于您了解如何在 Rails 中建立各种数据关系的模型并基于这些关系构建应用程序。为此,您将创建一个应用程序,该程序维护一个 articles 和 authors 数据库。

创建作者和文章

首先您将创建一个应用程序,以便通过该程序输入作者和文章。此外,您还将更改表单以便输入某篇文章时可显示含有作者的弹出列表并在 Listing 和 Show 页面中显示文章作者。该应用程序当前在表间是一对一的关系。执行以下步骤:

http://localhost:3000/articles

在 Title 处输入一个标题,对 Author 指定 Julia Child ,输入任何摘要信息。然后单击 Create

该文章创建成功。

请注意,这里的 Author 为 0。这是因为 author_id 的值无效,结果是“Julia Child”变成了 0。在下一节中,您将改进 new 和 edit 表单,以便能够通过一个弹出列表按姓名选择作者(该弹出列表显示姓名但在代码中返回 author_id)。

改进 Articles 表单和列表

您将对生成的表单进行改进,从而在创建文章时对 Author 域生成一个弹出列表。另外,您还将修改 listings 和 show 页面以便这些页面能正确显示作者。执行以下步骤:

<%= f.text_field :author_id %>
<%= f.collection_select(:author_id, Author.all, :id, :name) %>

该文件现在应如下所示。

<%= f.text_field :author_id %>
<%= f.collection_select(:author_id, Author.all, :id, :name) %>

该文件现在应如下所示。

create_table :articles_authors, :id => false do |t|     t.integer :author_id     t.integer :article_id     t.timestamps def self.down   drop_table :articles_authors

既然现在是多对多的关系,Article 模型就具有名为 authors author_ids 的方法而不是名为 author author_id 的方法。要纠正这个错误,需要在 new.html.erb 文件中创建和编辑弹出列表并显示您前面创建的用于处理多位作者的域。在终端窗口中输入以下命令:

gedit app/views/articles/new.html.erb
<%= f.collection_select(:author_id, Author.all, :id, :name) %>
<%= f.collection_select(:author_ids, Author.all, :id, :name, {},
                        {:multiple => :multiple} ) %>

该文件现在应如下所示。

<%= f.collection_select(:author_id, Author.all, :id, :name) %>
<%= f.collection_select(:author_ids, Author.all, :id, :name, {},
                        {:multiple => :multiple} ) %>

该文件现在应如下所示。

<td><%=h article.author.name %></td>
<td><%=h article.authors.map {|auth| auth.name}.join(", ") %></td>

该文件现在应如下所示。

再次输入以下 URL。注意列表中没有显示任何作者。这是因为您切换了表达式。您可能已纠正了这个问题,即已使用一个迁移将 articles 表中的数据引入到了 articles_authors 表中。但这里我们使用应用程序将作者添加回来。选择 Boning a Duck 标题的 Edit 链接。

http://localhost:3000/articles

对 Author 选择 Julia Child ,然后单击 Update 。注意这里的选择列表允许您选择多位作者而不是只选一位。

该文章更新成功。单击 Back

选择 The Importance of Butter 标题的 Edit 链接。

按下 Control 键的同时选择这两位作者,单击 Update

该文章更新成功。单击 Back

现在该文章显示不止一位作者。

结合使用 AJAX 和 Ruby on Rails

使用 AJAX 是为了让浏览器端更智能,使其表现得几乎就像“常规的”应用程序。您的页面可以是交互式的,并且可从服务器获取更新而不必刷新整个窗口。

例如,您可能会有一些文章,它们在开始时是正在进行的工作,随后您完成了它们。您希望总是能够看到未完成文章的列表。将一篇文章标记为已完成后,您希望更新该列表而不必刷新整个页面。


但在使用 AJAX 之前,开始时使用一个非 AJAX 的“unfinished”列表是最容易的。您需要更新数据库表以明确一篇文章是否已完成。文章开始时是未完成状态,将可以使用一个按钮来将它们标记为已完成。为了便于访问,您的页面布局中将有一个列表来显示所有未完成的文章。执行以下步骤:

  add_column :articles, :is_finished, :boolean
  add_column :articles, :is_finished, :boolean, :default => false
  Article.reset_column_information
  Article.find(:all).each do |a|
    a.is_finished = false

您需要更新页面布局以显示到未完成文章的链接。应使此代码与主布局分离开来。您将生成一个新的“局部模板”,其文件名以字符“_”(下划线)开头以表明它不是一个完整的模板。该模板将查找 is_finished 为 false 的文章并链接到这些文章。在终端窗口中执行以下命令:

gedit app/views/articles/_unfinished.html.erb

向下滚动至文件底部。在最后一个“end”的前面添加以下代码:

    # POST /articles/mark_finished/1
  # POST /articles/mark_finished/1.xml
  def mark_finished
    @article = Article.find(params[:id])
    @article.update_attribute(:is_finished, true)

    respond_to do |format|
      format.html { redirect_to(:action => :show) }
      format.xml  { head :ok }
    end
  end

您已更新了数据库,向页面布局添加了按钮和操作以及列表。现在可对该功能进行测试了。在浏览器中输入以下 URL:

    http://localhost:3000/articles

注意页面顶部列出了未完成的文章。选择 Boning a Duck 链接。

单击 Mark Finished 按钮。

您会看到 Boning a Duck 已从 Unfinished Articles 列表中删除。选择 Back

此时您可以尝试 AJAX 了!您希望将应用程序更改成这样:当您按下该按钮时,窗口会自动更新而不会刷新整个屏幕。为此,您想让“Mark Finished”按钮发送一个 AJAX 命令来执行 mark_finished 操作,然后再发回一个命令更新“Unfinished Articles”标题下的内容。首先将该按钮更改为一个远程 (AJAX) 表单。在终端窗口中执行以下命令:

  gedit app/views/articles/show.html.erb

当您按下该按钮时,应用程序将像以前一样进行更新,但不发送整个页面,您需要发送回某个 JavaScript 以便只更新未完成文章列表。通过使用一个远程 javascript 模板,Rails 可为您生成这个 JavaScript。在终端窗口中执行以下命令新建一个文件:

  gedit app/views/articles/mark_finished.js.rjs

将以下代码:

    # POST /articles/mark_finished/1
  # POST /articles/mark_finished/1.xml
  def mark_finished
    @article = Article.find(params[:id])
    @article.update_attribute(:is_finished, true)

    respond_to do |format|
      format.html { redirect_to(:action => :show) }
      format.xml  { head :ok }
    end
  end
    # *AJAX* /articles/mark_finished/1
  def mark_finished
    @article = Article.find(params[:id])
    @article.update_attribute(:is_finished, true)

    respond_to do |format|
      format.js
    end
  end

您已转为使用一个 AJAX 请求表单并发回 Javascript 以更新列表。在浏览器中输入以下 URL:

    http://localhost:3000/articles

新建一个文章。选择 New article

对 Title 输入 Gravity,对 Author 选择 Julia Child,对 Abstract 输入 Gravity makes cakes fall,然后单击 Create

您会看到 Unfinished Articles 列表中列出了 Gravity。选择 Gravity 链接。

单击 Mark Finished

Gravity 立即从列表中消失。该页面未完全刷新。这是 AJAX 的功劳。在像这样一个简单的屏幕上,使用和不使用 AJAX 之间的差别可能不太明显,因为您的 Web 服务器发送的页面较小。但在含有图像、flash 和/或大量数据的更为复杂的屏幕上,其差别可以是巨大的。如果使用 AJAX,则服务器可以只发送有变化的部分。因此,您确实会欣赏 Rails 对 AJAX 支持!

false 和 nil 为假,其他均为真。

if expr [then]
  expr...
[elsif expr [then]
  expr...]...
[else
  expr...]
end

unless expr [then]
  expr...
[else
  expr...]
end

expr1 if expr2          # 如果 expr2 为 true,则执行 expr1
expr1 unless expr2   # 如果 expr2 为 false,则执行 expr1

# 通过 === 运算符比较大小写
case expr
[when expr [, expr]...[then]
  expr..]..
[else
  expr..]
end

while expr [do]
  #代码位置
end

until expr [do]
  #代码位置
end

expr1 while expr2              # 当 expr2 为 true 时,继续对 expr1 求值。
expr1 untill expr2              # 继续对 expr1 求值,直到 expr2 为 true
begin expr1 until expr2     # 至少对 expr1 求值 1 次,直至 expr2 为 true

for lhs... in expr [do]
  expr..
end

# 在该循环体中可以使用两个特殊关键字:
next      # 跳到最内层循环的下一个迭代

redo    # 重新开始最内层循环的当前迭代,而无需检查
           # 循环条件