インストール
gem でインストール
gem install rails
MySQL を使うので
gem install mysql
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