Rails チュートリアル 13章
まえがき
Railsチュートリアル13章において押さえておいた方がいいポイントをまとめ、13章の学習内容を俯瞰し整理することを目的とする。
変更したファイル
| ファイル名 | 説明 |
|---|---|
| user.rb | micropostとの関係を1対多として設定するためhas_manyを追加。また、フィード機能を作るため、feedメソッドを追加。 |
| user_test.rb | ユーザーの削除に伴いポストが削除されるかのテストを追加。 |
| users_contoroller.rb | showアクションにインスタンス変数として@micropostsを追記。beforeフィルタのlogged_in_userを削除。 |
| show.html.erb | マイクロポスト表示領域について追記。 |
| seeds.rb | 最初の6人のユーザーに対して、マイクロポストを生成した。 |
| routes.rb | マイクロポストに追いてのルーティングを追加。RESTfulのcreate,destroyのみ指定して追加。また、ルーティングエラーに備えて、static_pages#homeに対し、名前付きルーティングを追加した。 |
| application_controller.rb | users_controller.rbにおいて削除したlogged_in_userをprivateとして記述。 |
| static_pages_controller.rb | homeアクションに内容を追加。 |
| _error_messages.html.erb | マイクロポストの方でもエラーメッセージパーシャルを使えるよう変更。 |
| _form.html.erb | usersコントローラー配下のビューのうち、error_messagesを読んでいるビューについて、objectを設定しなおした。 |
| edit.html.erb | password_resetsコントローラー配下のビューのうち、error_messagesを読んでいるビューについて、objectを設定しなおした。 |
| home.html.erb | static_pagesコントローラー配下におけるページをログイン済みか否かで分けて表示するように変更。また、各内容はパーシャルを用いてリファクタリングしてある。 |
| microposts.yml | テスト用のマイクロポストデータを追記。 |
| Gemfile | active_storage_validations(画像アップロード用),image_processing(画像処理用)を追加。group:productionにaws-sdk-s3(画像保存用クラウドストレージサービス)を追加。 |
| application.js | image_upload.jsのパスを追加してJSを読み取れるように追記。 |
| application.rb | RailsでImageMagickが使えるように設定を追記。 |
| storage.yml | amazonについての環境変数を指定する。 |
| production.rb | アップロードされたファイルの保存をAWSとするように指定。 |
追加したファイル
| ファイル名 | 作成方法 | 説明 |
|---|---|---|
| <タイムスタンプ>_create_microposts.rb | generate modelで自動作成 | user_id,created_at属性にインデックスを付与することにより検索性能を良くした。 |
| micropost.rb | generate modelで自動作成 | user_id,contentに対する制約を追記した。また、アップロードファイルとモデルを関連づけるhas_one_attachedメソッドを指定した。画像処理用のvalidationを設定した。 |
| microposts.yml | generate modelで自動作成 | ひとまず空にした。 |
| micropost_test.rb | generate modelで自動作成 | マイクロポスト自体が正当か、指定したユーザーが存在するのか、ポストの文字数についてのテストを追加。 |
| microposts_controller.rb | generate controllerで自動作成 | create,destroyアクションと、before_actionを追加。インスタンス変数に写真情報も追加。 |
| microposts_controller_test.rb | generate controllerで自動作成 | ポスト作成・削除時にログインしているかどうかのテスト、ユーザーは異なるユーザーのポストを削除できないことを確認するテストを追記。 |
| _micropost.html.erb | 手動で作成 | 1つのマイクロポストを表示するパーシャル。写真があれば表示するタグを追加。variantを追加することでリサイズを行うことができる。 |
| _user_info.html.erb | 手動で作成 | static_pagesのhomeに新しくユーザー情報についてのパーシャルを作成。 |
| _micropost_form.html.erb | 手動で作成 | static_pagesのhomeに新しくマイクロポスト投稿フォームについてのパーシャルを作成。ここに画像投稿用のfile_fieldを追加。 |
| _logged_in_user.html.erb | 手動で作成(自身で命名) | static_pagesコントローラー配下のhomeページのうち、ログイン済みユーザーに対して表示する画面のパーシャルを作成。また、ここにフィードのパーシャルを追加した。 |
| _not_logged_in_user.html.erb | 手動で作成(自身で命名) | static_pagesコントローラー配下のhomeページのうち、未ログインユーザーに対して表示する画面のパーシャルを作成。 |
| _feed.html.erb | 手動で作成 | ステータスフィードのパーシャルを作成。 |
| microposts_interface_test.rb | generate integration_testで自動作成 | マイクロポストの表示、サイドバー、画像のアップロードに関して、テストを追加。 |
| image_upload.js | 手動で作成 | 巨大画像のアップロードを防止するためのコードを作成。 |
micropost.userとは?
Micropostモデルの属性はおろか仮想属性としても、メソッドとしてもuserを宣言していないにも関わらず使えるこれは一体なんなのか。これは、モデル内で以下のようなモデル同士の関係性を示すような宣言をした時点で暗黙的に作成されるインスタンスメソッドである。
# micropost
belongs_to :user
#user
has_many :microposts
このメソッドによりお、そのマイクロポストを作成したユーザー情報を得ることができる。
使えるようになったメソッド一覧
| メソッド | 用途 |
|---|---|
| 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に紐づいていて、idが1であるマイクロポストを検索する。 |
belongs_to vs has_many
モデル同士の関係にも1対1、1対多がある。前者を表すのが、belongs_to,後者を表すのが、has_manyである。Twitterのようなアプリを考えた場合だと、ユーザー1に対してポストは多数となる。また、これが記述されたモデルにとって。という視点があるので注意。例えば、ポストから見たら、複数のユーザーに属していることはない。すなわちポストが1で固定されたらユーザーも1であり、1対1と捉えられる。逆に、ユーザー側からはユーザーを1で固定したとしてもポストを複数持ち得るため1対多となる。
default_scope
データベースの並び順については何も設定しない限り担保されないため、モデル内にdefault_scopeとして設定することによって、並び順を固定する。以下のように記述する。
default_scope -> { order(created_at: :desc)}
ポストの削除
本アプリではユーザーの削除に伴い、そのユーザーに紐づくポストも削除したい。よって、以下のようにすれば、ユーザの削除と共にポストを削除できる。このdependentはhas_manyのオプションであり、親(user)が消えたら、ポスト1件ずつdestroyされるということ。
has_many :microposts, dependent: :destroy
assert_match
assert_match <パターン>, <条件、範囲etc>
このassertionはassert_selectと似ているが、パターンが非常に許容度が高く、selectと異なり、タグを求めてこない。ただ、ここで指定するパターンは文字列でなくてはならないため、整数などは文字列に変換した上で比較する必要がある。
リクエストステータスとassert_responseいるか問題
# assert_resopnseなし
test "should redirect create when not logged in" do
assert_no_difference "Micropost.count" do
post_microposts_path, paramas: { micropost: { content: "Lorem ipsum" } }
end
assert_redirected_to login
end
# assert_resopnseあり
test "should redirect destroy when not logged in" do
assert_no_difference "Micropost.count" do
delete micropost_path(@micropost)
end
+ assert_response :see_other
assert_redirected_to login_url
end
行うリクエストによって、副作用のあるリクエスト(POST,PATCH,DELETE)というのがあり、この場合は、リダイレクトのHTTPステータスコードが303 See Otherであるべき。本来は、デフォルトでそのステータスコードが返ってようになっているのだが、このSee Otherが別のステータスコードになっていた場合、POST終了後に再びPOSTになりかねなく、安全設計上危険があるため、明示する。assert_redirected_toはリダイレクトされたことしか保証しないため、リクエストの種類などに制限をかけるためステータスコードを確認するassert_responseが使われる。
パーシャルにおけるインスタンス名の抽象化
以下のようにオプションを設定することで、そのような名前の変数をパーシャル内で使うことができるようになる。
<%= render "shared/_error_messages", object: f.object %>
このようにobjectオプションでf.objectを指定することで、パーシャル内でobjectという名の変数を用いることができるようになる。これでオブジェクトが@userであろうが@micropostであろうが動的にパーシャルを用いることができる。
whereメソッド
whererメソッドは()内で指定した値を持つオブジェクトを返す。条件に合うレコードを取り出すSQL関連のメソッド。以下のように使う。
Micropost.where("user_id=?", id)
?としておくことでSQLに代入する前にidがエスケープされ、直接SQL分に値を埋め込む必要がなく、SQLインジェクションを防ぐことができる。
暗黙のパーシャル選択
普通パーシャルはそのパーシャルの名前であったり、パスで指定するが以下のような指定の仕方が可能である。
<%= render @feed_items %>
このインスタンス変数の中に入った要素のクラス名を見て、そこからパーシャル名を推測した上で、パーシャルを呼び出すという仕組みがあるためこのような指定の仕方が可能である。@feed_itemsでは、micropostが要素として入っているため、_micropost.html.erbであると予測して呼んでくれるのである。
JSコードの理解
// 巨大画像のアップロードを防止する
document.addEventListener("turbo:load", function() {
document.addEventListener("change", function(event) {
let image_upload = document.querySelector('#micropost_image');
if (image_upload && image_upload.files.length > 0) {
> const size_in_megabytes = image_upload.files[0].size/1024/1024;
if (size_in_megabytes > 5) {
alert("Maximum file size is 5MB. Please choose a smaller file.");
> image_upload.value = "";
}
}
});
});
ハイライト部分が新しいだろうか。1つ目は/1024/1024と書いてあるがこれは、除算を行なっているのである。すなわちMBへの変更である。また、配列の先頭というのは、fileタイプのインプットの中で選択されたファイルの一番初めを持ってくるためこのように書かれている。
2つ目に関しては、空文字を入れることで選択されたファイルを取り消しているのである。つまり不正なファイルを弾いているわけである。
他にしたこと
- model内に記述されるbelogs_toとはその後ろに指定された属性と1対1関係であることを示す。
- created_at,updated_atという属性はマジックカラムであり、指定せずとも自動で作成される。
- referencesを利用すると、インデックスと外部キーがついた属性が用意される。この時指定した配列は複合キーインデックスとなる。この複合キーインデックスというのはデータベースでいうところの複合主キーとはまた違って、あくまで一位性を保証するものではなく、検索性能を良くするためのインデックスとなる。
- time_ago_in_wordsというヘルパーメソッドは
- ビューの紐づいたコントローラーのインスタンス変数を呼び出す分には、will_paginateにおいてインスタンス変数を明示する必要はないが、今回のケースでは、UserコントローラーのshowページでMicropostをページネーションしたいので、明示する必要がある。
- FakerのLorem.sentence機能を用いて、マイクロポストのサンプルデータを追加した。
- response.bodyではHTMLページ全体を精査することができる。つまりどこかしらに該当するものがあればいい。みたいな意味になる。
- ポストに関するルーティングとコントローラーアクションを追加。
- ポスト作成・削除におけるログインしているかどうかのテストを記述した。
- request.referrerで示されるのは直前のURL。
- referrerを用いずとも、redirect_back_or_toを用いれば同じ挙動が期待できる。
- 画像のアップロード用にActive Storageをインストールした。
- チュートリアルに従って、AWSアカウントを作成、IAMにユーザーを作成、アクセスキーを作成、バケットを作成を行い、Renderの環境変数を設定する。
Discussion