Rails入門(チュートリアル)
ruby on rails の勉強がてら、本家のチュートリアルを一通り実行してみる
3.2 プロジェクトの作成
$ rails new blog
- ヘルプが見れる(rails new -h)
3.3.1 DBの内容
config/database.yml
- 各環境の設定内容が記述されている
- デフォルトではsqlite3
3.4 DBの生成
$ rake db:create
4.1 サーバ起動
$ rails server
- http://localhost:3000 にアクセスするとwelcomeページが表示される
- 終了はCtrl+c
4.2 お決まりのHello world
- コントローラの生成
$ rails generate controller home index
- 生成されるコントローラは app/controllers/home_controller.rb
- view(html)を修正app/views/home/index.html.erb
<h1>Hello, Rails!</h1>
4.3 アプリケーションのHomeページを設定
- 必ずpublic/index.html が表示されてしまうため削除
$ rm public/index.html
- ディスパッチの設定を追加
- ファイル: config/routes.rb
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"
- その後再度 http://localhost:3000 にアクセスすると、先ほどのテンプレートが表示される
6 リソースの作成(MVCを作る)
- scaffold を指定すると、model, view, controllerを一括で作成してくれる
$ rails generate scaffold Post name:string title:string content:text
6.1 マイグレーション
- テーブルの作成や、変更などを管理する仕組み
- ファイル :db/migrate/20120508162203_create_posts.rb
- マイグレーションスクリプト実行
$ rake db:migrate
- productionで作成する場合はオプションをつける rake db:migrate RAILS_ENV=production
6.2 Topページにリンクを追加
- ファイル : app/views/home/index.html.erb
<h1>Hello, Rails!</h1>
<%= link_to "My Blog", posts_path %>
- link_to [タイトル], 遷移先?
6.4 モデルの確認
- ファイル : app/models/post.rb
6.5 バリデータの追加
- ファイル : app/models/post.rb
class Post < ActiveRecord::Base
attr_accessible :content, :name, :title
validates :name, :presence => true
validates :title, :presence => true,
:length => { :minimum => 5 }
end
- attr_accessibleは変更可能なパラメータ
- presence は必須、lengthは長さを指定
6.6 コンソールを使ってみよう
$ rails console
>> 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)"]
- バリデータが効いているのがわかる
6.7 一覧ページ
- ファイル : app/controllers/posts_controller.rb
def index
@posts = Post.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: @posts }
end
end
- @postsに投稿内容をすべて取得する
- html, jsonに対応
- http://localhost:3000/posts.html
- http://localhost:3000/posts.json
- テンプレート : app/views/posts/index.html.erb
- html エスケープしない場合、
6.8 カスタムレイアウト
- フッターやヘッダーを定義
- ファイル : app/views/layouts/application.html.erb
- 各コントローラ毎に指定する場合
- app/views/layouts/posts.html.erb
6.9 新規作成ページ
- ファイル : app/controllers/posts_controller.rb
def new
@post = Post.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: @post }
end
end
- テンプレート : app/views/posts/new.html.erb
<h1>New post</h1>
<%= render 'form' %>
<%= link_to 'Back', posts_path %>
- render 'form'を指定することで、app/views/posts/_form.html.erb の内容を出力する
- 投稿処理
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
6.10 詳細表示
def show
@post = Post.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render :json => @post }
end
end
- テンプレート : app/views/posts/show.html.erb
6.11 編集ページ
- 詳細表示と違い、jsonでのレスポンスがないためシンプル
def edit
@post = Post.find(params[:id])
end
- 更新処理
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
- update_attributesで必要なパラメータを更新
6.12 削除
- ファイル : app/controllers/posts_controller.rb
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
7.1 モデルの追加
- テーブル間のリレーションシップを指定 post:references
$ rails generate model Comment commenter:string body:text post:references
- モデルクラスにも定義されている
- ファイル : app/models/comment.rb
class Comment < ActiveRecord::Base
belongs_to :post
attr_accessible :body, :commenter
end
- 再度マイグレーションスクリプトが生成されているて、それを反映
- ファイル : db/migrate/20120508170658_create_comments.rb
$ rake db:migrate
7.2 関連づけモデル
- 1投稿に付き複数のコメントが存在するためPostのmodelを修正
- ファイル :app/models/post.rb
class Post < ActiveRecord::Base
attr_accessible :content, :name, :title
validates :name, :presence => true
validates :title, :presence => true,
:length => { :minimum => 5 }
has_many :comments
end
7.3 ルート(ディスパッチ)にコメントを追加
- ファイル : config/routes.rb
- posts部分を修正
resources :posts do
resources :comments
end
7.4 コントローラの追加
$ rails generate controller Comments
- 上記エラー出る場合、ルートファイルで追加した部分をコメントアウトする
- Postのテンプレートにcomment投稿を追加
- ファイル : app/views/posts/show.html.erb
<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', edit_post_path(@post) %> |
<%= link_to 'Back', posts_path %>
- コメントコントローラに処理を追記
- ファイル : app/controllers/comments_controller.rb
def create
@post = Post.find(params[:post_id])
@comment = @post.comments.create(params[:comment])
redirect_to post_path(@post)
end
- 詳細画面でコメントを閲覧できるように修正
- ファイル : app/views/posts/show.html.erb
<h2>Comments</h2>
<% @post.comments.each do |comment| %>
<p>
<b>Commenter:</b>
<%= comment.commenter %>
</p>
<p>
<b>Comment:</b>
<%= comment.body %>
</p>
<% end %>
8.リファクタリング
- テンプレートの共通部分を外だししてみる
8.1 コレクション
- コメント部分のテンプレを外だし
- ファイル : app/views/comments/_comment.html.erb
<p>
<b>Commenter:</b>
<%= comment.commenter %>
</p>
<p>
<b>Comment:</b>
<%= comment.body %>
</p>
- 参照部分の修正 app/views/posts/show.html.erbを修正 (まだ納得は行かない><)
- 修正前
<% @post.comments.each do |comment| %>
<p>
<b>Commenter:</b>
<%= comment.commenter %>
</p>
<p>
<b>Comment:</b>
<%= comment.body %>
</p>
<% end %>
- 修正後
<%= render @post.comments %>
8.2 Form部分の外だし
- ファイル : app/views/comments/_form.html.erb
<%= 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 %>
- 参照部分の修正 : app/views/posts/show.html.erb
- 今回は単純に置き換える
<%= render "comments/form" %>
9 コメントの削除
- リンクの追加 : app/views/comments/_comment.html.erb
<p>
<%= link_to 'Destroy Comment', [comment.post, comment],
:confirm => 'Are you sure?',
:method => :delete %>
</p>
- コントローラに追加 : app/controllers/comments_controller.rb
def destroy
@post = Post.find(params[:post_id])
@comment = @post.comments.find(params[:id])
@comment.destroy
redirect_to post_path(@post)
end
9.1 関連データの削除も対応
- 親の投稿データが削除された場合、一緒に削除
- ファイル : app/models/post.rb
class Post < ActiveRecord::Base
attr_accessible :content, :name, :title
validates :name, :presence => true
validates :title, :presence => true,
:length => { :minimum => 5 }
has_many :comments, :dependent => :destroy
end
10 セキュリティー
- Basic認証
- ファイル : app/controllers/posts_controller.rb
class PostsController < ApplicationController
http_basic_authenticate_with :name => "dhh", :password => "secret", :except => [:index, :show]
# GET /posts
# GET /posts.json
def index
@posts = Post.all
- :exceptで対象外を指定
- :only => :destroy を指定すると、削除のみ認証する
11 複数モデル
- タグモデルの生成
$ rails generate model tag name:string post:references
- マイグレーション実行
$ rake db:migrate
- モデルに関連づけを行う
- ファイル : app/models/post.rb
has_many :tag
accepts_nested_attributes_for :tags, :allow_destroy => :true,
:reject_if => proc { |attrs| attrs.all? { |k, v| v.blank? } }
- accepts_nested_attributes_forは親子関係の定義?
- allow_destroyはネストした属性にチェックボックスを表示
- reject_if は禁止条件を設定
- フォームテンプレートにタグを追加
- ファイル : views/posts/_form.html.erb
<h2>Tags</h2>
<%= render :partial => 'tags/form',
:locals => {:form => post_form} %>
- タグ用のフォームテンプレート
- ファイル : app/views/tags/_form.html.erb
<%= 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 %>
- 投稿テンプレートにタグを追加
- ファイル : app/views/posts/show.html.erb
<p>
<b>Tags:</b>
<%= @post.tags.map { |t|
t.name }.join(", ") %>
</p>
12 View Helpers
- ファイル : app/helpers/posts_helper.rb
def join_tags(post)
post.tags.map { |t|
t.name }.join(", ")
end
- 使ってみる : app/views/posts/show.html.erb
- 変更前
<p>
<b>Tags:</b>
<%= @post.tags.map { |t|
t.name }.join(", ") %>
</p>
- 変更後
<p>
<b>Tags:</b>
<%= join_tags(@post) %>
</p>