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) %>

に変更する。

Tags of current page

,