1、新建site
rails new SITENAME
手动终止,修改Gemfile,http://gems.ruby-china.org
bundle install 安装依赖
2、运行:
rails server
localhost:3000
3、生成控制器:
rails generate controller welcome index
4、修改路由:
root ‘welcome#index’ 根路径为welcome控制器的index方法
get ‘welcome/index’ 访问welcome/index时交给根方法,访问localhost:3000和访问localhost:3000/welcome/index效果相同
出现error:ExecJS::ProgramError in …
原因:js依赖没安装
解决:在Gemfile中添加 gem ‘coffee-script-source’, ‘1.8.0’
在命令行运行 bundle update coffee-script-source
http://stackoverflow.com/questions/28241981/rails-4-execjsprogramerror-in-pageswelcome
5、新建资源:
routes.rb中添加 resources :articles ,新建符合REST架构的资源
命令行运行rake/routes
$ bin/rake routes
Prefix Verb URI Pattern Controller#Action
articles GET /articles(.:format) articles#index
POST /articles(.:format) articles#create
new_article GET /articles/new(.:format) articles#new
edit_article GET /articles/:id/edit(.:format) articles#edit
article GET /articles/:id(.:format) articles#show
PATCH /articles/:id(.:format) articles#update
PUT /articles/:id(.:format) articles#update
DELETE /articles/:id(.:format) articles#destroy
root GET / welcome#index
6、生成article控制器:
rails g controller articles #generate可以简写成g
7、直接访问localhost:3000/articles/是访问index页面
控制器方法只能为public
在articles_controller.rb中定义空的new方法,在view/articles下新建new.html.rb文件,写入html内容,即可访问articles/new网页
8、new.html.rb中添加新建文章的表单:
问题是源代码显示form的action=”/articles/new”,提交表单显示没有post方法
<%= form_for :article do |f| %>
<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 %>
9、修改表单:
<%= form_for :article, url: articles_path do |f| %>
在rake routes中显示articles对应的URL是articles/,使得表单向这个路由发起POST请求,并自动调用create方法
10、新建create方法:
def create
render plain: params[:article].inspect
end
11、生成article模型:
rails generate model Article title:string text:text
该操作生成models/articles.rb和db/migrate/….create_articles.rb
12、数据库迁移:
rake db:migrate
13、修改create方法,与数据库连接
def create
@article = Article.new(params[:article])
@article.save
redirect_to @article
end
params[:article]中各属性值会自动映射,很方便
但rails的安全机制不允许直接使用参数,要修改成
def create
@article = Article.new(article_params)
@article.save
redirect_to @article
end
private
def article_params
params.require(:article).permit(:title, :text)
end
明确说明允许使用哪些参数
14、此时提交表单显示没有show方法
controller
def show
@article = Article.find(params[:id])
end
view
<p>
<strong>Title:</strong>
<%= @article.title %>
</p>
<p>
<strong>Text:</strong>
<%= @article.text %>
</p>
此时 localhost:3000/articles/id 网页将显示一篇文章
15、完成index方法,在访问localhost:3000/articles/时显示所有文章
def index
@articles = Article.all
end
<h1>Listing articles</h1>
<table>
<tr>
<th>Title</th>
<th>Text</th>
</tr>
<% @articles.each do |article| %>
<tr>
<td><%= article.title %></td>
<td><%= article.text %></td>
</tr>
<% end %>
</table>
16、添加链接
打开 app/views/welcome/index.html.erb 文件,改成这样:
<h1>Hello, Rails!</h1>
<%= link_to 'My Blog', controller: 'articles' %>
在localhost:3000/welcome/页面下出现My Blog链接,点击进入localhost:3000/articles/
接下来添加到其他页面的链接。先在 app/views/articles/index.html.erb 中添加“New Article”链接,放在 table 标签之前:
<%= link_to 'New article', new_article_path %>
然后在 app/views/articles/new.html.erb 中添加一个链接,位于表单下面,返回到 index 动作:
<%= form_for :article do |f| %>
...
<% end %>
<%= link_to 'Back', articles_path %>
最后,在 app/views/articles/show.html.erb 模板中添加一个链接,返回 index 动作,这样用户查看某篇文章后就可以返回文章列表页面了:
<p>
<strong>Title:</strong>
<%= @article.title %>
</p>
<p>
<strong>Text:</strong>
<%= @article.text %>
</p>
<%= link_to 'Back', articles_path %>
17、添加表单验证:
models/article.rb 中 显示article继承了ActiveRecord::Base类,可以使用Base类的方法进行数据验证
class Article < ActiveRecord::Base
validates :title, presence: true,
length: { minimum: 5 }
end
修改控制器
def new
@article = Article.new
end
def create
@article = Article.new(article_params)
if @article.save
redirect_to @article
else
render 'new'
end
end
private
def article_params
params.require(:article).permit(:title, :text)
end
在new方法中新建article对象,在create中通过判断article.save的返回值指定指向的网页,render方法将本次的article对象传给new使得渲染后的页面保留填写过的内容,而redirect_to方法会重新发起一次请求,转向articles页面
18、添加创建文章时的报错信息:
<%= form_for :article, url: articles_path 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 |msg| %>
<li><%= msg %></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 %>
<%= link_to 'Back', articles_path %>
pluralize 是 Rails 提供的帮助方法,接受一个数字和字符串作为参数。如果数字比 1 大,字符串会被转换成复数形式
之所以在new方法中创建article对象是为了传入视图模板中,否则这里的article就是nil
19、添加edit,页面指向update方法:
new ---> create
edit ---> update
def edit
@article = Article.find(params[:id])
end
<h1>Editing article</h1>
<%= form_for :article, url: article_path(@article), method: :patch 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 |msg| %>
<li><%= msg %></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 %>
<%= link_to 'Back', articles_path %>
method: :patch 选项告诉 Rails,提交这个表单时使用 PATCH 方法发送请求。根据 REST 架构,更新资源时要使用 HTTP PATCH 方法。
form_for可以传入对象和symbol,index页面中@articles是对象,edit中 : article 是symbol
def update
@article = Article.find(params[:id])
if @article.update(article_params)
redirect_to @article
else
render 'edit'
end
end
private
def article_params
params.require(:article).permit(:title, :text)
end
不用把所有的属性都提供给 update 动作。例如,如果使用 @article.update(title: ‘A new title’),Rails 只会更新 title 属性,不修改其他属性。
20、index页面加入edit链接:
<table>
<tr>
<th>Title</th>
<th>Text</th>
<th colspan="2"></th>
</tr>
<% @articles.each do |article| %>
<tr>
<td><%= article.title %></td>
<td><%= article.text %></td>
<td><%= link_to 'Show', article_path(article) %></td>
<td><%= link_to 'Edit', edit_article_path(article) %></td>
</tr>
<% end %>
</table>
show页面加入edit链接:
...
<%= link_to 'Back', articles_path %>
| <%= link_to 'Edit', edit_article_path(@article) %>
21、添加局部视图消除重复代码:
新建 app/views/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 |msg| %>
<li><%= msg %></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 %>
之所以能在两个动作中共用一个 form_for,是因为 @article 是一个资源,对应于符合 REST 架构的路由,Rails 能自动分辨使用哪个地址和请求方法。
下面来修改 app/views/articles/new.html.erb 视图,使用新建的局部视图,把其中的代码全删掉,替换成:
<h1>New article</h1>
<%= render 'form' %>
<%= link_to 'Back', articles_path %>
然后按照同样地方法修改 app/views/articles/edit.html.erb 视图:
<h1>Edit article</h1>
<%= render 'form' %>
<%= link_to 'Back', articles_path %>
22、删除文章:
注意rake/routes中删除的动作是DELETE,如果还使用 GET 请求,可以构建如下所示的恶意地址:
<a href='http://example.com/articles/1/destroy'>look at this cat!</a>
def destroy
@article = Article.find(params[:id])
@article.destroy
redirect_to articles_path
end
index页面中加入删除链接:
<h1>Listing Articles</h1>
<%= link_to 'New article', new_article_path %>
<table>
<tr>
<th>Title</th>
<th>Text</th>
<th colspan="3"></th>
</tr>
<% @articles.each do |article| %>
<tr>
<td><%= article.title %></td>
<td><%= article.text %></td>
<td><%= link_to 'Show', article_path(article) %></td>
<td><%= link_to 'Edit', edit_article_path(article) %></td>
<td><%= link_to 'Destroy', article_path(article),
method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</table>
:method 和 :’data-confirm’ 选项设置链接的 HTML5 属性,点击链接后,首先会显示一个对话框,然后发起 DELETE 请求。这两个操作通过 jquery_ujs 这个 JavaScript 脚本实现。生成程序骨架时,会自动把 jquery_ujs 加入程序的布局中(app/views/layouts/application.html.erb)。没有这个脚本,就不会显示确认对话框
23、创建评论模型:
rails generate model Comment commenter:string body:text article:references
模板comment.rb中自动生成belongs_to :article
数据库迁移:
rake db:migrate
相对应的修改article.rb文件:
class Article < ActiveRecord::Base
has_many :comments
validates :title, presence: true,
length: { minimum: 5 }
end
24、修改路由文件:
resources :articles do
resources :comments
end
这种做法叫嵌套资源,表明了文章和评论间的层级关系
25、生成控制器:
rails generate controller Comments
修改articles/show视图,用户可以写评论
<p>
<strong>Title:</strong>
<%= @article.title %>
</p>
<p>
<strong>Text:</strong>
<%= @article.text %>
</p>
<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 %>
<%= link_to 'Back', articles_path %>
| <%= link_to 'Edit', edit_article_path(@article) %>
26、定义create方法:
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
27、articles/show页面显示评论:
<p>
<strong>Title:</strong>
<%= @article.title %>
</p>
<p>
<strong>Text:</strong>
<%= @article.text %>
</p>
<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 %>
<%= link_to 'Edit Article', edit_article_path(@article) %> |
<%= link_to 'Back to Articles', articles_path %>
28、新增局部视图 views/comments/_comment.html.erb:
<p>
<strong>Commenter:</strong>
<%= comment.commenter %>
</p>
<p>
<strong>Comment:</strong>
<%= comment.body %>
</p>
修改article的show页面:
<p>
<strong>Title:</strong>
<%= @article.title %>
</p>
<p>
<strong>Text:</strong>
<%= @article.text %>
</p>
<h2>Comments</h2>
<%= render @article.comments %>
<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 %>
<%= link_to 'Edit Article', edit_article_path(@article) %> |
<%= link_to 'Back to Articles', articles_path %>
29、新建 app/views/comments/_form.html.erb
<%= 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 %>
修改article/show
<p>
<strong>Title:</strong>
<%= @article.title %>
</p>
<p>
<strong>Text:</strong>
<%= @article.text %>
</p>
<h2>Comments</h2>
<%= render @article.comments %>
<h2>Add a comment:</h2>
<%= render "comments/form" %>
<%= link_to 'Edit Article', edit_article_path(@article) %> |
<%= link_to 'Back to Articles', articles_path %>
%= render “comments/form” %,rails会自动识别form
30、删除评论:
先在 app/views/comments/_comment.html.erb 局部视图中加入删除评论的链接:
<p>
<strong>Commenter:</strong>
<%= comment.commenter %>
</p>
<p>
<strong>Comment:</strong>
<%= comment.body %>
</p>
<p>
<%= link_to 'Destroy Comment', [comment.article, comment],
method: :delete,
data: { confirm: 'Are you sure?' } %>
</p>
class CommentsController < ApplicationController
def create
@article = Article.find(params[:article_id])
@comment = @article.comments.create(comment_params)
redirect_to article_path(@article)
end
def destroy
@article = Article.find(params[:article_id])
@comment = @article.comments.find(params[:id])
@comment.destroy
redirect_to article_path(@article)
end
private
def comment_params
params.require(:comment).permit(:commenter, :body)
end
end
31、删除文章时要删除与其对应的评论:
class Article < ActiveRecord::Base
has_many :comments, dependent: :destroy
validates :title, presence: true,
length: { minimum: 5 }
end
32、简单认证:
class ArticlesController < ApplicationController
http_basic_authenticate_with name: "dhh", password: "secret", except: [:index, :show]
def index
@articles = Article.all
end
# snipped for brevity
class CommentsController < ApplicationController
http_basic_authenticate_with name: "dhh", password: "secret", only: :destroy
def create
@article = Article.find(params[:article_id])
...
end
# snipped for brevity