演習問題解答Railsの6章ではモデルの扱い方を覚えていきます。1.Railsはdb/ディレクトリの中にあるschema.rbというファイルを使っています。追跡するために使われます。内容を比べてみてください。db:migrateコマンドを実行し、db/schema.rbの内容が元に戻ったことを確認してください。4.4.4で紹介したテクニックを使ってみてください)。2.同様にして、ApplicationRecordがActiveRecord::Baseを継承していることについて確認してみてください。1.user.nameとuser.emailが、どちらもStringクラスのインスタンスであることを確認してみてください。1.nameを使ってユーザーオブジェクトを検索してみてください。(古いRailsアプリケーションでは、古いタイプのfind_byをよく見かけることでしょう)。2.実用的な目的のため、User.allはまるで配列のように扱うことができますが、実際には配列ではありません。User.allで生成されるオブジェクトを調べ、ArrayクラスではなくUser::ActiveRecord_Relationクラスであることを確認してみてください。Rubyの性質として、そのクラスを詳しく知らなくてもなんとなくオブジェクトをどう扱えば良いかわかる、という性質があります。呼び、よく次のような格言で言い表されています「もしアヒルのような容姿で、アヒルのように鳴くのであれば、それはもうアヒルだろう」。2016の基調講演で、Ruby作者のMatzがダックタイピングについて説明していました。1.userオブジェクトへの代入を使ってname属性を使って更新し、saveで保存してみてください。2.今度はupdate_attributesを使って、email属性を更新および保存してみてください。3.同様にして、マジックカラムであるcreated_atも直接更新できることを確認してみてください。更新するときは「1.year.ago」を使うと便利です。これはRails流の時間指定の1つで、現在の時刻から1年前の時間を算出してくれます。あることを確認してみましょう。2.6.1.3で生成したuserオブジェクトも有効であるかどうか、確認してみましょう。ことを確認してください。エラーメッセージを確認してみましょう。2.u.errors.messagesを実行すると、ハッシュ形式でエラーが取得できることを確認してください。1.長すぎるnameとemail属性を持ったuserオブジェクトを生成し、有効でないことを確認してみましょう。確認してみてください。string:に転記してみてください。6.21のメールアドレスチェックする正規表現は、foo@bar..comのようにドットが連続した無効なメールアドレスを許容してしまいます。6.19の無効なメールアドレスリストに追加し、これによってテストが失敗することを確認してください。6.23で示した、少し複雑な正規表現を使ってこのテストがパスすることを確認してください。6.23の正規表現をRubularで使ってみてください。有効なメールアドレスのみがすべてマッチし、無効なメールアドレスはすべてマッチしないことを確認してみましょう。6.32に追加してみましょう。ちなみに追加するテストコードでは、データベースの値に合わせて更新するreloadメソッドと、値が一致しているかどうか確認するassert_equalメソッドを使っています。greenになることを確認してみましょう。2.テストスイートの実行結果を確認しながら、before_saveコールバックをemail.downcase!に書き換えてみましょう。1.この時点では、userオブジェクトに有効な名前とメールアドレスを与えても、valid?で失敗してしまうことを確認してみてください。エラーメッセージを確認してみてください。1.有効な名前とメールアドレスでも、パスワードが短すぎるとuserオブジェクトが有効にならないことを確認してみましょう。(userオブジェクトを消去して)、このセクションで作ったuserオブジェクトを検索してみてください。2.オブジェクトが検索できたら、名前を新しい文字列に置き換え、saveメソッドで更新してみてください。3.今度は6.1.5で紹介したテクニックを使って、userの名前を更新してみてください。データベースの移行1.Railsはdb/ディレクトリの中にあるschema.rbというファイルを使っています。追跡するために使われます。内容を比べてみてください。解答vimで2つのファイルを縦に並べてみました。ファイル名でできますよ。ctrl+Wを2回でカーソル移動できます。:closeで1画面閉じることができます。migrateファイルの中身が反映されていますねぇ。この問題はこれでOK。これはdb:migrateによってできたものなのかがわからないですね。次の問題にいきましょう。(少なくとも本チュートリアルにおいてはすべてのマイグレーションを元に戻すことができます)。(rollback)と呼び、Railsではdb:rollbackというコマンドで実現できます。3.1ではマイグレーションに関する他のテクニックもまとめているので、参考にしてみてください)。上のコマンドでは、データベースからusersテーブルを削除するためにdrop_tableコマンドを内部で呼び出しています。これがうまくいくのは、drop_tableとcreate_tableがそれぞれ対応していることをchangeメソッドが知っているからです。この対応関係を知っているため、ロールバック用の逆方向のマイグレーションを簡単に実現することができるのです。あるカラムを削除するような不可逆なマイグレーションの場合は、changeメソッドの代わりに、upとdownのメソッドを別々に定義する必要があります。マイグレーション」を参照してください。db:rollbackを実行したことによりdrop_tableコマンドが発行されたことを確認できます。=============================なぜかキャプチャ使っているのですが、schema.rbの中身も変わっていることがわかります。db:migrateコマンドを実行し、db/schema.rbの内容が元に戻ったことを確認してください。db:migrateコマンドを実行後、schema.rbを見てみます。=============================元に戻っていますね。4.4.4で紹介したテクニックを使ってみてください)。2.同様にして、ApplicationRecordがActiveRecord::Baseを継承していることについて確認してみてください。解答superclassを繋げていけば良かったですよね。ユーザーオブジェクトを作成する1.user.nameとuser.emailが、どちらもStringクラスのインスタンスであることを確認してみてください。適当にユーザー作ってからclassで炙り出していきます。ActiveSupport::TimeWithZoneuser.nameとuser.emailはStringクラスのインスタンスであることが分かります。(問1)created_atとupdated_atはActiveSupport::TimeWithZoneクラスのインスタンスであることが分かります。ユーザーオブジェクトを検索する1.nameを使ってユーザーオブジェクトを検索してみてください。(古いRailsアプリケーションでは、古いタイプのfind_byをよく見かけることでしょう)。解答サクッと”AAA”という名前のユーザーを検索してみました。find_by_カラム(検索ワード)が成り立ちますね。2.実用的な目的のため、User.allはまるで配列のように扱うことができますが、実際には配列ではありません。User.allで生成されるオブジェクトを調べ、ArrayクラスではなくUser::ActiveRecord_Relationクラスであることを確認してみてください。解答Users.allで生成されるオブジェクトをusersに格納して、クラスを確認しています。User::ActiveRecord_Relationが表示されればOKです。Rubyの性質として、そのクラスを詳しく知らなくてもなんとなくオブジェクトをどう扱えば良いかわかる、という性質があります。呼び、よく次のような格言で言い表されています「もしアヒルのような容姿で、アヒルのように鳴くのであれば、それはもうアヒルだろう」。2016の基調講演で、Ruby作者のMatzがダックタイピングについて説明していました。User.all.length=3となります。ユーザーオブジェクトを更新する1.userオブジェクトへの代入を使ってname属性を使って更新し、saveで保存してみてください。2.今度はupdate_attributesを使って、email属性を更新および保存してみてください。3.同様にして、マジックカラムであるcreated_atも直接更新できることを確認してみてください。更新するときは「1.year.ago」を使うと便利です。これはRails流の時間指定の1つで、現在の時刻から1年前の時間を算出してくれます。解答これもまとめていきましょう名前が”AAA”というユーザーを作っていたので、それを”ABC”に更新(問1)メールアドレスを”AAA@email.com”(問2)created_atを1年前に改ざんし、(問3)データが変わったことを確認していきましょう。06:13:20">問1はuser.nameを使って代入しています。問2,問3はuser.update_attribute()を使っています。最後データをfindで検索し、データがきちんと更新されていることを確認します。更新されていればOKです。あることを確認してみましょう。2.6.1.3で生成したuserオブジェクトも有効であるかどうか、確認してみましょう。「オブジェクトが有効である」とはuser.valid?の結果trueが返ってくることですね。true最初にUser.newした状態でもtrueが返ります。(問1)6.1.3で作成したユーザーと同じデータを入れ込み、もう一度user.valid?してももちろん有効です。(問2)バリデーションが全く設定されていないから今は何してもtrueになります。この次からバリデーションを扱います。ことを確認してください。エラーメッセージを確認してみましょう。解答valid?でfalseになることと、invalid?でtrueになること。blank"]}2.u.errors.messagesを実行すると、ハッシュ形式でエラーが取得できることを確認してください。emailに関するエラー情報だけを取得したい場合、どうやって取得すれば良いでしょうか?解答エラーがハッシュになっているのは問1で確認済みですね。ハッシュなので、キーを指定してあげれば目的の情報のみ取得できますね。以下のようになります。nameは50文字、emailは255文字なので、それを超える文字数で確かめてみれば良いです。確認してみてください。実際にエラーメッセージを表示してみればOKです。string:に転記してみてください。以下のようにすればOKです。6.21のメールアドレスチェックする正規表現は、foo@bar..comのようにドットが連続した無効なメールアドレスを許容してしまいます。6.19の無効なメールアドレスリストに追加し、これによってテストが失敗することを確認してください。6.23で示した、少し複雑な正規表現を使ってこのテストがパスすることを確認してください。解答foo@bar..comをリストに追加します。invalid"endendテストに失敗します。}endこれでテストが通ります。-(ここはなくても良い))(.半角英小文字)日本語で表そうとすると難しいですね...。6.23の正規表現をRubularで使ってみてください。有効なメールアドレスのみがすべてマッチし、無効なメールアドレスはすべてマッチしないことを確認してみましょう。6.32に追加してみましょう。ちなみに追加するテストコードでは、データベースの値に合わせて更新するreloadメソッドと、値が一致しているかどうか確認するassert_equalメソッドを使っています。greenになることを確認してみましょう。解答リスト6.33の通り作ってみます。@user.reload.emailendbefore_saveの行をコメントアウトしてからテストを実行します。skipsコメントを外すとGREENになります。2.テストスイートの実行結果を確認しながら、before_saveコールバックをemail.downcase!に書き換えてみましょう。破壊的メソッドを使うやつですね。動作は変わらないのでテストは通ります。ユーザーがセキュアなパスワードを持っている1.この時点では、userオブジェクトに有効な名前とメールアドレスを与えても、valid?で失敗してしまうことを確認してみてください。解答コンソールで確認していきましょう。適当にname=user1,email=user1@email.comとしました。falsefalseとなることがわかりました。エラーメッセージを確認してみてください。解答メッセージを確認しています。blank"]}パスワード空っぽだめよーって言われてますね。パスワードの最小文字数1.有効な名前とメールアドレスでも、パスワードが短すぎるとuserオブジェクトが有効にならないことを確認してみましょう。解答パスワードをaaaとして確認してみます。falsefalseになってますね。characters)"]}短すぎるぞーって言われました。(userオブジェクトを消去して)、このセクションで作ったuserオブジェクトを検索してみてください。解答このセクションと同じようにemailを検索しましょう。"$2a$10$BWPb8gf1HqTQkVKVWEmbfuEBNKHBk2R29RXtUrQhdL....">出てきましたね。2.オブジェクトが検索できたら、名前を新しい文字列に置き換え、saveメソッドで更新してみてください。適当にtestuserとでもしてみます。falsesaveに失敗しました。メッセージを表示してみましょう。characters)"]}パスワード空じゃないよ。