Railsチュートリアル
1章 ゼロからデプロイまで
やったこと
- Railsアプリの作成
- Git管理
- Renderデプロイ
2章 Toyアプリケーション
やったこと
- scaffoldでToyアプリの作成
- Userモデル設計
- MicroPostモデル設計(ツイートのようなもの)
- usersリソースをscaffoldで作成
- REST
- micropostsリソースをscaffoldで作成
- userとmicropostsを関連づける
- クラスを継承することで多くの機能をデフォルトで使える
- Toyアプリのデプロイ
学んだこと
- scaffoldだけではバリデーションやテスト、レイアウトは作れない
3章 静的なページの作成
やったこと
- sample_appのセットアップ、デプロイ
- static_pagesコントローラを作成
- rails gとrails d
- rails db:migrateとdb:rollback
- HTTPメソッド
- erb
- テスト
- static_pagesコントローラのテスト
- テスト駆動開発
- titleを動的にする
- レイアウトファイル
- rootのルーティングを指定
- minitestの出力のフォーマット
- Guardによるテストの自動化
学んだこと
- コントローラはApplicationControllerを継承しているので、メソッドに何も書かなくても特有の処理を行う
- テストを先に書くか後に書くかの目安
- アプリケーションのコードよりも明らかにテストコードの方が短くシンプルになる(=簡単に書ける)のであれば、「先に」書く
- 動作の仕様がまだ固まりきっていない場合、アプリケーションのコードを先に書き、期待する動作を「後で」書く
- セキュリティが重要な課題またはセキュリティ周りのエラーが発生した場合、テストを「先に」書く
- バグを見つけたら、そのバグを再現するテストを「先に」書き、回帰バグを防ぐ体制を整えてから修正に取りかかる
- すぐにまた変更しそうなコード(HTML構造の細部など)に対するテストは「後で」書く
- リファクタリングするときは「先に」テストを書く。特に、エラーを起こしそうなコードや止まってしまいそうなコードを集中的にテストする
- content_forの代替がprovide
4章 Rails風味のRuby
やったこと
- 組み込みヘルパー
- カスタムヘルパー(自作メソッド)
- title属性を動的に変更
- Railsコンソール
- 文字列
- オブジェクトとメッセージ(メソッド)受け渡し
- 配列
- 範囲演算子(range)
- ブロック
- ハッシュ
- シンボル
- stylesheet_link_tag
- クラス
学んだこと
- シングルクォートとダブルクォートは違いはほぼないのでどっちでもいい
- nilもオブジェクト
- nilとfalseだけが論理値がfalseになる
- 配列の内容を変更したい場合はメソッドに!をつける
- Rubyでは異なるデータ型が配列の中で共存できる
- ブロックはメソッドの引数に渡す処理のようなもの
- inspectメソッドはオブジェクトを文字列で表現する = pメソッド
- ハッシュがメソッド呼び出しの最後の引数である場合は、波カッコを省略できる
- RubyではすべてのオブジェクトはBasicObjectを継承しているので、あらゆるものがオブジェクトである
- RubyのドキュメントではクラスメソッドはString.newのように.で表記して、インスタンスメソッドはString#lengthのように表記する
- RailsとRubyは別物なのでコントローラなどはnewを実行しなくても使える
- アクションには戻り値がない
- インスタンス変数はそのクラス内のどこからでもアクセスできる、ビューで使える
- インスタンス化するときに引数にハッシュを渡すことをマスアサインメントという
- 文字列の比較は1文字ずつだが、シンボルは一度に全体を比較できる
5章 レイアウトを作成
やったこと
- レイアウトを作成する
- 静的ページを作成
- Bootstrapを使う
- link_to
- image_tag
- パーシャルで整える
- ルーティング
- アセットパイプライン
- Sass
- ユーザー登録
- 統合テスト
学んだこと
- image_tagは画像ファイルのパスと任意のオプションハッシュを受け取る
- image_tagを使うとRailsは該当する画像ファイルをアセットパイプラインを通して
app/assets/images
から探す - image_tagのsrc属性にはimagesというディレクトリがなく、assetsディレクトリ直下の画像をapp/assets/imagesと紐づけている
- アセットパイプラインを理解するにはアセットディレクトリ、マニフェストファイル、プリプロセッサエンジンを理解する
- マニフェストファイルは静的ファイルをどのように1つにまとめるかを指示するファイル(画像ファイルは適用されない)
- アセットをまとめる処理を行うのSprockets
*= require_tree .
はapp/assets/stylesheetsの中のすべてのCSSファイルを含める*= require_self
は自身のファイルも対象に含める- アセットパイプラインのメリットは開発環境ではプログラマにとって読みやすいフォーマットで、本番環境では最適化されたアセットを自動的に生成できること
- 多くのRails開発者は、異なるビューの間で共通して使うパーシャル用のディレクトリとして、sharedディレクトリをよく使う、全ページ共通のパーシャルはlayoutsディレクトリに保存する
6章 ユーザーのモデルを作成する
やったこと
学んだこと
- マイグレーションのchangeメソッドはdrop_tableとcreate_tableが対応していることを知っているためロールバックが可能になる
- モデルクラスはApplicationRecordを継承するので、自動的にActiveRecord::Baseクラスのすべての機能を持つ
- newメソッドはメモリ上でオブジェクトを作成しただけ
- createメソッドは真偽値ではなく、オブジェクト自身を返す
- validatesは単なるメソッド
- 通常メールアドレスは大文字と小文字が区別されない
- インデックスを追加するときはそのカラムで検索が頻繁に発生するか考える
- セキュアなパスワードでは、パスワードと確認用のパスワードを入力させ、ハッシュ化してDBに保存する
- ハッシュ化とは、入力されたデータを復元不可能なデータに変換する処理
- ユーザーの認証はパスワードの送信→ハッシュ化→DB内のハッシュ化された値との比較で手順が進む
- セキュアなパスワードはUserモデルで、
has_secure_password
を呼び出すだけでほぼ完了するhas_secure_password
でDB内でpassword_digest属性を保存できる
has_secure_password
を使うにはbcryptライブラリが必要!!
を使うことでオブジェクトが対応する論理値に変換できるauthenticate
は成功するとユーザーオブジェクトを返すが、!!
をつけることで、trueを返す- 暗号化は元に戻せて、ハッシュ化は元に戻せない
7章 ユーザー登録
やったこと
- ユーザーページを表示する
- デバッグ
- Gravatarを使ったプロフィール写真の導入
- ユーザー登録フォーム
- form_with
- ユーザー登録失敗
- ストロングパラメータ
- エラーメッセージのパーシャル化
- ユーザー登録成功
- リダイレクト
- フラッシュメッセージ
- プロ品質のデプロイ
学んだこと
- findメソッドでは自動的に整数型に変換される
- form_withはActive Recordのオブジェクトを取り込み、その属性を使ってフォームを構築する
- form_withではモデルオブジェクトが必要
- ストロングパラメータでは必須と許可済みのパラメータを指定できる
- Raildではデフォルトでparamsハッシュを丸ごと渡すとエラーが発生する
- privateのメソッドのインデントは一段深くする
- TLSはローカルのサーバーからネットワークにデータを送信する前に大事な情報を暗号化する技術
- production.rbで
config.force_ssl
をtrueにすることでTLSを強制する - 本番サイトでTLSを使うためには自分のドメインで使用するTLS/SSL証明書が必要
- RailsではデフォルトでPumaが使える
- リンクのパスとしてモデルオブジェクトが渡されると自動的にidになる
8章 基本的なログイン機構
やったこと
- セッション
- ログイン
- ログインフォーム
- メールアドレスからユーザーを取得し、パスワードで認証する
- フラッシュメッセージでエラーを表示
- フラッシュのテスト
- セッションヘルパー
- セッションハイジャック
- セッション固定
- メモ化
- 短絡評価
- ログイン状態によってレイアウトリンクを変更する
- JavaScriptを導入する
- ドロップダウンメニュー
- モバイル向けスタイリング
- ログイン状態のレイアウトの変更のテスト
- fixtureでテストデータを定義
- ユーザー登録後にログインする
- テスト用のログイン状態のメソッドを作成
- ログアウト
- テストのリファクタリング
学んだこと
- Cookieとはユーザーのブラウザに保存される小さなテキストデータ
- セッションはブラウザを閉じると自動的に終了する
- Cookieにセッションを保存する
- セッションはActive Recordオブジェクトではないので、デフォルトのエラーメッセージはつかない、またform_withでもurlやスコープを指定する必要がある
- form_withで
scope
を指定することで、name属性の値を決める flash
ではなくflash.now
だとリクエストが発生すると消滅する- セッションはRailsの事前に定義されている
session
メソッドをハッシュのように使う session
メソッドを使うとユーザーのブラウザ内の一時cookieに暗号化された値が保存される- セッションハイジャックやセッション固定に備える必要がある
- セッション固定に対策するにはログイン前に
reset_session
を使う ||=
を使ってメソッドの結果を変数に保持して次回以降の呼び出しを再利用する方法をメモ化というcurrent_user
を渡すことでUserのページに遷移できる- ぼっち演算子を使うと、
object && object.method
をobject&.method
に短縮できる - ログアウトはセッションを削除するよりすべてのセッション変数をリセットして行う
- RailsでTurboを使うときはDELETEリクエスト後のリダイレクトが正しく動作するように
status: :see_other
を指定する必要がある - ヘルパーはコントローラ用で、モデルはconcern
- ブラウザによっては、「中断したところから続ける」機能でセッションを復元するオプションを提供しているものもあるが、Railsはこの動作を制御できない
9章 発展的なログイン機構
やったこと
学んだこと
- Remember meを実装するには記憶トークンを生成し、トークン認証を行う
- トークンとはパスワードの平文と同じような秘密情報で、パスワードはユーザーが、トークンはコンピュータが作成・管理する
- Railsでは自動的にビューテンプレートで入力した内容をすべてエスケープする
- 永続的セッションの作り方
10章 ユーザーの更新・表示・削除
やったこと
- ユーザーのプロフィールを更新する
- 認可モデル
- フレンドリーフォワーディング
- ユーザーが直前に開こうとしていたページにリダイレクト先を指定すること
- すべてのユーザーを一覧できる
- seedでサンプルデータを作成
- ページネーション
- リストのパーシャル化
- ユーザーの削除
- 管理ユーザー
- カラムを追加
- 本番環境でのサンプルデータ生成
学んだこと
- beforeフィルターでアクセス制御を実装する
- リンクの
target="_blank"
にはセキュリティ上の小さな問題がある - ブラウザはPATCHリクエストをそのままでは送信できないので、input hiddenを使ってPOSTリクエストをPATCHリクエストとして偽造する
- form_withは
new_record?
によって新規作成(POST)か更新(PATCH)を判断する - アプリの実装前に統合テストを書くことを受け入れテストという
- 名前やメールアドレスを変更するときにパスワードは入力せずに更新できると便利
- バリデーションで
allow_nil: true
にするとフィールドが空の時に有効にできる - 編集・更新処理はユーザー自身のページであるか確認する
request.original_url
でリクエスト先が取得できる- ブラウザはネイティブではDELETEリクエストを送信できないため、RailsではJavaScriptを使って「偽造」する
- JavaScriptが使えないブラウザをサポートする必要がある場合は、フォームとPOSTリクエストを使ってDELETEリクエストを偽造することも可能
- Railsではadmin属性をUserモデルに追加すると自動的に
admin?
という論理オブジェクトを返すメソッドが追加される - adminのような編集可能にしてはならない属性はテストを作るべき
11章 アカウントの有効化
やったこと
- メール送信
- アカウント有効化の作成手順
- アカウント有効化の手順
- メールのプレビューの作成
- メタプログラミング
- 本番環境でのメール送信
学んだこと
- 有効化トークンもハッシュ化する
- ユーザー登録を完了するにはアカウントの有効化が必要になるので、有効化トークンはユーザーオブジェクトが作られる前に作る必要がある
- クエリパラメータを設定するには名前付きルーティングに対してハッシュを追加する
- クエリパラメータを名前付きルーティングで定義すれば、Railsが特殊な文字を自動的にエスケープしてくれる
- Railsでは、特殊なURLにアクセスするとメールのメッセージをその場でプレビューできる
- メタプログラミングはプログラムでプログラムを作成すること
- メール送信で使うAction Mailerのメール送信のコントローラとビューを生成できる
- Action MailerはテキストとHTMLの両方のメールを使える
12章 パスワードの再設定
やったこと
- パスワードの再設定の手順
- ログイン画面でリンクをクリック
- 再設定画面に移動
- メールアドレスを送信
- パスワード再設定用のリンクを記載したメールを送信
- リンクをクリックする
- パスワード再設定ページに移動
- パスワード再設定の処理の流れ
- PasswordResetsのルーティングを生成
- PasswordResetsのコントローラを生成
- new
- create
- トークンを生成し、ハッシュ化
- パスワード再設定のメール送信
- フラッシュメッセージを表示
- ルートにリダイレクト
- edit
- update
- パスワード再設定の有効期限が切れていないか
- 無効なパスワードでないか
- パスワードが空ではないか
- パスワードが正しければ更新するw
- ログイン画面にパスワード再設定のリンクを追加
- usersテーブルに
reset_digest
とreset_sent_at
を追加するマイグレーションを作成 - パスワード再設定のメールテンプレートを作成
- プレビューを作成
- パスワード再設定のテスト
- Mailgunで本番環境でのメール送信
学んだこと
- パスワードの再設定はアカウント有効化とほぼ手順は同じ
- 再設定用のリンクはなるべく短時間で期限切れにすべきなので、再設定メールの送信時刻も記録する
13章 ユーザーのマイクロポスト
やったこと
- Micropostモデルの作成
- バリデーション
- Userとの関連付け
- default_scope
- マイクロポストを表示する
- マイクロポストのアクセス制御
- マイクロポストを作成
- マイクロポストを削除
- マイクロポストの画像投稿
- Active Storageのインストール
- JavaScriptで画像アップロードの警告を表示
- ImageMagickで画像のリサイズ
- AWS S3に画像を保存する
学んだこと
- String型でもText型でもパフォーマンスの差はない
- マイグレーションファイルの
add_index
で複数のカラムを配列に含めると、複合キーインデックスを作成する - default_scopeで使われる
->
はラムダというProcやlambda、無名関数と呼ばれるオブジェクトを作成する文法の省略記法で、ブロックを引数に取り、Procオブジェクトを返す - ラムダは
call
メソッドが呼ばれた時にブロック内の処理を評価する time_ago_in_words
で何日前かを表すことができるnew
ではなくbuild
を使うことで、関連付けしたオブジェクトを作成できる- SQLクエリに代入するときは
?
を使うことで、SQLインジェクションを回避できる - 1行のときは後置if文、2行以上のときは前置if文を使うのがRubyの慣習
- 常に元のページに戻るには
request.referrer
を使う - RailsでファイルをアップロードするにはActive Storageを使う
- Active StorageはPDFや音声ファイルも扱える
has_one_attached
でモデルとアップロードされたファイルを関連づけるhas_many_attached
でモデルと複数のアップロードされたファイルを関連づける- Active Storageにはバリデーションがないので、専用のgemを使う
- 開発環境と本番環境では画像の保存先が異なる
- JavaやC++といった言語の挙動とは異なり、RubyのPrivateメソッドは継承クラスからも呼び出すことができる
- エッジケースとは、滅多に起こらないがユーザーが遭遇する可能性があるバグのこと
14章 ユーザーをフォローする
やったこと
- リレーションシップモデル
- memberとcollection
- フォロー
- フォロー解除
- ステータスフィード
- Hotwire
- N+1問題
学んだこと
- フォロー機能では同じユーザーを2回フォローできないようにfollowed_idとfollower_idを使って複合インデックスキーを作ってユニークキーを指定する
- << 演算子は、has_many や has_many :through アソシエーションを通じて関連付けられたオブジェクトを追加し、その変更をデータベースに保存できる
- Hotwireを使うとページの一部分だけを変更できる
- Turboは、Turbo Streamsと呼ばれる部分を介して動作し、小さなHTMLスニペットをWebSocketで直接ページに送信する
- WebSocketは、クライアント(Webブラウザなど)とWebサーバーの間に持続的なコネクションを設定するWeb標準の技術
- Turbo Streamに応答するものが存在しない場合は、RailsがデフォルトでTurbo Streamリクエストを通常のHTMLリクエストと同様に扱う
- following_idsは、has_many :following関連付けを行うとActive Recordによって自動生成されるメソッド
- SQLで同じ変数を複数の場所に挿入したい場合は、後者のハッシュ形式の構文の方がより便利