Ruby on Rails を使ってみた
ruby on rails を使用してみたときのメモ。 http://guides.rubyonrails.org/getting_started.html の通りにやってみた。 環境は Ubuntu 11.10 で ruby 1.9.3 を使い、 rails は 3.1.3。
rails のディレクトリを作成
rails 本体は
gem install rails
でインストールしておく。
ガイドはブログの作成を例としている。
rails new blog
で rails 用のディレクトリ blog を作る。このとき
bundle install
が実行されるのだが sqlite3 の gem のインストールに失敗したので
sudo apt-get install libsqlite3-dev
でインストールする。
cd blog
として作成されたディレクトリに移動する。 bundle install を失敗したので
bundle install
を実行する。そして
rails server
でサーバを起動する。このとき、
Could not find a JavaScript runtime. See https://github.com/sstephenson/execjs for a list of available runtimes. (ExecJS::RuntimeUnavailable)
とエラーが出た。nodejs が足りなくて実行できないらしい。
sudo apt-get install nodejs
でインストールしてから、再度、
rails server
とする。http://localhost:3000/ で表示されればよい。
ガイドには
rake db:create
とあるが、実行しなくてもデータベースはディレクトリ db 以下に すでに存在しているようだ。 初期設定が変更されたのだろうか。
今使っている端末は rails server が実行されているので、 以降は、別の端末で blog ディレクトリに移動して使用する。
ホームページ
controller の作成
とりあえず
rails generate -h
でヘルプを見る。controller を生成する。
rails generate controller home index
http://localhost:3000/home/index にアクセスして確認する。 このページを変更するには app/views/home/index.html.erb を編集すれば良い。
作成したページをホームページにする
今作成したページがホームページになるように 不要なファイルを
rm public/index.html
で削除し、config/routes.rb のコメントアウトされている root の部分を利用して
root :to => "home#index"
と編集する。http://localhost:3000/ の表示が変わる。
ガイドに「dynamic contents より public ディレクトリのファイルを優先する」とあるが、 static file を使いたい場合は public ディレクトリ以下に配置するということで よいのだろうか。
投稿機能
scaffolding
rails generate scaffold Post name:string title:string content:text
scaffolding はモデル、ビュー、コントローラーを作成して、 database migration も作成する。
rake db:migrate
として http://localhost:3000/posts を確認する。 どういうときに scaffold を使うのかはっきりしないが、とりあえず先に進むことにした。
トップページにリンクを追加する
posts へのリンクをトップページに追加する。 app/views/home/index.html.erb を編集して
<h1>Hello, Rails!</h1> <%= link_to "My Blog", posts_path %>
とする。 http://localhost:3000 を確認。
model
app/models/post.rb をエディタで開く。 ActiveRecord::Base を継承しているので基本的なメソッドは定義されている。 これに、データのチェックを次のようにして追加する。
class Post < ActiveRecord::Base
validates :name, :presence => true
validates :title, :presence => true, :length => { :minimum => 5 }
end
console で確認
rails console
の中で
p = Post.new(:content => "A new post")
p.save
p.errors
として、きちんとしていないポストではエラーが起こることを確認する。
ファイルの確認
app/controllers/posts_controller.rb をファイルを開いて中身を確認する。 http://localhost:3000/posts.json で json をダウンロードできる。
app/views/posts/index.html.erb を見る。 http://localhost:3000/posts でアクセスできる。
レイアウト
すべてのコントローラに対して使用されるレイアウトは、 app/views/layouts/application.html.erb に記述する。
app/controllers/posts_controller.rb
投稿は http://localhost:3000/posts/new で controller の new メソッドが呼ばれ、 送信ボタンを押すと create メソッドが実行される。 表示は app/views/posts/new.html.erb を見るとわかる。
http://localhost:3000/posts/1 などは個々の投稿を表示し、 controller の show メソッドが実行される。 view は app/views/posts/view.html.erb
編集は http://localhost:3000/posts/1/edit から実行できて 送信ボタンを押すと controller の update メソッドが実行される。
controller の destroy で削除。
コメント機能
モデルを追加する
rails generate model Comment commenter:string body:text post:references
rake db:migrate
app/models/comment.rb を見ると
belongs_to :post
で Active Record association が設定されている。
post に関連づける
app/models/post.rb を開き、Post クラスに
has_many :comments
を追加する。
コメントには配列
@post.comments
でアクセスできる。
ルートを追加する
config/routes.rb
resources :posts
を
resources :posts do
resources :comments
end
に変更する。
コントローラを追加する
モデルだけを追加したので、そのコントローラも追加する。
rails generate controller Comments
app/views/posts/show.html.erb に次を追加して、ページにコメント投稿フォームを追加する。
<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 %>
次に、実際に投稿処理を行う CommentsController クラスに create メソッドを追加する。 app/controllers/comments_controller.rb を開き、
class CommentsController < ApplicationController
def create
@post = Post.find(params[:post_id])
@comment = @post.comments.create(params[:comment])
redirect_to post_path(@post)
end
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 %>
comment.commenter と comment.body の commenter と body はデータベースのカラムに対応している。
コメントの一覧のリファクタリング
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 のコメントの部分を
<h2>Comments</h2> <%= render @post.comments %>
とする。それぞれのコメントに対して _comment.html.erb が表示される。
コメントのフォームのリファクタリング
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 で
<h2>Add a comment:</h2> <%= render "comments/form" %>
のように利用する。
コメントの削除
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 の CommentsController クラスに
def destroy
@post = Post.find(params[:post_id])
@comment = @post.comments.find(params[:id])
@comment.destroy
redirect_to post_path(@post)
end
を追加する。
投稿を削除したときにコメントも削除するように app/models/post.rb の
has_many :comments
を
has_many :comments, :dependent => :destroy
とする。
認証機能
PostsControlle クラスに
http_basic_authenticate_with :name => "dhh", :password => "secret", :except => :index
を追加する。ユーザ名は dhh でパスワードは secret になる。 コメントの削除に認証を要求するには CommentsController クラスに
http_basic_authenticate_with :name => "dhh", :password => "secret", :only => :destroy
を追加する。
この設定は basic 認証だから実際には別の認証を用いたほうが良いのだろう。
タグ
タグ機能を追加する
タグを実装するためにモデルを追加する。
rails generate model tag name:string post:references
rake db:migrate
app/models/post.rb の Post クラスに
has_many :tags
accepts_nested_attributes_for :tags, :allow_destroy => :true,
:reject_if => proc { |attrs| attrs.all? { |k, v| v.blank? } }
を追加する。
app/views/posts/_form.html.erb を次のように変更する。
<% @post.tags.build %> <%= form_for(@post) do |post_form| %> <% if @post.errors.any? %> <div id="errorExplanation"> <h2><%= pluralize(@post.errors.count, "error") %> prohibited this post from being saved:</h2> <ul> <% @post.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %> <div class="field"> <%= post_form.label :name %><br /> <%= post_form.text_field :name %> </div> <div class="field"> <%= post_form.label :title %><br /> <%= post_form.text_field :title %> </div> <div class="field"> <%= post_form.label :content %><br /> <%= post_form.text_area :content %> </div> <h2>Tags</h2> <%= render :partial => 'tags/form', :locals => {:form => post_form} %> <div class="actions"> <%= post_form.submit %> </div> <% end %>
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>
を追加する。
View helper
view でよく使うメソッドを app/helpers 以下に定義する。 app/helpers/posts_helper.rb を
module PostsHelper
def join_tags(post)
post.tags.map { |t| t.name }.join(", ")
end
end
とする。 app/views/posts/show.html.erb の
<%= @post.tags.map { |t| t.name }.join(", ") %>
を
<%= join_tags(@post) %>
に変更する。