railstutorial.jp 10章メモ
- テーマ
- アソシエーション
- パーシャル
micropostsテーブルの追加
$ rails generate model Micropost content:string user_id:integer
$ rake db:migrate
$ rake test:prepare
やはりテストがこける
Failure/Error: let(:user) { FactoryGirl.create(:user) } ActiveRecord::RecordInvalid: Validation failed: Email has already been taken
いったんtest.sqlite3を削除するとうまくいった
$ rm db/test.sqlite3 $ rake db:create db:migrate RAILS_ENV=test $ rspec spec/models/micropost_spec.rb
User has_many Micropost
class User < ActiveRecord::Base # 複数形 # 自分が削除されたら関連先も削除する has_many :microposts, dependent: :destroy end
Micropost belongs_to User
class Micropost < ActiveRecord::Base # 単数形 belongs_to :user end
メソッド | 用途 |
---|---|
micropost.user | マイクロポストに関連付けられたユーザーオブジェクトを返す。 |
user.microposts | ユーザーのマイクロポストの配列を返す。 |
user.microposts.create(arg) | マイクロポストを作成する (user_id = user.id)。 |
user.microposts.create!(arg) | マイクロポストを作成する (失敗した場合は例外を発生する)。 |
user.microposts.build(arg) | 新しいMicropostオブジェクトを返す (user_id = user.id)。 |
FactoryGirlでの関連づけ
:micropostの定義内にuser
を含めるだけ。userはこのコードの上の方でfactory :user do
と定義しているので使えると思われる
factory :micropost do content "Lorem ipsum" # userと関連づけ user end
letはその変数が参照されたときに初めてつくられる(遅延)
let!は即座に変数を作るので順番が重要なときに便利
before { @user.save } let!(:older_micropost) do FactoryGirl.create(:micropost, user: @user, created_at: 1.day.ago) end let!(:newer_micropost) do FactoryGirl.create(:micropost, user: @user, created_at: 1.hour.ago) end # has_many関連づけの正しさをmicropostの順序でチェック it "should have the right microposts in the right order" do expect(@user.microposts.to_a).to eq [newer_micropost, older_micropost] end
デフォルトスコープ
ラムダ-> { }
を受け取り、遅延評価される
default_scope -> { order('created_at DESC') }
Procの生成を確かめる
$ rails c > -> { puts "foo" } => #<Proc:0x007fe63cf604d0@(irb):2 (lambda)> > -> { puts "foo" }.call foo => nil
to_aメソッドはオブジェクトのコピーを返している
microposts = @user.microposts.to_a @user.destroy # ユーザにひも尽くmicropostsは削除されていないこと expect(microposts).not_to be_empty
where
メソッドはない場合には空オブジェクトを返す
expect(Micropost.where(id: micropost.id)).to be_empty
find
メソッドは内場合には例外をスローする
expect do Micropost.find(micropost) end.to raise_error(ActiveRecord::RecordNotFound)
def show @user = User.find(params[:id]) @microposts = @user.microposts.paginate(page: params[:page]) end
usersコントローラにおいて@userがあると推測される場合は引数無しでもOKだが、そうでない場合には手動でpaginateされたコレクションを渡す
<%= will_paginate %> <%= will_paginate @microposts %>
カウント数
<h3>Microposts (<%= @user.microposts.count %>)</h3>
app/views/users/show.html.erb
で使っていたとしてもapp/views/microposts/_micropost.html.erb
(単数形!)がパーシャルとなる
<%= render @microposts %>
Fakerを使ってダミーテキストの生成
content = Faker::Lorem.sentence(5)
エラーになった。。。
$ rake db:reset $ rake db:populate rake aborted! ArgumentError: wrong number of arguments (1 for 0)
allの引数、limitの指定が間違ってた。
# users = User.all(limit: 6) users = User.all.limit(6)
$ rake db:reset $ rake db:populate $ rake test:prepare $ rm db/test.sqlite3 $ rake db:create db:migrate RAILS_ENV=test
rails sし直して、micropostsが表示された。
resources :microposts, only: [:create, :destroy]
onlyを明示的に指定しなければ全アクションがbefore_actionの対象になる
before_action :signed_in_user # before_action :signed_in_user, only: [:create, :destroy]
micropostsページの結合テストをgenerate
$ rails generate integration_test micropost_pages
error_messagesパーシャルに明示的にオブジェクトを渡す方法
object: f.object
で@micropost
を渡している
また、 object: f.object
はerror_messagesパーシャルの中でobjectという変数を使うように指定している
<%= form_for(@micropost) do |f| %> <%= render 'shared/error_messages', object: f.object %> ... <% end %>
def home @micropost = current_user.microposts.build if signed_in? end
テストが落ちる -> micropost_pages_spec.rbにrequire 'support/utilities.rb'
を追加すると直る
$ rspec spec/requests/micropost_pages_spec.rb FFF Failures: 1) Micropost pages micropost creation with invalid information should not create a micropost Failure/Error: before { sign_in user } NoMethodError: undefined method `sign_in' for #<RSpec::ExampleGroups::MicropostPages::MicropostCreation::WithInvalidInformation:0x007fc98bab2ff0> # ./spec/requests/micropost_pages_spec.rb:19:in `block (2 levels) in <top (required)>'
Rspecのinclude?メソッド
include?は本来、配列が指定の要素を含んでいるかどうか返すメソッド。
its(:feed) { should include(newer_micropost) } its(:feed) { should include(older_micropost) } its(:feed) { should_not include(unfollowed_post) }
プレイスホルダ
Micropost.where("user_id = ?", id)
li##{item.id}の最初の# は CSS idを示す Capybara独自の文法で、2番目の#は Rubyの式展開#{}の先頭部分
expect(page).to have_selector("li##{item.id}", text: item.content)
renderはコレクションに与えられたパーシャルfeed_item変数を使う
<%= render partial: 'shared/feed_item', collection: @feed_items %>
関連づけて検索
find_byはみつからないと、nilを返す
def correct_user @micropost = current_user.microposts.find_by(id: params[:id]) redirect_to root_url if @micropost.nil? end
findはみつからないと、例外をスローする
def correct_user @micropost = current_user.microposts.find(params[:id]) rescue redirect_to root_url end