"15分钟搭建一个博客系统",是Ruby On Rails非常经典Demo练习。rails的创始人DHH当年用一个视频演示了如何在15分钟内搭建一个博客系统,展示除了rails的优雅与灵活,也让更多的人开始关注rails。
之所以能在15分钟内搭建一个博客系统,是与rails的一个原则:“惯例优于设置“(Convention Over Configuration)分不开的。而在搭建过程中,最重要的是使用到了rails中的scaffold(脚手架)功能。
下面,我们来演示如何实现15分钟搭建一个博客系统。
系统环境版本
ubuntu-1404
ruby-2.3.0
rails-5.0.0.1
博客实现功能
- 可以新建、读取、更新、删除blog
- 提供blog的评论功能,每个blog下可以有多条评论
系统设计流程演示
(一)实现blog的新建、读取、更新、删除功能
- 新建工程
$ rails new blog_of_vito
- 打开工程,新建scaffold(脚手架)
$ rails g scaffold article
我们看到命令行中显示:
invoke active_record
create db/migrate/20161129044734_create_articles.rb
create app/models/article.rb
invoke test_unit
create test/models/article_test.rb
create test/fixtures/articles.yml
invoke resource_route
route resources :articles
invoke scaffold_controller
create app/controllers/articles_controller.rb
invoke erb
create app/views/articles
create app/views/articles/index.html.erb
create app/views/articles/edit.html.erb
create app/views/articles/show.html.erb
create app/views/articles/new.html.erb
create app/views/articles/_form.html.erb
invoke test_unit
create test/controllers/articles_controller_test.rb
invoke helper
create app/helpers/articles_helper.rb
invoke test_unit
invoke jbuilder
create app/views/articles/index.json.jbuilder
create app/views/articles/show.json.jbuilder
create app/views/articles/_article.json.jbuilder
invoke assets
invoke coffee
create app/assets/javascripts/articles.coffee
invoke scss
create app/assets/stylesheets/articles.scss
invoke scss
create app/assets/stylesheets/scaffolds.scss
短短一行代码,便为我们创建了这么多的功能,其中包括model层(M)、view层(V)和controller层(C),以及迁移(migration)、路由(routing)、helper方法、测试功能等其他一些方法。而在按照这种约定生成的文件中,我们可以发现已经自动生成了很多代码,我们只需要添加部分具体实现功能的代码即可完成整个系统。换句话说,通过rails的约定,scaffold为我们提供了一个框架,通过填增加一些必要的代码,我们便可以轻松地实现我们需要的功能。真的是很方便呐!
- 为blog添加字段
blog需要有题目和内容,我们需要在Article模型对应的表上添加这两个字段。
我们打开/db/migrate/*_create_articles.rb
文件,修改为:
def change
create_table :articles do |t|
t.string :title
t.text :text
t.timestamps
end
end
运行迁移:
$ rails db:migrate
如果rails版本低于5.0,执行命令:
$ rake db:migrate
看到打印信息:
== 20161129044734 CreateArticles: migrating ===================================
-- create_table(:articles)
-> 0.0012s
== 20161129044734 CreateArticles: migrated (0.0012s) ==========================
说明字段已经添加到表中了。
- 修改
articles_controller.rb
文件
打开文件,我们发现scaffold已经为我们生成了index
、show
、new
、edit
、create
、update
、destroy
方法,还生成了两个私有方法:set_article
、article_params
。这些方法基本实现了整个blog的新建、读取、更新、删除的功能。我们只需要告诉controller我们需要使用的字段即可。
修改article_params
方法为:
def article_params
params.require(:article).permit(:title, :text)
end
- 修改view文件
1.修改/app/view/articles/_form.html.erb
文件为:
<%= form_for @article do |f| %>
<% if @article.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@article.errors.count, "error") %> prohibited this article from being saved:</h2>
<ul>
<% @article.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br>
<%= f.text_area :text %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
在这个文件中,我们将前几行中的article
换成@article
,同时添加了blog填写及提交title和text的表单内容。
_form.html.erb
是一个局部视图,按照约定,局部视图的文件名以下划线开头。由于在编辑文章页面和新建文章页面很相似,显示表单的代码是相同的,使用局部视图可以去掉两个视图中的重复代码,因此我们直接在这个文件中添加表单即可,而不用分别在new.html.erb
和edit.html.erb
文件中添加相同的代码。
之所以能在两个动作中共用一个 form_for,是因为@article
是一个资源,对应于符合 REST 架构的路由,Rails 能自动分辨使用哪个地址和请求方法。
2.修改index.html.erb
文件为:
<p id="notice"><%= notice %></p>
<h1>Articles</h1>
<table>
<thead>
<tr>
<th>Title</th><!--new-->
<th>Text</th><!--new-->
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% @articles.each do |article| %>
<tr>
<td><%= article.title %></td><!--new-->
<td><%= article.text %></td> <!--new-->
<td><%= link_to 'Show', article %></td>
<td><%= link_to 'Edit', edit_article_path(article) %></td>
<td><%= link_to 'Destroy', article, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to 'New Article', new_article_path %>
新加的部分我们使用``标示出来,用来实现显示文章题目和内容的功能。
3.修改show.html.erb
文件为:
<p id="notice"><%= notice %></p>
<p>
<strong>Title:</strong>
<%= @article.title %>
</p>
<p>
<strong>Text:</strong>
<%= @article.text %>
</p>
<%= link_to 'Edit', edit_article_path(@article) %> |
<%= link_to 'Back', articles_path %>
这里添加显示blog的title和text的功能。
- 修改
app/models/article.rb
文件
这里修改model文件,是为了添加一些约束,这里我们要求blog的title不能为空且长度大于3,当不满足要求时页面会报错。
修改article.rb
为;
class Article < ApplicationRecord
validates :title, presence: true,
length: { minimum: 3 }
end
这样我们成功建立了一个可以新建、读取查询、更新和删除文章的blog系统,是不是很简单啊!
- 系统首页效果图:
这样,我们的第一部分功能就算是实现了。
(二)添加blog评论功能
这部分我们需要添加blog的评论功能。我们知道对一篇博客评论时我们只需要在对应的博客显示页面下进行评论即可,因此对于评论来说,只需要MC层,而其V层则在/app/views/articles/show.html.erb
中实现即可。
因此,我们在这里不使用scaffold来建立评论。
- 新建评论model层:
$ rails g model comment article_id:integer commenter:string body:text
- 运行迁移(rails >= 5.0)
$ rails db:migrate
- 添加评论与文章的对应关系
一个评论只能对应一篇文章,一篇文章可以又多条评论。
修改article.rb
为:
class Article < ApplicationRecord
validates :title, presence: true,
length: { minimum: 3 }
has_many :comments
end
修改comment.rb
为:
class Comment < ApplicationRecord
belongs_to :article
end
- 生成comment控制器
$ rails g controller comments
- 修改
comments.rb
为:
class CommentsController < ApplicationController
def create
@article = Article.find(params[:article_id])
@comment = @article.comments.create(comment_params)
redirect_to article_path(@article)
end
private
def comment_params
params.require(:comment).permit(:commenter, :body)
end
end
- 在
/app/views/articles/show.html.erb
文件中添加:
<h2>Comments</h2>
<% @article.comments.each do |comment| %>
<p>
<strong>Commenter:</strong>
<%= comment.commenter %>
</p>
<p>
<strong>Comment:</strong>
<%= comment.body %>
</p>
<% end %>
<h2>Add a comment:</h2>
<%= form_for([@article, @article.comments.build]) do |f| %>
<p>
<%= f.label :commenter %><br>
<%= f.text_field :commenter %>
</p>
<p>
<%= f.label :body %><br>
<%= f.text_area :body %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
这部分代码负责添加评论及评论的显示功能。
- 在
routes.rb
中修改路由为:
resources :articles do
resources :comments
end
现在,我们的blog系统就已经搭建完成了。
我们来看看添加评论后的效果:
总结
我们成功的搭建了一个博客系统。在搭建过程中,我们看到了scaffold的强大功能,短短一行代码为我们实现了很多很多的功能,而这就是rails的魅力。
事实上,我们完全可以自己来实现scaffold的功能,在文中我们将scaffold中的mvc简单地介绍了一下。scaffold为我们提够了足够多的功能,但并非所有的都是我们需要的,在遇到实际问题时,需不需要使用scaffold,还得根据实际需求来决定。