转自:http://www.cnblogs.com/x116/articles/1035589.html
非常高兴我的Rails 2.0 视频有很好的收视率,超过1,500名有独立IP的浏览者收看。那个想法是展现Rails2.0非常快的优点,展示了在不到30分钟的时间里能做些什么。
现在,我把视频分解成几个主要模块,然后做成了围绕Rails2.0非常快特征的第一个的完整教程。像其他的教程一样,这也没有完全100%的覆盖Rails2.0的知识点,仅仅是一些主要特征粘在一起的应用。 我推荐检查下Peepcode's Rails2 PDF 和 Ryan Bates 的 Railscasts.com 以获得更多详细信息。
这教程分为两部分,查看第二部分,点击这里。
现在我们开始。
1.认识一下环境
这个教程是连接前面那些已经有一些Rails1.2基础的人。请查阅一些关于Rails1.2有用的好的教程在互联网上(没有也没关系,Step by step 嘛)。
首先你得更新你的Gems:
Z:/ruby>gem install rails --include-dependencies
你可能最好还要升级一下你的RubyGems。
关于安装Ruby,Gem管理器,Rails等网上有非常多的教程,不多叙述。
首先要做的第一件事,让我建立一个新的Rails 应用程序:
切换到你的Ruby on Rails 工作目录,如果基于MySql数据库就键入以下命令:
G:/rlab>rails -d mysql blog
如果使用默认的sqlite3数据库则键入:
G:/rlab>rails blog
这将创建我们的通用Rails项目目录结构.搭建这个环境后要注意到的第一件事情,我们现在有了这个主结构:
.config/environment.rb
.config/initializers/inflections.rb
.config/initializers/mime_types.rb
在blog/config/initializers目录内的所有事情载入的时候都会在同一时间初始化environment.rb,因此当你在你的项目里用了一些不同的plugins 和 gems后,去维护这个environment.rb文件的护理工作会变得混乱和困难。现在我们有一个简单的办法来模块化我们的配置。
2.数据库
第二件事,我们要配置一下我们的数据库。首先用同样的方法配置blog/config/database.yml:(应注意yml文件中,“:”后面的空格)
development:
adapter: mysql
encoding: utf8
database: blog_development
username: root
password: 123
host: localhost
test:
adapter: mysql
encoding: utf8
database: blog_test
username: root
password: 123
host: localhost
production:
adapter: mysql
encoding: utf8
database: blog_production
username: root
password: 123
host: localhost
注意这里,你有个‘encoding’选项默认它设置成了 utf8 。Rails 应用程序最好是用它自己默认装载的KCODE = true , 意思是说在它默认启动的的时候就已经支持Unicode(无符号字符)了,这会非常好。但是这个'encoding' 配置最好是有一个新的用法,每当Rails连接数据库的时候它将告诉它设置'encoding',像说'SET NAMES UTF8'.
我们能这样DRY(Don't Repeat Yoursel) database.yml
defaults: & defaults
adapter: mysql
encoding: utf8
username: root
password: 123
host: localhost
development:
database: blog_development
<< : * defaults
test:
database: blog_test
<< : * defaults
production:
database: blog_production
<< : * defaults
看,多好。我们最好做一个新的Rake工作。他们中一些与数据库有关的:
- db:charset 检索当前环境下数据库的字符设置
- db:collation 检索当前环境下数据库的校对
- db:create 用config/database.yml中的定义创建当前 RAILS_ENV 项目环境下的数据库
- db:create:all 用config/database.yml中的定义创建所有数据库
- db:drop 删除当前 RAILS_ENV项目环境中的数据库
- db:drop:all 删除所有在 config/database.yml中定义的数据库
- db:reset 从db/schema.rb中为当前环境重建数据库(先删后建).
- db:rollback 回滚(清华出版社一本SQLSERVER书的名词[很奇怪为什么不直接用滚回])数据库到前一个版本. 指定回滚到哪一步要用 STEP=n 参数
- db:version 检索当前模式下的版本
我们有一个很好的远程管理支持,在老版本的Rails里,现在我们就要登录到我们的数据库管理员控制台去手工创建数据库。但现在,我们能这样简单的做:
G:/rlab/blog>rake db:create:all
注意切换到blog目录下,以后我们的命令操作基本上是基于这个目录,所以下面的文章中就不带目录直接写命令了。如果我们要从刻痕处开始,我们能用 db:drop:all。之后在开发中我们能用db:rooback 到没有操作的最近的移植(migration)文件。
3.(性感|苗条)?(Sexyness)
当数据库设置和准备开始后,我们就能建立我们的第一笔资料。记住现在Rails 2.0 默认是RESTful(GOOGLE有解释)。
ruby script/generate scaffold Post title:string body:text
这儿仅仅不同的是"scaffold"行为,像前面我们用的"scaffold_resource",老的非RESTful的scaffold已经不用了。在ActionController类里面没有了"scaffold"方法,它已经默认动态组装到你的空控制器里了。所以,现在每个脚手架scaffold我们已经RESTful啦。
它将创建"嫌疑惯犯"('The Usual Suspects' 著名美国电影,译名有《非常嫌疑犯》《嫌疑惯犯》)们:Controller , Helper, Model , Migration ,Unit Test, Functional Test.
主要的不同是在 Migration 文件里:
1 class CreatePosts < ActiveRecord::Migration
2 def self.up
3 create_table :posts do | t |
4 t.string :title
5 t.text :body
6
7 t.timestamps
8 end
9 end
10
11 def self.down
12 drop_table :posts
13 end
14 end
我们把这个叫 Sexy Migrations ,是"Err the Blog"发明的,首先只是作为一个插件,后来这方法很多人都觉得不错就加进了核心。理解了这个好的方法后,再看下和Rails1.2的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
摆脱了't.column'的重复,现在用't.column_type'格式和把自动时间列集中在了一个't.timestamps'语句里。没有改变任何行为,仅仅是把代码搞苗条了。
好,现在我们像前面一样的运行migration:
rake db:migrate
以前我们要回滚一个migration需要这样做:
rake db:migrate VERSION=xxx
XXX就是我们要返回的那个版本号,现在我们能简单发号指令:
rake db:rollback
那确实,很好,很优美!都设置好后,我们就能像前面做的一样启动我们的服务器看下产生的页面:
ruby script/server
这将装载到 Mongrel, Webrick 或者 Lightpd等任一WEB服务器的3000端口, 和以前一样我们有同样的首页,显现出index.html页。在视频教程里的一则小道信息我没展出来的在这:
ActionController::Routing::Routes.draw do | map |
map.root :controller => ' posts '
map.resources :posts
end
这里有条新语句:'map.root' ,和 map.connect ,:controller => 'posts' 的效果一样。这语句没做什么其他的事仅仅是把Routes变的精练了点。当你设置好了这个后,别忘记了删除 blog/public/index.html文件。现在根URL将总是定位到 Posts 控制器。
注意这里图片里显示的那些记录是点New post发布之后的。
你看,和以前相比每件事情感觉都简单多了。所有的脚手架模板都一样,你能浏览,创建新行等等。
4.路由嵌套
好,让我们来建一个发表同步评论的资源(表格Table)。这将完成我们的资料库(数据库Database)创建。
ruby script/generate scaffold Comment post:references body:text
rake db:migrate
同样的事情:在命令行里创建脚手架资源(scaffold the resource),配置字段名和数据格式,migration文件就被设置了。注意另一个小添加:关键字“references” 。当我的朋友Arthur 提醒我,这会让migrations更苗条。比较一下,以前的方法我们是这样做的:
ruby script/generate scaffold Comment post_id_:integer body:text
外键仅仅是实现了些细节,没关系,看一下新的migration文件(blog/db/migrate/002_create_comments.rb):
create_table :comments do | t |
t.references :post
t.text :body
t.timestamps
end
end
class Post < ActiveRecord::Base
has_many :comments
end
# app/models/comment.rb
class Comment < ActiveRecord::Base
belongs_to :post
end
http: // localhost:3000/posts/1/comments/new
http: // localhost:3000/posts/1/comments/3
意味着:我们能看到详细POST下面的Comments,但scaffold生产器只能去生产下面的这些URL:
http: // localhost:3000/comments/new
http: // localhost:3000/comments/3
这是因为在路由文件(config/routes.rb)里面有:
map.resources :comments
map.root :controller => ' posts'
map.resources :posts
end
让我们把他们拧一下,就像是在一个model里面一样,我们能创建一种嵌套路由(Nested Route):
ActionController::Routing::Routes.draw do |map|
map.root :controller => ' posts'
map.resources :posts, :has_many => :comments
end
像这样我们就能看到前面说的嵌套的URL。
Rails 就会这样分析它:
· 载入CommentsController
· 设置参数[:post_id] = 1
· 既然情况这样,叫‘index’行动
我们已经准备好让Comments controller被嵌套了,如此一来我们将要做下面的这样一些改变:
class CommentsController < ApplicationController
before_filter :load_post
......
def load_post
@post = Post.find(params[:post_id])
end
end
这将设置好@post的所有行为准备好做 Comments controller。现在我们就还要做下面的这些替换(app/controllers/comments_controller.rb):
改变前: | 替换为: |
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) |
这样就会让Comments controller准备好。现在我们更改4个视图(view)文件,如果你打开 new.html.erb 和 edit.html.erb 你将发现以下这些新特征:
form_for(@comment) do |f|
![](https://i-blog.csdnimg.cn/blog_migrate/b854634c0904529d4018c4c3336be836.gif)
end
这是我们以前1.2里面做这些事的旧语句:
form_for(:comment, :url => comments_url) do |f|
![](https://i-blog.csdnimg.cn/blog_migrate/b854634c0904529d4018c4c3336be836.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/b854634c0904529d4018c4c3336be836.gif)
end
# old edit.rhtml
form_for(:comment, :url => comment_url(@comment),
:html => { :method => :put }) do |f|
![](https://i-blog.csdnimg.cn/blog_migrate/b854634c0904529d4018c4c3336be836.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/b854634c0904529d4018c4c3336be836.gif)
end
注意在new.rhtml和edit.rhtml 中一样的语句form_for。这样是因为Rails能推断根据名为@comment类的实例做什么,但现在用了嵌套路由,comments依靠Post了,所以我们要做:
form_for([@post, @comment]) do |f|
![](https://i-blog.csdnimg.cn/blog_migrate/b854634c0904529d4018c4c3336be836.gif)
end
Rails 会很快明白嵌套路由里面的这些数组表达的内容,将会检查routes.rb文件,计算出名为
post_comment_url(@post, @comment) 的路由。
让我们解释下叫这些名字的路由先,当我们设置 route.rb文件里的路由资源的时候,我们获得了这些名字的路由:
路由名 | HTTP 传送方式 | 控制器行为 |
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 |
"7种行为统治他们所有人" :-)
你能给他们加后缀 " path"或者" url" ,不同点就是:
comments_url | http://localhost:3000/comments |
comments_path | /comments |
最后你能给他们加前缀“ formatted” ,会给你:
formatted_comments_url(:atom) | http://localhost:3000/comments.atom |
formatted_comment_path(@comment, :atom) | /comments/1.atom |
现在,Comments嵌套到Post里面去了,我们有责任添加前缀'post' 。在Rails1.2里,这个前缀是可选的,他能通过数字和参数告诉你通往不同的post 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里面一样。所以我们需要改变一下默认的脚手架生产代码去适应嵌套:
< % form_for([@post, @comment]) do |f| % >
< p >
< b > Body </ b >< br />
< % = f.text_area :body % >
</ p >
< p >
< % = f.submit button_name % >
</ p >
< % end % >
< 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) %>
< h1 > New comment </ h1 >
< % = error_messages_for :comment % >
< % = render : partial => @comment,
:locals => { :button_name => " Create " } % >
< % = link_to ' Back', post_comments_path(@post) %>
< p >
< b > Body: </ b >
< % = h @comment.body % >
</ p >
< % = link_to ' Edit', [:edit, @post, @comment] %> |
< % = link_to ' Back', post_comments_path(@post) %>
< 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) % >
·注意这里我创建了一个 partial DRY的new和edit表单,但重视替换 :partial => ‘comment’,我做了":partial => @comment ",他们将从类名里推断partial,如果我们通过了一个集合,他将做相当于旧语句":partial, :collection"的事。
·我能用 post_comment_path(@post, @comment)或者简单的[@post, @comment]
·更要重视的是别忘记了路由背后的任何名字。
最后,链接Comments列表页面到Post页面,我需要做:
< % = link_to ' Comments', post_comments_path(@post) %>
< % = link_to ' Edit', edit_post_path(@post) %> |
< % = link_to ' Back', posts_path %>
我仅仅添加了一个链接,让我再来看看它看起来会像什么:
![](http://www.akitaonrails.com/assets/2007/12/12/Picture_3_1.png)
![](http://www.akitaonrails.com/assets/2007/12/12/Picture_4_1.png)
完成视图
好,看起来不错,但现在它还不太像一个将要运行的博客,post页面最好还应该显示已经有的comments列表和一个提交新注解的表单,所以我们现在还需要进行一些小小改编,和传统的 Rails相比没有什么新改变,让我们开始改视图:
< 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 %>
更多注释:
· 这个迭代程序里面没有任何新东西,仅仅是把注解列出来
· 这里通过@comment变量对partial语句
最后的一点调整:当提交一个新注解之后我们希望回到它的POST页面,这就需要改写下comments_controller.rb中的路由
把app/controllers/comments_controller.rb中的旧路由:
redirect_to(@post, @comment)
替换成新路由:
redirect_to(@post)
路由命名空间:
好,我们现在已经有一个露骨的迷你博客了,有点15分钟视频里面的大卫在2005年做的经典博客的仿制品的味道了。现在,让我们更进一步,公布的文章(post)不可以被所有人编辑,我们需要一个Administration在我们的网站里面进行管理工作。让我们创建一个新的控制器:
ruby script/generate controller Admin::Posts
Rails2.0现在支持命名空间,这将创建一个叫"app/controllers/admin"的子目录。
我们要做这些事:
1,创建一个新的路由
2,拷贝所有旧Posts controller里面的actions到新的Admin::posts,只留下"index和"show"这两个。
3,拷贝所有旧Posts views到app/views/admin/* 只留下旧Posts views的"index"和"show"这两个,意思是最好把旧posts views下面new和edit视图都删除掉。
4,改编actions和views我们仅仅拷贝过来它们还不能明白应该怎么和admin controller一起工作。
首先的首先,让我们再编辑路由文件(config/routes.rb):
admin.resources :posts
end
实际上这意味着我们现在有的作为posts的路由名字带了个前缀"admin",这将消除新添加的的admin posts路由和旧的posts路由之间的歧义,像这样:
posts_path | /posts |
post_path(@post) | /posts/:post_id |
admin_posts_path | /admin/posts |
admin_post_path(@post) | /admin/posts/:post_id |
现在让我们把actions从旧post controller拷贝到admin postcontroller里并改变使之适应新的namespace:
![](https://i-blog.csdnimg.cn/blog_migrate/b854634c0904529d4018c4c3336be836.gif)
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
![](https://i-blog.csdnimg.cn/blog_migrate/b854634c0904529d4018c4c3336be836.gif)
别忘记了删掉 app/controllers/posts_controller.rb里面的所有方法,只留下index 和show这两个。
现在,让我们来拷贝这些视图(也可以直接在windows里面拖放):
copy app/views/posts/*.erb app/views/admin/posts
del app/views/posts/new.html.erb
del app/views/posts/edit.html.erb
现在,让我们来编辑app/views/admin/posts下面的那些视图(views):
< h1 > Editing post </ h1 >
< % = error_messages_for :post % >
< % form_for([:admin, @post]) do |f| % >
![](https://i-blog.csdnimg.cn/blog_migrate/b854634c0904529d4018c4c3336be836.gif)
< % end % >
< % = link_to ' Show', [:admin, @post] %> |
< % = 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| % >
![](https://i-blog.csdnimg.cn/blog_migrate/b854634c0904529d4018c4c3336be836.gif)
< % end % >
< % = link_to ' Back', admin_posts_path %>
< 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 %>
![](https://i-blog.csdnimg.cn/blog_migrate/b854634c0904529d4018c4c3336be836.gif)
< % 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 它将完全能好好工作。但是,它的样子看起来将是有点丑,这是因为我们没有全局的应用程序外观(app layout),当我们做第一个脚手架的时候,Rails产生器单独给Post和Comment做了个外观,所以,我们现在删除他们然后建立现在这样的一个通用的外观:
把 app/views/layouts/posts.html.erb文件改名为:application.html.erb,再删掉app/views/layouts/comments.html.erb
这样仅仅保留了从早先的旧Posts controllers的 index 和 show 页面,它们仍然有链接方法,我们要删掉,所以让我们把这些链接撕断:
< 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 >
<p>
<b>Title:</b>
<%=h @post.title %>
</p>
<p>
<b>Body:</b>
<%=h @post.body %>
</p>
<!--#comments list -->
<% 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身份验证:
这儿有好几种方式进行身份验证,有个插件叫 restful_authentication 使用得非常广泛。
但是,我们不需要搞得每件事情都异常复杂。Rails2.0给我们提供了一种强大的验证方法。这理念就是,让我们使用HTTP,已经给我们的HTTP Basic Authentication.它的缺点就是你将要使用SSL当成为产品的时候。但是,当然你能使用任何方法做到。HTML 表单验证在没有SSL的时候是没有被保护的。
好,我们现在编辑我们的Admin::Posts controller 添加身份验证:
class Admin::PostsController < ApplicationController
before_filter :authenticate
![](https://i-blog.csdnimg.cn/blog_migrate/b854634c0904529d4018c4c3336be836.gif)
def authenticate
authenticate_or_request_with_http_basic do |name, pass|
#User.authenticate(name, pass)
name == 'admin ' && pass == 'admin'
end
end
我们实现这个方法还增加点保密的趣味"authenticate_or_request_with_http-basic" 方法让我们配置好一个块。它在浏览器里给我们这个输入用户名和密码的对话框,我们就要使用用户名和密码才能登录管理博客的数据库,但这是个非常简单的例子,里面的用户名和密码用了明码且无需到数据库验证,真正的产品需要进行其他工作,但是你获得了身份验证这个好主意。
![](http://www.akitaonrails.com/assets/2007/12/12/Picture_6_1.png)