Railsチュートリアル13章 

Railsチュートリアル13章で大事なとこメモっておく

テストコードはRspecで書いています。

 

・複合キーインデックス

micropostのマイグレーションファイル

class CreateMicroposts < ActiveRecord::Migration[6.1]
def change
create_table :microposts do |t|
t.text :content
t.references :user, null: false, foreign_key: true

t.timestamps
end

add_index :microposts, [:user_id, :created_at] ここ!!
end
end

 

複数のキーをセットで扱うことができる。
こうすることでuser_idに紐付けられたmicropostsを作成順(今回は作成の逆順にする)で取り出す
 
・default scope
 
 Micropostモデル
class Micropost < ApplicationRecord
belongs_to :user
has_one_attached :image
default_scope -> { order(created_at: :desc) } ここ!!

このようにして作成の逆順で取り出すよう設定している。

 

time_ago_in_wordsメソッド

<li id="micropost-<%= micropost.id %>">
<%= link_to gravatar_for(micropost.user, size: 50), micropost.user %>
<span class="user"><%= link_to micropost.user.name, micropost.user %></span>
<span class="content"><%= micropost.content %>
<%= image_tag micropost.display_image if micropost.image.attached? %>
</span>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
           ここ!!
<% if current_user?(micropost.user) %>
<%= link_to "delete", micropost, method: :delete,
data: { confirm: "You sure?" } %>
<% end %>
</span>
</li>

何分前とかって出るようにする。例えば3分前、3時間前、3日前。

 

マクロポストの作成(build)
 
root_pathのhomeでuserに紐ずくmicropostを作成する。
何かに紐付いたものを生成する場合、newではなくbuildになる。
作成はhomeで保存はmicropostのcreate
class StaticPagesController < ApplicationController
def home
if logged_in?
@micropost = current_user.microposts.build
                  ここ!!
@feed_items = current_user.feed.paginate(page: params[:page])
end
end
micropostのcreateアクションがこれでmergeでuser_id:current_user.idってしてないんだよね。だからcurrent_user.buildってしてるんだ
def create
@micropost = current_user.microposts.build(micropost_params)
@micropost.image.attach(params[:micropost][:image])
if @micropost.save
flash[:success] = "Micropost created!"
redirect_to root_url
else
@feed_items = current_user.feed.paginate(page: params[:page])
render 'static_pages/home'
end
end
def micropost_params
params.require(:micropost).permit(:content, :image) ここねmergeしてない
end
 
referrer
def destroy
@micropost.destroy
flash[:success] = "Micropost deleted"
redirect_to request.referrer || root_url
            ここ!!# これで前のページに戻している
homeでdeleteしたらhome, user/showでdeleteしたらuser/showに戻る root_urlはnilだった場合の対処
end
feed
userモデル
def feed
Micropost.where("user_id = ?", id)
end
 ?を使うことでidをエスケープしている
ここのidとは右側のこと。self.idのことつまりはUser.id
SQL文に変数を代入する場合は常にエスケープする必要がある
 
 
 
app/controllers/static_pages_controller.rb
 
class StaticPagesController < ApplicationController
def home
if logged_in?
@micropost = current_user.microposts.build
@feed_items = current_user.feed.paginate(page: params[:page]) ここ!!
end
end
 
app/views/static_pages/home.html.erb
<div class="col-md-8">
<h3>Micropost Feed</h3>
<%= render 'shared/feed' %>
</div>
 
app/views/shared/_feed.html.erb
<% if @feed_items.any? %>
<ol class="microposts">
<%= render @feed_items %> ここ!!
</ol>
<%= will_paginate @feed_items,
params: { controller: :static_pages, action: :home } %>
<% end %>

<%= render @feed_items %>

@feed_itemsはMicropostsのクラスを持っているため、micropostsのパーシャルを呼び出すことができた。_micropost.html.erbを呼び出してる

 

microposts作成失敗の場合に備えて

 app/controllers/microposts_controller.rb

def create
@micropost = current_user.microposts.build(micropost_params)
@micropost.image.attach(params[:micropost][:image])
if @micropost.save
flash[:success] = "Micropost created!"
redirect_to root_url
else
@feed_items = current_user.feed.paginate(page: params[:page]) ここ!!
renderする際に@feed_itemsを渡してる
render 'static_pages/home'
end
end

 

ページネートでエラーが出るので解決。

micropostsをcreateするcreateアクションがMicropostsコントローラーにあるので、Micropostsコントローラーでページネートしようとしている。

そこで、コントローラーとアクションを指定してあげることで解決する。

app/views/shared/_feed.html.erb

<% if @feed_items.any? %>
<ol class="microposts">
<%= render @feed_items %>
</ol>
<%= will_paginate @feed_items, ここ!!
params: { controller: :static_pages, action: :home } %>
 コントローラーとアクションを指定
<% end %>

 

 

テストコード(Rspec)

factorybot

FactoryBot.define do
 
factory :micropost do
content { Faker::Lorem.sentence }
# created_at { 10.minutes.ago }
association :user
 
end

trait :yesterday do
content { "昨日" }
created_at { 1.day.ago }
end

trait :now do
content { "今" }
created_at { Time.zone.now }
end

trait :one_week_ago do
content { "1週間前" }
created_at { 1.week.ago }
end

 
end

モデルテスト micropost

require 'rails_helper'

RSpec.describe Micropost, type: :model do
before do
@micropost = FactoryBot.create(:micropost)
ここはbuildじゃダメだった。
end

it "should be valid" do
expect(@micropost).to be_valid
end

it "user id should be present" do
@micropost.user = nil
expect(@micropost).not_to be_valid
end

it "content should be present" do
@micropost.content = " "
expect(@micropost).not_to be_valid
end

it "content should be at most 140 characters" do
@micropost.content = "a" * 141
expect(@micropost).not_to be_valid
end


end

RSpec.describe Micropost, type: :model do
before do
factroybot見て
@one_week_ago = FactoryBot.create(:micropost,:one_week_ago)
@now = FactoryBot.create(:micropost,:now)
@yesterday = FactoryBot.create(:micropost,:yesterday)
 
 
end

it 'order should be most recent first' do
expect(Micropost.first).to eq @now
end

end

RSpec.describe Micropost, type: :model do
before do
@micropost = FactoryBot.create(:micropost)
end

it 'associated microposts should be destroyed' do
expect do
@micropost.user.destroy
end.to change(Micropost, :count).by(-1)

end


end

 system spec micropost

equire 'rails_helper'

RSpec.describe "Microposts", type: :system do
before do
@user = FactoryBot.create(:user)
@micropost = FactoryBot.create(:micropost)
# @microposts = FactoryBot.create_list(:micropost, 32)
ここ!! micropost34個分作ってる
34.times do
content = Faker::Lorem.sentence(word_count: 5)
@user.microposts.create!(content: content)
end
 
end

it 'user/showのmicroposts' do
log_in(@user)
visit user_path(@user)
Micropost.paginate(page: 1).each do |micropost|
# expect(page).to have_link href: user_path(micropost.user)
expect(page).to have_content(micropost.user.name)
expect(page).to have_content(micropost.content)
end
end

it 'micropost sidebar count' do
log_in(@user)
visit root_path
expect(page).to have_content("34 microposts") s
log_in(@micropost.user)
visit root_path
expect(page).to have_content("1 micropost") sなし

end

it 'micropost interface 全体の流れ(micropost)' do
log_in(@user)
visit root_path
expect(page).to have_link"Next"
# これでページネーションあること(30個以上micropostあること)示す
fill_in 'micropost_content', with: "How are you?"
# labelがない場合idでもいい micropost_contentはid
画像のテスト!
image_path = Rails.root.join('spec/fixtures/kitten.jpg')
attach_file('micropost[image]', image_path, make_visible: true)
           これはname
# attach_file 'micropost_image', with: "#{Rails.root}/spec/fixtures/kitten.jpg"
expect do
find('input[name="commit"]').click
end.to change { Micropost.count }.by(1)
expect do
all('ol li')[0].click_link 'delete'      34個あるのmicropostの1番目 olとliはcss,
ul, li と一緒
ここ大事!!
# deleteを押すとYou sureというconfirmが出るのでそれの対処
# expectのブロック内にひとつ以上のexpectもしくはfindを入れないと、
# ダイアログが表示されてacceptされる前に次へ進んでしまうので注意が必要らしい
page.accept_confirm "You sure?"
# ここ!!!大事 これでOKの役割
expect(page).to have_content("Micropost deleted")
# 上の理由によりこれもいる
end.to change { Micropost.count }.by(-1)
# +@
# pageインスタンスにaccept_confirmとdismiss_confirmという
# ブラウザのダイアログ操作用メソッドが用意されていてる
# dismissに変えたところmicropostは減らなかった
visit user_path(@micropost.user)
expect(page).not_to have_link 'delete'
 
 
end
 

end

RSpec.describe "Microposts", type: :request do
before do
@micropost = FactoryBot.create(:micropost)
@other_micropost = FactoryBot.create(:micropost)
# @user = FactoryBot.create(:user)
end
# ****今気づいたけどuserとmicropostはネストの関係じゃないんだ。だからuserのidとかいらない
# ここではそういう風にしてない ネストにしない理由はcurrent_userでuserの情報を持ってこれるから
it 'should redirect create when not logged in' do
expect do
post microposts_path, params: { micropost: { content: "Lorem ipsum" } }
# こことかuserのidいらない
end.to change { Micropost.count }.by(0)
expect(response).to redirect_to login_url

end

it 'should redirect destroy when not logged in' do
expect do
delete micropost_path(@micropost)
# こことか
end.to change { Micropost.count }.by(0)
expect(response).to redirect_to login_url

end

it "should redirect destroy for wrong micropost" do
log_in_as(@micropost.user)
expect do
delete micropost_path(@other_micropost)
# こことか
end.to change { Micropost.count }.by(0)
expect(response).to redirect_to root_path
end

end