Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rails Tutorial 第13章 #84

Closed
kenta0425 opened this issue Feb 2, 2021 · 6 comments
Closed

Rails Tutorial 第13章 #84

kenta0425 opened this issue Feb 2, 2021 · 6 comments
Assignees

Comments

@kenta0425
Copy link
Collaborator

WHY

WHAT

  • 第13章のマイクロポストを理解し、実装すること
@kenta0425 kenta0425 self-assigned this Feb 2, 2021
This was referenced Feb 2, 2021
@kenta0425
Copy link
Collaborator Author

マイクロポスト

Micropostモデル

  • content属性
    • マイクロポスト(投稿)の内容を保存する
    • text型にし、文字数を多く格納できるようにする
  • user_id属性
    • 特定のユーザーとマイクロポストを関連付ける
    • reference型を利用することで、自動的にインデックスと外部キー参照付きのuser_idカラムが追加され、UserとMicropostを関連付けする下準備をしてくれる

Micropostのバリデーション

  • presence: trueにしてuser_id属性とcontent属性が存在しなければならないようにする
  • content属性に文字数制限を付ける

User/Micropostの関連付け
マイクロポストは1人のユーザーと関連付けられ、それぞれのユーザーは複数のマイクロポストと関連付けられいる

  • belongs_to
    • belongs_tomicropost.user(投稿者)が見れるようになる
      micropost(id: 3, user_id: 1) →  user(id: 1)
  • has_many
    • has_manyuser.microposts(そのユーザーの投稿)が見れる
      user(id: 1) → micropost(id: 3, user_id: 1)、mircropost(id: 4, user_id: 1)、micropost(id: 7, user_id: 1)

メソッド一覧

  • 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であるマイクロポストを検索する

default_scope
ユーザーのマイクロポストを特定の順序で取得できるようにする

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

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

has_many :microposts, dependent: :destroy

users/showにおけるwill_paginate

  • <%= will_paginate %>はUsersコントローラのコンテキストに置いて、@usersインスタンス変数が存在していることを前提にしているので、@microposts変数をwill_paginateに渡すことでmicropostでも使える
@microposts = @user.microposts.paginate(page: params[:page])

any?

  • ユーザーのマイクロポストが1つもない場合には空のリストを表示させない
<% if @user.microposts.any? %>

count

  • マイクロポストの投稿数を表示することができる
<%= @user.microposts.count %>

プロフィール画面のテスト

  • include ApplicationHelper
    • Applicationヘルパーを読み込んだことでヘルパーないに定義してあるfull_titleヘルパーが利用できる

Micropostのアクション
Micropostリソースへのインターフェイスは、主にプロフィールページとHomeページのコントローラを経由して実行されるので、Micropostコントローラにはneweditアクションは不要になり、createdestroyアクションがあれば十分

logged_in_userメソッド

  • Micropostコントローラでも必要なので、Userコントローラに定義していたが、各コントローラが継承するApplicationコントローラに移す

Micropostコントローラのcreateアクション

  • buildするためにUser/ Micropost関連付けを行っている
@micropost = current_user.microposts.build(micropost_params)
  • micropost_paramsでStrong Parametersを使っていることにより、マイクロポストのcontent属性だけがWeb経由で変更可能になる
def micropost_params
  params.require(:micropost).permit(:content)
end

HomeページでMicropostを作成する

  • buildする為にhomeアクションに@micropostを定義する
def home
  @micropost = current_user.microposts.build if logged_in?
end
  • エラーメッセージが@user変数を直接参照しており、@micropost変数を使う必要があるので、フォーム変数ff.objectとすることで関連付けられたオブジェクトにアクセスできる
<%= render 'shared/error_messages', object: f.object %>

エラーメッセージの@userの部分もobjectにかえる

フィード

  • feedメソッドを使う
  • ?(疑問符)があることで、SQLクエリに代入する前にidがエスケープされるため、SQLインジェクションというセキュリティホールをさせることができる
  • id属性は単なる整数(self.idはユーザーのid)である
def feed
  Micropost.where("user_id = ?", id)
end
  • ログインユーザーのフィード用にインスタンス変数@feed_itemsを追加し、Homeページにはフィード用のパーシャルを追加する
@feed_items = current_user.feed.paginate(page: params[:page])
  • @feed_itemsの各要素がMicropostのクラスを持っているため、Micropostのパーシャルを呼び出すことができる
  • 投稿が失敗した時のために@feed_itemsをMicropostコントローラのcreateアクションに追記する
  • Homeページに対応するcontrollerパラメータ(static_pages)とactionパラメータ(home)を明示的にwill_paginateに渡すことでfeedパーシャルのwill_paginateがうまく動く

マイクロポストの削除

  • deleteリンクを作る
  • Micropostコントローラのdestroyアクションを定義する
  • admin_userフィルターで@user変数を使うのではなく、関連付けを使ってマイクロポストを見つける
@micropost = current_user.microposts.find_by(id: params[:id])

request.referrerメソッド

  • 一つ前のURLを返す
  • マイクロポストがHomeページから削除された場合でもプロフィールページから削除された場合でも、request.referrerを使うことでDELETEリクエストが発行されたページに戻る

Active Storage
Rails に組み込まれている機能で、画像を簡単に扱うことや画像に関連付けるモデルも自由に指定できる

  • has_one_attachedメソッド
    -- 指定のモデルと、アップロードされたファイルを関連付ける
has_one_attached :image
  • attachメソッドが提供されるので、Micropostコントローラのcreateアクションの中で、アップロードされた画像を@micropostオブジェクトにアタッチする
@micropost.image.attach(params[:micropost][:image])

画像投稿のバリデーション

  • サポートする画像フォーマットを制限する
content_type: { in: %w[image/jpeg image/gif image/png],
                message: "must be a valid image format" }
  • ファイルサイズを最大5MBに制限する
size: { less_than: 5.megabytes,
        message: "should be less than 5MB" }
  • Active Storageが提供するvariantメソッドで変換済み画像を作成するためにresize_to_limitオプションを用いて、画像の幅や高さが500ピクセルを超えないように制約をかける
image.variant(resize_to_limit: [500, 500])

@kenta0425
Copy link
Collaborator Author

kenta0425 commented Feb 3, 2021

演習

  • Q. 最初のユーザーをuserに代入してmicropost = user.microposts.create(content: "Lorem ipsum")を実行すると、どのような結果が得られるでしょうか?
    A.
=> Micropost id: 1, content: "Lorem ipsum", user_id: 1, created_at: "2021-02-03 00:47:20", updated_at: "2021-02-03 00:47:20"
  • Q. user.microposts.find(micropost.id)を実行して、本当に追加されたのかを確かめてみよう
    A.
=> Micropost id: 1, content: "Lorem ipsum", user_id: 1, created_at: "2021-02-03 00:47:20", updated_at: "2021-02-03 00:47:20"
  • Q.micropost.idの部分をmicropostに変更すると、結果はどうなるか
    A.
=> ArgumentError (You are passing an instance of ActiveRecord::Base to `find`. Please pass the id of the object by calling `.id`.)
  • Q. user == micropost.userを実行した結果はどうなるか
    A.
=> true
  • Q. user.microposts.first == micropostを実行した結果はどうなるか
    A.
=> true
  • Q. MIcropost.firstとMIcropost.lastを実行した時のSQL文はどうなっているか
    A.
Micropost.first
=> SELECT `microposts`.* FROM `microposts` ORDER BY `microposts`.`created_at` DESC LIMIT 1
Micropost.last
=> SELECT `microposts`.* FROM `microposts` ORDER BY `microposts`.`created_at` ASC LIMIT 1
  • Q. 最初のユーザーをuserに代入して、そのuserオブジェクトが最初に投稿したマイクロポストのidはいくつか
    A.
=> id: 1
  • Q. helperオブジェクトのtime_ago_in_wordsメソッドを使って、3.weeks.agoや6.months.agoを実行してみよう
    A.
helper.time_ago_in_words(3.week.ago)
=> "21 days"
helper.time_ago_in_words(6.month.ago)
=> "6 months"
  • Q. helper.time_ago_in_words(1.year.ago)と実行すると、どういった結果が返ってくるか
    A.
helper.time_ago_in_words(1.year.ago)
=> "about 1 year"
  • Q. micropostsオブジェクトのクラスは何? 
    A.
=> Micropost::ActiveRecord_AssociationRelation
  • Q. (1..10).to_a.take(6)というコードの実行結果を推測して、値が合っているかコンソールを使って確認してみよう
    A.
=> [1, 2, 3, 4, 5, 6]
  • Q. to_aメソッドの部分は本当に必要か確認してみよう
    A.
=> [1, 2, 3, 4, 5, 6] # 必要ない
  • Q. Fakerを使ってlorem ipsum以外に架空の大学名など出力してみましょう。
    A.
Faker::University.name
=> "The Vermont Academy"
  • Q. なぜUsersコントローラ内にあるlogged_in_userフィルターを残したままにするとマズイか
    A. Rails のDRYに反するため

  • Q. マイクロポストを投稿して、Railsサーバーのログ内にあるINSERT文では、どういった内容をデータベースに送っているのか
    A.

INSERT INTO `microposts` (`content`, `user_id`, `created_at`, `updated_at`) VALUES ('hello', 1, '2021-02-03 05:59:32.497494', '2021-02-03 05:59:32.497494')
  • Q. 作成したマイクロポストを削除し、RailsサーバーのログのDELETE文の内容をみよう
    A.
DELETE FROM `microposts` WHERE `microposts`.`id` = 301

@haruhiko95
Copy link
Collaborator

確認事項

  • "Rails のDRY"とは何を指しますか?
    Q. なぜUsersコントローラ内にあるlogged_in_userフィルターを残したままにするとマズイか
    A. Rails のDRYに反するため
    

@kenta0425
Copy link
Collaborator Author

確認事項

  • "Rails のDRY"とは何を指しますか?
    Q. なぜUsersコントローラ内にあるlogged_in_userフィルターを残したままにするとマズイか
    A. Rails のDRYに反するため
    

繰り返しを避ける(Don't Repeat Yourself)を指します。
ここの場合はbeforeフィルターのlogged_in_userメソッドをusersコントローラとrelationshipsコントローラで使用するため、applicationコントローラに定義することで重複を避けています。

@haruhiko95
Copy link
Collaborator

繰り返しを避ける(Don't Repeat Yourself)を指します。

そうですね👍

"Railsの"という記述にはどのような意図がありますか?
(細かい点を突いてすみません🙏 どのような解釈をしているか確認したく、もし異なった解釈をしているようなら今のうちに改めておいてもらいたいので🙏)

@kenta0425
Copy link
Collaborator Author

"Railsの"という記述にはどのような意図がありますか?
(細かい点を突いてすみません🙏 どのような解釈をしているか確認したく、もし異なった解釈をしているようなら今のうちに改めておいてもらいたいので🙏)

Railsの原則という意味を含めて記述しました。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants