基礎課題3

2021.2.25

*gemを使用して、font-awesome、bootstrapをそれぞれインストール後、使用できるように設定

バージョンはこのサイトで調べられる。

RubyGems.org | your community gem host

 

<font-awesome5(最新版対応)> (Rails5に導入するとき)

❶ Gemfileにgemを追加する。

(Gemfile)

gem 'font-awesome-sass' バージョン

 

❷ bundle install を実行する

 

❸ (app/assets/stylesheets/application.scss)に@importで追記する。

    *application.cssを使用している際は、scssに変換する。

順番を入れ替えてしまうと、アイコンが表示されないので注意。

(app/assets/stylesheets/application.scss)

@import 'font-awesome-sprockets';

@import 'font-awesome';

 

<bootstrap>

❶ Gemfileにgemを追加

(Gemfile)

gem 'bootstrap' , '~> 4.4.1'(←バージョン)

gem 'jquery-rails' (Rails5.1以上を利用しているときのみ追記)

*Bootstrap JavaScriptJQueryに依存しているので、Rails 5.1以上を利用している場合は、jquery-rails gemをGemfileに追記する必要がある。

 

 

❷sprockets-rails gemがv2.3.2以上であることを確認する。

(ターミナル)

$ bundle show |fgrep sprockets-rails

 

❸ bundle install を実行する

マニフェストファイル(アプリの設定が書かれたファイル)である(app/assets/stylesheets/application.scss)に読み込ませる。拡張子が.scssになっているか確認しとく。

(app/assets/stylesheets/application.scss)

@import "bootstrap";

 

❺Bootstrapのツールチップ機能とポップオーバー機能はpopper_jsに依存していて、Bootstrap gemはpopper_jsに依存しているので、マニフェストファイルであるapplication.jsに次のように書き加える。

(application.js)

//= require jquery3

//= require popper 

//= require bootstrap-sprockets

 

*注意

//= require jquery3よりも前に //= require_tree . を記載するとエラーになるケースがあるので注意。//= require_tree . は一番最後でいい。

 //= require_tree .で読み込んだファイルでjqueryのコードを参照していると jquery3が読み込まれる前に評価されるためメソッドが未定義となりエラーになる場合がある。

 

 

 

基礎課題2

2021.2.23-24

 

<ターミナルコマンド>

cd ~/  ホームディレクトリに戻る

cd-    一つ前の戻る

cd ..  一つ前の場所に戻る

cd ../..  二つ前の場所に戻る

history (数指定するときは数を入力)   コマンド履歴確認

rm ディレクトリやファイル 指定したファイルやディレクトリを削除

 

<ターミナルでファイルをコピー>

cd ファイルの上のディレクトリに移動

cp コピーしたい内容があるファイル コピー先のファイル(最終的に残る)

 

<gitコマンド>

git reset HEAD                      git add したファイルを全て戻す

git reset HEAD ファイル名 git add したファイルを指定して戻す

 

<controller命名規則>

rails g controller コントローラー名 アクション名」

コントローラー名 =  キャメルケースで複数形

→コントローラー名をスネークケースにしたファイルを自動的に作成する。

ex) rails g controller StaticPages topで

StaticPagesコントローラー、static_pages_controller.rbを自動作成。topアクションなども。(rails g static_pages topにしても同じだが慣習的に↑)

・モデル名は単数形で頭文字が大文字

 

<rootメソッド>

railsがルート「'/'」とするべき場所を指定できる。

ex)AコントローラのBアクションの指定するファイルをtopページに。

(routes.rb)

root to: 'A#B'

→省略すると root 'A#B'

*rake routesで確認するといい。=>は古い書き方なので使わない。

 

<sccsファイルから別の sccsファイルをインポートする>

@import 'インポートするファイル名' 

ex)

・入力する(application.scss)

@import 'color.scss'

main{color:$main-color;}

 

・インポートされるファイル(colors.scss)

$main-color:black;

 

・出力(css)

main{color:black;}

 

<image_tag>

<img   />タグを生成する。デフォルトではpublic/imagesディレクトリ下より読み込まれる。その下にディレクトリを作成しても大丈夫。

ex)altやidやclassなども追加可能。

<%= image_tag "top.png", alt: "top", id: "topimage", class:"nav_bar" %>

ex)image/〇〇/top.pngの時

<%= image_tag "〇〇/image_tag" %>

 

 

 

基礎課題1

2021.2.22

 

-gemについて-

gemとはrubyのライブラリのこと。(=結局railsのライブラリ)

 

パッケージ=プログラムの部品の部分のまとめ

便利な機能が入っている

→自分で1からコードを書かずに使える。

 

 

-bundler-

gemの管理ツール

bundlerもgemの1種

 

-gemコマンド-

$ gem install bundler    bundlerインストール

$ bundler -v   インストールされているか確認

$ gem install ○○ gemをインストール

$ gem uninstall ○○ gemをアンインストール

$ gem list  gemの一覧表示

 

-rbenv-

Ruby環境のバージョン切り替えを行う。

 

-Ajax-

実装方式の呼び方。JavaScriptを使用して、非同期通信を使うこと。

わかりやすいサイト↓

https://wa3.i-3-i.info/word12672.html

 

-VSCode選択分コメントアウト-

shift option A

 

-rails generateで作成するものを制限する-

(config/application.rb)に

config.generators do |g|
g.skip_routes true ❶
g.assets false ❷
g.helper false ❷
g.test_framework false ❸
end

 

❶ルーティングが作成されないようにする

❷ assetsとhelperファイルが作成されないようにする。

❸テストファイルが作成されないようにする。

 

rails generate で作成したものは rails destroy で元に戻せる

ex)作成して、元に戻す。

rails g controller A

rails destroy controller A

 

 

最終補講課題まとめ

2021.2.21

-ターミナルコマンド-

ls ディレクトリのファイル確認

vim ファイルの内容をターミナルで確認。

       o 入力

      escキー 設定戻す

   :wq  保存して終了

touch ファイル作成

mv [ファイル名(全て)] [移動先ディレクト(app/viewsみたいな)] ファイル移動

 

-テキストエディタショートカットキー-

Command p ファイル名検索

Command shift f ファイル名検索

Tab control ファイル名検索

Command x  現在開いているファイルを閉じる

Command -z  一つ前に戻る

 

-rails serverがcontrol c で終了しない時-

$ lsof -i:3000   でPIDを調べ

$ kill -9 PID番号(入力) で切る

 

-http://localhost:3000/ で表示するファイルを指定-

(root.rb)

root to:  ディレクトリ名#ファイル名

 

-オブジェクト/インスタンス作成 DBに保存-

❶ post = Post.new(title: "" , number: "")

  post.save

❷ post=Post.new

  post.title=""

       post.number= ""

      post.save

❸ post=Post.create(title: "", number: "")

*❸はcreateでsaveまで完了

 

 

 

テスト わからなかったところまとめ

db/schema.rb

DBのスキーマー情報(〇〇テーブルがあります。〇〇テーブルには▲▲というカラムがある。みたいな)が定義されてる。

rails db:schema:load 定義を元にテーブルを作成する。

 

・config/database.yml

利用するDBの設定を書く。

rails db:createで設定したDBを作成する。

 

・config/seed.rb

アプリを動かすために必要なデータをcreateする記述を書く。

 

rubyの記法で書く。自動的に削除する機能はない。

rails db:seedコマンドでseedに書かれた内容をデータベースに反映させることができる。

 

・config/application.rb

アプリ全般の設定を行う。タイムゾーンや国際化の設定など。

 

・原則としてアクション名と同じ名前のテンプレートファイルが利用される。 (ex showアクション=show.html.erb)

明示的にテンプレートファイルを指定したいときはrender :newのように自分で書く。

 

・form_forやform_withはセキュリティーを考慮し、authenticity_tokenという名前の隠しフィールドを生成する。(CSRFトークン)

→ランダムな文字列を生成してくれて、nameやemailとかと一緒にPOSTしている。

 

・テキストを生成するテキスト

f.text_field:カラム名

<%= form_for User.new do |f|>

<%=f.text_field:カラム名 %>

<%end%>

|f|はform_for(form_with)のブロックの引数。

do |form|のように好きな引数を設定可能だがこの場合form.text_field :カラム名 のように一致させなければならない。

 

・saveメソッド

保存に失敗したときにfalseを返す。

ユーザーの入力ミスがあり得るときに使用

 

・save!メソッド

保存に失敗したときに例外が起きる

何かしら必ず成功すると考えられる時に使用

 

・resourcesメソッド 7つのメソッド

config/routes.rbにresources :(リソースの対象)の形で記入。

あるリソースに対する標準的な操作(CRUD)がまとめてルート定義される。

CRUD =DBのリソースに対する操作のcreate,read,update,deleteをまとめて略したもの。Webアプリケーションで基本的に実装されている4つの機能。

 

・resourceメソッド 6つのメソッド

= 単一リソース(単数形)であるため、idが必要ない。

index(一覧)アクションに対するルートが定義されない。

show、edit、deleteのパスにはパラメーターを要求しない。

アプリケーション全体の設定をリソースであらわしたいとき、アプリ全体で1つなのでresource config が使える。

 

・情報を永久化する=SQLを発行しDBに保存すること。

 

 

 

チュートリアル14章

2021.1.28

14章 ユーザーをフォローする

・他のユーザーをフォロー(フォロー解除)する仕組み

・フォローしているユーザーの投稿をステータスフィールドに表示

 

14.1.1 データモデルの問題(と解決策)

・英語的にも理解しやすいように...

followersではなくfollowingという呼称を使う。

ex)calvinさんがフォローしている全てのユーザーの集合は「calvin.following」となる。

 

・followingテーブルとuserテーブルでhas_many関連付けを行ってフォローしているユーザーのモデリングができる。(14.6図参照)

 

ユーザーの名前、パスワードなど無駄なもが多すぎるのでなし。

 

・calvinがHobbesをフォローしていて、Hobbesはcalvinをフォローしていない時

calvinはHobbesに対して「能動的関係(Active Relationship)」

Hobbesはcalvinに対して「受動的関係(Passive Relationship)」

--能動的関係も受動的関係も最終的にはDBの同じテーブルを使う。テーブル名はrelationships--

<能動的関係>(14.1.5で詳しく)

フォローしているユーザーはfollowed_idがあれば識別可能。

(14.6図)のfollowingテーブルをactive_relationshipdテーブルと見立てみる。ユーザーid以外の情報は削除。usersテーブルのフォローされているユーザーを見つける。

 

 

・データモデルを実装

$ rails g model Relationship follewr_id:integer followed_id:integer

 

・このリレーションシップは今後follower_idと followed_idで頻繁に検索するので、カラムにインデックスを追加しておく。(13章でやった。特定のカラムからデータを取得する際に、テーブルの中の特定のカラムのデータを複製し検索しやすいようにする。)

(db:migrate/[timestamp]_create_relationships.rb)

class CreateRelationships < ActiveRecord::Migration[6.0]
 
.
.
add_index :relationships, :follower_id
add_index :relationships, :followed_id
add_index :relationships, [:follower_id, :followed_id], unique: true ❶
end
end

❶ 複合キーインデックス、unique: trueによって : follower_id  followed_id の組み合わせが必ずユニーク(一意性)であることを保証している。

これによりあるユーザーが同じユーザーを2回以上フォローすることを防ぐ。

 

14.1.2 User/Relationshipの関連付け

13.1.3で作成したものと似ているが、2つ大きな違いがある。

①(app/models/user.rb) 13.3より

class User < ApplicationRecord
has_many :microposts
end

引数の:micopostsシンボルからrailsはこれに対するMicropostモデルを探した。

 

同じ形で書くと、今回は

has_many :active_relationships

となり、Active_relationshipsモデルを探してしまい、Relationshipモデルを見つけられない。→railsに探して欲しいモデルのクラス名を明示的に伝える必要がある。

 

② ①の逆のケース

(app/models/micropost.rb) 13.3より

class Micropost < ApplicationRecord
belongs_to :user
end

micropostsテーブルにはuser_id属性があるので、これをたどって対応する所有者(ユーザー)を特定することができた。DBの2つのテーブルを繋ぐ時、このようなidは外部キーと呼ばれる。すなわち、userモデルにつなげる外部キーがmicropostモデルのuser_id属性である。railsのデフォルトでは外部キーの名前を「<class=小文字に変換される>_id」といったパターンとして理解する。

→今回はフォローしているユーザーをfollower_idという外部キーを使用して特定しなければならない。followerというクラス名は存在しないので、railsに正しいクラス名を伝える必要がある。

 

・能動的関係に対して1対多(has_many)の関連づけを実装する。

(app/models/user.rb)

class User < ApplicationRecord
has_many :microposts, dependent: :destroy
has_many :active_relationships, class_name: "Relationship",
foreign_key: "follower_id",
dependent: :destroy ❶
.
.
end

❶ dependent: :destroy ユーザーを削除したら、ユーザーのリレーションシップも同時に削除される必要があるので、関連付けさせてる。

 

・リレーションシップ/フォロワーに対して、belongs_toの関連づけを追加する

(app/models/relationship.rb)

class Relationship < ApplicationRecord
belongs_to :follower, class_name: "User"
belongs_to :followed, class_name: "User"
end

 

この2つの関連付けにより表13.1で前回見たように多くのメソッドが使用可能になる。

active_relationship.follower フォロワーを返します
active_relationship.followed フォローしているユーザーを返します
user.active_relationships.create(followed_id: other_user.id) userと紐付けて能動的関係を作成/登録する
user.active_relationships.create!(followed_id: other_user.id) userを紐付けて能動的関係を作成/登録する(失敗時にエラーを出力)
user.active_relationships.build(followed_id: other_user.id) userと紐付けた新しいRelationshipオブジェクトを返す

 

14.1.3 Relationshipのバリテーション

モデルの検証を追加する。

・Relationshipモデルのバリテーションをテストする。

(test/models/relationship_test.rb)

require 'test_helper'

class RelationshipTest < ActiveSupport::TestCase
def setup
@relationship = Relationship.new(follower_id: users(:michael).id,followed_id: users(:archer).id)
end

test "should be valid" do
assert @relationship.valid?
end

test "should require a follower_id" do
@relationship.follower_id = nil
assert_not @relationship.valid?
end

test "should require a followed_id" do
@relationship.followed_id = nil
assert_not @relationship.valid?
end
end

 

・Relationshipモデルに対してバリテーションを追加する。

(app/models/relationship.rb)

class Relationship < ApplicationRecord
validates :follower_id, presence: true
validates :followed_id, presence: true
end

 

・生成されたRelationship用のfixtureではマイグレーション(db:migrate/[timestamp]_create_relationships.rb)で制約された一位性を満たすことができない。ユーザーの時と同じで、(リスト6.31)今の時点ではRelationship用のfixtureファイルを空にしておく。

(test/fixtures/relationships.yml)

#空にする

 

14.1.4 フォローしているユーザー

<has_many through >という関連付けでは、Railsはモデル名(単数形)に対する外部キーを探す。

つまり次のコードでは

has_many :followeds, through: :active_relationships

:followeds」というシンボル名を見て、「followed」という単数形に変え、relationshipsテーブルの「followed_id」を使って対象のユーザーを取得する。

しかし、14.1.1で見たように「user.followeds」は英語として不適切。代わりに「user.following」を使用する。

Railsのデフォルトを上書きする。「:source」パラメーター(=ソースの関連付け元の名前を指定する)を使用し、「following配列の元はfollowedのidの集合である」と明示的にRailsに伝える。

・Userモデルにfollowingの関連付けを追加する。

(app/models/user.rb)

class User < ApplicationRecord
has_many :following, through: :active_relationships, source: :followed

→フォローしているユーザーを配列のように扱えるようになった。

 

・followingで取得した集合をより簡単に扱うためにfollowやunfollowという便利メソッドとこれに関するfollowing?倫理値メソッドを追加し、あるユーザーが誰かをフォローしているかどうかを確認。

 

・following関連のメソッドをテストする

❶ following?メソッドであるユーザーをまだフォローしていない

❷ followメソッドでそのユーザーをフォロー

❸ following?メソッドを使ってフォロー中になったことを確認

❹ unfollowメソッドでフォロー解除できたことを確認

(test/models/user_test.rb)

test "should follow and unfollow a user" do
michael = users(:michael)
archer = users(:archer)
assert_not michael.following?(archer)
michael.follow(archer)
assert michael.following?(archer)
michael.unfollow(archer)
assert_not michael.following?(archer)
end

 

・following関連のメソッドを追加する(表14.1参考に)

followingは関連つけたので使える

(app/models/user.rb)

# ユーザーをフォローする
def follow(other_user)
following << other_user
end

# ユーザーをフォロー解除する
def unfollow(other_user)
active_relationships.find_by(followed_id: other_user.id).destroy
end

# 現在のユーザーがフォローしてたらtrueを返す
def following?(other_user)
following.include?(other_user)
end

 

14.1.5 フォロワー

・user.followersメソッドを追加する。user.followingと対になる。

activerelationshipsテーブルを再利用する。(必要な情報が揃っているので。)

 

14.2 [Follow]のWEBインターフェイス

フォロー・フォローの解除の基本的なインテーフェイスを実行する。

 

14.2.1 フォローのサンプルデータ

先にサンプルデータを自動作成する「$ rails db:seed」でDBにサンプルデータを登録する。→Webページの見た目のデザインを先に取り掛かることができる。

・サンプルデータにfollowing/followerの関係性を追加する。

 

14.2.2 統計と[Follow]フォーム

プロフィールページとHomeページを更新する。

・フォローしているユーザーとフォロワーの統計情報を表示するためのパーシャルを作成

・フォロー用とフォロー解除用のフォームを作成する

・フォローしているユーザーの一覧とフォロワーの一覧を表示

 

--ルーティングオプションを使用しfollowingアクションとfollowersアクションを追加する--

<memberオプション>

resources :users do
member do
get :A, :B
end
end
GETリクエストとそれに伴う/users/1/Aと/users/1/Bを認識。
リクエストをusersコントローラーのAアクションにルーティングし、リソースidをparams[:id]に渡す。
リクエストをusersコントローラーのBアクションにルーティングし、リソースidをparams[:id]に渡す。
users_A_urlとusers_A_pathも作成。Bのぶんも。
 
resources :users do
collection do
get "A
end
end

GETリクエスト+/users/Aなどの(:idを伴わない)パスを認識する。

リクエストをusersコントローラーのAアクションにルーティングする。

users_A_urlとusers_A_pathを作成。

 

14.2.4 [follow]ボタン(基本編)

[Follow]/[Unfollow]ボタン作成

フォローとフォロー解除はそれぞれリレーションシップの作成削除に対応している。

・Relationshipsコントローラーを作成する。

・テストを行う

・before_action、アクションを作成する。

 

14.2.5 [Follow]ボタン(Ajax編)

Ajaxを使ってWebページからサーバーに非同期でページを移送することなくリクエストを送ることができる。(14.2.4ではredirectで元のページにリダイレクトしていた。)

 

・フォロー、フォロー解除で使うフォームの

form_with(model: ..., local:true)をfoem_with(model: ..., remote:true)に変更。

これによってdate-remote="true"にしている=JapaScriptによるフォーム操作を許可している。

 

・RelationshipsコントローラをAjaxリクエストに応答できるようにしておく。

<respond_toメソッド>を使用→リクエストの種類によって応答を場合わけする。

 

 

・JS.ERBファイルを作成し、DOMを使ってページ操作をできるようにする。

 

14.2.6 フォローをテストする

xhr: true

Ajaxでリクエストを発行することができる。

 

14.3 ステータスフィールド

 

14.3.2 フィードを初めて実装する

<folloeing_ids> メソッド

has_many :following の関連づけをしたときにActiveRecordが自動生成したもの。

これによりuser.followingコレクションに対応するidを得るためには関連付けの名前の末尾に_idsを付け足すだけで済む。

 

14.3.3 サブセレクト

SQLのサブセレクト(subselect)を使う。人数が多くなると14.3.2でのコードでは全てのユーザーにアクセスするので、時間がかかる。

 

 

 

14.3.4

演習3はやってない。

 

チュートリアル13章

2021-1-24

チュートリアル13章 Micropostモデル

ユーザーが短いメッセージを投稿するためのリソース「マイクロポスト」を追加する。(ツイート的な)

 

13.1 Micropostモデル作成(2,4,6章参照)

UserとMicropostの関連づけ

  --User has_many Microposts

  --Micropost bgelongs_to User

トピックブランチ作成

$ git checkout -b user-microposts

 

13.1.1基本的なモデル

Micropostモデルは2つの属性だけを持つ。

・マイクロポストの内容を保存するcontent属性(text)

・特定のユーザーとマイクロポストを関連づけるuser_id属性 (intger)

id,create_at,update_atはデフォルトで作成される。

 

*マイクロポストの内容を保存するcontent属性(text)について

String型ではなくtext型を使用している。

→Textはある程度(1〜4294967296文字)の量のテキストを格納し、String型は225文字を格納する。Text型の方が表現豊かなマイクロポストを実現することができる。自然な投稿フォームを実現、言語に応じて投稿の長さを調整することができる。パフォーマンスはStringと変わりない。

 

・Micropostモデルを生成する

$ rails generate model Micropost content:text user:references(13.1.3で詳しく説明)

❶user:referencesによって ユーザーと1対1の関係であることを表すbelongs_toのコードがMicropostモデルのファイルに追加される。

・自動生成されたMicropostモデル

(app/models/micropost.rb)

class Micropost < ApplicationRecord
belongs_to :user
end

 

❷user:referencesによって 自動生成されたMicropostモデルのマイグレーションファイルでは自動的にインデックス (index)と外部キー参照付きのuser_idカラムが追加され、UserとMicropostを関連付けする準備をしてくれる。

・自動生成されたMicropostモデルのマイグレーションファイル

(db/migrate/[timestamp]_create_microposts.rb)

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

t.timestamps ❶
end
add_index :microposts, [:user_id, :created_at] ❷
end
end

 

❶t.timestamp(マジックカラムが自動的に生成される)→create_at,updated_atが追加される。

❷複合キーインデックス(ActiveRecordによって作成される)

よくあるクエリ(DBからデータを抽出したり操作したりといった処理を行うための命令)を高速化するために使う。今回はuser_idとcreated_atがよく一緒に取り出されるのでこの形。

user_idに関連付けられた全てのマイクロポスト(投稿)を作成時刻の逆順で取り出しやすくなる。

 

13.1.2Micropostのバリテーション

流れ testを書く → validatesをかく

-Micropostモデルを動くようにするテスト-

①fixtureのサンプルユーザーと紐付けした新しいマイクロポストを作成する

②作成したマイクロポストが有効か確認

③マイクロポストはユーザーのidを持っているべきなので、user_idの存在性のバリテーションを確認

(test/models/micropost_test.rb)

require 'test_helper'

class MicropostTest < ActiveSupport::TestCase

def setup ①
@user = users(:michael)
# ↓このコードは慣習的に正しくない
@micropost = Micropost.new(content: "Lorem ipsum", user_id: @user.id)
end

test "should be valid" do ②
assert @micropost.valid?
end

test "user id should be present" do ③
@micropost.user_id = nil
assert_not @micropost.valid?
end
end

 

・↑の③のtestがパスするようにバリテーションを書く

(app/model/micropost.rb)

class Micropost < ApplicationRecord
belongs_to :user
validates :user_id, presence: true
end

 

-マイクロポストのcontent属性に対するテスト-

① content属性が存在すること

② マイクロポストが140文字より長くならないこと。

 (test/models/micropost_test.rb)

test "content should be present" do ①
@micropost.content = " "
assert_not @micropost.valid?
end

test "content should be at most 140 characters" do ②
@micropost.content = "a" * 141
assert_not @micropost.valid?
end

 

・↑がパスするようにバリテーションをかける

(app/model/micropost.rb)

class Micropost < ApplicationRecord
belongs_to :user
validates :user_id, presence: true
validates :content, presence: true, length: { maximum: 140 }
end

 

13.1.3 User/Micropostの関連付け

それぞれのマイクロポストは1人のユーザーと関連づけられ、それぞれのユーザーは (潜在的に)複数のマイクロポストと関連づけられる。

・マイクロポストがユーザーに所属する関連づけ

                             belongs_to(1対1)

micropostのuser_id    micropost.userのid

(app/models/micropost.rb)

class Micropost < ApplicationRecord
belongs_to :user
end

・ユーザーがマイクロポストを複数所有する関連づけ

                             has_many(1対多)

userのid                          micropostsのuser_id

                                         micropostsのuser_id

                                         micropostsのuser_id

(app/models/uer.rb)

class User < ApplicationRecord
has_many :microposts
end

 

*belomgs_to/mas_many関連づけ(Association)が生成するメソッド(↓表一覧)

これらのメソッドを使うと、紐づいているユーザーを通してマイクロポストを作成することが可能。user_idは自動的に正しい値に設定される。

                               モデル単体   →  関連付け

Micropost.find_by(user_id: user_id)  → user.microposts(=これ自体がメソッドとして扱われる。)

Micropost.create            → user.microposts.create

Micropost.create!                                 → user.microposts.create!

Micropost.new(user_id: user_id)         → user.microposts.build  (=user_idを関連つけた上で、空のインスタンスを作る。newでもできるけど、関連付けしていることをすぐわかるようにbuild)

Micropost.find_by(user_id: uer_id, ...) →user.microposts.find_by() (=user_idのところは省略)

メソッド 用途
micropost.user Micropostに紐付いたUserオブジェクトを返す
user.microposts Userのマイクロポストの集合をかえす
user.microposts.create(arg) userに紐付いたマイクロポストを作成する
user.microposts.create!(arg) userに紐付いたマイクロポストを作成する(失敗時に例外を発生)
user.microposts.build(arg) userに紐付いた新しいMicropostオブジェクトを返す
user.microposts.find_by(id: 1) userに紐付いていて、id1であるマイクロポストを検索する

・13.4のsetupメソッドを修正する。

(test/models/micropost_test.rb)

def setup
@user = users(:michael)
# ↓正しく変更
@micropost = @user.microposts.build(content: "Lorem ipsum", user_id: @user.id)
endd

 

 

13.1.4 マイクロポストを改良する

・ユーザーのマイクロポストを降順で取得する

・マイクロポストをユーザーに依存させ、ユーザーが削除されたらマイクロポストも自動的に削除されるようにする

 

-デフォルトのスコープ-

default_scope -> { order(created_at: :desc) }(↓説明あり)

Micropostの表示順序を降順にする。Micropost.firstで最新の投稿が手に入る。

*見せかけの成功に陥りやすいので、正しいテストを書く為、テスト駆動開発(TDDテストを先に書く)で進める。

 

・DB上の最初のマイクロポストがfixture内のマイクロポスト(most_recent)と同じであるか検証する。

(test/models/micropost_test.rb)

test "order should be most recent first" do
assert_equal microposts(:most_recent), Micropost.first
end

 

・↑のtestはマイクロポストのfixtureがあるという前提に依存している。(11.5参照)

今回はcontent属性とそれに関連づけられたuser属性を定義する。

❶userをmichaelと認識されると、ユーザーfixtureにある対応ユーザーに関連づけることをrailsに指示できる。

❷埋め込みrubyを使用する。

(test/fixtures/microposts.yml)

orange:
content: "I just ate an orange!"
created_at: <%= 10.minutes.ago %> ❷
user: michael ❶

tau_manifesto:
content: "Check out the @tauday site by @mhartl: https://tauday.com"
created_at: <%= 3.years.ago %>
user: michael

cat_video:
content: "Sad cats are sad: https://youtu.be/PKffm2uI4dk"
created_at: <%= 2.hours.ago %>
user: michael

most_recent:
content: "Writing a short test"
created_at: <%= Time.zone.now %>
user: michael

 

-default_scopeメソッド-

default_scope -> { order(created_at: :desc) }

DBから要素を取得したときの、デフォルトの順序を指定するメソッド

特定の順序にしたい場合は、default_scopeの引数にorderを与える。

ex)created_at絡むの順にしたいとき

order(created_at) 

これではデフォルトが昇順になっているので、古い順になってしまう。だから

order(created_at: :desc

と表記する。

 

・default_scopeでマイクロポストを順序付ける

(app/models/micropost.rb)

class Micropost < ApplicationRecord
default_scope -> { order(created_at: :desc) }
validates :user_id, presence: true
end

 

-> {...} RubyのProcオブジェクト

ブロックを引数に取り、callメソッドが呼ばれたときに、初めてブロック内の処理を評価する。

ex)こんな感じ

f:id:mmm_st:20210125110136p:plain

 

-Dependent: destroy-

ユーザーが破棄されたとき、ユーザーのマイクロポストも破棄する。

・has_manyメソッドにオプションを渡すと、実装可能。

dependent: :destroyオプション : ユーザーが削除された時に、ユーザーに紐づいたマイクロポストも一緒に削除される。

(app/models/user.rb)*Userモデルに関するものは、user_test.rbでテスト

class User < ApplicationRecord
has_many :microposts, dependent: :destroy
end

 

・testで検証(リスト10.62参照)

❶ ユーザーを作成

❷ユーザーに紐づいたマイクロポストを作成

❸削除すると、、、❹

❹マイクロポストの数が減っているか確認(ブロックdo-end)

・dependent: :destroyのテスト

(test/models/user_test.rb)

test "associated microposts should be destroyed" do
@user.save ❶
@user.microposts.create!(content: "Lorem ipsum") ❷
assert_difference 'Micropost.count', -1 do ❹
@user.destroy ❸
end
end

 

 

13.2 Micropostを表示する(7,8,9章参照)

今回はマイクロポストの独立したindexページは作らずに、ユーザーのshowページで直接マイクロポストを表示させる。

 

13.2.1 マイクロポスト

ユーザーのプロフィール画面で(show.html.erb)でそのユーザーのマイクロポストなどを表示する。

 

・DBをリセットし、サンプルデータを再生成。

$ rails db:migrate:reset

$ rails db:seed

 

・Micropostのコントローラとビューを作成する(ここではviewのみ使用)

$ rails g controller Microposts

 

・マイクロポストをユーザーのshowページ(プロフィール画面)に追加する。<完成図>

(app/views/users/show.html.erb)

.
.
<div class="col-md-8">
<% if @user.microposts.any? %> ❶
<h3>Microposts (<%= @user.microposts.count %>)</h3> ❺
<ol class="microposts"> ❸
<%= render @microposts %> ❷
</ol>
<%= will_paginate @microposts %> ❹
<% end %>
</div>

❶ ユーザーのマイクロポストが1つもない場合には、からのリストを表示させないようになっている。

 ❷_micropost.html.erbパーシャルを使ってマイクロポストのコレクションを参照する。(10.3.5参照)

 

・1つのマイクロポストを表示する_micropost.html.erbパーシャル

(app/views/microposts/_micropost.html.erb)

<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 %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago. ①
</span>
</li>

①time_ago_in_wordヘルパーメソッド「3分前に投稿」といった文字列を出力する。

 

❸マイクロポストが特定の順序に依存している為、順序付きリストのolタグを使用。

 

❹ページネーションのwill_paginateメソッドを使って、引数を指定する。(10.45参照)

*Userコントローラーのコンテキストからマイクロポストをページネーションしたい為(10.45と違い今回はコンテキストが違うので)、明示的に@microposts変数をwill_pagenateに渡す必要がある。

今回は新しくviewは作らずに、usersリソースの中のshowテンプレートにお邪魔しているので、 引数に何も渡さないと、デフォルトを表示する。(=今回はusersリソースだからuserのデータをとってくる。)

 

 

・@micropostsインスタンス変数をshowアクションに追加する

 →マイクロポストの関連付けを経由して、micropostsテーブルに到達し、必要なマイクロポストのページを引き出してくれる。

(app/controllers/users_controller.rb)

def show
@user = User.find(params[:id])
@microposts = @user.microposts.paginate(page: params[:page])
end

 

❺countメソッドを使いマイクロポストの投稿数を表示する。

pagenateメソッドと同様に、関連付けを通してcountメソッドを読み込みことができる。

 

13.2.2 マイクロポストのサンプル

サンプルデータ生成タスクにマイクロポストも追加する。

takeメソッドを使用して最初の6人に追加

orderメソッドを経由することで、生成されたユーザーの最初の6人を呼び出す。

Lorem.sentencdeメソッド:ダミーのテキストを返す

(app/seed.rb)

#ユーザーの一部を対象にマイクロポストを生成する
users = User.order(:created_at).take(6)
50.times do
content = Faker::Lorem.sentence(word_count: 5)
users.each { |user| user.microposts.create!(content: content) }
end

 

マイクロポスト用のcssを追加する。

 

13.2.3 プロフィール画像のマイクロポストをテストする

・プロフィール画面用の統合テスト作成

$ rails g integration_test users_profile

 

・fixtureにマイクロポスト用のテストデータを追加

埋め込みRubyを使うと簡単

 (test/fixtures/microposts.yml)

 
.
.
.
<% 30.times do |n| %>
micropost_<%= n %>:
content: <%= Faker::Lorem.sentence(word_count: 5) %>
created_at: <%= 42.days.ago %>
user: michael
<% end %>

 

・Userプロフィール画面に対するテスト

❶そのページのどこかしらにマイクロポストの投稿が存在するか確認(bodyタグのことではない。)

 assert_selectではどのTHMLtタグなのか伝える必要があるが、assert_matchでは必要ない。そのページの完全なHTMLが含まれているから。

❷Applicatopnヘルパーを読み込んだことで、full_titleヘルパーメソッドが使える。

❸ネストした文法を使用している。h1タグのgravatarクラス付きのimgタグがあるか確認

(test/integration/users_profile_test.rb)

require 'test_helper'

class UsersProfileTest < ActionDispatch::IntegrationTest
include ApplicationHelper ❷

def setup
@user = users(:michael)
end

test "profile display" do
get user_path(@user)
assert_template 'users/show'
assert_select 'title', full_title(@user.name)
assert_select 'h1', text: @user.name
assert_select 'h1>img.gravatar' ❸
assert_match @user.microposts.count.to_s, response.body ❶
assert_select 'div.pagination'
@user.microposts.paginate(page: 1).each do |micropost|
assert_match micropost.content, response.body ❶
end
end
end

 

13.3マイクロポストを操作する(10章参照)

Web経由でそれらを生成するためのインターフェイスを作る。

主にプロフィールページとHomeページのコントローラーを経由し実行されているので、createとdestroyがあれば十分である。

(config/routs.rb)

Rails.application.routes.draw do
.
.
resources :microposts, only: [:create, :destroy]
end

 

13.3.1マイクロポストのアクセス制御

・関連付けられたユーザーを通してマイクロポストにアクセスするので、Micropostsコントローラー内のcreateアクションやdestroyアクションを利用するユーザーはログイン済みでなければならない。

 

・Micropostsコントローラの認可テスト

(test/controoller/microposts_controller_test.rb)

require 'test_helper'

class MicropostsControllerTest < ActionDispatch::IntegrationTest
 
def setup
@micropost = microposts(:orange)
end

test "should redirect create when not logged in" do
assert_no_difference 'Micropost.count' do
post microposts_path, params: { micropost: { content: "Lorem ipsum" } }
end
assert_redirected_to login_url
end

test "should redirect destroy when not logged in" do
assert_no_difference 'Micropost.count' do
delete micropost_path(@micropost)
end
assert_redirected_to login_url
end
end

 

↑テストをパスさせるために、10.2.1で行ったbeforeフィルターのlogged_in_userメソッドを使ってログイン要求しなければならない。

10.2.1では、Userコントローラー内にlogged_in_userメソッドがあったのでbeforeフィルターで指定したが、このMicropostsコントローラーでも必要。

→各コントローラーが継承するApplicationコントローラーにこのメソッドを移動させる。そうすれば、他のコントローラーでも呼び出せるようになる。

(app/controllers/applicaton/application_controller.rb)

class ApplicationController < ActionController::Base
include SessionsHelper
private
# ユーザーのログインを確認する
def logged_in_user
unless logged_in?
store_location
flash[:danger] = "Please log in."
redirect_to login_url
end
end
end

・コードが重複しないように、Userコントローラ(app/controllers/users_controller.rb)からloged_in_userを削除する。

・Applicationコントローラーに移動させたので、Micropostsコントローラーからも呼び出せるようになったので、createアクションやdestroyアクションへのアクセス権限がbeforeフィルターで簡単に実装できるようになった。

(app/controllers/microposts_controller.rb)

class MicropostsController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy]
def create
end
def destroy
end
end

 

13.3.2 マイクロポストを作成する

micropost/newパージを使う代わりにホーム画面(つまりルートパス)にフォームをおく。ユーザーのログイン状態に応じてホーム画面の表示を変更する。(13.37で実装)

 

 ・createアクションを作成する(7.26と似ている)

❶ beforeアクションでログインしているユーザーのみになっているので、current_userを使用。新しいマイクロポストをbuildするためにUser/Micropost関連づけを使う。

❷ micropost_paramsでStrongPatametersを使い、content属性のみがWeb経由で変更可能にする。

(app/controllers/microposts_controller.rb)

class MicropostsController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy]

def create
@micropost = current_user.microposts.build(micropost_params) ❶
if @micropost.save
flash[:success] = "Micropost created!"
redirect_to root_url
else
render 'static_pages/home' #ホーム画面
end
end
def destroy
end
private
def micropost_params ❷
params.require(:micropost).permit(:content)
end
end

 

・マイクロポスト作成フォームを構築するために、サイト訪問者がログインしているかどうかに応じて異なるHTMLを提供するコードを使う。(演習によってパーシャル済)

(app/views/static_pages/home.html.erb)

<% if logged_in? %>
<div class="row">
<aside class="col-md-4">
<section class="user_info">
<%= render 'shared/user_info' %> #--A
</section>
<section class="micropost_form">
<%= render 'shared/micropost_form' %> #--B
</section>
</aside>
</div>
<% else %>
<div class="center jumbotron">
<h1>Welcome to the Sample App</h1>

<h2>
This is the home page for the
<a href = "https://railstutorial.jp/">Ruby on Rails Tutorial</a>
sample application.
</h2>

<%= link_to "Sing up now!", signup_path, class: "btn btn-lg btn-primary" %>
</div>

<%= link_to image_tag("rails.svg", alt: "Rails logo", width: "200px"),"https://rubyonrails.org/" %>
<% end %>

↑を動かすためにパーシャルを作成する

・Homeページの新しいサイドバー (--A)

(app/views/shared/_user_info.html.erb)

<%= link_to gravatar_for(current_user, size: 50), current_user %>
<h1><%= current_user.name %></h1>
<span><%= link_to "view my profile", current_user %></span>
<span><%= pluralize(current_user.microposts.count, "micropost") %></span> ❶

pluralizeメソッド:数値を受け取ってそれに応じて英語の「単数形/複数形」活用を行ってくれるヘルパーメソッドです。数値が1より大きい場合は、引数の文字列を自動的に複数形に変更する。

ヘルパーメソッド:viewをよりシンプルにDRYにかくもの

 

・マイクロポスト投稿フォームのパーシャル(--B)

(app/views/shared/_micropost_form.hrml.erb)

<%= form_with(model: @micropost, local: true) do |f| %> ❶
<%= render 'shared/error_messages', object: f.object %> ❷
<div class="field">
<%= f.text_area :content, placeholder: "Compose new micropost..." %>
</div>
<%= f.submit "Post", class: "btn btn-primary" %>
<% end %>

 ↑が動く為に行うこと2つ

❶関連付けを使って@micropostを定義する。form_withを使っている。モデル(@micropost)の中身が空なのでcreateメソッドを呼び出してくれる。

beforeアクションでログインしている時しか使えないようになっているので、current_userを使用。=@micropost変数もログイン時のみ定義される。

(app/controllers/satic_pages_controller.rb)

class StaticPagesController < ApplicationController
def home
@micropost = current_user.microposts.build if logged_in?
end
end

 

❷エラーメッセージのパーシャルを再定義する

↓変更しないとこれが動かないので。

<%= render 'shared/error_messages', object: f.object %>

 

リスト7.20ではこのようにera-messe-jipa-syaruga@user変数を直接参照していた

(app/views/shared/_error_messages.html.erb)

<% if @user.errors.any? %>
  <div id="error_explanation">
    <div class="alert alert-danger">
      The form contains <%= pluralize(@user.errors.count, "error") %>.
    </div>
    <ul>
    <% @user.errors.full_messages.each do |msg| %>
      <li><%= msg %></li>
    <% end %>
    </ul>
  </div>
<% end %>

 今回は代わりに@micropost変数を使う必要がある。

毎回直接参照しているとキリがないので、これらのケースをまとめる。

<%= form_with(model: @user, local: true) do |f| %>
<%= form_with(model: @micropost, local: true) do |f| %>

フォーム変数fをf.objectにして、関連付けられたオブジェクトにアクセスできるようにする。

<わかりやすく>(一番わかりやすいのは12章中編の動画24分ぐらいのとこ)

ex) (app/views/shared/_micropost_form.hrml.erb)を使って解説。

<%= form_with(model: @micropost, local: true) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "Compose new micropost..." %>
</div>
<%= f.submit "Post", class: "btn btn-primary" %>
<% end %>

フォームのオブジェクト「f」は便利。(文字はfじゃなくてもfooでもなんでもいいらしい。)

<%= form_with(model: @micropost, local: true) do |f| %>

このオブジェクト(f)の中で、sharedメッセージを呼び出すときに、「f.object」と書くと、そのフォームを作るときに、参照したデータ

model: @micropost

を「f.object」で引っ張ってくることができる。

今回はこの機能を使っていく。

renderの後で渡すパーシャルはどんな変数でも、パーシャルの中に渡すことができる。例えば、

<%= render 'shared/error_messages', hogehoge: @foo %>

パーシャルの先でhogehogeというローカル変数にアクセスすることができる。で、@fooを渡せ、ローカル変数として扱える。

今回は

<%= form_with(model: @micropost, local: true) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
 

なので、@micropostを参照している際は、objectというローカル変数にアクセスすると、@micropostの内容が渡される。

 

・objectに変えておく。そのデータのエラーがあれば、そのエラーを表示する。

(app/views/shared/_error_messages.html.erb)

<% if object.errors.any? %>
<div id="error_explanation">
<div class="alert alert-danger">
The form contains <%= pluralize(object.errors.count, "error") %>.
</div>
<ul>
<% object.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>

 

・error_messagesパーシャルを他で使っているので、そこも直す。

ユーザー登録、パスワード再設定、ユーザー編集のそれぞれのビューを更新。

<%= render 'shared/error_messages', object: f.object %>

 こんな感じで。

 

13.3.3 フィードの原型(プロトタイプ)

Homeのページにマイクロポストを表示する。

全てのユーザーがフィードを持つので、feedメソッドをUserモデルで作る。

・現在ログインしているユーザーのマイクロポストを全て取得する。

次章で完全なフィードを実装する為、whereメソッドで作成する。

(app/models/user.rb)

# 試作feedの定義
# 完全な実装は次章の「ユーザーをフォローする」を参照
def feed
Micropost.where("user_id = ?", (self.省略可)id)
end

current_user.micropostsが一番簡単だが、今後他のユーザーの投稿もタイムラインに投稿することになる。=SQL(DBの世界に入ることになる)を使う。今まではRailsActiveRecordに頼っていたが...

SQLでは、〇〇.whereで部分的なところを書き足すことができる。

Micropostの情報の全体の中から、次のSQLのセレクト文に一致したデータをDBに問い合わせる。

("user_id = ?", id) =SQLに問い合わせる文。rails的にはエラーは出ない。

↑ ?にidがエスケープされる。(?とidが一致することを示している。)

例えば、("user_id = ? ? ?", id,2,3)は?(1つ目) = id/?(2つ目)=2/?(3つ目)=3みたいな感じではいる。(14章で詳しく)

SQLインジェクションと呼ばれる、深刻なセキュリティーホールを避けることができる。SQL文に変数を代入する際は、常にエスケープするべき。

 

・フィード機能を導入するために、ログインユーザーのフィード用にインスタンス変数「@feed_items」をhomeアクションに追加。(=renderでeach文の代わりに使えるし!)

(app/controllers/satic_pages_controller.rb)

 
def home
if logged_in?
@micropost = current_user.microposts.build
@feed_items = current_user.feed.paginate(page: params[:page])
end
end

 

・Homeページにはフィード用のパーシャルを追加。

(app/views/shared/_feed.html.erb)

<% if @feed_items.any? %> ❷
<ol class="microposts">
<%= render @feed_items %>❶
</ol>
<%= will_paginate @feed_items %> ❸
<% end %>

❶@feed_items = micropostのインスタンスの集合(コレクション)にrenderをかける→railsは自動的にmicropostのコレクションを列挙し、@feed_itemsの各要素がMicropostクラスを持っていた為Micropostのパーシャル(_micropost.html.erb)で出力する。(10章のリスト10.52がわかりやすい)

❷1つ以上投稿があるか?

❸ページネートしている。

 

・Homeページにステータスフィードを追加する。

(app/views/static_pages_home.html.erb)

今回はパーシャルにいれた(前の章の演習でやったので))

(app/views/sratic_pages/_home_logged_in.html.erb)

<div class="row">
<aside class="col-md-4">
<section class="user_info">
<%= render 'shared/user_info' %>
</section>
<section class="micropost_form">
<%= render 'shared/micropost_form' %>
</section>
</aside>
<div class="col-md-8">
<h3>Micropost Feed</h3>
<%= render 'shared/feed' %>
</div>
</div>

 

・エラーについて 2つ

1.マイクロポストの投稿が失敗した時Homeページは@feed_itemsインスタンス変数を期待しているので現状ではエラーが出てしまう。

なぜ?=Homeページにはフィード用のパーシャル(app/views/shared/_feed.html.erb)を追加しているので、その中で<% if @feed_items.any? %>を確認しているので、@feed_itemsインスタンス変数がなかった場合を求めている。

→Micropostsコントローラーのcreateアクションへの送信が失敗した場合に備えて、必要な変数をブランチに渡しておく。

 

・createアクションに空の@feed_itemsインスタンス変数を追加する。

(app/controller/microposts_controller.rb)

def create
@micropost = current_user.microposts.build(micropost_params)
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

 

2.デバックパラムズ --controllerとactionを指定 (5章参照)

ページを次に行こうとすると、rootが見当たらないとなる。存在しない”/microposts”を探してしまう。

→controllerパラメータとactionパラメータを明示的にwill_pagenateに渡す。

・コントローラとアクションを明示的に設定する。

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

 

13.3.4 マイクロポストを削除する

・マイクロポストのパーシャルに削除リンクを追加する(投稿の持ち主が自分ならリンクを表示)

(app/views/microposts/_micropost.html.erb)

<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 %></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?" } %> ❶#micropostコントローラのdeleteアクション
<% end %>
</span>
</li>

 ❶もしcurrent_userならば、deleteアクションを/micropost/idに渡す。

  GET以外は「method: :〇〇」を指定。今回はDELETEだった。

「data: { confirm: "You sure?" }」:クリックした際に出る確認メッセージ。

 

・Micropostsコントローラのdestroyアクションを定義する

admin_userフィルターで関連付けを使ってマイクロポストを削除する。これにより他のユーザーの投稿を削除しようとすると、自動的に失敗する。

→current_userフィルター内でfindメソッドを呼び出し、現在のユーザーが削除対象のマイクロポストを所有しているかどうかを確認する。

・Micropostsコントローラーのdestroyアクション

(app/controllers/microposts_controller.rb)

 
class MicropostsController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy]
before_action :correct_user, only: :destroy
.
 
def destroy #DELETE /microposts/:id ❷
@micropost.destroy
flash[:success] = "Micropost deleted"
redirect_to request.referrer || root_url ❶
end

private
.
def correct_user
@micropost = current_user.microposts.find_by(id: params[:id]) ❸
redirect_to root_url if @micropost.nil?
end
end

❶ request.referrerメソッド:1つ前のURLを返す。(今回はHomeページ)

こうすることで、プロフィールページから削除されても、Homeページから削除されても、このメソッドによって、DELETEリクエストが発行されたページに戻すことができる。元に戻すURLが見つからなくても、デフォルトでroot_urlを設定しているので大丈夫。

❸ 自分の投稿の中に自分が消したい投稿の「id」が入っているか。

 params[:id] = ❷のDELETE  /microposts/:idのid (= micropostのid)

ターミナルでrails routesで出てくる

f:id:mmm_st:20210127234358p:plain

 

13.3.5 フィード画面のマイクロポストをテストする

・マイクロポスト用のfixtureに別のユーザーに紐付けられたマイクロポストを追加

(test/fixture/microposts.yml)

ants:
content: "Oh, is that what you want? Because that's how you get ants!"
created_at: <%= 2.years.ago %>
user: archer

zone:
content: "Danger zone!"
created_at: <%= 3.days.ago %>
user: archer

tone:
content: "I'm sorry. Your words made sense, but your sarcastic tone did not."
created_at: <%= 10.minutes.ago %>
user: lana

van:
content: "Dude, this van's, like, rolling probable cause."
created_at: <%= 4.hours.ago %>
user: lana

 

・自分以外のユーザーのマイクロポストは削除しようとすると、適切にリダイレクトするか確認。

(test/controllers/microposts_controller_test.rb)

test "should redirect destroy for wrong micropost" do
log_in_as(users(:michael))
micropost = microposts(:ants)
assert_no_difference 'Micropost.count' do
delete micropost_path(micropost) #引数で持ってきている。手帳にメモあり
end
assert_redirected_to root_url
end

 

・統合テストを書く

・ファイルを作成

$rails g integration_test microposts_interface

 

・マイクロポストのUIに対する統合テスト

❶ ログイン

❷マイクロポストのページ分割

❸無効なマイクロポストを投稿

❹有効なマイクロポストを投稿

❺マイクロポストの削除

❻他のユーザーのマイクロポストには[delete]リンクが表示されない

(test/integration/microposts_interface_test.rb)

require 'test_helper'

class MicropostsInterfaceTest < ActionDispatch::IntegrationTest
def setup
@user = users(:michael)
end

test "micropost interface" do
log_in_as(@user)
get root_path
assert_select 'div.pagination'
# 無効な送信
assert_no_difference 'Micropost.count' do
post microposts_path, params: { micropost: { content: "" } }
end
assert_select 'div#error_explanation'
assert_select 'a[href=?]', '/?page=2' # 正しいページネーションリンク
# 有効な送信
content = "This micropost really ties the room together"
assert_difference 'Micropost.count', 1 do
post microposts_path, params: { micropost: { content: content } }
end
assert_redirected_to root_url
follow_redirect!
assert_match content, response.body
# 投稿を削除する
assert_select 'a', text: 'delete'
first_micropost = @user.microposts.paginate(page: 1).first
assert_difference 'Micropost.count', -1 do
delete micropost_path(first_micropost)
end
# 違うユーザーのプロフィールにアクセス(削除リンクがないことを確認)
get user_path(users(:archer))
assert_select 'a', text: 'delete', count: 0
end
end

 

13.4 マイクロポストの画像投稿 

省略

13.4.1

13.4.2

13.4.3

13.4.4

13.5 まとめ

・Active Recordモデルの力によって、マイクロポストも(ユーザーと同じで)リソースとして扱える

Railsは複数のキーインデックスをサポートしている

・Userは複数のMicropostsを持っていて(has_many)、Micropostは1人のUserに依存している(belongs_to)といった関係性をモデル化した

・has_manyやbelongs_toを利用することで、関連付けを通して多くのメソッドが使えるようになった

user.microposts.build(...というコードは、引数で与えたユーザーに関連付けされたマイクロポストを返す

・default_scopeを使うとデフォルトの順序を変更できる

・default_scopeは引数に無名関数(->)を取る

・dependent: :destroydependmentオプションを使うと、関連付けされたオブジェクトと自分自身を同時に削除する

・paginateメソッドやcountメソッドは、どちらも関連付けを通して実行され、効率的にデータベースに問い合わせしている

・fixtureは、関連付けを使ったオブジェクトの作成もサポートしている

・パーシャルを呼び出すときに、一緒に変数を渡すことができる

・whereメソッドを使うと、Active Recordを通して選択(部分集合を取り出すこと)ができる

・依存しているオブジェクトを作成/削除するときは、常に関連付けを通すようにすることで、よりセキュアな操作が実現できる

・Active Storageを用いて画像アップロードや画像リサイズができる