1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Railsチュートリアルの記録や学び

Last updated at Posted at 2020-03-17

はじめに

こちらの素晴らしいRailsチュートリアルを実施した記録、およびそこで得た学びについて記載していきます。

基本的に追記していく形で記録していく予定です。
最後まで完走したい。

やっていて思ったこと

  • 「本章のまとめ」を先に読んだほうがいいんじゃないか説
    • 各章ごとにまとめが書かれてあり、具体的に何をやったかを振り返っているリストがある
    • それを先に読んでおくことで、「実現したいこと」と「具体的にどうやって実現するのか」を結びつけやすいんじゃないか、と感じた

環境

  • Windows Subsystem for Linux(Windows 10)
    • ディストリビューションは Ubuntu 18.04
  • エディタはVS Code

第1章

環境構築とHerokuにDeployするところまで。
Herokuを数年前に触ったときは英語のみだった気がするけれど、いつの間にか日本語に対応されていた。
少し時間がかかった箇所としては、rails newのあとにbundle install一発で完了しなかったことや環境構築周りくらい。

第2章

大量の機能を自動的に生成するscaffoldジェネレータというスクリプトを使ってアプリケーションをすばやく生成し、それを元に高度なRailsプログラミングとWebプログラミングの概要を学びます。

とのこと。
MVCモデルとRESTアーキテクチャについてが該当すると思われる。

  • ProgateのWeb開発パス(Ruby on Rails)で似たようなことは学べていた
    • あと、RESTという言葉は出てこなかったと思う
  • Routingでresource、Modelでhas_manybelongs_toなど、Progateでは出てこなかったコードが学べる
  • Ruby 2.7.0をインストールしていたため、コマンド実行時にWarningがたくさん出てきてしまった
    • 第3章では次点の2.6.5にして進めることにする
    • railsのバージョンを上げたりすることでも回避可能かも知れないけれど、チュートリアルと挙動が変わったら面倒なのでそれはしない方針で行う
  • rails consoleUser.allなどが見づらい問題
  • rails db:resetでテーブル内容をすべて削除できる

ハマった箇所

usersmicropostsを関連付けしたあと、micropostsで新しいレコードを作成できなくなった。

irb(main):004:0> PP.pp User.all
  User Load (0.2ms)  SELECT "users".* FROM "users"
[#<User:0x00007f4fdc5ce918
  id: 1,
  name: "あ",
  email: "あ",
  created_at: Tue, 17 Mar 2020 14:58:59 UTC +00:00,
  updated_at: Tue, 17 Mar 2020 14:58:59 UTC +00:00>]
=> #<IO:/dev/pts/2>
irb(main):015:0> m = Micropost.create!(content: 'aaaaaaaaaa', user_id: 1)
   (0.1ms)  begin transaction
   (0.0ms)  rollback transaction
Traceback (most recent call last):
        1: from (irb):15
ActiveRecord::RecordInvalid (Validation failed: Users must exist)

どうしたか?

単なる自分のtypoが引き起こしていた問題でした。
belongs_to :usersとなっていた箇所をbelongs_to :userとした。

belongs_toでは・・・

belongs_to関連付けで指定するモデル名は必ず「単数形」にしなければなりません。

一方、has_manyでは・・・

has_many関連付けを宣言する場合、相手のモデル名は「複数形」にする必要があります。

わかってみれば「なんだ、そういうことか」という感じではあるけれど、Validation failed: Users must existだと、文法ミスだということに気づきづらいなぁ、とは思いました。

第3章

静的ページを作成し、さらに自動テストも作成する。
TDD(テスト駆動開発)の「Red→Green→Refactor」を体験できる。

  • Ruby 2.6.5にした
  • Security Issueの対応
    • GitHubにpushしたところ、以下のような内容のメールが来た
      • Known moderate severity security vulnerability detected in puma < 3.12.4 defined in Gemfile.lock.
      • Known critical severity security vulnerability detected in actionview >= 5.1.0, <= 5.1.6.1 defined in Gemfile.lock.
    • 前者は3.12.4に変更、後者はrails 5.1.6のままだとインストールできなかったので、5.1系では最新の5.1.7に変更して対処
      • チュートリアルを進める上でなにか不都合が出てきたら、その際に別途対処する
  • rails generateまたはrails db:migrateをしたあと、元に戻す方法
    • generateした際と同じ引数でdestroyを行う
      • 元に戻ったかどうかの検証のためにgit diffを行ってみたところ、相違ないことが確認できた
    • コントローラーの場合
      • rails generate controller StaticPages home helpのあとに
      • rails destroy controller StaticPages home helpをやる
    • モデルの場合
      • rails generate model User name:string email:stringのあとに
      • rails destroy model Userをやる ※テーブルのカラムに該当する箇所は省略可能
    • マイグレーションの場合
      • rails db:migrateのあとに
      • rails db:rollbackをやる
      • あるいはrails db:migrate VERSION=0 ※デフォルトに戻す
  • 「結局テストはいつ行えばよいのか」
  • .erb
    • Embedded Rubyのこと。<% %>または<%= %>というブロック内でRubyのコードを書くことが出来る。
    • 前者は実行されるだけ。後者は実行結果がテンプレートに反映される。
  • provideyield
    • provide(:title, "About")のように記載し、yield(:title)とするとAboutの文字列を取得できる
    • こういった使い方もできるというだけで、厳密には違うかも
  • root_url
    • routes.rbrootを定義すると、root_urlというRailsのヘルパーを使用することができるようになる

第4章

一部の言語仕様を学んでいく・・・章だと思われる。
別の言語を使っていた人であれば、ruby独自の記法や慣習の部分だけ動作を確認するなどすれば良さそう。
ただ、演習がrails consoleで実行することが想定されているようで、やや面倒。

  • %記法(パーセントきほう)
  • symbol-to-proc
    • %w[A B C].map { |char| char.downcase }%w[A B C].map(&:downcase)は同じ結果になる
  • ハッシュロケット
    • =>のこと
    • 次のようにキーと値をハッシュロケットと呼ばれる=> によってリテラル表現するほうが簡単です。
  • ハッシュ
    • キーにはシンボル(:nameみたいなやつ)をrailsではよく使っているよ、とのこと
      • .e.g) { :name => "Michael Hartl", :email => "michael@example.com" }
    • ruby1.9からはjavascriptなどの言語のようにもかけるようになったらしい(個人的にはこちらのほうが慣れているから嬉しい)
      • e.g.) { name: "Michael Hartl", email: "michael@example.com" }
  • stylesheet_link_tag

第5章

レイアウトを作成。bootstrapSassを使用する。

  • Railsのpartial
    • <%= render 'layouts/shim' %>というふうに指定する。
    • ただし、ファイル名は_shim.html.erbという具合に、アンダースコアから始める
  • アセットパイプライン
  • インテグレーションテスト
    • rails generate integration_test hoge
    • ActionDispatch::IntegrationTestを継承したclassのファイルが生成される

第6章

第6章から第12章を通して、Railsのログインと認証システムをひととおり開発します。

とあるように、ユーザーのログイン機能を作るために、ユーザーのモデルを作成する。
progateのほうでRailsのパスを実施済みの場合は、見覚えるのある内容が多い。

  • マイグレーション(ファイル)
    • ブロックの最後の行t.timestampsは特別なコマンドで、created_atとupdated_atという2つの「マジックカラム (Magic Columns)」を作成します。
    • DBのテーブルにユニークになるようにインデックスを張りたい場合
      • add_index <:table_name>, <:column_name>, unique: true
      • これを、マイグレーションファイルのchangeメソッド内で実行されるように記載する
  • reload
    • モデルオブジェクトを.reloadすると、変更(save)前の状態に戻る(取り消される)
  • update_attributes
    • e.g.) user.update_attributes(name: "The Dude", email: "dude@abides.org")
    • どれか1つでもバリデーションエラーになると失敗する
  • updated_atの更新タイミング
    • saveした際に更新されるのではなく、オブジェクトの内容を変更した際に更新される。
    • そのため、オブジェクトを何も変更せずに、ただsaveしただけではupdated_atは更新されない。
  • 1.year.ago
    • 上記は、現在時刻の1年前という意味
    • 同様に、1.month1.dayも存在する。末尾にsをつけて複数形にしても同じ種類のオブジェクトが返却される
  • ActiveRecordでよく使用されるValidation
    • presence
      • 存在性。いわゆる必須項目として扱わせることができる
    • length
    • format
    • confirmation
  • rails test test/models/user_test.rb
    • 特定のtestだけ実行したいとき
    • rails test --helpで使い方を出力できる
  • 国際化ドメイン名
  • before_save { self.email = email.downcase }
    • 右の式ではselfを省略できるものの、個人的には省略しないほうがわかりやすいな、と感じた
    • before_save { self.email.downcase! } としても良さそう。一般的にはどのパターンが多いのだろうか
  • 多重代入
    • @user.password = @user.password_confirmation = "a" * 5
    • これは、@user.password@user.password_confirmationに同じ値("a" * 5)を代入している

疑問点

例えば、Foo@ExAMPle.Comfoo@example.comが別々の文字列だと解釈してしまうデータベースがありますが、私達のアプリケーションではこれらの文字列は同一であると解釈されるべきです。
この問題を避けるために、今回は「データベースに保存される直前にすべての文字列を小文字に変換する」という対策を採ります。

この対策で良いのかどうかは、実際には対象となるシステムで考慮すべき点だと感じた。
これは"チュートリアルだから"という理由で小文字に変換しているけれど、ユーザー目線で見ると入力していないメールアドレスが、例えば自分しか見られないページに表示されてしまうことになる。
downcaseで一律小文字にしてしまうと復元できなくなるため、検索用のカラムと表示用のカラムで分けて保存する、という対策もありだと思われる。

第7章

ユーザー登録機能を追加する。

  • Rails.env.development?
    • Rails.env == 'development'と等価
    • node.jsでいうところのNODE_ENV === 'development'みたいなものという理解
  • console、server、migrateでデフォルト以外の環境にする方法
    • rails console production
    • rails server --environment production
    • rails db:migrate RAILS_ENV=production
    • どうしてバラバラなんだろうか?
  • Sassのミックスイン機能
    • @mixinでCSSを定義(名前をhogeとする)
    • 別のCSSの定義内で、@include hogeで呼び出す
  • resources :<リソース名>
    • route.rbに書くだけでRESTfulなアクションがすべて利用可能になる
    • 一部のアクションだけに絞りたい場合
      • resources :<リソース名> :only => [:show, :new]
  • debuggerメソッド
    • rails serverを実行していたターミナルで、rails consoleのようなコマンドを実行できるようになる
    • Ctrl + Dでプロンプトから抜けられるようになる
  • flash
    • なんらかのアクションが成功または失敗したときなどに、画面の上部にぴょこっとメッセージを表示させたいときに使う
  • rails db:migrate:reset
    • DBを初期化してマイグレーションをやり直す
  • assert_difference
    • テストの前後で何らかのカウントが変わっているか(orいないか)を確認するときに使う(という認識)
    • たとえばユーザーの追加によってユーザー数が増えていること、削除によって減っていること...etc
  • content_tag
    • 以下のコードは等価
      • <div class="alert alert-<%= message_type %>"><%= message %></div>
      • <%= content_tag(:div, message, class: "alert alert-#{message_type}") %>
  • SSLを有効化
    • config/environments/production.rbconfig.force_ssl = trueを追記する
    • または、上記のコードがコメントアウトされていれば、アンコメントする
    • この状態のとき、httpでアクセスしても、307 Internal Redirecthttpsのほうへリダイレクトされる

第8章

ユーザーのログイン/ログアウト機能を作る

  • セッションのコントローラーを作成する
    • rails generate controller Sessions new
    • なぜコントローラーを作らないといけないんだろう?という疑問がある
      • なんとなく、Railsで用意されたヘルパーなどが存在していると思っていた
    • と思ったら、log_inメソッドの節で、別途sessionというメソッドが出てきた
  • rails routes
    • いま現在のルーティングの状態を表示できる
  • セッションの種類
    • 一時セッション: sessionメソッドで作成
    • 永続的(または持続的)セッション: cookiesメソッドで作成
  • find()find_by()で対象が見つからなかった場合の返り値の違い
    • find: ActiveRecord::RecordNotFound
    • find_by: nil
    • 使い分けの一例として、ログインしているかどうかの判別をする際にはfind_byを使ったほうが良さそう
      • 例外処理を実装した上でfindを使うことも出来なくはないだろうけど、そこまでする必要はない、という感じ?
  • Ruby的なコードの短縮形(の1つ)
    • @current_user = @current_user || User.find_by(id: session[:user_id])
    • 上記のコードは下記のように書ける
    • @current_user ||= User.find_by(id: session[:user_id])
    • x = x + 1x += 1と書けるのと同じようなものとして考えればわかりやすい

疑問点

  • integrationテストコードのファイル名は、やりたいテストの内容(目的)にしている(?)
    • rails generate integration_test users_loginというコマンドが出てきた
    • app/views以下のディレクトリおよびファイル名に沿った名前のほうが、プロダクションコードと対応していることが自明になりわかりやすいんじゃないかと思えるのだけど、どうなんだろう?
    • controllerに対しては、そのままの名前になっている。e.g.) sessions_controller_test.rb
    • 今思えば、ここまででユニットテストと呼ばれているテストがないことに気づいた(ユニットテストの定義に依るけど)
      • 強いて言えばApplicationHelperfull_titleに対してぐらい?

第9章

ログイン機能をさらに発展させていく。
ブラウザを閉じてもログインしたままにするかどうかをユーザーが任意で設定できるようにする。

  • セッションハイジャックのうち、有名な4つの方法とその対策
    • 管理の甘いネットワークを通過するネットワークパケットからパケットスニッファという特殊なソフトウェアで直接cookieを取り出す
      • SSLにより、ネットワークデータを暗号化して保護する
    • データベースから記憶トークンを取り出す
      • 記憶トークンをハッシュ化して保存する
    • クロスサイトスクリプティング (XSS) を使う
      • railsの場合は自動的に対策(エスケープ)されている。
    • ユーザーがログインしているパソコンやスマホを直接操作してアクセスを奪い取る
      • 物理的な攻撃はシステム側では防衛できない。
      • ただし、被害を最小限に留める方法はある、とのこと
        • ユーザーが (別端末などで) ログアウトしたときにトークンを必ず変更する
        • セキュリティ上重要になる可能性のある情報を表示するときはデジタル署名 (digital signature) を行う
  • 「Ruby的に正しい」クラスメソッドの定義方法
    • 方法は2つ
      • def self.method_name ... end
      • class << self ... endのブロック内でdef method_name ... end
    • ここまでの解説ではClassName.method_nameとしていた
    • 実際の業務ではプロジェクトごとにスタイルガイドは決まっているだろうから、基本はそれに従う形で良さそう
    • 新規で書く場合、自分であればclass << selfのパターンにすると思う
      • 理由は、ブロック内に書く必要があるから、より明示的でわかりやすくなりそうな気がするため
  • cookiesメソッド
    • 個別のcookiesは、1つのvalue (値) と、オプションのexpires (有効期限) からできている
    • 有効期限は省略可能
    • e.g.) cookies[:remember_token] = { value: remember_token, expires: 20.years.from_now.utc }
      • permanentという専用メソッドがあり、下記のコードは上記と同じ
      • cookies.permanent[:remember_token] = remember_token
  • 複数タブ、複数ブラウザによるバグ
    • 2つの目立たないバグでは上記のバグの対策を講じている
    • 実際、この手のバグは発生しやすいと思われる。何故なら、仕様を定義する際に見落とされやすいから
    • ありがちなバグとして認識しておければ良さそう
  • テスト内ではcookiesメソッドにシンボルを使えない
    • cookies[:remember_token]ではなくcookies['remember_token']とする
    • これはcookiesに対してだけなのか?
  • メソッド名と変数名が同じだと混乱してしまった
    • current_user@current_userみたいな。さらにローカル変数としてcurrent_userも出てくると・・・(このときは定義できない? 上書き?)
  • herokuデプロイ時にメンテナンスモードにする
    • heroku maintenance:on
    • 上記のあと、git push herokuheroku run rails db:migrateする。
    • おわったらheroku maintenance:off

第10章

ユーザーの更新、表示、削除を追加し、RESTアクションを完成させる。

  • WebブラウザはネイティブではPATCHリクエストを送信できない

  • <a target="_blank" rel="noopener">

    • rel="noopener"を追加して、セキュリティ対策を行う。参考URL
  • フレンドリーフォワーディング

    • 「ログインしていないユーザーが編集ページにアクセスしようとしていたなら、ユーザーがログインした後にはその編集ページにリダイレクトされるようにするのが望ましい動作」
  • redirect_to(:url)について

    • 「 実は、明示的にreturn文やメソッド内の最終行が呼び出されない限り、リダイレクトは発生しません。」
    • redirect_toは直ちに実行されるわけではない、という仕様
  • ページネーション

    • めちゃくちゃ簡単に実装できる・・・。
      • will_paginateというgemをインストール
      • <%= will_paginate %>を対象のviewに書く
      • 対象のviewに対応するcontrollerではpaginateメソッドで結果を返す
        • e.g.) @users = User.paginate(page: params[:page])
  • Railsは自動で色々してくれる、系が難しい(覚えることも増える)

    • パーシャルのリファクタリング
      • 「ここでは、renderをパーシャル (ファイル名の文字列) に対してではなく、Userクラスのuser変数に対して実行している点に注目してください12。この場合、Railsは自動的に_user.html.erbという名前のパーシャルを探しにいくので、このパーシャルを作成する必要があります」
      • 「Railsは@users をUserオブジェクトのリストであると推測します。さらに、ユーザーのコレクションを与えて呼び出すと、Railsは自動的にユーザーのコレクションを列挙し、それぞれのユーザーを_user.html.erbパーシャルで出力します」
    • 上記に限らないが、この手の振る舞いや記法を知っていないと、コードリーディングも難しくなりそうだなと感じた

第11章

アカウントの有効化。利用できる機能を制限。
メール送信機能を作成し、メールによる本人確認を行う。

  • 11.3.3 有効化のテストとリファクタリングの演習でのつまづき
    • 「ここまでの演習課題で変更したコードをテストするために、/users と /users/:id の両方に対する統合テストを作成してみましょう。」とあったので、test/integrationのindexとshow(ファイル追加)に対してテストを追加したが、これで正しかったのか?
      • 懸念としては、想定外のテストを書いた影響で今後のチュートリアルに影響出てしまわないかどうか
      • それはそれで、なぜ動かなくなってしまったのかを調査する練習にはなるけれど
    • 追加したテスト
      • index: activateされていないユーザーは一覧に表示されていないこと。assert_select ... count: 0で対応
      • show: activate済みおよびされていないユーザーごとに、リダイレクト先のURLが正しいかどうか。assert_responseまたはassert_redirected_toで対応
      • このために、fixtureにactivateされていないユーザーをひとり追加した
  • 本番環境でのメール送信
    • 今回は読むだけに留める。

第12章

パスワードの再設定。

  • rails generate controller ... --no-test-framework
    • --no-test-frameworkを渡すと、テストを自動生成しなくなる
  • 名前付きルートの*_path*_urlの違い
    • (byebug) puts new_password_reset_path
      • /password_resets/new
    • (byebug) puts new_password_reset_url
      • http://localhost:3000/password_resets/new

(若干だれてきた・・・)

第13章

マイクロポストの実装。

  • rails generate modelにおけるreferencesタイプ
  • belongs_tohas_manyで関連付けされている場合
    • Micropost.newではなく、user.microposts.buildのようにして呼び出すほうが"慣習的に正しい"らしい
  • デフォルトスコープ
    • 「データベースから要素を取得したときの、デフォルトの順序を指定するメソッド」
    • default_scope -> { order(created_at: :desc) }
      • ApplicationRecordで書いておくと、上記の場合は「作成日時が新しい順」で返却されるようになる
    • whereとかも使えるとのこと

第14章

最後の章。
ユーザーをフォローする、ユーザーにフォローされる。
フォローしているユーザーのフィード(マイクロポスト)の一覧を表示する。
全体的に複数行のコードが多くなっているので、箇条書きでの説明が少しむずかしい。この章に限ってはチュートリアルへのリンクを多く貼っていく感じにする。

機能拡張

サンプルアプリケーションの機能を拡張するにお題がいくつかある。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?