Rails 初上手指南

$ gem install rails
 
<div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">$ gem install rails
</pre></div>
<p></p>
<p><span class="caps">TIP</span>: 如果你在 Window 下面工作,你可以使用 <a href="http://railsinstaller.org">Rails Installer</a> 快速安装 Ruby and Rails。</p>
<p>确认所有的(依赖)安装正确,你应该运行如下命令:</p>
<div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">$ rails --version
</pre>
</div>
<p>如果终端提示类似这样的文字 “Rails 3.2.2” 那么你已经准备好继续了。</p>
<h4 id="5-2">5.2 Creating the Blog Application</h4>
<p>开始,打开一个 terminal,导航至一个你有权限创建文件的文件夹,并输入:</p>
<div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">$ rails new blog #--skip-bundle   
# Don't run bundle install这样在国内就不会由于连不上gem即便上能够上也会很慢半天没反映 可以尝试使用 淘宝gem镜像
</pre>
</div>
<p>这里将在名称为 blog 的目录中创建一个名叫 Blog 的 Rails 应用程序。</p>
<p><span class="caps">TIP</span>: 你可以通过运行 rails new -h,查看 Rails 应用程序创建器的所有命令(开关)。</p>
<p>当你创建了这个 blog 程序,跳转到它所在的文件夹中(直接对这个程序编辑)。
</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">$ cd blog
 
<div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">$ cd blog
</pre><p></p>
</div>
<p></p>
<p>命令 <tt>rails new blog</tt> 将会在你的工作目录中创建一个名为 <tt>blog</tt> 的文件夹。目录 <tt>blog</tt> 包含一些自动生成的文件(夹),它构成了 Rails  应用程序的结构。在这个体验中的大多数的工作都是在 app/ 这个文件夹中完成的,这里对 Rails 默认创建的每一个文件和文件夹的功能做出了一个概述:</p>
<table>
     <tbody><tr>
         <th>File/Folder</th>
         <th>Purpose</th>
     </tr>
     <tr>
         <td>app/</td>
         <td>包含 controllers, models, views 和 你应用程序的 assets(资源),再接下来的手册中你主要的注意力应该放在这里。</td>
     </tr>
     <tr>
         <td>config/</td>
         <td>配置你的应用程序的运行的规则,(url)路由,数据库和其他,更多的信息查看 <a href="http://guides.rubyonrails.org/configuring.html">Configuring Rails Applications</a></td>
     </tr>
     <tr>
         <td>config.ru</td>
         <td>基于 Rack 服务器使用这个应用程序的 Rack 配置用于开始应用程序(Rack configuration for Rack based servers used to start the application)</td>
     </tr>
     <tr>
         <td>db/</td>
         <td>显示你当前的数据库结构(database schema),同样也显示数据迁移。</td>
     </tr>
     <tr>
         <td>doc/</td>
         <td>应用程序的(深入)全面的文档。</td>
     </tr>
     <tr>
         <td>Gemfile<br>Gemfile.lock</td>
         <td>这个文件让你可以(添加)你的 Rails 所需要的特殊的 Gem 依赖关系。这个文件被 Bundler gem 使用,更多的信息查看 <a href="http://gembundler.com">the Bundler website</a> </td>
     </tr>
     <tr>
         <td>lib/</td>
         <td>应用程序用到的扩展库(本手册没有涉及)</td>
     </tr>
     <tr>
         <td>log/</td>
         <td>应用程序的日志文件</td>
     </tr>
     <tr>
         <td>public/</td>
         <td>这是外部可见的唯一文件夹。包含静态文件和编译资源。</td>
     </tr>
     <tr>
         <td>Rakefile</td>
         <td>这个文件定位和载入能够在命令行中运行的任务。这个任务定义贯穿整个 Rails 的组件。除了修改 Rakefile,你更应该添加你自己的任务的文件到你的应用程序的 lib/tasks 目录。</td>
     </tr>
     <tr>
         <td><span class="caps">README</span>.rdoc</td>
         <td>这是一个简单的说明手册。你需要编辑这个文件告诉其他人你的应用程序可以做什么,怎么安装等等。</td>
     </tr>
     <tr>
         <td>script/</td>
         <td>包含运行你的 app 的 rails 脚本,或者其他用来配置或运行你的应用程序的 scripts。</td>
     </tr>
     <tr>
         <td>test/</td>
         <td>单元测试, fixtures,或者其他 test 工具。他们在 <a href="http://guides.rubyonrails.org/testing.html">Testing Rails Applications</a> 里面有完整的讲述。</td>
     </tr>
     <tr>
         <td>tmp/</td>
         <td>临时文件</td>
     </tr>
     <tr>
         <td>vendor/</td>
         <td>放置第三方代码的地方。在一个典型的 Rails 应用程序中,这里包含 Ruby Gems,Rails 源代码(如果你把 Rails 安装到你的项目中)还包含一些预先安装好的额外的插件</td>
     </tr>
</tbody></table>
<h3 id="6">6 配置一个数据库</h3>
<p>几乎每个 Rails 应用程序都会和一个数据库交互。使用的数据库在一个配置文件 <tt>config/database.yml</tt> 中被指定。如果你打开一个在新的 Rails 应用程序的中的配置文件,你将会看到默认的数据库被配置使用 SQLite3。这个文件包含 Rails 默认能够运行的三个不同环境的部分:</p>
<ul>
     <li>development 环境被使用在你的开发/本地计算机作为你与应用程序的手动交互。</li>
     <li>test 环境被使用来运行自动测试。</li>
     <li>production 环境在你部署你的应用程序给所有人使用的时候使用。</li>
</ul>
<p><span class="caps">TIP</span>: 你不必手动的更新数据库配置。如果你查看应用程序创建器的选项,你将会发现其中一个选项叫—— database。这个选项允许你从最常使用的关系数据库列表中选择一个适配器。你甚至可以反复运行创建器命令: <tt>cd .. && rails new blog —database=mysql</tt>。当你确认重写 <tt>config/database.yml</tt> 文件,你的应用程序将会被配置为 MySQL 以替代 SQLite。下面是一些常用数据库连接的详细的例子。</p>
<h4 id="6-1">6.1 配置一个 SQLite3 数据库</h4>
<p>Rails 内置支持 <a href="http://www.sqlite.org/">SQLite</a> ,这是一个轻量级的非服务器(serverless)数据库应用程序。虽然 SQLite 可能无法应对繁忙的产品环境,但它在开发和测试环境可以很好的工作。Rails 在创建一个新的项目的时候默认使用一个 SQLite 数据库,但是你总是可以在以后改变它。</p>
<p>这里是一个开发环境(数据库)连接信息默认配置文件(<tt>config/database.yml</tt>)的节选:</p>
<div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">development:
   adapter: sqlite3
   database: db/development.sqlite3
   pool: 5
   timeout: 5000
</pre>
</div>
<p><span class="caps">TIP</span>: 在这个教程中我们使用一个 SQLite3 数据库来存储数据,因为它无需配置就可以工作。Rails 同样也支持“开箱即用”的 MySQL 和 PostgreSQL,以及很多数据库系统的插件。如果你正在产品环境中使用一个数据库,大多数情况下 Rails 都有一个与之相应的插件。</p>
<h4 id="6-2">6.2 配置一个 MySQL 数据库</h4>
<p>如果你选择使用一个 MySQL 替代(项目初始化)附带的 SQLite3 数据库,你的 <tt>config/database.yml</tt> 将会看起来有些不同。这里是 development 部分:</p>
<div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">development:
   adapter: mysql2
   encoding: utf8
   database: blog_development
   pool: 5
   username: root
   password:
   socket: /tmp/mysql.sock
</pre>
</div>
<p>如果你的开发电脑的 MySQL 安装的时候包含了一个名叫 ‘root’ 的用户以及一个空密码,这个配置文件应该会为你工作。否则,在 development 部分更改为合适的用户名和密码。</p>
<h4 id="6-3">6.3 配置一个 PostgreSQL 数据库</h4>
<p>如果你选择使用 PostgreSQL,你的 <tt>config/database.yml</tt> 将会定制来使用 PostgreSQL 数据库:</p>
<div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">development:
   adapter: postgresql
   encoding: unicode
   database: blog_development
   pool: 5
   username: blog
   password:
</pre>
</div>
<h4 id="6-4">6.4 配置一个用于 JRuby 平台的 SQLite3 数据库</h4>
<p>如果你选择使用 SQLite3 并且使用 JRuby,你的 <tt>config/database.yml</tt> 将会看起来有点不同。这里是 development 部分:</p>
<div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">development:
   adapter: jdbcmysql
   database: blog_development
   username: root
   password:
</pre>
</div>
<h4 id="6-5">6.5 配置一个用于 JRuby 平台的 PostgreSQL 数据库</h4>
<p>最后如果你选择使用 PostgreSQL 并且正在使用 JRuby,你的 <tt>config/database.yml</tt>将会看起来有点不同。这里是 development 部分:</p>
<div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">development:
   adapter: jdbcpostgresql
   encoding: unicode
   database: blog_development
   username: blog
   password:
</pre>
</div>
<h3 id="7">7 Hello, Rails!</h3>
<p>开始学习一门新语言的一种比较传统的方式是快速的在屏幕上输出一些文字。要得到这样的结果你需要使你的 Rails 程序能够正常运行。</p>
<h4 id="7-1">7.1 启动web服务</h4>
<p>你实际上已经有了一个 Rails 功能的应用程序了。看一看它,你需要在你的开发机器上启动一个 web 服务,你可以这样来启动。</p>
<div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">$ rails server
</pre>
</div>
<p><span class="caps">TIP</span>: 编译 CoffeeScript 到 JavaScript需要一个 JavaScript 运行库,缺少运行库将会给出一个 <tt>execjs</tt> error。通常 Mac OS X 和 Windows 自带了一个 JavaScript 运行库。Rails 添加了 <tt>therubyracer</tt>  gem 到 Gemfile 的注释行中,如果你需要,可以取消对它的注释。对于 JRuby 用户,推荐使用 <tt>therubyrhino</tt> 运行环境。它已经默认添加到 Gemfile 中,如果应用程序在 JRuby 中创建。你可以查看所有的支持的运行库 <a href="https://github.com/sstephenson/execjs#readme。">ExecJS</a></p>
<p>这里默认将开启一个 WEBrick 服务器的的实例(Rails 也可能使用一些其他的 web 服务器)。查看你的应用程序的行为,打开一个浏览器并且导航到 <a href="http://localhost:3000">http://localhost:3000</a> 你将会看到一个 Rails 默认的信息页面。</p>
<p><img src="images/rails_welcome.png" title="Welcome Aboard screenshot" alt="Welcome Aboard screenshot" data-pinit="registered"></p>
<p><span class="caps">TIP</span>: 要终止 web 服务,在命令运行的终端中按下 Ctrl+C 。在开发环境模式中,Rails 一般不需要你停止服务;你所做的更改将自动的编译进需要的文件中并且重启服务。</p>
<p>这个欢迎界面是新的 Rails 应用程序的 <a href="http://www.hudong.com/wiki/%E5%86%92%E7%83%9F%E6%B5%8B%E8%AF%95">冒烟测试</a> ,用以确保你的软件正确配置完毕,足以运行一个页面。你可以点击 ‘About your application’s environment’ 查看你的应用程序运行环境的摘要信息。</p>
<h4 id="7-2">7.2 Rails 说 Hello"</h4>
<p>要使 Rails 说出(显示)“你好”,你至少需要创建一个 controller 和 view。幸运的是,你可以通过一行命令完成这些。在终端中输入:</p>
<div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">$ rails generate controller home index
</pre>
</div>
<p><span class="caps">TIP</span>: 如果你在输入这个命令的时候出现"没有这个命令"的错误,你需要明确的使用 ruby 来执行 Rails 命令。</p>
<p>Rails 将会为你创建一些文件,包含 <tt>app/views/home/index.html.erb</tt> 。这是一个模板,用来显示在 home controller 中的 index action (method) 的结果。在文本编辑器中打开这个文件并输入:</p>
<p><code class="html">
<h1>Hello, Rails!</h1>
</code></p>
<h4 id="7-3">7.3 设置应用程序主页</h4>
<p>现在我们已经创建了 controller 和 view,我们还需要告诉 Rails 我们想在什么时候显示出来。在本例中,我们想让它在我们访问站点 url 根目录 <a href="http://localhost:3000">http://localhost:3000</a> 的时候替代 “Welcome Aboard” 显示。</p>
<p>首先移除应用程序中的默认页面。</p>
<div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">$ rm public/index.html
</pre>
</div>
<p>我们必须这样做,因为 Rails 将会优先传送 public 文件夹中的静态文件,而不是我们从 controllers 中生成的动态(显示)内容。</p>
<p>现在你还必须告诉 Rails 你实际上的主页在哪里。在文本编辑器中打开 <tt>config/routes.rb</tt> 。这是你应用程序的路由文件,它采用 <span class="caps">DSL</span> 语言囊括了告诉 Rails 怎样连接请求信息到 controllers 和 actions的所有条目。这个文件包含许多简单的路由命令,其中一条就是用于告诉我们怎样连接你站点根目录到一个指定的 controller and action。找到以 root :to 开头的那一行,注释掉它改成如下内容:</p>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">Blog::Application.routes.draw do
 
   #...
   # You can have the root of your site routed with "root"
   # just remember to delete public/index.html.
   root :to => "home#index"
</pre>
</div>
<p><tt>root :to => "home#index"</tt> 告诉 Rails 映射请求到应用程序的 root action 到 home 控制器的 index action。</p>
<p>现在你在浏览器中访问 <a href="http://localhost:3000">http://localhost:3000</a> ,你将会看到“Hello, Rails!”.</p>
<p>NOTE. 更多的信息请参见 <a href="routing.html">Rails Routing from the Outside In</a>.</p>
<h3 id="8">8 使用 Scaffolding 快速创建并运行</h3>
<p>Rails ‘scaffolding’ 是一个建立应用程序的一些重要组件的快速的方法。如果你想使用单行操作为新资源创建 models,views 和 controllers,Scaffolding 是一个不错的工具。</p>
<h3 id="9">9 创建一个资源</h3>
<p>在本示例中的 blog 应用程序,你可以使用 scaffolded 产生 post 资源:它表现为一个简单的 blog posting。要完成这些,在终端输入如下命令:</p>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">rails generate scaffold Post name:string title:string content:text
</pre>
</div>
<p>创建器将会在应用程序中的一些文件夹中生成一些文件,并且还会编辑 <tt>config/routes.rb</tt>。下面是这些产生的文件的大概说明:</p>
<table>
     <tbody><tr>
         <th>File/Folder</th>
         <th>Purpose</th>
     </tr>
     <tr>
         <td>db/migrate/20100207214725_create_posts.rb </td>
         <td>将创建的posts表单迁移到你的数据库(会在你的命名前面加上时间戳)</td>
     </tr>
     <tr>
         <td>app/models/post.rb </td>
         <td>Post模型</td>
     </tr>
     <tr>
         <td>test/unit/post_test.rb </td>
         <td>posts 模型的单元测试工具</td>
     </tr>
     <tr>
         <td>test/fixtures/posts.yml </td>
         <td>测试使用的 posts 样例</td>
     </tr>
     <tr>
         <td>config/routes.rb  </td>
         <td>用以编辑 posts 的路由信息</td>
     </tr>
     <tr>
         <td>app/controllers/posts_controller.rb</td>
         <td>Posts 控制器</td>
     </tr>
     <tr>
         <td>app/views/posts/index.html.erb</td>
         <td>显示所有 posts 的视图</td>
     </tr>
     <tr>
         <td>app/views/posts/edit.html.erb </td>
         <td>编辑 post 的视图</td>
     </tr>
     <tr>
         <td>app/views/posts/show.html.erb </td>
         <td>显示一条 post 的视图</td>
     </tr>
     <tr>
         <td>app/views/posts/new.html.erb </td>
         <td>创建 post 的视图</td>
     </tr>
     <tr>
         <td>app/views/posts/_form.html.erb </td>
         <td>一个局部用于控制编辑和创建新视图的整体视效的表单</td>
     </tr>
     <tr>
         <td>test/functional/posts_controller_test.rb</td>
         <td>posts 控制器的功能测试工具</td>
     </tr>
     <tr>
         <td>app/helpers/posts_helper.rb </td>
         <td>用于 post 视图的帮助函数(Helper functions)</td>
     </tr>
     <tr>
         <td>test/unit/helpers/posts_helper_test.rb  </td>
         <td>the posts helper 的单元测试工具</td>
     </tr>
     <tr>
         <td>app/assets/javascripts/posts.js.coffee </td>
         <td>用于 the posts controller 的 CoffeeScript</td>
     </tr>
     <tr>
         <td>app/assets/stylesheets/posts.css.scss</td>
         <td>用于 the posts controller 的 <span class="caps">CSS</span></td>
     </tr>
     <tr>
         <td>app/assets/stylesheets/scaffolds.css.scss </td>
         <td>使创建的视图更优美的 <span class="caps">CSS</span> 层叠样式</td>
     </tr>
</tbody></table>
<p>NOTE. 即便 scaffolding 使你创建和运行非常快捷,但是产生的代码不可能完全适合你的应用程序。你大多数都需要定制产生的代码。很多有经验的Rails开发人员完全不使用 scaffolding,宁愿从头编写全部的代码。无论如何,Rails 使得为生成的models,controllers,views或者其他代码定制模板非常简单。你可以在 <a href="http://guides.rubyonrails.org/generators.html">Creating and Customizing Rails Generators & Templates</a> 看到更多信息。</p>
<h4 id="9-1">9.1 执行数据迁移</h4>
<p><tt>rails generate scaffold</tt> 命令的一个产物就是数据迁移。Migrations是一个ruby类,用于简化数据库表单的创建和修改。Rails使用 rake 命令来执行迁移,它还可以撤销已经应用的修改。 迁移文件名包含了一个时间戳,以确保迁移工作能够按照正确的顺序完成。</p>
<p>如果你查看 <tt>db/migrate/20100207214725_create_posts.rb</tt> 这个文件(记住,你看到的名字可能会略微不同),你将会看到如下代码:</p>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">class CreatePosts < ActiveRecord::Migration
   def change
     create_table :posts do |t|
     t.string :name
     t.string :title
     t.text :content
 
     t.timestamps
     end
   end
end
</pre>
</div>
<p>整个 migration 创建了一个名叫 change 的方法,该方法在你运行这个 migration 的时候被调用。这个方法中定义的行为也是可逆的,那就是说,如果你需要恢复到上一次数据,Rails 知道怎样逆向改变这个 migration。默认情况下,当你运行这个 migration,他将会创建一个包含两个字符串列和一个 text 列的表单。关于 Rails migration 的更多信息请阅读 <a href="http://guides.rubyonrails.org/migrations.html">Rails Database Migrations</a> 手册。</p>
<p>这个时候,你可以使用 rake 命令运行 migration 了:</p>
<div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">rake db:migrate
</pre>
</div>
<p>Rails 将会执行这个 migration 命令并且通知你它创建了 Post 表单。</p>
<div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">==  CreatePosts: migrating =============================================
-- create_table(:posts)
    -> 0.0019s
==  CreatePosts: migrated (0.0020s) ====================================
</pre>
</div>
<p><span class="caps">TIP</span>: 由于你默认工作在开发环境中,这个命令将会应用于开发环境会话的数据库(在你的 <tt>config/database.yml</tt> 中有定义)。如果你想在其他环境中执行 migration,比如以产品(环境)为实例,你必须通过命令行中执行:<tt>rake db:migrate RAILS_ENV=production</tt> 来明确的调用。</p>
<h4 id="9-2">9.2 添加一个 Link</h4>
<p>你已经创建好的 post 挂到主页上,你可以通过添加一个 link 到主页。打开 <tt>app/views/home/index.html.erb</tt> 并且按照下面所示更改:</p>
<div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false"><h1>Hello, Rails!</h1>
<%= link_to "My Blog", posts_path %>
</pre>
</div>
<p>这个链接方法是 Rails 在 view helpers 的内建方法之一 。它创建一个基于文字的超级链接并显示到哪里,在这个实例中(跳转)到 posts。</p>
<h4 id="9-3">9.3 Working with Posts in the Browser</h4>
<p>现在你已经准备好在 posts 中工作了。导航至 <a href="http://localhost:3000,并且点击">http://localhost:3000</a> “My Blog” 链接。</p>
<p><img src="images/posts_index.png" title="Posts Index screenshot" alt="Posts Index screenshot" data-pinit="registered"></p>
<p>这就是 Rails 渲染你的 posts 视图后的结果。在你点击 “New Post” 链接并创建一个新的 post 之前,数据库里面是没有任何 post 的。随后你可以编辑,查看详细内容,或者删除他们。post 的所有的 logic 和 <span class="caps">HTML</span> 都是通过 <tt>rails generate scaffold</tt> 命令生成的。</p>
<p><span class="caps">TIP</span>: 在开发模式中(你的默认工作模式),Rails 会在每个浏览器请求的时候重新载入你的应用程序,因此你不需要停止或者重启 web 服务。</p>
<p>恭喜,你已经驯服了 rails!现在是时候去看看它怎样工作的了。</p>
<h4 id="9-4">9.4 The Model</h4>
<p>如你所见, model 文件 <tt>app/models/post.rb</tt> 非常简单:</p>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">class Post < ActiveRecord::Base
     attr_accessible :content, :name, :title
end
</pre>
</div>
<p>这个文件内容并不多——但是注意 <tt>Post</tt> 类继承于 <tt>ActiveRecord::Base</tt>。Active Record 免费为你的 models 提供了强大的功能,包括基本数据库的 CRUD(创建,读取,更新,删除)操作,数据验证,以及复杂的的查询与其它数据表单多关联的字段的支持能力。另外一个重要的部分是 <tt>attr_accessible</tt> 。它指定了允许批量更新的属性的白名单。</p>
<h4 id="9-5">9.5 添加一些验证</h4>
<p>Rails 包含一些帮助你验证发送到models的数据的方法。打开 <tt>app/models/post.rb</tt> 并编辑:</p>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">class Post < ActiveRecord::Base
   attr_accessible :content, :name, :title  
   validates :name,  :presence => true
   validates :title, :presence => true,
                     :length => { :minimum => 5 }
end
</pre>
</div>
<p>这些更改会确保所有的 post 都有一个 name 和 titile 并且 title 长度至少五个字符。Rails 可以验证很多种字段,比如字段能否为空和独特性,字段的格式,以及字段的关联。验证详细描述在 <a href="active_record_validations_callbacks.html#validations-overview">Active Record Validations and Callbacks</a></p>
<h4 id="9-6">9.6 使用控制台</h4>
<p>要想在action里面查看你的验证你可以使用 console。console 是一个可以让你在你的应用程序的上下文中执行 Ruby 代码的命令行工具:</p>
<div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">$ rails console
</pre>
</div>
<p><span class="caps">TIP</span>: 默认的 console 将会改变你的数据库。你可以通过运行 <tt>rails console
—sandbox</tt>,这样你可以(在退出控制台后)回滚你的任何操作。</p>
<p>在载入控制台后,你可以使用它来对你应用程序的models进行工作:</p>
<div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">>> p = Post.new(:content => "A new post")
=> #<Post id: nil, name: nil, title: nil,
      content: "A new post", created_at: nil,
      updated_at: nil>
>> p.save
=> false
>> p.errors.full_messages
=> ["Name can't be blank", "Title can't be blank", "Title is too short (minimum is 5 characters)"]
</pre>
</div>
<p>这段代码演示了创建一个 <tt>Post</tt> 实例,并企图保存到数据库,结果得到一个 <tt>false</tt> 的返回值(说明保存失败的原因),同时检查了 post 的错误信息。</p>
<p>当你操作完成,输入 <tt>exit</tt> 并回车退出 console。</p>
<p><span class="caps">TIP</span>: 不像开发环境的 web 服务器,console 不会自动导入你每行输入的新的代码。如果你改变了你的 models 并且 console 是打开的,输入 <tt>reload!</tt> 那么 console 会立即导入他们。</p>
<h4 id="9-7">9.7 Listing All Posts</h4>
<p>让我们稍微深入 Rails 代码,去看看程序是怎样展示 post 列表给我们的。打开文件 <tt>app/controllers/posts_controller.rb</tt> 并且查看 <tt>index</tt> action:</p>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">def index
   @posts = Post.all
 
   respond_to do |format|
     format.html  # index.html.erb
     format.json  { render :json => @posts }
   end
end
</pre>
</div>
<p><tt>Post.all</tt> 调用 Post model 并返回当前在数据库中的所有 post 为一个 <tt>Post</tt> 记录的数组。并且我们将这个数组存储在一个叫做@posts的实例变量中。</p>
<p><span class="caps">TIP</span>: 有关 Active Record 更多的信息,可以查看 <a href="active_record_querying.html">Active
Record Query Interface</a> 相关记录。</p>
<p>这个 <tt>respond_to</tt> 块处理了这个动作的 <span class="caps">HTML</span> 和 <span class="caps">JSON</span> 请求。如果你浏览 <a href="http://localhost:3000/posts.json,你将会看到一个包含着所有">http://localhost:3000/posts.json</a> post 的 <span class="caps">JSON</span> 数组。这个 <span class="caps">HTML</span> 格式在 <tt>app/views/posts/</tt> 的 view 中查找相对应的动作名称。Rails 使来自 action 的所有的(可用的)实例变量对应到view。<tt>app/views/posts/index.html.erb</tt> 代码如下。</p>
<div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false"><h1>Listing posts</h1>
 
<table>
   <tr>
     <th>Name</th>
     <th>Title</th>
     <th>Content</th>
     <th></th>
     <th></th>
     <th></th>
   </tr>
 
<% @posts.each do |post| %>
   <tr>
     <td><%= post.name %></td>
     <td><%= post.title %></td>
     <td><%= post.content %></td>
     <td><%= link_to 'Show', post %></td>
     <td><%= link_to 'Edit', edit_post_path(post) %></td>
     <td><%= link_to 'Destroy', post, :confirm => 'Are you sure?',
                                      :method => :delete %></td>
   </tr>
<% end %>
</table>
 
<br />
 
<%= link_to 'New post', new_post_path %>
</pre>
</div>
<p>这个 view 迭代 <tt>@posts</tt> 数组所有的内容并显示相关的内容和链接。关于视图备注一些信息:</p>
<ul>
     <li><tt>link_to</tt> 创建一个超链接到一个地方</li>
     <li><tt>edit_post_path</tt> 和 <tt>new_post_path</tt> 是 Rails 提供的 RESTful 路由向导。你将会在不同 controller 看到一系列的不同的 actions helpers。</li>
</ul>
<p><span class="caps">NOTE</span>: 在以前的版本的 Rails 中,你必须使用 <tt><%=h post.name %></tt> 以避免一些HTML可能会在插入到页面之前转义。在 Rails 3.0 以及更高版本,这已作为默认。要得到一个非转义的 HTML,你现在使用 <tt><%= raw post.name %></tt>。</p>
<p><span class="caps">TIP</span>: 了解更过关于渲染处理流程,阅读 <a href="layouts_and_rendering.html">Layouts and Rendering in
Rails</a>.</p>
<h4 id="9-8">9.8 定制布局</h4>
<p>view 仅仅告诉 <span class="caps">HTML</span> 在你的 web 浏览器里面要显示什么(内容)。Rails 也有关于 <tt>layouts</tt> 的概念(定义),那就是布局是对 view 的包装。当 Rails 渲染一个 view 到浏览器,它通常是这样做: 把 view 的 <span class="caps">HTML</span> 放进布局的 <span class="caps">HTML</span> 中。在以前的版本中,<tt>rails generate scaffold</tt> 命令将会自动创建 controller 对应指定的布局,就像 <tt>app/views/layouts/posts.html.erb</tt> 对应于 posts controller。 然而在 rails3.0中有所不同了。一个应用程序指定的布局适用于所有的 controllers,可以在 <tt>app/views/layouts/application.html.erb</tt> 中找到(这就好像是django的base.html)。打开这个 layout 在你的编辑器中并且修改 <tt>body</tt> 标签:</p>
<div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false"><!DOCTYPE html>
<html>
   <head>
     <title>Blog</title>
     <%= stylesheet_link_tag "application" %>
     <%= javascript_include_tag "application" %>
     <%= csrf_meta_tags %>
   </head>
   <body style="background-color: #EEEEEE;">
     <%= yield %>
   </body>
</html>
</pre>
</div>
<p>现在你刷新 <tt>/posts</tt> 页面,你将会看到一个灰色的页面背景。并且相同的灰色背景将会使用在 posts 的所有视图中。</p>
<h4 id="9-9">9.9 建立新文章</h4>
<p>创建一个 new post 包含两个 actions。首先是 <tt>new</tt> action,它会实例化一个空的 <tt>Post</tt> 对象:</p>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">def new
   @post = Post.new
 
   respond_to do |format|
     format.html  # new.html.erb
     format.json  { render :json => @post }
   end
end
</pre>
</div>
<p>这个 <tt>new.html.erb</tt> 视图显示这个空的 post 给用户:</p>
<div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false"><h1>New post</h1>
 
<%= render 'form' %>
 
<%= link_to 'Back', posts_path %>
</pre>
</div>
<p><tt><%= render 'form' %></tt> 是我们第一个介绍的 Rails 的 partials。一个 partial 是一个 <span class="caps">HTML</span> 代码片段和 Ruby 代码的组合,可以在多目标对象中重用。(类似于django的include ‘other.html’)。
在本例中, form 用于创建 new post,它相当于一个用于编辑一个 post 的表单,这个表单有一个 name text fields 和 title text fields 以及一个 content 的 text area 还有一个用于添加一个新的 post 或者更新已经存在的 post 的按钮。</p>
<p>如果你看一下 <tt>views/posts/_form.html.erb</tt> 这个文件,你将会发现下面的内容:</p>
<div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false"><%= form_for(@post) do |f| %>
   <% if @post.errors.any? %>
   <div id="errorExplanation">
     <h2><%= pluralize(@post.errors.count, "error") %> prohibited
         this post from being saved:</h2>
     <ul>
     <% @post.errors.full_messages.each do |msg| %>
       <li><%= msg %></li>
     <% end %>
     </ul>
   </div>
   <% end %>
 
   <div class="field">
     <%= f.label :name %><br />
     <%= f.text_field :name %>
   </div>
   <div class="field">
     <%= f.label :title %><br />
     <%= f.text_field :title %>
   </div>
   <div class="field">
     <%= f.label :content %><br />
     <%= f.text_area :content %>
   </div>
   <div class="actions">
     <%= f.submit %>
   </div>
<% end %>
</pre>
</div>
<p>这个 partial 接收在 view 文件中定义的所有的实例变量。因此在本例中,controller 申请新的 Post 对象给@post,@post 在 view 和 partial 都是可用的。</p>
<p>有关 partials 的更多信息,参考 <a href="layouts_and_rendering.html#using-partials">Layouts and Rendering in
Rails</a> 指导。</p>
<p><tt>form_for</tt> 代码块用于创建一个HTML表单。在这个代码块中你可以在访问方法的基础上在表单上创建各种控制。比如,<tt>f.text_field :name</tt> 告诉 Rails 在表单中创建一个 text input 并且对应于显示实例的 <tt>name</tt> 属性。form 使用的方法基于 model 的相对应的字段属性(类型如 text_field 或 text_area)(例如本例中的 <tt>name</tt>, <tt>title</tt>, and <tt>content</tt>)。Rails 偏好于使用(偏向于使用)<tt>form_for</tt> 列出你要输入的 <span class="caps">HTML</span> 行因为这样代码更加简洁,并且这样使得 form 和 particular model 实例关系更加明显。</p>
<p><tt>form_for</tt> 代码块同样也足够你定制你的 <em>New Post</em> 和 <em>Edit Post</em> action,并且将会设置 form <tt>action</tt> 标签以及在 <span class="caps">HTML</span> 输出中显示的提交按钮的名称。</p>
<p><span class="caps">TIP</span>: 如果你需要创建一个显示任意的域的 <span class="caps">HTML</span> 表单,而不绑定到 model 字段中,你应该使用 <tt>form_tag</tt> 方法,它提供了建立不必绑定到 model 实例的 forms 的捷径。</p>
<p>当用户点击这张表单上面的 <tt>Create Post</tt> 按钮,浏览器将会发回信息到controller的 <tt>create</tt> action(Rails知道调用 <tt>create</tt> 方法,因为form是以HTTP POST请求发送,这是我随后提到的一种协议之一):</p>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">def create
   @post = Post.new(params[:post])
 
   respond_to do |format|
     if @post.save
       format.html  { redirect_to(@post,
                     :notice => 'Post was successfully created.') }
       format.json  { render :json => @post,
                     :status => :created, :location => @post }
     else
       format.html  { render :action => "new" }
       format.json  { render :json => @post.errors,
                     :status => :unprocessable_entity }
     end
   end
end
</pre>
</div>
<p><tt>create</tt> action 实例化一个新的 Post 对象,这个对象给 form 提供数据支持,Rails 会生成有效的 <tt>params</tt> hash。当成功的保存了新 post,<tt>create</tt> 返回用户请求的适当的格式(在本例中是 HTML)。然后重定向用户页面到结果显示的 post show action页面并且给出提示 Post 成功的创建了。</p>
<p>如果 post 没有保存成功,是因为(数据)验证错误,然后 controller 控制用户页面回到 <tt>new</tt> action(包含验证错误新息)给用户,以便用户更改错误并重新提交。</p>
“Post was successfully created.” 这条消息被存储在 Rails 的 flash 的 hash 表中,(通常之叫它 <em>the flash</em> )因此消息可以转载到另一个 action,在请求状态中提供有用的信息给用户。在这个新建例子(数据验证失败)中,用户实际上从来不看任何在页面创建进程中的渲染页面,因为一旦 Rails 保存了这个记录,它立刻重定向页面到 new Post。Flash装载消息到接下来的 action,因此当用户被重定向到了 show action,他们立刻收到了一条消息 “Post was successfully created”。
<h4 id="9-10">9.10 显示一条 Post</h4>
<p>当你在posts 的主页面点击一个 post 的 <tt>show</tt> 这个超链接,他将会产生一个 url: <tt>http://localhost:3000/posts/1</tt>。Rails解释这是一个resource的<tt>show</tt> action 调用。这里是 <tt>show</tt> action:</p>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">def show
   @post = Post.find(params[:id])
 
   respond_to do |format|
     format.html  # show.html.erb
     format.json  { render :json => @post }
   end
end
</pre>
</div>
<p>这里的 <tt>show</tt> action使用 <tt>Post.find</tt> 通过对应记录的id来查找单个记录。当找到记录,Rails使用 <tt>app/views/posts/show.html.erb</tt> 来显示它:</p>
<div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false"><p id="notice"><%= notice %></p>
 
<p>
   <b>Name:</b>
   <%= @post.name %>
</p>
 
<p>
   <b>Title:</b>
   <%= @post.title %>
</p>
 
<p>
   <b>Content:</b>
   <%= @post.content %>
</p>
 
 
<%= link_to 'Edit', edit_post_path(@post) %> |
<%= link_to 'Back', posts_path %>
</pre>
</div>
<h4 id="9-11">9.11 编辑Posts</h4>
<p>类似创建一个新的 post,编辑一个 post 分为两部分。首先是到 <tt>edit_post_path(@post)</tt> 请求一个特定的 post。这里是调用的在 controller 中的 <tt>edit</tt> action:</p>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">def edit
   @post = Post.find(params[:id])
end
</pre>
</div>
<p>再找到了请求的 post, Rails 试图使用 <tt>edit.html.erb</tt> 来显示它:</p>
<div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false"><h1>Editing post</h1>
 
<%= render 'form' %>
 
<%= link_to 'Show', @post %> |
<%= link_to 'Back', posts_path %>
</pre>
</div>
<p>就像 <tt>new</tt> action,<tt>edit</tt>  action也使用 <tt>form</tt> partial,这次有所不同,form将会提交一个PUT action到 <tt>PostsController</tt> 并且提交按钮将会显示为 “Update Post”。</p>
<p>提交的form由上面这个视图创建的并且还会调用 controller 中的 <tt>update</tt>  action:</p>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">def update
   @post = Post.find(params[:id])
 
   respond_to do |format|
     if @post.update_attributes(params[:post])
       format.html  { redirect_to(@post,
                     :notice => 'Post was successfully updated.') }
       format.json  { head :no_content }
     else
       format.html  { render :action => "edit" }
       format.json  { render :json => @post.errors,
                     :status => :unprocessable_entity }
     end
   end
end
</pre>
</div>
<p>在 <tt>update</tt> action中,Rails 首先使用 <tt>:id</tt> 参数从 edit view(传值到)数据库记录下刚才编辑的内容。当获取了请求的 <tt>post</tt> parameter(一个hash字典)就会 call <tt>update_attributes</tt> 并且 应用 hash 字典的值到相应的记录。如果一切成功,用户会被重定向到 post 的 show 视图。如果期间发生了任何错误,它将回到 edit 视图并(要求)改正他们。</p>
<h4 id="9-12">9.12 删除一个post</h4>
<p>最后,点击一个 <tt>destroy</tt> 链接发送相关的id到 <tt>destroy</tt> 动作:</p>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">def destroy
   @post = Post.find(params[:id])
   @post.destroy
 
   respond_to do |format|
     format.html { redirect_to posts_url }
     format.json { head :no_content }
   end
end
</pre>
</div>
<p>Active Record model 的实例的这个 <tt>destroy</tt> 方法的功能是从数据库中移除相应的记录。当这个(操作)完成,这里没有任何记录显示,因此 Rails 重定向用户的浏览器到 model 的index view。</p>
<h3 id="10">10 添加第二个 Model(comment)</h3>
<p>你已经知道了通过 scaffolding 生成的 model 看起来是怎样的。第二个 model 用来处理 blog post 的评论。</p>
<h4 id="10-1">10.1 构造一个 model</h4>
<p>Rails 中的 Models 使用一个单数名称,同时它们相关的数据库表使用一个复数名称。对于评论在 models 中的代名词,习惯上使用的的是 <tt>Comment</tt>。即使你不想完完全全的使用 scaffolding,大多数的 Rails 仍然使用生成器来生成像 models 和 controllers 这些代码。要创建一个新的 model,在终端中运行下面这条命令:</p>
<div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">$ rails generate model Comment commenter:string body:text post:references
</pre>
</div>
<p>这条命令将会生成四个文件:</p>
<table>
     <tbody><tr>
         <th>File                                       </th>
         <th>Purpose</th>
     </tr>
     <tr>
         <td>db/migrate/20100207235629_create_comments.rb </td>
         <td> 用于在数据库中创建 comments 表的移植代码(你的文件名可能会包含一个不同的时间戳 </td>
     </tr>
     <tr>
         <td> app/models/comment.rb                       </td>
         <td> Comment 模型 </td>
     </tr>
     <tr>
         <td> test/unit/comment_test.rb                   </td>
         <td> comments model 的单元测试工具</td>
     </tr>
     <tr>
         <td> test/fixtures/comments.yml                  </td>
         <td> 用于测试的 comments 样例</td>
     </tr>
</tbody></table>
<p>首先,看一看 <tt>comment.rb</tt>:</p>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">class Comment < ActiveRecord::Base
   belongs_to :post
end
</pre>
</div>
<p>这和你刚刚看到 post.rb 很近似。不同的是这行 belongs_to :post,他会设定一个 Active Record association。你将会在接下来的 guide 学习一点有关 associations 的内容。</p>
<p>除了模型,Rails 同样也产生了一个 migration 来创建相应的数据库表单:</p>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">class CreateComments < ActiveRecord::Migration
   def change
     create_table :comments do |t|
       t.string :commenter
       t.text :body
       t.references :post
 
       t.timestamps
     end
 
     add_index :comments, :post_id
   end
end
</pre>
</div>
<p>对于 <tt>t.references</tt> 这行,会在两个 models 之间生成一个外键列从而形成一个关系(组合)。而且 <tt>add_index</tt> line生成一个索引关联到这个关系行:</p>
<div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">$ rake db:migrate
</pre>
</div>
<p>Rails 能够智能的只针对对没有被运行过的表单,执行 migrations 生成当前的数据库,因此这里你只会看到:</p>
<div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">==  CreateComments: migrating =================================================
-- create_table(:comments)
    -> 0.0008s
-- add_index(:comments, :post_id)
    -> 0.0003s
==  CreateComments: migrated (0.0012s) ========================================
</pre>
</div>
<h4 id="10-2">10.2 Associating Models</h4>
<p>Active Record associations 让你很容易的申明两个models之间的关系。在本例中的 comments 和 posts,你可以这样描述关系:</p>
<ul>
     <li>一条 comment 对应于一个 post。</li>
     <li>一个 post 可以对应于多个 comments。</li>
</ul>
<p>实际上,这与接近 Rails 声明的 这个关系的语句语义 。你已经看到了在 Comment model 中的使每个 comment对应于一个 post的代码。</p>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">class Comment < ActiveRecord::Base
   belongs_to :post
end
</pre>
</div>
<p>你将会需要编辑 <tt>post.rb</tt> 文件来添加另一半关系。</p>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">class Post < ActiveRecord::Base
   attr_accessible :content, :name, :title
   validates :name,  :presence => true
   validates :title, :presence => true,
                     :length => { :minimum => 5 }
 
   has_many :comments
end
</pre>
</div>
<p>These two declarations enable a good bit of automatic behavior. For example, if you have an instance variable <tt>@post</tt> containing a post, you can retrieve all the comments belonging to that post as an array using <tt>@post.comments</tt>.
这两个声明启用了一个很好的自动关联功能。例如,如果你有一个包含一个 post 的实例变量 <tt>@post</tt>,你可以使用 <tt>@post.comments</tt> 以数组的形式取回属于这一 post 的所有评论。</p>
<p><span class="caps">TIP</span>: 更多关于Active Record associations的信息查看 <a href="association_basics.html">Active Record
Associations</a> guide 。</p>
<h4 id="10-3">10.3 给Comments添加路由</h4>
<p>作为 <tt>welcome</tt> controller,我们将还需要添加一个路由让 Rails 知道我们导航到哪里可以看到 <tt>comments</tt>。再次打开 <tt>config/routes.rb</tt> 文件,你将会看到 scaffold 创建器在顶部为 <tt>posts</tt> 自动添加的入口,<tt>resources
:posts</tt>,把它改成如下:</p>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">resources :posts do
   resources :comments
end
</pre>
</div>
<p>这里把 creates <tt>comments</tt> 作为一个嵌套资源放在 <tt>posts</tt> 中。这正是 posts 和comments 的分层关系的表现。</p>
<p><span class="caps">TIP</span>: 关于 routing 的更多的信息,查看 <a href="routing.html">Rails Routing from the Outside
In</a> guide。</p>
<h4 id="10-4">10.4 构造一个 Controller</h4>
<p>Model 已经到手了,你可以把你的注意力放到创建一个匹配的 controller 上了。类似的,像这样构造:</p>
<div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">$ rails generate controller Comments
</pre>
</div>
<p>这里新建了6个文件和一个空目录:</p>
<table>
     <tbody><tr>
         <th>File/Directory                             </th>
         <th>Purpose                                 </th>
     </tr>
     <tr>
         <td> app/controllers/comments_controller.rb      </td>
         <td> Comments 控制器                  </td>
     </tr>
     <tr>
         <td> app/views/comments/                         </td>
         <td> 控制器视图保存在这里 </td>
     </tr>
     <tr>
         <td> test/functional/comments_controller_test.rb </td>
         <td> 控制器功能测试 </td>
     </tr>
     <tr>
         <td> app/helpers/comments_helper.rb              </td>
         <td> 视图帮助文件                       </td>
     </tr>
     <tr>
         <td> test/unit/helpers/comments_helper_test.rb   </td>
         <td> helper 的单元测试           </td>
     </tr>
     <tr>
         <td> app/assets/javascripts/comment.js.coffee    </td>
         <td> 控制器的 CoffeeScript         </td>
     </tr>
     <tr>
         <td> app/assets/stylesheets/comment.css.scss     </td>
         <td> 控制器的 <span class="caps">CSS</span>/Cascading style sheet </td>
     </tr>
</tbody></table>
<p>就像大多数 blog,我们的读者将会在他们阅读 post 的时候直接发表他们的评论,并且一旦他们添加评论成功,将会回到 post show 页面去查看他们刚刚列出的评论。正因为这样(的考虑),我们的 CommentsController如下,它提供一个方法来创建 comments 和删除垃圾评论。</p>
<p>因此首先,我们来到 Post show template(<tt>/app/views/posts/show.html.erb</tt>),创建一个新的 comment:</p>
<div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false"><p id="notice"><%= notice %></p>
 
<p>
   <b>Name:</b>
   <%= @post.name %>
</p>
 
<p>
   <b>Title:</b>
   <%= @post.title %>
</p>
 
<p>
   <b>Content:</b>
   <%= @post.content %>
</p>
 
<h2>Add a comment:</h2>
<%= form_for([@post, @post.comments.build]) do |f| %>
   <div class="field">
     <%= f.label :commenter %><br />
     <%= f.text_field :commenter %>
   </div>
   <div class="field">
     <%= f.label :body %><br />
     <%= f.text_area :body %>
   </div>
   <div class="actions">
     <%= f.submit %>
   </div>
<% end %>
 
<%= link_to 'Edit Post', edit_post_path(@post) %> |
<%= link_to 'Back to Posts', posts_path %> |
</pre>
</div>
<p>This adds a form on the <tt>Post</tt> show page that creates a new comment by
calling the <tt>CommentsController</tt> <tt>create</tt> action. Let’s wire that up:
这里在 Post show 页面添加一个 forms 用来创建一个新的评论,它将会调用 <tt>CommentsController</tt> 的 <tt>create</tt> action,因此让我们补充上下面的内容:</p>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">class CommentsController < ApplicationController
   def create
     @post = Post.find(params[:post_id])
     @comment = @post.comments.create(params[:comment])
     redirect_to post_path(@post)
   end
end
</pre>
</div>
<p>这里你看到的会比你在 controller 中为 posts 做的要复杂点。那就是你刚刚补充的副作用;每个面向 comment 的请求都保持了它所依附的 post 的踪迹(id),因此这样初始化 <tt>find</tt> 的时候匹配相应的 <tt>Post</tt> model(时)得到了答案。</p>
<p>此外,上面的代码带来的好处就是使得一些对于 association 的方法可用。我们使用 <tt>@post.comments</tt> 中的 <tt>create</tt> 方法来新建和保存 comment。这里将会自动连接link使得 comment 依附于对应的 post。</p>
<p>一旦我们评论过后,我们使用 <tt>post_path(@post)</tt> 导引用户到先前的 post。正如我们已经看到的,这里调用 <tt>PostsController</tt> 的 <tt>show</tt> action,它将返回并渲染show.html.erb 模板。这里也是我们想让 comment 显示的地方,因此让我们添加(那些代码)到 <tt>app/views/posts/show.html.erb</tt>。</p>
<div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false"><p id="notice"><%= notice %></p>
 
<p>
   <b>Name:</b>
   <%= @post.name %>
</p>
 
<p>
   <b>Title:</b>
   <%= @post.title %>
</p>
 
<p>
   <b>Content:</b>
   <%= @post.content %>
</p>
 
<h2>Comments</h2>
<% @post.comments.each do |comment| %>
   <p>
     <b>Commenter:</b>
     <%= comment.commenter %>
   </p>
 
   <p>
     <b>Comment:</b>
     <%= comment.body %>
   </p>
<% end %>
 
<h2>Add a comment:</h2>
<%= form_for([@post, @post.comments.build]) do |f| %>
   <div class="field">
     <%= f.label :commenter %><br />
     <%= f.text_field :commenter %>
   </div>
   <div class="field">
     <%= f.label :body %><br />
     <%= f.text_area :body %>
   </div>
   <div class="actions">
     <%= f.submit %>
   </div>
<% end %>
 
<br />
 
<%= link_to 'Edit Post', edit_post_path(@post) %> |
<%= link_to 'Back to Posts', posts_path %> |
</pre>
</div>
<p>现在你可以添加 posts 和 comments 到你的 blog 同时随后他们会在相应的地方显示出来。</p>
<h3 id="11">11 重构</h3>
<p>现在我们已经有 Posts 和 Comments 开始工作了,如果我们注意一下 <tt>app/views/posts/show.html.erb</tt> 模板,发现它变得太长而且很别扭。我们可以使用 partials 来整理它。</p>
<h4 id="11-1">11.1 Rendering Partial Collections</h4>
<p>首先我们会创建一个 comment partial 来专门显示 post 的所有的 comments。创建文件 <tt>app/views/comments/_comment.html.erb</tt> 并输入如下内容:</p>
<div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false"><p>
   <b>Commenter:</b>
   <%= comment.commenter %>
</p>
 
<p>
   <b>Comment:</b>
   <%= comment.body %>
</p>
</pre>
</div>
<p>然后在  <tt>app/views/posts/show.html.erb</tt> 你可以相应的这样更改:</p>
<div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false"><p id="notice"><%= notice %></p>
 
<p>
   <b>Name:</b>
   <%= @post.name %>
</p>
 
<p>
   <b>Title:</b>
   <%= @post.title %>
</p>
 
<p>
   <b>Content:</b>
   <%= @post.content %>
</p>
 
<h2>Comments</h2>
<%= render @post.comments %>
 
<h2>Add a comment:</h2>
<%= form_for([@post, @post.comments.build]) do |f| %>
   <div class="field">
     <%= f.label :commenter %><br />
     <%= f.text_field :commenter %>
   </div>
   <div class="field">
     <%= f.label :body %><br />
     <%= f.text_area :body %>
   </div>
   <div class="actions">
     <%= f.submit %>
   </div>
<% end %>
 
<br />
 
<%= link_to 'Edit Post', edit_post_path(@post) %> |
<%= link_to 'Back to Posts', posts_path %> |
</pre>
</div>
<p>这里会把 <tt>@post.comments</tt> 集合的每一个 comment 在 <tt>app/views/comments/_comment.html.erb</tt> 模板中渲染。当渲染方法迭代完 <tt>@post.comments</tt> 集合时候,它分配每个 comment 为与 partial 名相同的本地变量(这里为comment),通过这样在 partial 中的 <tt>comment</tt> 就可以显示给我们的用户了。</p>
<h4 id="11-2">11.2 Rendering a Partial Form</h4>
<p>同样让我们移动 new comment 部分到它自己的地方吧。类似的,创建一个文件 <tt>app/views/comments/_form.html.erb</tt> 并且在里面放入下面代码:</p>
<div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false"><%= form_for([@post, @post.comments.build]) do |f| %>
   <div class="field">
     <%= f.label :commenter %><br />
     <%= f.text_field :commenter %>
   </div>
   <div class="field">
     <%= f.label :body %><br />
     <%= f.text_area :body %>
   </div>
   <div class="actions">
     <%= f.submit %>
   </div>
<% end %>
</pre>
</div>
<p>接着你这样修改 <tt>app/views/posts/show.html.erb</tt> 文件:</p>
<div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false"><p id="notice"><%= notice %></p>
 
<p>
   <b>Name:</b>
   <%= @post.name %>
</p>
 
<p>
   <b>Title:</b>
   <%= @post.title %>
</p>
 
<p>
   <b>Content:</b>
   <%= @post.content %>
</p>
 
<h2>Comments</h2>
<%= render @post.comments %>
 
<h2>Add a comment:</h2>
<%= render "comments/form" %>
 
<br />
 
<%= link_to 'Edit Post', edit_post_path(@post) %> |
<%= link_to 'Back to Posts', posts_path %> |
</pre>
</div>
<p>第二个 render 仅仅定义了一个我们想渲染的 partial template:<tt>comments/form</tt>,Rails 可以智能的识别字符串代表的含义,并且知道你是想 渲染在目录 <tt>app/views/comments</tt> 中的 <tt>_form.html.erb</tt>模板。</p>
<p><tt>@post</tt> 可以在任何的视图中 partials 渲染,因为我们把它定义为实例变量。</p>
<h3 id="12">12 Deleting Comments</h3>
<p>另一个重要的功能就是可以删除垃圾评论。要达到这样的效果,我们需要在 view 中实现某种链接和在 <tt>CommentsController</tt> 中的 <tt>DELETE</tt> 动作。</p>
<p>首先,在 <tt>app/views/comments/_comment.html.erb</tt> partial中添加delete link:</p>
<div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false"><p>
   <b>Commenter:</b>
   <%= comment.commenter %>
</p>
 
<p>
   <b>Comment:</b>
   <%= comment.body %>
</p>
 
<p>
   <%= link_to 'Destroy Comment', [comment.post, comment],
                :confirm => 'Are you sure?',
                :method => :delete %>
</p>
</pre>
</div>
<p>点击 “Destroy Comment”,link 将会发送 <tt><span class="caps">DELETE</span>
/posts/:id/comments/:id</tt> 到我们的 <tt>CommentsController</tt>,<tt>CommentsController</tt>将会利用刚刚收到的(消息)找到我们想删除哪条评论,因此让我们接着添加一个 destroy action 到我们的 controller:</p>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">class CommentsController < ApplicationController
 
   def create
     @post = Post.find(params[:post_id])
     @comment = @post.comments.create(params[:comment])
     redirect_to post_path(@post)
   end
 
   def destroy
     @post = Post.find(params[:post_id])
     @comment = @post.comments.find(params[:id])
     @comment.destroy
     redirect_to post_path(@post)
   end
 
end
</pre>
</div>
<p><tt>destroy</tt> action 将会找到那个我们正在阅读的 post,并且定位 comment 在 <tt>@post.comments</tt> 集合,然后从数据库 remove 它,最后传回消息到 post 的 show action。</p>
<h4 id="12-1">12.1 Deleting Associated Objects</h4>
<p>如果你删除一个了 post,那么与之相关联的 comments 也需要被删除。否则他们将会只是在数据库中占用空间(别无它用)。Rails 允许你通过关系的依赖选项完成(上述功能)。修改Post model,<tt>app/models/post.rb</tt>,像这样:</p>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">class Post < ActiveRecord::Base
   validates :name,  :presence => true
   validates :title, :presence => true,
                     :length => { :minimum => 5 }
   has_many :comments, :dependent => :destroy
end
</pre>
</div>
<h3 id="13">13 Security</h3>
<p>如果你就这样 publish 你的 blog 在互联网,任何人都可以添加,编辑和删除 post 或者删除 comments。</p>
<p>Rails 提供了一个非常简单的 <span class="caps">HTTP</span> 认证系统,在这样的情况下会非常适合。</p>
<p>在 <tt>PostsController</tt> 中我们需要一个方法来阻止没有通过认证的用户的操作,这里我们可以使用Rails的  <tt>http_basic_authenticate_with</tt> 这个方法,如果这个方法允许,就运行访问请求。</p>
<p>要使用这个认证系统,我们需要在PostsController 的顶部指定(即引用)它,这样我们希望用户在进行每个action的时候都是通过授权的,除了index和show,因此我们这样写:</p>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">class PostsController < ApplicationController
 
   http_basic_authenticate_with :name => "dhh", :password => "secret", :except => [:index, :show]
 
   # GET /posts
   # GET /posts.json
   def index
     @posts = Post.all
     respond_to do |format|
# snipped for brevity
</pre>
</div>
<p>我们同样希望只有授权用户能够删除评论,因此在 <tt>CommentsController</tt> 这样写:</p>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">class CommentsController < ApplicationController
 
   http_basic_authenticate_with :name => "dhh", :password => "secret", :only => :destroy
 
   def create
     @post = Post.find(params[:post_id])
# snipped for brevity
</pre>
</div>
<p>现在如果你尝试创建一个新的 post,你将会迎来一个简单的 <span class="caps">HTTP</span> 认证的挑战。</p>
<p><img src="images/challenge.png" title="Basic HTTP Authentication Challenge" alt="Basic HTTP Authentication Challenge" data-pinit="registered"></p>
<h3 id="14">14 Building a Multi-Model Form</h3>
<p>你的 blog 的另一个功能是能够给 posts 添加 tag。要想在你的程序中实现这个功能需要在一个 form 中与超过一个 model 互动。Rails 提供了嵌套 forms。</p>
<p>为了演示这个功能,你将会在你创建 post 的 form 中添加 post 的多 tag 支持。首先创建一个 new model 来存放 tags:</p>
<div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">$ rails generate model Tag name:string post:references
</pre>
</div>
<p>再次运行 migration 来创建数据库表单:</p>
<div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">$ rake db:migrate
</pre>
</div>
<p>接下来:编辑 <tt>post.rb</tt> 文件来创建来创建另一个成员,并且告诉Rails(通过 the  <tt>accepts_nested_attributes_for</tt> 宏)你打算通过posts form来编辑tags。</p>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">class Post < ActiveRecord::Base
   validates :name,  :presence => true
   validates :title, :presence => true,
                     :length => { :minimum => 5 }
 
   has_many :comments, :dependent => :destroy
   attr_accessible :tags_attributes
   has_many :tags
   accepts_nested_attributes_for :tags, :allow_destroy => :true,
   :reject_if => proc { |attrs| attrs.all? { |k, v| v.blank? } }
end
</pre>
</div>
<p><tt>:allow_destroy</tt> 选项是告诉 Rails 通过嵌套属性的声明开启删除 tags 的功能。显示一个 “remove” 复选框在视图中那样你可以快速创建(tags)。对于 <tt>:reject_if</tt> 保证不保存没有任何内容的tags。</p>
<p>我们将要修改 <tt>views/posts/_form.html.erb</tt> 来渲染partial,并创建tag:</p>
<div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false"><% @post.tags.build %>
<%= form_for(@post) do |post_form| %>
   <% if @post.errors.any? %>
   <div id="errorExplanation">
     <h2><%= pluralize(@post.errors.count, "error") %> prohibited this post from being saved:</h2>
     <ul>
     <% @post.errors.full_messages.each do |msg| %>
       <li><%= msg %></li>
     <% end %>
     </ul>
   </div>
   <% end %>
 
   <div class="field">
     <%= post_form.label :name %><br />
     <%= post_form.text_field :name %>
   </div>
   <div class="field">
     <%= post_form.label :title %><br />
     <%= post_form.text_field :title %>
   </div>
   <div class="field">
     <%= post_form.label :content %><br />
     <%= post_form.text_area :content %>
   </div>
   <h2>Tags</h2>
   <%= render :partial => 'tags/form',
              :locals => {:form => post_form} %>
   <div class="actions">
     <%= post_form.submit %>
   </div>
<% end %>
</pre>
</div>
<p>注意:我们已经更改 <tt>form_for(@post) do |f|</tt> 中的 <tt>f</tt> 为 <tt>post_form</tt> 这样会更加容易明白是怎么回事。</p>
<p>这个例子在 render helper 中使用另个方式,是为了说明我们希望的是在 <tt>form</tt> 中使用局部变量指向的 <tt>post_form</tt> 对象。</p>
<p>我们还在form的顶部添加 <tt>@post.tags.build</tt>。这里是为了确保每个新的 tag 都被用户填上了 name。如果你不创建新 tag,form 将不会显示它。</p>
<p>现在创建一个 <tt>app/views/tags</tt> 文件夹并且在里面新建一个 <tt>_form.html.erb</tt> 它包含tag form:</p>
<div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false"><%= form.fields_for :tags do |tag_form| %>
   <div class="field">
     <%= tag_form.label :name, 'Tag:' %>
     <%= tag_form.text_field :name %>
   </div>
   <% unless tag_form.object.nil? || tag_form.object.new_record? %>
     <div class="field">
       <%= tag_form.label :_destroy, 'Remove:' %>
       <%= tag_form.check_box :_destroy %>
     </div>
   <% end %>
<% end %>
</pre>
</div>
<p>最后编辑 <tt>app/views/posts/show.html.erb</tt> 模板显示我们的 tags:</p>
<div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false"><p id="notice"><%= notice %></p>
 
<p>
   <b>Name:</b>
   <%= @post.name %>
</p>
 
<p>
   <b>Title:</b>
   <%= @post.title %>
</p>
 
<p>
   <b>Content:</b>
   <%= @post.content %>
</p>
 
<p>
   <b>Tags:</b>
   <%= @post.tags.map { |t| t.name }.join(", ") %>
</p>
 
<h2>Comments</h2>
<%= render @post.comments %>
 
<h2>Add a comment:</h2>
<%= render "comments/form" %>
 
 
<%= link_to 'Edit Post', edit_post_path(@post) %> |
<%= link_to 'Back to Posts', posts_path %> |
</pre>
</div>
<p>通过这写修改,你会发现你可以直接在 post form中编辑 tags。</p>
<p>另外, <tt>@post.tags.map { |t| t.name }.join(", ")</tt> 这个方法很别扭,我们可以通过编写一个helper method。</p>
<h3 id="15">15 View Helpers</h3>
<p>View Helpers 放置在 <tt>app/helpers</tt>,它提供了可重用的小代码片段给 view。在本例,我们想要一个方法把(tag)放在一起(一个字符串中),并且使用逗号分割。要想这样在Post show 模板,我们在 PostHelper 中写入:</p>
<p>现在你可以在 <tt>app/helpers/posts_helper.rb</tt> 中更改:</p>
<div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false">module PostsHelper
   def join_tags(post)
     post.tags.map { |t| t.name }.join(", ")
   end
end
</pre>
</div>
<p>现在你可以在 <tt>app/views/posts/show.html.erb</tt> 中编辑视图,像这样:</p>
<div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false"><p id="notice"><%= notice %></p>
 
<p>
   <b>Name:</b>
   <%= @post.name %>
</p>
 
<p>
   <b>Title:</b>
   <%= @post.title %>
</p>
 
<p>
   <b>Content:</b>
   <%= @post.content %>
</p>
 
<p>
   <b>Tags:</b>
   <%= join_tags(@post) %>
</p>
 
<h2>Comments</h2>
<%= render @post.comments %>
 
<h2>Add a comment:</h2>
<%= render "comments/form" %>
 
 
<%= link_to 'Edit Post', edit_post_path(@post) %> |
<%= link_to 'Back to Posts', posts_path %> |
</pre>
</div>
<h3 id="16">16 What’s Next?</h3>
<p>现在你已经看到了你的第一个 Rails 应用程序,你应该可以很轻松的继续更新它或者试验一下你的想法。当你在更新和运行 Rails 的时候需要援助,可以随时咨询下面推荐的资源:</p>
<ul>
     <li>The <a href="index.html">Ruby on Rails guides</a></li>
     <li>The <a href="http://railstutorial.org/book">Ruby on Rails Tutorial</a></li>
     <li>The <a href="http://groups.google.com/group/rubyonrails-talk">Ruby on Rails mailing list</a></li>
     <li>The <a href="irc://irc.freenode.net/#rubyonrails">#rubyonrails</a> channel on irc.freenode.net</li>
</ul>
<p>Rails 同样也带有内置的帮助,你可以使用 rake 命令实用工具在你的应用程序中创建帮助文档:</p>
<ul>
     <li>运行 <tt>rake doc:guides</tt> 将会输出所有 Rails Guides 的文档到你的应用程序中的 <tt>doc/guides</tt> 中。在你的浏览器中打开/guides/index.html浏览 Guides。</li>
     <li>运行 <tt>rake doc:rails</tt> 将会输出所有Rails <span class="caps">API</span> 的文档到你的应用程序中的 <tt>doc/api</tt> 中。在你的浏览器中打开 <tt>doc/api/index.html</tt> 浏览API documentation。</li>
</ul>
<h3 id="17">17 Configuration Gotchas配置陷阱</h3>
<p>The easiest way to work with Rails is to store all external data as <span class="caps">UTF</span>-8. If
you don’t, Ruby libraries and Rails will often be able to convert your native
data into <span class="caps">UTF</span>-8, but this doesn’t always work reliably, so you’re better off
ensuring that all external data is <span class="caps">UTF</span>-8.
使用 Rails 最简单的工作方式是存储所有的外部数据为 <span class="caps">UTF</span>-8编码。如果不那样做,Ruby libraries 和 Rails 通常会将你的自然数据转换成 <span class="caps">UTF</span>-8 编码,但是这样不是很可靠,因此你最好保证所有的外部数据是 <span class="caps">UTF</span>-8 编码。如果你在这里犯了错误,一般的症状就是在浏览器中出现钻石符号(可能是^)变成了问号。 另一个普遍症状是 “ü” 变成了 “ü”。</p>
<p>Rails 在国际化上下了很多的功夫,大部分的此类错误都能够自动发现并错误,然而如果你有一些不是用 <span class="caps">UTF</span>-8 存储的特殊的数据恐怕就会出现一些奇怪的问题了。</p>
<p>两种典型的非 <span class="caps">UTF</span>-8 编码的源数据:</p>
<ul>
     <li>你的文本编辑器:多数文本编辑器(比如 Textmate),默认保存文件的编码格式就是 <span class="caps">UTF</span>-8。如果你的编辑器不是这样,在浏览器中,你输入模板的符号(比如 é )看起来像是带问号的钻石符。你的 I18N 翻译文件可能也会这样。多数默认不是 <span class="caps">UTF</span>-8 编码的编辑器提供了将编码转换为 <span class="caps">UTF</span>-8 的功能,就用它吧。</li>
     <li>你的数据库。Rails 默认将数据库读出的数据转换成 <span class="caps">UTF</span>-8 编码。然而,如果你的数据库内部不是用 <span class="caps">UTF</span>-8 编码,它可能无法将用户输入的字符全部保存起来。比如,如果你的数据库内部用 Latin-1 编码,用户输入了俄语、希伯来语或者日语字符的话,这些数据进入数据库后就会永久消失。所以请尽可能的使用 <span class="caps">UTF</span>-8 作为你的数据库的默认编码方式。<p></p></li></ul></pre></div>
 
                 <h3>关于 Rails Guides 中文</h3>
         <p>
           你也可以为 Rails Guides 中文做出贡献.
         </p>
         <p>
           如果你发现了任何翻译上的错误或者不足.请到 <a href="https://github.com/ruby-china/rails-guides/">GitHub 帐号</a> clone 并且 fork 一份进行更正提交,我们会很快做出回应。
           如果你发现了任何代码上的 bug 或者不足,你可以到 <a href="https://github.com/lifo/docrails">docrails</a> 进行反馈与更正。
           如果你发现了某一篇文章已经过时了并且并没有做出相应的提示,你也同样可以在 <a href="https://github.com/ruby-china/rails-guides/issues">这里</a> 上发起一个请求来提醒我们。
         </p>
         <p> 如果对 Rails 有什么建议的话去 <a href="https://github.com/rails/rails/issues/">这里</a>.  </p>
         <p>当然你不管你有什么想要说的都可以发到 <a href="http://ruby-china.org">Ruby China</a> 讨论或者 <a href="mailto:cool.zhikai@gmail.com">mail我</a>.欢迎加入到 Ruby on Rails 社区!</p>
Python网络爬虫与推荐算法新闻推荐平台:网络爬虫:通过Python实现新浪新闻的爬取,可爬取新闻页面上的标题、文本、图片、视频链接(保留排版) 推荐算法:权重衰减+标签推荐+区域推荐+热点推荐.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值