2009/12/09

Ruby on Rails のメモ

インストール

gem でインストール

gem install rails

MySQL を使うので

gem install mysql

Getting Started with Rails

Getting Started with Rails をやってみる。

アプリケーションの作成。

MySQL を使う。

rails blog -d mysql

これで blog ディレクトリ以下にいっぱいファイルが作成される。

rake -T

で、rake のタスクがいっぱい表示された。

RSpec を使うために

script/plugin install git://github.com/dchelimsky/rspec.git -r 'refs/tags/1.2.9' script/plugin install git://github.com/dchelimsky/rspec-rails.git -r 'refs/tags/1.2.9' script/generate rspec

DB を作成する。

次で blog_development MySQL にデータベースが作成される。 RSpec のために test の方も作る。

rake db:create rake db:create RAILS_ENV=test

Web サーバの実行

script/server

http://localhost:3000/ にアクセスして動いていることを確認。

About your application’s environment をクリックすると各バージョンが表示される。

script/about でも表示される。

RSpec の実行

次でずっと全テストが走るつづける。

autospec

緑は OK 赤は NG。

apps/blog/spec/spec.opts の —format progress を —format specdoc とすると仕様が表示されるようになる。

次で一回だけ全テストが走る。

rake spec

最初のページを作る

home コントローラを作る。inedx アクション付きで。 RSpec のために rspec_controller で。

script/generate rspec_controller home index

app/views/home/index.html.erb を次の内容に編集する。

<h1>Hello, Rails!</h1>
<p>まみむめも♪</p>

http://localhost:3000/home/index にアクセスすると上記内容が表示される。

このページを http://localhost:3000/ で表示するには public/index.html を削除し、config/routes.rb で map.root を指定する。

  map.root :controller => "home"
scaffold を使ってみる

モデル、ビュー、コントロールが一気にできちゃう。これも rspec_scaffold で。

script/generate rspec_scaffold Post name:string title:string content:text rake db:migrate rake db:migrate RAILS_ENV=test

M-x rinari-sql してできたテーブルを確認してみる。

mysql> show tables;
+----------------------------+
| Tables_in_blog_development |
+----------------------------+
| posts |
| schema_migrations |
+----------------------------+
2 rows in set (0.00 sec)

mysql> SHOW FIELDS FROM `posts`;
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | YES | | NULL | |
| title | varchar(255) | YES | | NULL | |
| content | text | YES | | NULL | |
| created_at | datetime | YES | | NULL | |
| updated_at | datetime | YES | | NULL | |
+------------+--------------+------+-----+---------+----------------+
6 rows in set (0.00 sec)

id, created_at, updated_at はデフォルトで存在するらしい。

app/views/home/index.html.erb にポストページへのリンクを追加する。

<h1>Hello, Rails!</h1>
<p>まみむめも♪</p>
<%= link_to "ブログへ", posts_path %>

posts_path は config/routes.rb に次の行が script/generate scaffold によって追加されて使えるようになっている。レストフルなフレーバー?

map.resources :posts

追加したリンクをクリックすると posts の CRUD ができるようになっている。

autospec が赤になっているので、テストのためのテスト的に spec/views/home/index.html.erb_spec.rb を修正する。 11行目。「まみむめも♪」と書かれた p タグがあることを期待している。

    response.should have_tag('p', %r[まみむめも♪])
validation を追加する

Post に次の validation を追加する。

  • name は必須
  • title は必須
  • title は5文字以上

app/models/post.rb をいじる。

class Post < ActiveRecord::Base
validates_presence_of :name, :title
validates_length_of :title, :minimum => 5
end

RSpec も。

# -*- coding: utf-8 -*-
require 'spec_helper'

describe Post do
before(:each) do
@valid_attributes = {
:name => "value for name",
:title => "value for title",
:content => "value for content"
}
end

it "should create a new instance given valid attributes" do
Post.create!(@valid_attributes)
end

it "name がないと不正" do
post = Post.create(@valid_attributes)
post.name = nil
post.save
post.should_not be_valid
end

it "title がないと不正" do
post = Post.create(@valid_attributes)
post.title = nil
post.save
post.should_not be_valid
end

it "title が4文字だと不正" do
post = Post.create(@valid_attributes)
post.title = "あいうえ"
post.save
post.should_not be_valid
end

it "title が5文字だと正常" do
post = Post.create(@valid_attributes)
post.title = "あいうえお"
post.save
post.should be_valid
end

it "title が6文字でも正常" do
post = Post.create(@valid_attributes)
post.title = "あいうえおか"
post.save
post.should be_valid
end
end
パーシャルテンプレート

app/views/posts/new.html.erb と app/views/posts/edit.html.erb の共通部分をパーシャルテンプレートにする。

共通部分を選択して M-x rinari-extract-partial とすると、名前をきいてくるので form と入力する。 _form.html.erb が作成され、new.html.erb と edit.html.erb は次のようにに編集する。

<%= render :partial => 'form' %>
コメントできるようにする

ポストに対してコメントできるようにする。 rspec_model で Comment モデルを作成。

script/generate rspec_model Comment commenter:string body:text post:references
rake db:migrate
rake db:migrate RAILS_ENV=test

Post は Comment をいくつか持っている。has_many で指定。

class Post < ActiveRecord::Base
validates_presence_of :name, :title
validates_length_of :title, :minimum => 5

has_many :comments
end

config/routes.rb でネスト。

map.resources :posts, :has_many => :comments

rspec_controller で Comment のコントローラを作る。ビューのあるアクションのみ引数に指定する。

script/generate rspec_controller Comments index show new edit

できた app/controllers/CommentsController の中身を実装する。

view を編集(コピペ)。

app/views/posts/show.html.erb にコメントへのリンクを追加。

<%= link_to 'Back', post_comments_path(@post) %>
Building a Multi-Model Form

複数のモデルを扱うフォーム。タグを付けられるようにする。

script/generate rspec_model tag name:string post:references
rake db:migrate
rake db:migrate RAILS_ENV=test

Post モデルを修正。

# -*- coding: utf-8 -*-
class Post < ActiveRecord::Base
validates_presence_of :name, :title
validates_length_of :title, :minimum => 5

has_many :comments

has_many :tags
accepts_nested_attributes_for(:tags,
# 削除チェックボックスのために
:allow_destroy => :true,
:reject_if => proc { |attrs|
# すべての属性が必須
attrs.all? { |k, v|
v.blank?
}
})
end

apps/blog/app/views/posts/_form.html.erb を修正。 new のときのために1行目に ... build if ... を追加。 <% post_form.fields_for :tags do |tag_form| %> を追加。

<% @post.tags.build if @post.tags.empty? %>
<% form_for(@post) do |post_form| %>
<%= post_form.error_messages %>

<p>
<%= post_form.label :name %><br />
<%= post_form.text_field :name %>
</p>
<p>
<%= post_form.label :title %><br />
<%= post_form.text_field :title %>
</p>
<p>
<%= post_form.label :content %><br />
<%= post_form.text_area :content %>
</p>
<h2>タグ</h2>
<% post_form.fields_for :tags do |tag_form| %>
<p>
<%= tag_form.label :name, 'タグ:' %>
<%= tag_form.text_field :name %>
</p>
<% unless tag_form.object.nil? || tag_form.object.new_record? %>
<p>
<%= tag_form.label :_delete %>
<%= tag_form.check_box :_delete %>
</p>
<% end %>
<% end %>
<p>
<%= post_form.submit '保存' %>
</p>
<% end %>

テスト

RSpec でのテスト

ビューのテスト

mock_model でモデルを作って assigns でビューから見えるようにする、ということだろうか。

spec/views/comments/index.html.erb_spec.rb

# -*- coding: utf-8 -*-
require 'spec_helper'

describe "/comments/index" do
before(:each) do
@comment = mock_model(Comment,
:commenter => '田中京子',
:body => '寒かったですね。')
@post = mock_model(Post,
:name => '山田太郎',
:title => '今日のできごと',
:content => '今日はくもりでした。')
assigns[:post] = @post
assigns[:comments] = [@comment]
render 'comments/index'
end

#Delete this example and add some real ones or delete this file
it "should tell you where to find the file" do
response.should have_tag('td', @comment.commenter)
response.should have_tag('td', @comment.body)
end
end

spec/views/comments/show.html.erb_spec.rb

# -*- coding: utf-8 -*-
require 'spec_helper'

describe "/comments/show" do
before(:each) do
@comment = mock_model(Comment,
:commenter => '田中京子',
:body => '寒かったですね。')
@post = mock_model(Post,
:name => '山田太郎',
:title => '今日のできごと',
:content => '今日はくもりでした。',
:comments => [@comment])
assigns[:post] = @post
assigns[:comment] = @comment
render 'comments/show'
end

#Delete this example and add some real ones or delete this file
it "should tell you where to find the file" do
response.should have_tag('p', "#{@comment.commenter} さんのコメント")
response.should have_tag('p', @comment.body)
end
end

spec/views/comments/edit.html.erb_spec.rb

# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
require 'spec_helper'

describe "/comments/edit" do
before(:each) do
@comment = mock_model(Comment,
:commenter => '田中京子',
:body => '寒かったですね。')
@post = mock_model(Post,
:comments => [@comment])
assigns[:post] = @post
assigns[:comment] = @comment
render 'comments/edit'
end

#Delete this example and add some real ones or delete this file
it "should tell you where to find the file" do
response.should have_tag('input[id=comment_commenter][value=?]',
@comment.commenter)
response.should have_tag('textarea[id=comment_body]',
@comment.body)
end
end
モデルのテスト

自動的に作られた comments_controller_spec.rb のテストが失敗するので、 before(:each) でモックを仕込む。

  before(:each) do
@mock_comment = mock_model(Comment, :name => '田中京子',
:body => 'あ')
@mock_post = mock_model(Post,
:name => '山田太郎',
:comments => [@mock_comment])
@mock_post.comments.stub!(:build).and_return(Comment.new)
Post.stub!(:find).and_return(@mock_post)
end

0 件のコメント: