和rails 2.0一起摇摆- rails2.0全面教程-第一部分
一篇关于介绍 rails 2.0的文章,共分两部分。
以下这篇文章翻译自这里 ,还是新人翻译不好请大家多多指教.
我很高兴的看到 我的rails2.0视频教程被广泛接受。超过1500人次看过它,做他的目的是想简单快速地介绍下rails2.0,展示下那些可以在30分钟以内做到的事。
现在,我将要把那段视频分成几个主要的部分,并且建立一个包含一些新特性的逐步讲解的rails2.0教程.
像其他教程一样,他并不能涵盖rails2.0 100%的特性,详细的我推荐你去看看
Peepcode’s Rails2 PDF 和Ryan Bates Railscasts.com .
这是一个两部分教程,第二部分请点击这里 关于这个教程的全部源代码在这里。
让我们开始把。
Recognizing the Environment
这个教程是面向那些已经掌握了一些rails1.2知识的人,请参考互联网上许多优秀的关于 rails1.2的教程。
一开始你所要做的事更新你的gems:
Ruby代码 复制代码
1. sudo gem install rails --include-dependencies
sudo gem install rails --include-dependencies
你可能还想要更新的你的 RubyGems:
Ruby代码 复制代码
1. sudo gem update --system
sudo gem update --system
首先,我们创建一个rails应用程序:
Ruby代码 复制代码
1. rails blog
rails blog
这将会创建我们常见的rails 文档结构。首先注意到的是开发环境。这是我们现在拥有的主要结构:
* config/environment.rb
* config/initializers/inflections.rb
* config/initializers/mime_types.rb
所有在 config/initializers 文件夹里的东西将会和environment.rb 同时被载入。那是因为当你的项目使用了许多不同的插件和gems的时候,environment.rb会变得越来越臃肿并且难以维护。现在我们有了一个更简单的方式去模块化管理我们的配置。
Database
第二个要配置的东西是我们的数据库。这和以前要干的事没两样,都是在 config/database.yml:
Ruby代码 复制代码
1. development:
2. adapter: mysql
3. encoding: utf8
4. database: blog_development
5. username: root
6. password: root
7. socket: /opt/local/var/run/mysql5/mysqld.sock
8.
9. test:
10. adapter: mysql
11. encoding: utf8
12. database: blog_test
13. username: root
14. password: root
15. socket: /opt/local/var/run/mysql5/mysqld.sock
16.
17. production:
18. adapter: mysql
19. encoding: utf8
20. database: blog_production
21. username: root
22. password: root
23. socket: /opt/local/var/run/mysql5/mysqld.sock
development:
adapter: mysql
encoding: utf8
database: blog_development
username: root
password: root
socket: /opt/local/var/run/mysql5/mysqld.sock
test:
adapter: mysql
encoding: utf8
database: blog_test
username: root
password: root
socket: /opt/local/var/run/mysql5/mysqld.sock
production:
adapter: mysql
encoding: utf8
database: blog_production
username: root
password: root
socket: /opt/local/var/run/mysql5/mysqld.sock
注意到现在你有一个‘encoding’ 选项默认设置成 utf-8. rails 应用程序也是默认读取 KCODE=true, 意味着他一开始就应经支持了UNICODE了,这是相当棒的。但"encoding"配置还有另个用途:每个连接到数据库的rails都会被告之使用 encoding的设置。就像是设置了 '"SET NAMES UTF8"
一个小技巧用来对 database.yml实现 DRY 原则的是:
Ruby代码 复制代码
1. defaults: &defaults
2. adapter: mysql
3. encoding: utf8
4. username: root
5. password: root
6. socket: /opt/local/var/run/mysql5/mysqld.sock
7.
8. development:
9. database: blog_development
10. <<: *defaults
11.
12. test:
13. database: blog_test
14. <<: *defaults
15.
16. production:
17. database: blog_production
18. <<: *defaults
defaults: &defaults
adapter: mysql
encoding: utf8
username: root
password: root
socket: /opt/local/var/run/mysql5/mysqld.sock
development:
database: blog_development
<<: *defaults
test:
database: blog_test
<<: *defaults
production:
database: blog_production
<<: *defaults
好多了,我们有个新的rask任务。并且其中的一些和数据库有关:
db:charset Retrieves the charset for the current environment’s database
db:collation Retrieves the collation for the current environment’s database
db:create Create the database defined in config/database.yml for the current RAILS_ENV
db:create:all Create all the local databases defined in config/database.yml
db:drop Drops the database for the current RAILS_ENV
db:drop:all Drops all the local databases defined in config/database.yml
db:reset Drops and recreates the database from db/schema.rb for the current environment.
db:rollback Rolls the schema back to the previous version. Specify the number of steps with STEP=n
db:version Retrieves the current schema version number
我们有更好的数据库管理支持。在以前,我们得登入到数据库控制台然后手动创建这些数据库,现在我们只要简单地:
rake db:create:all
如果我们现在想要重头做起,我们可以 db:drop:all. 如果是在开发当中我们可以利用 db:rollback 回退到上个版本。
Sexyness(性感)
在数据库的设置好了下面我们就可以创建我们的第一个资源。记得吧,现在 rails2.0是默认 RESTful 的。(for brazilians: 我也另外写了篇关于restful 的教程)。
.
/script/generate scaffold Post title:string body:text
这里唯一不同的是'scaffold'的用处就和我们以前用的'scaffold_resource'一样,和那个旧的并不是 restful 的 scaffold 也已经没了。你现在也没有 ActionController类方法'scaffold'来动态创建一个带有默认action的空的控制器了。所以任何我们所scaffold 的东西都变成 RESTful的了。
他会创建那些常见的东西:Controller, Helper, Model, Migration, Unit Test, Functional Test.
主要的不同是在迁移文件:
Ruby代码 复制代码
1. # db/migrate/001_create_posts.rb
2. class CreatePosts < ActiveRecord::Migration
3. def self.up
4. create_table :posts do |t|
5. t.string :title
6. t.text :body
7.
8. t.timestamps
9. end
10. end
11.
12. def self.down
13. drop_table :posts
14. end
15. end
# db/migrate/001_create_posts.rb
class CreatePosts < ActiveRecord::Migration
def self.up
create_table :posts do |t|
t.string :title
t.text :body
t.timestamps
end
end
def self.down
drop_table :posts
end
end
这叫做 性感迁移(Sexy Migrations),首先发明自 “Err the Blog” 作为一个插件随之进入到了rails内核中。了解他们有何异同最好的方式就是去看看他在rails1.2中长什么样:
Ruby代码 复制代码
1. class CreatePosts < ActiveRecord::Migration
2. def self.up
3. create_table :posts do |t|
4. t.column :title, :string
5. t.column :body, :text
6. t.column :created_at, :datetime
7. t.column :updated_at, :datetime
8. end
9. end
10.
11. def self.down
12. drop_table :posts
13. end
14. end
class CreatePosts < ActiveRecord::Migration
def self.up
create_table :posts do |t|
t.column :title, :string
t.column :body, :text
t.column :created_at, :datetime
t.column :updated_at, :datetime
end
end
def self.down
drop_table :posts
end
end
他去除了重复的't.column',现在使用‘t.column_type’的格式并且自动加上的时间戳被浓缩成了一行语句‘t.timestamps’.并没有改变什么行为,只是让代码更加"性感"些
现在,像以前一样运行一个迁移任务
rake db:migrate
在以前,如果我要回退到某一个数据迁移我需要这样做:
rake db:migrate VERSION=xxx
'xxx'是代表我们想要回退到的版本号,现在我们仅仅需要:
rake db:rollback
更加的简洁优雅,这是肯定的。一切都设置好了,现在我们可以像以前一样启动服务器,看看生成的页面。
./script/server
他会自动载入不管是在 Mongrel, Webrick 或 Lightpd 在 3000 端口。我们和以前一样拥有一个根页面,为 index.html.有一个小点我在视频里面没有提到的是:
Ruby代码 复制代码
1. # config/routes.rb
2. ActionController::Routing::Routes.draw do |map|
3. map.root :controller => 'posts'
4. map.resources :posts
5. end
# config/routes.rb
ActionController::Routing::Routes.draw do |map|
map.root :controller => 'posts'
map.resources :posts
end
这里有一个新的语句‘map.root’,该语句和 “map.connect ’’, :controller => ‘posts’.有同样的效果。只是一点小小的花招并没有做什么大动作只是让路由文件看起来更优美些。一旦设置了它,别忘了了删除 public/index.html 文件。 根URL就总会指向POSTS控制器。
正如你所看到的,一切都和以前一样。所有的脚手架模板都还是一样。你可以浏览一下,创建一些新行等等。
嵌套路由
让我们来创建一些和POST有关的COMMENT。他会完成创建我们 blog的资源:
Ruby代码 复制代码
1. ./script/generate scaffold Comment post:references body:text
2. rake db:migrate
./script/generate scaffold Comment post:references body:text
rake db:migrate
和上面一样:scaffold一下资源,在命令行中设置组名和数据类型然后迁移文件就会自动设置好。注意到另外一个新增的东西:关键字”references“ 正如 许多朋友提醒我的,这会使数据迁移更性感。
比较起来,这是以前做这件事用的方法:
Ruby代码 复制代码
1. ./script/generate scaffold Comment post:references body:text
./script/generate scaffold Comment post:references body:text
外键只是一个无关紧要的实现细节,看看新的数据迁移文件:
Ruby代码 复制代码
1. def self.up
2. create_table :comments do |t|
3. t.references :post
4. t.text :body
5.
6. t.timestamps
7. end
8. end
def self.up
create_table :comments do |t|
t.references :post
t.text :body
t.timestamps
end
end
看这里关于新的关键字” references“的细节。所以,运行 db:migrate 将在数据库中创建表。
然后我们设置 ActiveRecord模型让他们和彼此互相关联,像这样:
Ruby代码 复制代码
1. # app/models/post.rb
2. class Post < ActiveRecord::Base
3. has_many :comments
4. end
5.
6. # app/models/comment.rb
7. class Comment < ActiveRecord::Base
8. belongs_to :post
9. end
# app/models/post.rb
class Post < ActiveRecord::Base
has_many :comments
end
# app/models/comment.rb
class Comment < ActiveRecord::Base
belongs_to :post
end
好了,这里没有什么新东西了,我们已经知道怎么处理 ActiveRecord关联了 ,但我们依旧工作在RESTFUL资源环境下。在新的RAILS方式里,我们可以拥有像这样的url:
http://localhost:3000/posts/1/comments
http://localhost:3000/posts/1/comments/new
http://localhost:3000/posts/1/comments/3
意思是 从这个具体的POST里取出相关的comment,脚手架生成器只准备了如下这种url:
http://localhost:3000/posts/1
http://localhost:3000/comments/new
http://localhost:3000/comments/3
那是因为在config/routes.rb 中:
Ruby代码 复制代码
1. # config/routes.rb
2. ActionController::Routing::Routes.draw do |map|
3. map.resources :comments
4.
5. map.root :controller => 'posts'
6. map.resources :posts
7. end
# config/routes.rb
ActionController::Routing::Routes.draw do |map|
map.resources :comments
map.root :controller => 'posts'
map.resources :posts
end
让我们稍微调整一下,就像在model中,我们可以这样调用一个嵌套路由:
Ruby代码 复制代码
1. # config/routes.rb
2. ActionController::Routing::Routes.draw do |map|
3. map.root :controller => 'posts'
4. map.resources :posts, :has_many => :comments
5. end
# config/routes.rb
ActionController::Routing::Routes.draw do |map|
map.root :controller => 'posts'
map.resources :posts, :has_many => :comments
end
就像这样! 现在我们可以想上面这样嵌套url. 首先要理解的是当我打入这样的URL:http://localhost:3000/posts/1/comments
rails 会将它解析成这样:
* 载入控制器
* 设置 params[:post_id] = 1
* 在这种情况下,调用' index ' 的action.
我们必须准备让CommentsController被嵌套。所以这是我们接下来要去修改的:
Ruby代码 复制代码
1. class CommentsController < ApplicationController
2. before_filter :load_post
3. ...
4. def load_post
5. @post = Post.find(params[:post_id])
6. end
7. end
class CommentsController < ApplicationController
before_filter :load_post
...
def load_post
@post = Post.find(params[:post_id])
end
end
这是的 @post已经在所有的术语comments控制器中的action中设置好了。现在我们必须做这些修改:
之前 修改后
Comment.find @post.comments.find
Comment.new @post.comments.build
redirect_to(@comment) redirect_to([@post, @comment])
redirect_to(comments_url) redirect_to(post_comments_url(@post))
这是的comment 控制器准备好了。现在让我们改变一下4个在 app/views/comments中的视图。
如果你打开不管是 new.html.erb 或者是 edit.html.erb你将会注意到如下这些新的特性:
Ruby代码 复制代码
1. # new edit.html.erb and new.html.erb
2. form_for(@comment) do |f|
3. ...
4. end
# new edit.html.erb and new.html.erb
form_for(@comment) do |f|
...
end
这是新的定义方式:
Ruby代码 复制代码
1. # old new.rhtml
2. form_for(:comment, :url => comments_url) do |f|
3. ...
4. end
# old new.rhtml
form_for(:comment, :url => comments_url) do |f|
...
end
Ruby代码 复制代码
1. # old edit.rhtml
2. form_for(:comment, :url => comment_url(@comment),
3. :html => { :method => :put }) do |f|
4. ...
5. end
# old edit.rhtml
form_for(:comment, :url => comment_url(@comment),
:html => { :method => :put }) do |f|
...
end
注意到同样的 form_for 语句是怎样适应 'new' 和 'edit' 的情况。
这是因为Rails可以根据 @comment模型实例的类名去推断出做些什么,但是现在
对于嵌套路由来说,comment是依赖于post,所以我们必须这样做:
Ruby代码 复制代码
1. # new edit.html.erb and new.html.erb
2. form_for([@post, @comment]) do |f|
3. ...
4. end
# new edit.html.erb and new.html.erb
form_for([@post, @comment]) do |f|
...
end
Rails会变得足够聪明去辨识这个数组是表示一个嵌套路由,它会去检查 routes.rb 并且找出
他是post_comment_url(@post, @comment)的具名路由
让我们先来解释下具名路由。当我们在routes.rb设置资源路由时,我们可以得到下列具名路由:
oute HTTP verb Controller Action
comments GET index
comments POST create
comment(:id) GET show
comment(:id) PUT update
comment(:id) DELETE destroy
new_comment GET new
edit_comment(:id) GET edit
“七个 Action足以对付一切 …” :-)
你可以给他们加上 'path'或是'url'的后缀,不同在:
comments_url http://localhost:3000/comments
comments_path /comments
最后你可以给他们加上 'formatted'的前缀,给你:
comments_url http://localhost:3000/comments
comments_path /comments
现在,当comment已经嵌套在post里面,我们必须加上 'post'前缀,在 rails1.2里,这个前缀是可选择的,
他将能够依据传到具名路由helper里的的数字或者参数区分它们。但这会带来许多歧义性,所以现在必须强制加上前缀,像这样:
route HTTP verb URL
post_comments(@post) GET /posts/:post_id/comments
post_comments(@post) POST /posts/:post_id/comments
post_comment(@post, :id) GET /posts/:post_id/comments/:id
post_comment(@post, :id) PUT /posts/:post_id/comments/:id
post_comment(@post, :id) DELETE /posts/:post_id/comments/:id
new_post_comment(@post) GET /posts/:post_id/comments/new
edit_post_comment(@post, :id) GET /posts/:post_id/comments/edit
所以,总结起来,我们必须让 comments 视图的行为更像是嵌套在一个POST里一样。
所以我们还必须对从默认脚手架生成的代码到内嵌的表格中的具名路由做些改变:
Ruby代码 复制代码
1. <!-- app/views/comments/_comment.html.erb -->
2. <% form_for([@post, @comment]) do |f| %>
3. <p>
4. <b>Body</b><br />
5. <%= f.text_area :body %>
6. </p>
7.
8. <p>
9. <%= f.submit button_name %>
10. </p>
11. <% end %>
<!-- app/views/comments/_comment.html.erb -->
<% form_for([@post, @comment]) do |f| %>
<p>
<b>Body</b><br />
<%= f.text_area :body %>
</p>
<p>
<%= f.submit button_name %>
</p>
<% end %>
Ruby代码 复制代码
1. <!-- app/views/comments/edit.html.erb -->
2. <h1>Editing comment</h1>
3.
4. <%= error_messages_for :comment %>
5.
6. <%= render :partial => @comment,
7. :locals => { :button_name => "Update"} %>
8.
9. <%= link_to 'Show', [@post, @comment] %> |
10. <%= link_to 'Back', post_comments_path(@post) %>
<!-- app/views/comments/edit.html.erb -->
<h1>Editing comment</h1>
<%= error_messages_for :comment %>
<%= render :partial => @comment,
:locals => { :button_name => "Update"} %>
<%= link_to 'Show', [@post, @comment] %> |
<%= link_to 'Back', post_comments_path(@post) %>
Ruby代码 复制代码
1. <!-- app/views/comments/new.html.erb -->
2. <h1>New comment</h1>
3.
4. <%= error_messages_for :comment %>
5.
6. <%= render :partial => @comment,
7. :locals => { :button_name => "Create"} %>
8.
9. <%= link_to 'Back', post_comments_path(@post) %>
<!-- app/views/comments/new.html.erb -->
<h1>New comment</h1>
<%= error_messages_for :comment %>
<%= render :partial => @comment,
:locals => { :button_name => "Create"} %>
<%= link_to 'Back', post_comments_path(@post) %>
Ruby代码 复制代码
1. <!-- app/views/comments/show.html.erb -->
2. <p>
3. <b>Body:</b>
4. <%=h @comment.body %>
5. </p>
6.
7.
8. <%= link_to 'Edit', [:edit, @post, @comment] %> |
9. <%= link_to 'Back', post_comments_path(@post) %>
<!-- app/views/comments/show.html.erb -->
<p>
<b>Body:</b>
<%=h @comment.body %>
</p>
<%= link_to 'Edit', [:edit, @post, @comment] %> |
<%= link_to 'Back', post_comments_path(@post) %>
Ruby代码 复制代码
1. <!-- app/views/comments/index.html.erb -->
2. <h1>Listing comments</h1>
3.
4. <table>
5. <tr>
6. <th>Post</th>
7. <th>Body</th>
8. </tr>
9.
10. <% for comment in @comments %>
11. <tr>
12. <td><%=h comment.post_id %></td>
13. <td><%=h comment.body %></td>
14. <td><%= link_to 'Show', [@post, comment] %></td>
15. <td><%= link_to 'Edit', [:edit, @post, comment] %></td>
16. <td><%= link_to 'Destroy', [@post, comment],
17. :confirm => 'Are you sure?', :method => :delete %></td>
18. </tr>
19. <% end %>
20. </table>
21.
22. <br />
23.
24. <%= link_to 'New comment',
25. new_post_comment_path(@post) %>
<!-- app/views/comments/index.html.erb -->
<h1>Listing comments</h1>
<table>
<tr>
<th>Post</th>
<th>Body</th>
</tr>
<% for comment in @comments %>
<tr>
<td><%=h comment.post_id %></td>
<td><%=h comment.body %></td>
<td><%= link_to 'Show', [@post, comment] %></td>
<td><%= link_to 'Edit', [:edit, @post, comment] %></td>
<td><%= link_to 'Destroy', [@post, comment],
:confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<% end %>
</table>
<br />
<%= link_to 'New comment',
new_post_comment_path(@post) %>
提几点:
注意到我创建了一个局部模板去 DRY(不要重复你自己) new 和 edit中表格。但请注意,
并非使用 :partial=> 'comment' 而使用 :partial=>@comment.
然后再一次他能够从类名推断出局部模板的名字。如果我们传进一个集合他会将其转换成旧的语句 ':partial,:collection'
我可以使用post_comment_path(@post, @comment),或是更简单的 [@post, @comment]
请注意不要忘记背后的那些具名路由。
最后,最好将comment列表页的链接放到POST视图里,我们这样做:
Ruby代码 复制代码
1. <!-- app/views/posts/show.html.erb -->
2. <%= link_to 'Comments', post_comments_path(@post) %>
3. <%= link_to 'Edit', edit_post_path(@post) %> |
4. <%= link_to 'Back', posts_path %>
<!-- app/views/posts/show.html.erb -->
<%= link_to 'Comments', post_comments_path(@post) %>
<%= link_to 'Edit', edit_post_path(@post) %> |
<%= link_to 'Back', posts_path %>
我只是加了个链接,让我们瞧瞧他看起来怎么样:
完成视图
好了,看起来还不错。但这并不像 一个blog的行为! POST的show 视图应该有些comment罗列在那里,并且新建评论的表格应该也在那里。所以让我们做些小改变。这里没有什么新的,只是传统的rails。让我们从视图开始:
Ruby代码 复制代码
1. <!-- app/views/posts/show.html.erb -->
2. <p>
3. <b>Title:</b>
4. <%=h @post.title %>
5. </p>
6.
7. <p>
8. <b>Body:</b>
9. <%=h @post.body %>
10. </p>
11.
12. <!-- #1 -->
13. <% unless @post.comments.empty? %>
14. <h3>Comments</h3>
15. <% @post.comments.each do |comment| %>
16. <p><%= h comment.body %></p>
17. <% end %>
18. <% end %>
19.
20. <!-- #2 -->
21. <h3>New Comment</h3>
22. <%= render :partial => @comment = Comment.new,
23. :locals => { :button_name => 'Create'}%>
24.
25. <%= link_to 'Comments', post_comments_path(@post) %>
26. <%= link_to 'Edit', edit_post_path(@post) %> |
27. <%= link_to 'Back', posts_path %>
<!-- app/views/posts/show.html.erb -->
<p>
<b>Title:</b>
<%=h @post.title %>
</p>
<p>
<b>Body:</b>
<%=h @post.body %>
</p>
<!-- #1 -->
<% unless @post.comments.empty? %>
<h3>Comments</h3>
<% @post.comments.each do |comment| %>
<p><%= h comment.body %></p>
<% end %>
<% end %>
<!-- #2 -->
<h3>New Comment</h3>
<%= render :partial => @comment = Comment.new,
:locals => { :button_name => 'Create'}%>
<%= link_to 'Comments', post_comments_path(@post) %>
<%= link_to 'Edit', edit_post_path(@post) %> |
<%= link_to 'Back', posts_path %>
再提几点:
1.迭代中并没有什么新的东西,只是列出一些评论
2.再一次,我们传进@comment给 partial语句
最后一个小调整,任何时候我们创建一个新POST,我们会想回到同样的POST中的show视图,所以我们改变CommentController的行为像这样:
Ruby代码 复制代码
1. # app/controllers/comments_controller.rb
2. # old redirect:
3. redirect_to([@post, @comment])
4. # new redirect:
5. redirect_to(@post)
# app/controllers/comments_controller.rb
# old redirect:
redirect_to([@post, @comment])
# new redirect:
redirect_to(@post)
命名空间路由
好了,现在我们有一个骨瘦如柴的迷你博客像是模仿经典david在2005念做的一段15分钟创建一个BLOG的视频。现在我们更进一步:Post不应该让所有的人都可以去编辑他,我们的网站需要一个管理部分。让我们为它创建一个控制器:
./script/generate controller Admin::Posts
rails2.0 现在支持命名空间。他会创建一个子目录叫 app/controllers/admin.
我们所要作的是:
1.创建一个新的路由
2.把所有在旧的 posts控制器中的 action复制到新的 Admin::posts中
3.复制所有旧的posts视图到app/views/admin* ,在旧的posts 控制器中只留下 ‘index’和‘show’ 这两个action,
这意味着也要删除new 和 edit.
4.修改 我们刚刚复制的actions和views,让他能够知道他是在admin控制器中
首先,我们再次编辑 config/routes.rb:
Ruby代码 复制代码
1. map.namespace :admin do |admin|
2. admin.resources :posts
3. end
map.namespace :admin do |admin|
admin.resources :posts
end
这意味着我们现在有了带着 'admin'前缀的 posts的具名路由。这会使旧的POST路由和新的
admin post路由不会想混。像这样:
posts_path /posts
post_path(@post) /posts/:post_id
admin_posts_path /admin/posts
admin_post_path(@post) /admin/posts/:post_id
现在让我们从旧的POST控制器中拷贝ACTION并修改路由地址去适应新的命名空间:
Ruby代码 复制代码
1. # app/controllers/admin/posts_controller.rb
2. ...
3. def create
4. # old:
5. format.html { redirect_to(@post) }
6. # new:
7. format.html { redirect_to([:admin, @post]) }
8. end
9.
10. def update
11. # old:
12. format.html { redirect_to(@post) }
13. # new:
14. format.html { redirect_to([:admin, @post]) }
15. end
16.
17. def destroy
18. # old:
19. format.html { redirect_to(posts_url) }
20. # new:
21. format.html { redirect_to(admin_posts_url) }
22. end
23. ...
# app/controllers/admin/posts_controller.rb
...
def create
# old:
format.html { redirect_to(@post) }
# new:
format.html { redirect_to([:admin, @post]) }
end
def update
# old:
format.html { redirect_to(@post) }
# new:
format.html { redirect_to([:admin, @post]) }
end
def destroy
# old:
format.html { redirect_to(posts_url) }
# new:
format.html { redirect_to(admin_posts_url) }
end
...
不要忘记删除所有在app/controllers/posts_controller.rb中的方法,只要留下 ‘index’ 和‘show’两个方法。
现在,让我们拷贝视图(假设你的 shell已经在项目的根文件夹下):
cp app/views/posts/*.erb app/views/admin/posts
rm app/views/posts/new.html.erb
rm app/views/posts/edit.html.erb
现在让我们编辑 app/views/admin/posts中的视图:
Ruby代码 复制代码
1. <!-- app/views/admin/posts/edit.html.erb -->
2. <h1>Editing post</h1>
3.
4. <%= error_messages_for :post %>
5.
6. <% form_for([:admin, @post]) do |f| %>
7. ...
8. <% end %>
9.
10. <%= link_to 'Show', [:admin, @post] %> |
11. <%= link_to 'Back', admin_posts_path %>
<!-- app/views/admin/posts/edit.html.erb -->
<h1>Editing post</h1>
<%= error_messages_for :post %>
<% form_for([:admin, @post]) do |f| %>
...
<% end %>
<%= link_to 'Show', [:admin, @post] %> |
<%= link_to 'Back', admin_posts_path %>
Ruby代码 复制代码
1. <!-- app/views/admin/posts/new.html.erb -->
2. <h1>New post</h1>
3.
4. <%= error_messages_for :post %>
5.
6. <% form_for([:admin, @post]) do |f| %>
7. ...
8. <% end %>
9.
10. <%= link_to 'Back', admin_posts_path %>
<!-- app/views/admin/posts/new.html.erb -->
<h1>New post</h1>
<%= error_messages_for :post %>
<% form_for([:admin, @post]) do |f| %>
...
<% end %>
<%= link_to 'Back', admin_posts_path %>
Ruby代码 复制代码
1. <!-- app/views/admin/posts/show.html.erb -->
2. <p>
3. <b>Title:</b>
4. <%=h @post.title %>
5. </p>
6.
7. <p>
8. <b>Body:</b>
9. <%=h @post.body %>
10. </p>
11.
12. <%= link_to 'Edit', edit_admin_post_path(@post) %> |
13. <%= link_to 'Back', admin_posts_path %>
<!-- app/views/admin/posts/show.html.erb -->
<p>
<b>Title:</b>
<%=h @post.title %>
</p>
<p>
<b>Body:</b>
<%=h @post.body %>
</p>
<%= link_to 'Edit', edit_admin_post_path(@post) %> |
<%= link_to 'Back', admin_posts_path %>
Ruby代码 复制代码
1. <!-- app/views/admin/posts/index.html.erb -->
2. ...
3. <% for post in @posts %>
4. <tr>
5. <td><%=h post.title %></td>
6. <td><%=h post.body %></td>
7. <td><%= link_to 'Show', [:admin, post] %></td>
8. <td><%= link_to 'Edit', edit_admin_post_path(post) %></td>
9. <td><%= link_to 'Destroy', [:admin, post],
10. :confirm => 'Are you sure?', :method => :delete %></td>
11. </tr>
12. <% end %>
13. </table>
14.
15. <br />
16.
17. <%= link_to 'New post', new_admin_post_path %>
<!-- app/views/admin/posts/index.html.erb -->
...
<% for post in @posts %>
<tr>
<td><%=h post.title %></td>
<td><%=h post.body %></td>
<td><%= link_to 'Show', [:admin, post] %></td>
<td><%= link_to 'Edit', edit_admin_post_path(post) %></td>
<td><%= link_to 'Destroy', [:admin, post],
:confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<% end %>
</table>
<br />
<%= link_to 'New post', new_admin_post_path %>
基本上完成了:如果你测试 http://localhost:3000/admin/posts,他应该能正常的工作。但看起来却很丑陋,那是因为我们没有全局的布局模板。
当我们生成第一个脚手架时,rails为post和 comment生成各自相关的布局模板。所以让我们删掉他们并且创建一个通用的:
cp app/views/layouts/posts.html.erb \
app/views/layouts/application.html.erb
rm app/views/layouts/posts.html.erb
rm app/views/layouts/comments.html.erb
然后让我们改一下标题:
Java代码 复制代码
1. <!-- app/views/layouts/application.html.erb -->
2. ...
3. <title>My Great Blog</title>
4. ...
<!-- app/views/layouts/application.html.erb -->
...
<title>My Great Blog</title>
...
他只剩下旧的在posts控制器里的'index' 和 ‘show’页面,他们仍然拥有我们有链接到我们删除过的方法的链接,所以我们可以删掉他们。
Ruby代码 复制代码
1. <!-- app/views/posts/index.html.erb -->
2. <h1>My Great Blog</h1>
3.
4. <table>
5. <tr>
6. <th>Title</th>
7. <th>Body</th>
8. </tr>
9.
10. <% for post in @posts %>
11. <tr>
12. <td><%=h post.title %></td>
13. <td><%=h post.body %></td>
14. <td><%= link_to 'Show', post %></td>
15. </tr>
16. <% end %>
17. </table>
<!-- app/views/posts/index.html.erb -->
<h1>My Great Blog</h1>
<table>
<tr>
<th>Title</th>
<th>Body</th>
</tr>
<% for post in @posts %>
<tr>
<td><%=h post.title %></td>
<td><%=h post.body %></td>
<td><%= link_to 'Show', post %></td>
</tr>
<% end %>
</table>
Ruby代码 复制代码
1. <!-- app/views/posts/show.html.erb -->
2. <p>
3. <b>Title:</b>
4. <%=h @post.title %>
5. </p>
6.
7. <p>
8. <b>Body:</b>
9. <%=h @post.body %>
10. </p>
11.
12. <% unless @post.comments.empty? %>
13. <h3>Comments</h3>
14. <% @post.comments.each do |comment| %>
15. <p><%= h comment.body %></p>
16. <% end %>
17. <% end %>
18.
19. <h3>New Comment</h3>
20.
21. <%= render :partial => @comment = Comment.new,
22. :locals => { :button_name => 'Create'}%>
23.
24. <%= link_to 'Back', posts_path %>
<!-- app/views/posts/show.html.erb -->
<p>
<b>Title:</b>
<%=h @post.title %>
</p>
<p>
<b>Body:</b>
<%=h @post.body %>
</p>
<% unless @post.comments.empty? %>
<h3>Comments</h3>
<% @post.comments.each do |comment| %>
<p><%= h comment.body %></p>
<% end %>
<% end %>
<h3>New Comment</h3>
<%= render :partial => @comment = Comment.new,
:locals => { :button_name => 'Create'}%>
<%= link_to 'Back', posts_path %>
我们可以从浏览器中测试任何东西,进入 http://localhost:3000/admin/posts ,就能看到一切都工作的很好。但是,我们依旧少了样东西:一个系统管理部分不应该公开。现在你可以进去编辑任何东西了。我们需要认证。
HTTP基本认证
有许多实现验证和授权的方式。一个用的很广泛的插件是restful_authentication.
但是在这里我们不想做些太炫的东西。而RAILS2.0给了我们一个很好的方式去做验证。这个就是:我们用HTTP已经给我们的东西:HTTP基本认真。
缺点是:当在生产环境下时你肯定会想用SSL。当然,你还是会这样做。HTML 表单验证并没有让SSL保护。
所以,让我们编辑我们的Admin::Posts控制器,添加验证功能:
以下这篇文章翻译自这里 ,还是新人翻译不好请大家多多指教.
我很高兴的看到 我的rails2.0视频教程被广泛接受。超过1500人次看过它,做他的目的是想简单快速地介绍下rails2.0,展示下那些可以在30分钟以内做到的事。
现在,我将要把那段视频分成几个主要的部分,并且建立一个包含一些新特性的逐步讲解的rails2.0教程.
像其他教程一样,他并不能涵盖rails2.0 100%的特性,详细的我推荐你去看看
Peepcode’s Rails2 PDF 和Ryan Bates Railscasts.com .
这是一个两部分教程,第二部分请点击这里 关于这个教程的全部源代码在这里。
让我们开始把。
Recognizing the Environment
这个教程是面向那些已经掌握了一些rails1.2知识的人,请参考互联网上许多优秀的关于 rails1.2的教程。
一开始你所要做的事更新你的gems:
Ruby代码 复制代码
1. sudo gem install rails --include-dependencies
sudo gem install rails --include-dependencies
你可能还想要更新的你的 RubyGems:
Ruby代码 复制代码
1. sudo gem update --system
sudo gem update --system
首先,我们创建一个rails应用程序:
Ruby代码 复制代码
1. rails blog
rails blog
这将会创建我们常见的rails 文档结构。首先注意到的是开发环境。这是我们现在拥有的主要结构:
* config/environment.rb
* config/initializers/inflections.rb
* config/initializers/mime_types.rb
所有在 config/initializers 文件夹里的东西将会和environment.rb 同时被载入。那是因为当你的项目使用了许多不同的插件和gems的时候,environment.rb会变得越来越臃肿并且难以维护。现在我们有了一个更简单的方式去模块化管理我们的配置。
Database
第二个要配置的东西是我们的数据库。这和以前要干的事没两样,都是在 config/database.yml:
Ruby代码 复制代码
1. development:
2. adapter: mysql
3. encoding: utf8
4. database: blog_development
5. username: root
6. password: root
7. socket: /opt/local/var/run/mysql5/mysqld.sock
8.
9. test:
10. adapter: mysql
11. encoding: utf8
12. database: blog_test
13. username: root
14. password: root
15. socket: /opt/local/var/run/mysql5/mysqld.sock
16.
17. production:
18. adapter: mysql
19. encoding: utf8
20. database: blog_production
21. username: root
22. password: root
23. socket: /opt/local/var/run/mysql5/mysqld.sock
development:
adapter: mysql
encoding: utf8
database: blog_development
username: root
password: root
socket: /opt/local/var/run/mysql5/mysqld.sock
test:
adapter: mysql
encoding: utf8
database: blog_test
username: root
password: root
socket: /opt/local/var/run/mysql5/mysqld.sock
production:
adapter: mysql
encoding: utf8
database: blog_production
username: root
password: root
socket: /opt/local/var/run/mysql5/mysqld.sock
注意到现在你有一个‘encoding’ 选项默认设置成 utf-8. rails 应用程序也是默认读取 KCODE=true, 意味着他一开始就应经支持了UNICODE了,这是相当棒的。但"encoding"配置还有另个用途:每个连接到数据库的rails都会被告之使用 encoding的设置。就像是设置了 '"SET NAMES UTF8"
一个小技巧用来对 database.yml实现 DRY 原则的是:
Ruby代码 复制代码
1. defaults: &defaults
2. adapter: mysql
3. encoding: utf8
4. username: root
5. password: root
6. socket: /opt/local/var/run/mysql5/mysqld.sock
7.
8. development:
9. database: blog_development
10. <<: *defaults
11.
12. test:
13. database: blog_test
14. <<: *defaults
15.
16. production:
17. database: blog_production
18. <<: *defaults
defaults: &defaults
adapter: mysql
encoding: utf8
username: root
password: root
socket: /opt/local/var/run/mysql5/mysqld.sock
development:
database: blog_development
<<: *defaults
test:
database: blog_test
<<: *defaults
production:
database: blog_production
<<: *defaults
好多了,我们有个新的rask任务。并且其中的一些和数据库有关:
db:charset Retrieves the charset for the current environment’s database
db:collation Retrieves the collation for the current environment’s database
db:create Create the database defined in config/database.yml for the current RAILS_ENV
db:create:all Create all the local databases defined in config/database.yml
db:drop Drops the database for the current RAILS_ENV
db:drop:all Drops all the local databases defined in config/database.yml
db:reset Drops and recreates the database from db/schema.rb for the current environment.
db:rollback Rolls the schema back to the previous version. Specify the number of steps with STEP=n
db:version Retrieves the current schema version number
我们有更好的数据库管理支持。在以前,我们得登入到数据库控制台然后手动创建这些数据库,现在我们只要简单地:
rake db:create:all
如果我们现在想要重头做起,我们可以 db:drop:all. 如果是在开发当中我们可以利用 db:rollback 回退到上个版本。
Sexyness(性感)
在数据库的设置好了下面我们就可以创建我们的第一个资源。记得吧,现在 rails2.0是默认 RESTful 的。(for brazilians: 我也另外写了篇关于restful 的教程)。
.
/script/generate scaffold Post title:string body:text
这里唯一不同的是'scaffold'的用处就和我们以前用的'scaffold_resource'一样,和那个旧的并不是 restful 的 scaffold 也已经没了。你现在也没有 ActionController类方法'scaffold'来动态创建一个带有默认action的空的控制器了。所以任何我们所scaffold 的东西都变成 RESTful的了。
他会创建那些常见的东西:Controller, Helper, Model, Migration, Unit Test, Functional Test.
主要的不同是在迁移文件:
Ruby代码 复制代码
1. # db/migrate/001_create_posts.rb
2. class CreatePosts < ActiveRecord::Migration
3. def self.up
4. create_table :posts do |t|
5. t.string :title
6. t.text :body
7.
8. t.timestamps
9. end
10. end
11.
12. def self.down
13. drop_table :posts
14. end
15. end
# db/migrate/001_create_posts.rb
class CreatePosts < ActiveRecord::Migration
def self.up
create_table :posts do |t|
t.string :title
t.text :body
t.timestamps
end
end
def self.down
drop_table :posts
end
end
这叫做 性感迁移(Sexy Migrations),首先发明自 “Err the Blog” 作为一个插件随之进入到了rails内核中。了解他们有何异同最好的方式就是去看看他在rails1.2中长什么样:
Ruby代码 复制代码
1. class CreatePosts < ActiveRecord::Migration
2. def self.up
3. create_table :posts do |t|
4. t.column :title, :string
5. t.column :body, :text
6. t.column :created_at, :datetime
7. t.column :updated_at, :datetime
8. end
9. end
10.
11. def self.down
12. drop_table :posts
13. end
14. end
class CreatePosts < ActiveRecord::Migration
def self.up
create_table :posts do |t|
t.column :title, :string
t.column :body, :text
t.column :created_at, :datetime
t.column :updated_at, :datetime
end
end
def self.down
drop_table :posts
end
end
他去除了重复的't.column',现在使用‘t.column_type’的格式并且自动加上的时间戳被浓缩成了一行语句‘t.timestamps’.并没有改变什么行为,只是让代码更加"性感"些
现在,像以前一样运行一个迁移任务
rake db:migrate
在以前,如果我要回退到某一个数据迁移我需要这样做:
rake db:migrate VERSION=xxx
'xxx'是代表我们想要回退到的版本号,现在我们仅仅需要:
rake db:rollback
更加的简洁优雅,这是肯定的。一切都设置好了,现在我们可以像以前一样启动服务器,看看生成的页面。
./script/server
他会自动载入不管是在 Mongrel, Webrick 或 Lightpd 在 3000 端口。我们和以前一样拥有一个根页面,为 index.html.有一个小点我在视频里面没有提到的是:
Ruby代码 复制代码
1. # config/routes.rb
2. ActionController::Routing::Routes.draw do |map|
3. map.root :controller => 'posts'
4. map.resources :posts
5. end
# config/routes.rb
ActionController::Routing::Routes.draw do |map|
map.root :controller => 'posts'
map.resources :posts
end
这里有一个新的语句‘map.root’,该语句和 “map.connect ’’, :controller => ‘posts’.有同样的效果。只是一点小小的花招并没有做什么大动作只是让路由文件看起来更优美些。一旦设置了它,别忘了了删除 public/index.html 文件。 根URL就总会指向POSTS控制器。
正如你所看到的,一切都和以前一样。所有的脚手架模板都还是一样。你可以浏览一下,创建一些新行等等。
嵌套路由
让我们来创建一些和POST有关的COMMENT。他会完成创建我们 blog的资源:
Ruby代码 复制代码
1. ./script/generate scaffold Comment post:references body:text
2. rake db:migrate
./script/generate scaffold Comment post:references body:text
rake db:migrate
和上面一样:scaffold一下资源,在命令行中设置组名和数据类型然后迁移文件就会自动设置好。注意到另外一个新增的东西:关键字”references“ 正如 许多朋友提醒我的,这会使数据迁移更性感。
比较起来,这是以前做这件事用的方法:
Ruby代码 复制代码
1. ./script/generate scaffold Comment post:references body:text
./script/generate scaffold Comment post:references body:text
外键只是一个无关紧要的实现细节,看看新的数据迁移文件:
Ruby代码 复制代码
1. def self.up
2. create_table :comments do |t|
3. t.references :post
4. t.text :body
5.
6. t.timestamps
7. end
8. end
def self.up
create_table :comments do |t|
t.references :post
t.text :body
t.timestamps
end
end
看这里关于新的关键字” references“的细节。所以,运行 db:migrate 将在数据库中创建表。
然后我们设置 ActiveRecord模型让他们和彼此互相关联,像这样:
Ruby代码 复制代码
1. # app/models/post.rb
2. class Post < ActiveRecord::Base
3. has_many :comments
4. end
5.
6. # app/models/comment.rb
7. class Comment < ActiveRecord::Base
8. belongs_to :post
9. end
# app/models/post.rb
class Post < ActiveRecord::Base
has_many :comments
end
# app/models/comment.rb
class Comment < ActiveRecord::Base
belongs_to :post
end
好了,这里没有什么新东西了,我们已经知道怎么处理 ActiveRecord关联了 ,但我们依旧工作在RESTFUL资源环境下。在新的RAILS方式里,我们可以拥有像这样的url:
http://localhost:3000/posts/1/comments
http://localhost:3000/posts/1/comments/new
http://localhost:3000/posts/1/comments/3
意思是 从这个具体的POST里取出相关的comment,脚手架生成器只准备了如下这种url:
http://localhost:3000/posts/1
http://localhost:3000/comments/new
http://localhost:3000/comments/3
那是因为在config/routes.rb 中:
Ruby代码 复制代码
1. # config/routes.rb
2. ActionController::Routing::Routes.draw do |map|
3. map.resources :comments
4.
5. map.root :controller => 'posts'
6. map.resources :posts
7. end
# config/routes.rb
ActionController::Routing::Routes.draw do |map|
map.resources :comments
map.root :controller => 'posts'
map.resources :posts
end
让我们稍微调整一下,就像在model中,我们可以这样调用一个嵌套路由:
Ruby代码 复制代码
1. # config/routes.rb
2. ActionController::Routing::Routes.draw do |map|
3. map.root :controller => 'posts'
4. map.resources :posts, :has_many => :comments
5. end
# config/routes.rb
ActionController::Routing::Routes.draw do |map|
map.root :controller => 'posts'
map.resources :posts, :has_many => :comments
end
就像这样! 现在我们可以想上面这样嵌套url. 首先要理解的是当我打入这样的URL:http://localhost:3000/posts/1/comments
rails 会将它解析成这样:
* 载入控制器
* 设置 params[:post_id] = 1
* 在这种情况下,调用' index ' 的action.
我们必须准备让CommentsController被嵌套。所以这是我们接下来要去修改的:
Ruby代码 复制代码
1. class CommentsController < ApplicationController
2. before_filter :load_post
3. ...
4. def load_post
5. @post = Post.find(params[:post_id])
6. end
7. end
class CommentsController < ApplicationController
before_filter :load_post
...
def load_post
@post = Post.find(params[:post_id])
end
end
这是的 @post已经在所有的术语comments控制器中的action中设置好了。现在我们必须做这些修改:
之前 修改后
Comment.find @post.comments.find
Comment.new @post.comments.build
redirect_to(@comment) redirect_to([@post, @comment])
redirect_to(comments_url) redirect_to(post_comments_url(@post))
这是的comment 控制器准备好了。现在让我们改变一下4个在 app/views/comments中的视图。
如果你打开不管是 new.html.erb 或者是 edit.html.erb你将会注意到如下这些新的特性:
Ruby代码 复制代码
1. # new edit.html.erb and new.html.erb
2. form_for(@comment) do |f|
3. ...
4. end
# new edit.html.erb and new.html.erb
form_for(@comment) do |f|
...
end
这是新的定义方式:
Ruby代码 复制代码
1. # old new.rhtml
2. form_for(:comment, :url => comments_url) do |f|
3. ...
4. end
# old new.rhtml
form_for(:comment, :url => comments_url) do |f|
...
end
Ruby代码 复制代码
1. # old edit.rhtml
2. form_for(:comment, :url => comment_url(@comment),
3. :html => { :method => :put }) do |f|
4. ...
5. end
# old edit.rhtml
form_for(:comment, :url => comment_url(@comment),
:html => { :method => :put }) do |f|
...
end
注意到同样的 form_for 语句是怎样适应 'new' 和 'edit' 的情况。
这是因为Rails可以根据 @comment模型实例的类名去推断出做些什么,但是现在
对于嵌套路由来说,comment是依赖于post,所以我们必须这样做:
Ruby代码 复制代码
1. # new edit.html.erb and new.html.erb
2. form_for([@post, @comment]) do |f|
3. ...
4. end
# new edit.html.erb and new.html.erb
form_for([@post, @comment]) do |f|
...
end
Rails会变得足够聪明去辨识这个数组是表示一个嵌套路由,它会去检查 routes.rb 并且找出
他是post_comment_url(@post, @comment)的具名路由
让我们先来解释下具名路由。当我们在routes.rb设置资源路由时,我们可以得到下列具名路由:
oute HTTP verb Controller Action
comments GET index
comments POST create
comment(:id) GET show
comment(:id) PUT update
comment(:id) DELETE destroy
new_comment GET new
edit_comment(:id) GET edit
“七个 Action足以对付一切 …” :-)
你可以给他们加上 'path'或是'url'的后缀,不同在:
comments_url http://localhost:3000/comments
comments_path /comments
最后你可以给他们加上 'formatted'的前缀,给你:
comments_url http://localhost:3000/comments
comments_path /comments
现在,当comment已经嵌套在post里面,我们必须加上 'post'前缀,在 rails1.2里,这个前缀是可选择的,
他将能够依据传到具名路由helper里的的数字或者参数区分它们。但这会带来许多歧义性,所以现在必须强制加上前缀,像这样:
route HTTP verb URL
post_comments(@post) GET /posts/:post_id/comments
post_comments(@post) POST /posts/:post_id/comments
post_comment(@post, :id) GET /posts/:post_id/comments/:id
post_comment(@post, :id) PUT /posts/:post_id/comments/:id
post_comment(@post, :id) DELETE /posts/:post_id/comments/:id
new_post_comment(@post) GET /posts/:post_id/comments/new
edit_post_comment(@post, :id) GET /posts/:post_id/comments/edit
所以,总结起来,我们必须让 comments 视图的行为更像是嵌套在一个POST里一样。
所以我们还必须对从默认脚手架生成的代码到内嵌的表格中的具名路由做些改变:
Ruby代码 复制代码
1. <!-- app/views/comments/_comment.html.erb -->
2. <% form_for([@post, @comment]) do |f| %>
3. <p>
4. <b>Body</b><br />
5. <%= f.text_area :body %>
6. </p>
7.
8. <p>
9. <%= f.submit button_name %>
10. </p>
11. <% end %>
<!-- app/views/comments/_comment.html.erb -->
<% form_for([@post, @comment]) do |f| %>
<p>
<b>Body</b><br />
<%= f.text_area :body %>
</p>
<p>
<%= f.submit button_name %>
</p>
<% end %>
Ruby代码 复制代码
1. <!-- app/views/comments/edit.html.erb -->
2. <h1>Editing comment</h1>
3.
4. <%= error_messages_for :comment %>
5.
6. <%= render :partial => @comment,
7. :locals => { :button_name => "Update"} %>
8.
9. <%= link_to 'Show', [@post, @comment] %> |
10. <%= link_to 'Back', post_comments_path(@post) %>
<!-- app/views/comments/edit.html.erb -->
<h1>Editing comment</h1>
<%= error_messages_for :comment %>
<%= render :partial => @comment,
:locals => { :button_name => "Update"} %>
<%= link_to 'Show', [@post, @comment] %> |
<%= link_to 'Back', post_comments_path(@post) %>
Ruby代码 复制代码
1. <!-- app/views/comments/new.html.erb -->
2. <h1>New comment</h1>
3.
4. <%= error_messages_for :comment %>
5.
6. <%= render :partial => @comment,
7. :locals => { :button_name => "Create"} %>
8.
9. <%= link_to 'Back', post_comments_path(@post) %>
<!-- app/views/comments/new.html.erb -->
<h1>New comment</h1>
<%= error_messages_for :comment %>
<%= render :partial => @comment,
:locals => { :button_name => "Create"} %>
<%= link_to 'Back', post_comments_path(@post) %>
Ruby代码 复制代码
1. <!-- app/views/comments/show.html.erb -->
2. <p>
3. <b>Body:</b>
4. <%=h @comment.body %>
5. </p>
6.
7.
8. <%= link_to 'Edit', [:edit, @post, @comment] %> |
9. <%= link_to 'Back', post_comments_path(@post) %>
<!-- app/views/comments/show.html.erb -->
<p>
<b>Body:</b>
<%=h @comment.body %>
</p>
<%= link_to 'Edit', [:edit, @post, @comment] %> |
<%= link_to 'Back', post_comments_path(@post) %>
Ruby代码 复制代码
1. <!-- app/views/comments/index.html.erb -->
2. <h1>Listing comments</h1>
3.
4. <table>
5. <tr>
6. <th>Post</th>
7. <th>Body</th>
8. </tr>
9.
10. <% for comment in @comments %>
11. <tr>
12. <td><%=h comment.post_id %></td>
13. <td><%=h comment.body %></td>
14. <td><%= link_to 'Show', [@post, comment] %></td>
15. <td><%= link_to 'Edit', [:edit, @post, comment] %></td>
16. <td><%= link_to 'Destroy', [@post, comment],
17. :confirm => 'Are you sure?', :method => :delete %></td>
18. </tr>
19. <% end %>
20. </table>
21.
22. <br />
23.
24. <%= link_to 'New comment',
25. new_post_comment_path(@post) %>
<!-- app/views/comments/index.html.erb -->
<h1>Listing comments</h1>
<table>
<tr>
<th>Post</th>
<th>Body</th>
</tr>
<% for comment in @comments %>
<tr>
<td><%=h comment.post_id %></td>
<td><%=h comment.body %></td>
<td><%= link_to 'Show', [@post, comment] %></td>
<td><%= link_to 'Edit', [:edit, @post, comment] %></td>
<td><%= link_to 'Destroy', [@post, comment],
:confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<% end %>
</table>
<br />
<%= link_to 'New comment',
new_post_comment_path(@post) %>
提几点:
注意到我创建了一个局部模板去 DRY(不要重复你自己) new 和 edit中表格。但请注意,
并非使用 :partial=> 'comment' 而使用 :partial=>@comment.
然后再一次他能够从类名推断出局部模板的名字。如果我们传进一个集合他会将其转换成旧的语句 ':partial,:collection'
我可以使用post_comment_path(@post, @comment),或是更简单的 [@post, @comment]
请注意不要忘记背后的那些具名路由。
最后,最好将comment列表页的链接放到POST视图里,我们这样做:
Ruby代码 复制代码
1. <!-- app/views/posts/show.html.erb -->
2. <%= link_to 'Comments', post_comments_path(@post) %>
3. <%= link_to 'Edit', edit_post_path(@post) %> |
4. <%= link_to 'Back', posts_path %>
<!-- app/views/posts/show.html.erb -->
<%= link_to 'Comments', post_comments_path(@post) %>
<%= link_to 'Edit', edit_post_path(@post) %> |
<%= link_to 'Back', posts_path %>
我只是加了个链接,让我们瞧瞧他看起来怎么样:
完成视图
好了,看起来还不错。但这并不像 一个blog的行为! POST的show 视图应该有些comment罗列在那里,并且新建评论的表格应该也在那里。所以让我们做些小改变。这里没有什么新的,只是传统的rails。让我们从视图开始:
Ruby代码 复制代码
1. <!-- app/views/posts/show.html.erb -->
2. <p>
3. <b>Title:</b>
4. <%=h @post.title %>
5. </p>
6.
7. <p>
8. <b>Body:</b>
9. <%=h @post.body %>
10. </p>
11.
12. <!-- #1 -->
13. <% unless @post.comments.empty? %>
14. <h3>Comments</h3>
15. <% @post.comments.each do |comment| %>
16. <p><%= h comment.body %></p>
17. <% end %>
18. <% end %>
19.
20. <!-- #2 -->
21. <h3>New Comment</h3>
22. <%= render :partial => @comment = Comment.new,
23. :locals => { :button_name => 'Create'}%>
24.
25. <%= link_to 'Comments', post_comments_path(@post) %>
26. <%= link_to 'Edit', edit_post_path(@post) %> |
27. <%= link_to 'Back', posts_path %>
<!-- app/views/posts/show.html.erb -->
<p>
<b>Title:</b>
<%=h @post.title %>
</p>
<p>
<b>Body:</b>
<%=h @post.body %>
</p>
<!-- #1 -->
<% unless @post.comments.empty? %>
<h3>Comments</h3>
<% @post.comments.each do |comment| %>
<p><%= h comment.body %></p>
<% end %>
<% end %>
<!-- #2 -->
<h3>New Comment</h3>
<%= render :partial => @comment = Comment.new,
:locals => { :button_name => 'Create'}%>
<%= link_to 'Comments', post_comments_path(@post) %>
<%= link_to 'Edit', edit_post_path(@post) %> |
<%= link_to 'Back', posts_path %>
再提几点:
1.迭代中并没有什么新的东西,只是列出一些评论
2.再一次,我们传进@comment给 partial语句
最后一个小调整,任何时候我们创建一个新POST,我们会想回到同样的POST中的show视图,所以我们改变CommentController的行为像这样:
Ruby代码 复制代码
1. # app/controllers/comments_controller.rb
2. # old redirect:
3. redirect_to([@post, @comment])
4. # new redirect:
5. redirect_to(@post)
# app/controllers/comments_controller.rb
# old redirect:
redirect_to([@post, @comment])
# new redirect:
redirect_to(@post)
命名空间路由
好了,现在我们有一个骨瘦如柴的迷你博客像是模仿经典david在2005念做的一段15分钟创建一个BLOG的视频。现在我们更进一步:Post不应该让所有的人都可以去编辑他,我们的网站需要一个管理部分。让我们为它创建一个控制器:
./script/generate controller Admin::Posts
rails2.0 现在支持命名空间。他会创建一个子目录叫 app/controllers/admin.
我们所要作的是:
1.创建一个新的路由
2.把所有在旧的 posts控制器中的 action复制到新的 Admin::posts中
3.复制所有旧的posts视图到app/views/admin* ,在旧的posts 控制器中只留下 ‘index’和‘show’ 这两个action,
这意味着也要删除new 和 edit.
4.修改 我们刚刚复制的actions和views,让他能够知道他是在admin控制器中
首先,我们再次编辑 config/routes.rb:
Ruby代码 复制代码
1. map.namespace :admin do |admin|
2. admin.resources :posts
3. end
map.namespace :admin do |admin|
admin.resources :posts
end
这意味着我们现在有了带着 'admin'前缀的 posts的具名路由。这会使旧的POST路由和新的
admin post路由不会想混。像这样:
posts_path /posts
post_path(@post) /posts/:post_id
admin_posts_path /admin/posts
admin_post_path(@post) /admin/posts/:post_id
现在让我们从旧的POST控制器中拷贝ACTION并修改路由地址去适应新的命名空间:
Ruby代码 复制代码
1. # app/controllers/admin/posts_controller.rb
2. ...
3. def create
4. # old:
5. format.html { redirect_to(@post) }
6. # new:
7. format.html { redirect_to([:admin, @post]) }
8. end
9.
10. def update
11. # old:
12. format.html { redirect_to(@post) }
13. # new:
14. format.html { redirect_to([:admin, @post]) }
15. end
16.
17. def destroy
18. # old:
19. format.html { redirect_to(posts_url) }
20. # new:
21. format.html { redirect_to(admin_posts_url) }
22. end
23. ...
# app/controllers/admin/posts_controller.rb
...
def create
# old:
format.html { redirect_to(@post) }
# new:
format.html { redirect_to([:admin, @post]) }
end
def update
# old:
format.html { redirect_to(@post) }
# new:
format.html { redirect_to([:admin, @post]) }
end
def destroy
# old:
format.html { redirect_to(posts_url) }
# new:
format.html { redirect_to(admin_posts_url) }
end
...
不要忘记删除所有在app/controllers/posts_controller.rb中的方法,只要留下 ‘index’ 和‘show’两个方法。
现在,让我们拷贝视图(假设你的 shell已经在项目的根文件夹下):
cp app/views/posts/*.erb app/views/admin/posts
rm app/views/posts/new.html.erb
rm app/views/posts/edit.html.erb
现在让我们编辑 app/views/admin/posts中的视图:
Ruby代码 复制代码
1. <!-- app/views/admin/posts/edit.html.erb -->
2. <h1>Editing post</h1>
3.
4. <%= error_messages_for :post %>
5.
6. <% form_for([:admin, @post]) do |f| %>
7. ...
8. <% end %>
9.
10. <%= link_to 'Show', [:admin, @post] %> |
11. <%= link_to 'Back', admin_posts_path %>
<!-- app/views/admin/posts/edit.html.erb -->
<h1>Editing post</h1>
<%= error_messages_for :post %>
<% form_for([:admin, @post]) do |f| %>
...
<% end %>
<%= link_to 'Show', [:admin, @post] %> |
<%= link_to 'Back', admin_posts_path %>
Ruby代码 复制代码
1. <!-- app/views/admin/posts/new.html.erb -->
2. <h1>New post</h1>
3.
4. <%= error_messages_for :post %>
5.
6. <% form_for([:admin, @post]) do |f| %>
7. ...
8. <% end %>
9.
10. <%= link_to 'Back', admin_posts_path %>
<!-- app/views/admin/posts/new.html.erb -->
<h1>New post</h1>
<%= error_messages_for :post %>
<% form_for([:admin, @post]) do |f| %>
...
<% end %>
<%= link_to 'Back', admin_posts_path %>
Ruby代码 复制代码
1. <!-- app/views/admin/posts/show.html.erb -->
2. <p>
3. <b>Title:</b>
4. <%=h @post.title %>
5. </p>
6.
7. <p>
8. <b>Body:</b>
9. <%=h @post.body %>
10. </p>
11.
12. <%= link_to 'Edit', edit_admin_post_path(@post) %> |
13. <%= link_to 'Back', admin_posts_path %>
<!-- app/views/admin/posts/show.html.erb -->
<p>
<b>Title:</b>
<%=h @post.title %>
</p>
<p>
<b>Body:</b>
<%=h @post.body %>
</p>
<%= link_to 'Edit', edit_admin_post_path(@post) %> |
<%= link_to 'Back', admin_posts_path %>
Ruby代码 复制代码
1. <!-- app/views/admin/posts/index.html.erb -->
2. ...
3. <% for post in @posts %>
4. <tr>
5. <td><%=h post.title %></td>
6. <td><%=h post.body %></td>
7. <td><%= link_to 'Show', [:admin, post] %></td>
8. <td><%= link_to 'Edit', edit_admin_post_path(post) %></td>
9. <td><%= link_to 'Destroy', [:admin, post],
10. :confirm => 'Are you sure?', :method => :delete %></td>
11. </tr>
12. <% end %>
13. </table>
14.
15. <br />
16.
17. <%= link_to 'New post', new_admin_post_path %>
<!-- app/views/admin/posts/index.html.erb -->
...
<% for post in @posts %>
<tr>
<td><%=h post.title %></td>
<td><%=h post.body %></td>
<td><%= link_to 'Show', [:admin, post] %></td>
<td><%= link_to 'Edit', edit_admin_post_path(post) %></td>
<td><%= link_to 'Destroy', [:admin, post],
:confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<% end %>
</table>
<br />
<%= link_to 'New post', new_admin_post_path %>
基本上完成了:如果你测试 http://localhost:3000/admin/posts,他应该能正常的工作。但看起来却很丑陋,那是因为我们没有全局的布局模板。
当我们生成第一个脚手架时,rails为post和 comment生成各自相关的布局模板。所以让我们删掉他们并且创建一个通用的:
cp app/views/layouts/posts.html.erb \
app/views/layouts/application.html.erb
rm app/views/layouts/posts.html.erb
rm app/views/layouts/comments.html.erb
然后让我们改一下标题:
Java代码 复制代码
1. <!-- app/views/layouts/application.html.erb -->
2. ...
3. <title>My Great Blog</title>
4. ...
<!-- app/views/layouts/application.html.erb -->
...
<title>My Great Blog</title>
...
他只剩下旧的在posts控制器里的'index' 和 ‘show’页面,他们仍然拥有我们有链接到我们删除过的方法的链接,所以我们可以删掉他们。
Ruby代码 复制代码
1. <!-- app/views/posts/index.html.erb -->
2. <h1>My Great Blog</h1>
3.
4. <table>
5. <tr>
6. <th>Title</th>
7. <th>Body</th>
8. </tr>
9.
10. <% for post in @posts %>
11. <tr>
12. <td><%=h post.title %></td>
13. <td><%=h post.body %></td>
14. <td><%= link_to 'Show', post %></td>
15. </tr>
16. <% end %>
17. </table>
<!-- app/views/posts/index.html.erb -->
<h1>My Great Blog</h1>
<table>
<tr>
<th>Title</th>
<th>Body</th>
</tr>
<% for post in @posts %>
<tr>
<td><%=h post.title %></td>
<td><%=h post.body %></td>
<td><%= link_to 'Show', post %></td>
</tr>
<% end %>
</table>
Ruby代码 复制代码
1. <!-- app/views/posts/show.html.erb -->
2. <p>
3. <b>Title:</b>
4. <%=h @post.title %>
5. </p>
6.
7. <p>
8. <b>Body:</b>
9. <%=h @post.body %>
10. </p>
11.
12. <% unless @post.comments.empty? %>
13. <h3>Comments</h3>
14. <% @post.comments.each do |comment| %>
15. <p><%= h comment.body %></p>
16. <% end %>
17. <% end %>
18.
19. <h3>New Comment</h3>
20.
21. <%= render :partial => @comment = Comment.new,
22. :locals => { :button_name => 'Create'}%>
23.
24. <%= link_to 'Back', posts_path %>
<!-- app/views/posts/show.html.erb -->
<p>
<b>Title:</b>
<%=h @post.title %>
</p>
<p>
<b>Body:</b>
<%=h @post.body %>
</p>
<% unless @post.comments.empty? %>
<h3>Comments</h3>
<% @post.comments.each do |comment| %>
<p><%= h comment.body %></p>
<% end %>
<% end %>
<h3>New Comment</h3>
<%= render :partial => @comment = Comment.new,
:locals => { :button_name => 'Create'}%>
<%= link_to 'Back', posts_path %>
我们可以从浏览器中测试任何东西,进入 http://localhost:3000/admin/posts ,就能看到一切都工作的很好。但是,我们依旧少了样东西:一个系统管理部分不应该公开。现在你可以进去编辑任何东西了。我们需要认证。
HTTP基本认证
有许多实现验证和授权的方式。一个用的很广泛的插件是restful_authentication.
但是在这里我们不想做些太炫的东西。而RAILS2.0给了我们一个很好的方式去做验证。这个就是:我们用HTTP已经给我们的东西:HTTP基本认真。
缺点是:当在生产环境下时你肯定会想用SSL。当然,你还是会这样做。HTML 表单验证并没有让SSL保护。
所以,让我们编辑我们的Admin::Posts控制器,添加验证功能:
相关推荐
Rails 2.0 API 文档是一个非常宝贵的资源,它为开发者提供了全面的指南,以便于在使用Ruby on Rails 2.0版本时更好地理解和利用其框架功能。Ruby on Rails(简称Rails)是一个开源的Web应用框架,它遵循MVC(模型-...
Rails 2.0是Rails系列中的一个里程碑式的版本,它在Rails 1.x的基础上进行了大量的改进和优化,包括但不限于性能提升、API增强以及对新特性的支持等。对于开发者而言,掌握Rails 2.0不仅可以提高开发效率,还能为...
10. **社区支持**:Rails 2.0时,Rails社区已经相当活跃,有大量的教程、插件和工具可供开发者使用,这极大地丰富了Rails的生态系统。 总之,Ruby on Rails 2.0是一个功能强大且成熟的框架,它简化了Web应用开发的...
Ruby on Rails 2.0 是这个流行的Web开发框架的一个重大更新,发布于2007年底。Rails以其快速的版本迭代和创新的功能而闻名,从1.0到2.0的升级也不例外。这次更新带来了许多新特性,提升了开发效率和用户体验。 首先...
总体来说,Rails 2.0虽然没有带来革命性的变化,但它通过一系列小幅度的改进和优化,提升了开发效率,增强了系统的稳定性和安全性。对于正在使用Rails进行Web开发的团队来说,了解这些新特性是非常有价值的。
软件2.0是一个概念,它代表了互联网应用程序发展的新阶段,强调数据和服务的网络化,以及用户参与度的提升。在这个时代,软件不再仅仅依赖于本地计算机上的安装,而是更多地依赖于云计算和Web服务。通过PPT的形式,...
《jRuby on Rails WEB2.0》是一部由Ola Bini撰写的书籍,深入探讨了如何将Ruby on Rails这一敏捷开源框架与Java平台相结合,以构建高效、灵活的Web 2.0应用。作为JRuby项目的领头人和核心开发者,Ola Bini以其丰富的...
《JRuby on Rails Web 2.0 实用项目》 英文PDF + 源码
在Ruby on Rails 2.0框架下,我们经常会遇到创建和操作数据库的需求。在这个实例中,我们将探讨如何在Rails应用中使用SQLite数据库,一个轻量级且易于上手的数据库管理系统,尤其适合开发阶段。标题提到的“mybook”...
规范中加入了很多的特性比如:将 Facelets 作为视图技术; 通过使用标签为 Java EE 5 提供了基于 annotation 驱动的配置特性;...支持 RAILS_ENV 的开发风格;支持对标准组件集进行扩展 等等还有很多;
Ruby on Rails,简称RoR,是由Ruby编程语言构建的开源Web应用框架,以其高效和简洁的设计原则闻名。RoR遵循MVC(Model-View-Controller)架构模式,旨在简化Web应用开发,降低开发者的工作负担,提高开发效率。该...
rails-hackernews-reddit-producthunt-clone, 黑客 news/reddit/social 链接分享网站 用 Rails 构建 Rails 上的 Reddit-Hackernews-ProductHunt克隆演示 这是一个 readme.md的Ruby on Rails 应用程序,模仿了 Hacker...
Rails 2.0版本在当时是一个重要的里程碑,引入了许多新特性并优化了已有的功能。 在Rails API文档中,你可以找到关于以下关键知识点的详尽解释: 1. **路由(Routing)** Rails的路由系统将HTTP请求映射到控制器的...
9. **插件和 gems**:Rails社区提供了大量的插件和第三方gem库,它们扩展了Rails的功能。学会查找、安装和使用gem是Rails开发者的日常任务。 10. **部署**:了解如何将Rails应用部署到各种服务器环境,如Heroku、...
Rails,全称Ruby on Rails,是一款基于Ruby语言的开源Web应用程序框架,以其“Don't Repeat Yourself”(DRY)和“Convention over Configuration”(CoC)的原则,深受开发者喜爱。Awesome Rails Gem 是一个广泛...
InstantRails-2.0-win是一款专为Windows用户设计的Rails开发环境快速安装工具。它集成了Ruby编程语言、Ruby on Rails框架、SQLite数据库和Webrick服务器等核心组件,使得开发者能够在Windows系统上便捷地搭建并运行...
《Agile Web Development with Rails-Second Edition-Beta》是一本专注于使用Ruby on Rails进行敏捷Web开发的书籍。这本书的第二版beta版提供了关于如何利用Rails框架高效构建动态、响应式网站的深入指导。作者们...
rails3-mongoid-devise, 示例 Rails 3.2应用,带有数据 Mongoid,用于验证 Rails 4.1有关设计的Rails 4.1示例应用程序,请参见:rails...类似示例和教程这是来自 RailsApps项目的一系列 Rails 示例应用程序和教程中的一