Ruby on Rails Tutorial最新版の演習と解答です。
GEEKLYのIT・WEB・ソーシャルゲーム業界への転職支援サービス
- 目的
- 演習問題と解答
- あわせて読みたい記事
全くの未経験からIT/Webエンジニアに転職した私(30代)のロードマップ - 新米パパの育児留学
目的
Ruby on Rails チュートリアル 5.0(第4版)を学習中です。
学習を進める中で演習問題の解答がなかった(*1,2)ので、自分なりにまとめていくこととしました。
アウトプットし、自分の理解を深めることを目的としています。 もし、記載内容に誤りがあった場合はコメントいただけると幸いです。
(*1)著者による有償版の解答はあるようです。正式な解答をご希望の方はこちらを参照ください。
Learn Web Development with Rails: Michael Hartl's Ruby on Rails Tutorial | Softcover.io
(*2)旧版に関する解答はありましたが、最新版 5.0(第4版)に関しては検索しても出てきませんでした。
演習問題と解答
演習7.1.1
演習7.1.1.1
<問題> ブラウザから /about にアクセスし、デバッグ情報が表示されていることを確認してください。このページを表示するとき、どのコントローラとアクションが使われていたでしょうか? paramsの内容から確認してみましょう。
<解答> 以下のデバッグ情報が表示されます。 "static_pages"コントローラーの"about"アクションが実行された。
--- !ruby/object:ActionController::Parameters parameters: !ruby/hash:ActiveSupport::HashWithIndifferentAccess controller: static_pages action: about permitted: false
演習7.1.1.2
<問題> Railsコンソールを開き、データベースから最初のユーザー情報を取得し、変数userに格納してください。その後、puts user.attributes.to_yamlを実行すると何が表示されますか? ここで表示された結果と、yメソッドを使ったy user.attributesの実行結果を比較してみましょう。
<解答> 実行結果は以下の通り。 "puts user.attributes.to_yaml"の実行結果と"y user.attributes"の実行結果は同じです。
>> user=User.first User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] => #<User id: 1, name: "Yamada", email: "mhartl@example.com", created_at: "2017-02-05 02:01:37", updated_at: "2017-02-05 02:36:54", password_digest: "$2a$10$E.gAC.d0Ch5dvmrM6vnNhu6gIpNtwFD8JhHmKPyc/V3...">
>> puts user.attributes.to_yaml --- id: 1 name: Yamada email: mhartl@example.com created_at: !ruby/object:ActiveSupport::TimeWithZone utc: &1 2017-02-05 02:01:37.355601000 Z zone: &2 !ruby/object:ActiveSupport::TimeZone name: Etc/UTC time: *1 updated_at: !ruby/object:ActiveSupport::TimeWithZone utc: &3 2017-02-05 02:36:54.834356000 Z zone: *2 time: *3 password_digest: "$2a$10$E.gAC.d0Ch5dvmrM6vnNhu6gIpNtwFD8JhHmKPyc/V3u1di9B67dO" => nil
>> y user.attributes --- id: 1 name: Yamada email: mhartl@example.com created_at: !ruby/object:ActiveSupport::TimeWithZone utc: &1 2017-02-05 02:01:37.355601000 Z zone: &2 !ruby/object:ActiveSupport::TimeZone name: Etc/UTC time: *1 updated_at: !ruby/object:ActiveSupport::TimeWithZone utc: &3 2017-02-05 02:36:54.834356000 Z zone: *2 time: *3 password_digest: "$2a$10$E.gAC.d0Ch5dvmrM6vnNhu6gIpNtwFD8JhHmKPyc/V3u1di9B67dO" => nil
演習7.1.2
演習7.1.2.1
<問題> 埋め込みRubyを使って、マジックカラム (created_atとupdated_at) の値をshowページに表示してみましょう (リスト 7.4)。
<解答>
<%= @user.name %>, <%= @user.email %>, <%= @user.created_at %>, <%= @user.updated_at %>
演習7.1.2.2
<問題> 埋め込みRubyを使って、Time.nowの結果をshowページに表示してみましょう。ページを更新すると、その結果はどう変わっていますか? 確認してみてください。
<解答>
<%= @user.name %>, <%= @user.email %>, <%= @user.created_at %>, <%= @user.updated_at %>, <%= Time.now %>
更新すると表示時間も更新されます。
演習7.1.3
演習7.1.3.1
<問題> showアクションの中にdebuggerを差し込み (リスト 7.6)、ブラウザから /users/1 にアクセスしてみましょう。その後コンソールに移り、putsメソッドを使ってparamsハッシュの中身をYAML形式で表示してみましょう。ヒント: 7.1.1.1の演習を参考にしてください。その演習ではdebugメソッドで表示したデバッグ情報を、どのようにしてYAML形式で表示していたでしょうか?
<解答>
(byebug) a=params <ActionController::Parameters {"controller"=>"users", "action"=>"show", "id"=>"1"} permitted: false> (byebug) puts a.to_yaml --- !ruby/object:ActionController::Parameters parameters: !ruby/hash:ActiveSupport::HashWithIndifferentAccess controller: users action: show id: '1' permitted: false nil
7.1.1.1の演習に関して、 debugヘルパーは、YAMLフォーマットを使ったオブジェクトを描画した\<pre>タグを返します。
演習7.1.3.2
<問題> newアクションの中にdebuggerを差し込み、/users/new にアクセスしてみましょう。@userの内容はどのようになっているでしょうか? 確認してみてください。
<解答>
(byebug) @user nil
演習7.1.4
Gravatarを使用してなぜか画像が表示されないという方は、演習5.1.2.2でCSSで画像を非表示にしていることが原因の可能性があります。下記コードを削除してみてください。
img { display: none; }
演習7.1.4.1
<問題> (任意) Gravatar上にアカウントを作成し、あなたのメールアドレスと適当な画像を紐付けてみてください。メールアドレスをMD5ハッシュ化して、紐付けた画像がちゃんと表示されるかどうか試してみましょう。
<解答> 省略
演習7.1.4.2
<問題> 7.1.4で定義したgravatar_forヘルパーをリスト 7.12のように変更して、sizeをオプション引数として受け取れるようにしてみましょう。うまく変更できると、gravatar_for user, size: 50といった呼び出し方ができるようになります。重要: この改善したヘルパーは10.3.1で実際に使います。忘れずに実装しておきましょう。
<解答> リスト7.12参照。
演習7.1.4.3
<問題> オプション引数は今でもRubyコミュニティで一般的に使われていますが、Ruby 2.0から導入された新機能「キーワード引数 (Keyword Arguments)」でも実現することができます。先ほど変更したリスト 7.12を、リスト 7.13のように置き換えてもうまく動くことを確認してみましょう。この2つの実装方法はどういった違いがあるのでしょうか? 考えてみてください。
<解答> 動作確認のみなので省略。 オプション引数よりもキーワード引数の方が簡潔に実装できる。
演習7.2.1
演習7.2.1.1
<問題> 試しに、リスト 7.15にある:nameを:nomeに置き換えてみましょう。どんなエラーメッセージが表示されるようになりますか?
<解答> undefined method `nome'
演習7.2.1.2
<問題> 試しに、ブロックの変数fをすべてfoobarに置き換えてみて、結果が変わらないことを確認してみてください。確かに結果は変わりませんが、変数名をfoobarとするのはあまり良い変更ではなさそうですね。その理由について考えてみてください。
<解答> 変数fは “form” のfを使用している。 "f.label"等がHTMLフォーム要素に対応することがわかるようにする。 "foobar"はメタ構文変数(意味を持たない名前)として認識されるため適さない。
演習7.2.2
演習7.2.2.1
<問題> Learn Enough HTML to Be DangerousではHTMLをすべて手動で書き起こしていますが、なぜformタグを使わなかったのでしょうか? 理由を考えてみてください。
<解答> formタグは、入力・送信フォームを作成するために使用される。 本文中では、入力・送信が必要ないので使用していない。 (ログインページにはformタグが使用されています。)
演習7.3.2
演習7.3.2.1
<問題> /users/new?admin=1 にアクセスし、paramsの中にadmin属性が含まれていることをデバッグ情報から確認してみましょう。
<解答>
--- !ruby/object:ActionController::Parameters parameters: !ruby/hash:ActiveSupport::HashWithIndifferentAccess admin: '1' controller: users action: new permitted: false
演習7.3.3
演習7.3.3.1
<問題> 最小文字数を5に変更すると、エラーメッセージも自動的に更新されることを確かめてみましょう。
<解答>
[user.rb] class User < ApplicationRecord (中略) validates :password, presence: true, length: { minimum: 5 } end
演習7.3.3.2
<問題> 未送信のユーザー登録フォーム (図 7.12) のURLと、送信済みのユーザー登録フォーム (図 7.18) のURLを比べてみましょう。なぜURLは違っているのでしょうか? 考えてみてください。
<解答> 未送信のユーザー登録フォーム (図 7.12) のURLは以下。 "・・・/signup"
ルーティングの以下が実行されている。
[routes.rb] (前略) get '/signup', to: 'users#new' (後略)
送信済みのユーザー登録フォーム (図 7.18) のURLは以下。 "・・・/users"
ルーティングの以下が実行されている。
[routes.rb]
(前略)
resources :users
(後略)
これは、表7.1のルートを指し、その中の"ユーザーを作成するアクション"が実行されている。
post '/users' 'users#create'
演習7.3.4
演習7.3.4.1
<問題> リスト 7.20で実装したエラーメッセージに対するテストを書いてみてください。どのくらい細かくテストするかはお任せします。リスト 7.25にテンプレートを用意しておいたので、参考にしてください。
<解答>
[users_signup_test.rb] require 'test_helper' class UsersSignupTest < ActionDispatch::IntegrationTest test "invalid signup information" do get signup_path (中略) assert_template 'users/new' assert_select 'div#error_explanation' assert_select 'div.alert' end end
演習7.3.4.2
<問題> 未送信のユーザー登録フォームと送信直後のURLは、それぞれ /signup と /users になり、URLが異なっています。これは、リスト 5.43で追加した名前付きルートと、デフォルトのRESTfulなルーティング (リスト 7.3) を設定したことによって生じた差異です。リスト 7.26とリスト 7.27の内容を追加し、この問題を解決してみてください。うまくいけば、いずれのURLも /signup となるはずです。あれ、でもテストは greenのままになっていますね...、なぜでしょうか? (考えてみてください)
<解答> リスト7.26とリスト7.27の内容を追加すると、いずれのURLも /signup となる。 GREENのままとなっている理由は演習7.3.4.3及び演習7.3.4.4参照。
演習7.3.4.3
<問題> リスト 7.25のpost部分を変更して、上の演習課題で作られた新しいURLに合わせてみましょう。また、テストが依然として greenのままになっている点も確認してください。
<解答>
[users_signup_test.rb] require 'test_helper' class UsersSignupTest < ActionDispatch::IntegrationTest test "invalid signup information" do get signup_path assert_no_difference 'User.count' do post signup_path, params: { user: { name: "", (後略)
テストはGREEN。
演習7.3.4.4
<問題> リスト 7.27のフォームを以前の状態 (リスト 7.20) に戻してみて、テストがやはり greenになっていることを確認してください。これは問題です! なぜなら、現在postが送信されているURLは正しくないのですから。assert_selectを使ったテストをリスト 7.25に追加し、このバグを検知できるようにしてみましょう (テストを追加して redになれば成功です)。その後、変更後のフォーム (リスト 7.27) に戻してみて、テストが green になることを確認してみましょう。ヒント: フォームから送信してテストするのではなく、’form[action="/signup"]’という部分が存在するかどうかに着目してテストしてみましょう。
<解答> リスト 7.27のフォームを以前の状態 (リスト 7.20) に戻してもテストはGREEN。
テストを以下の通りとするとテストはRED。
[users_signup_test.rb] require 'test_helper' class UsersSignupTest < ActionDispatch::IntegrationTest test "invalid signup information" do (中略) assert_select 'form[action="/signup"]' end end
リスト7.27に戻してテストするとGREEN。
演習7.4.1
演習7.4.1.1
<問題> 有効な情報を送信し、ユーザーが実際に作成されたことを、Railsコンソールを使って確認してみましょう。
<解答> 以下のユーザー情報を送信。
二人目のユーザー情報が作成されました。(表示のさせ方はいろいろあります。)
>> User.second User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? OFFSET ? [["LIMIT", 1], ["OFFSET", 1]] => #<User id: 2, name: "Yamada", email: "yamada@mail.com", created_at: "2017-02-09 06:56:06", updated_at: "2017-02-09 06:56:06", password_digest: "$2a$10$0JljS52iRoEmJQ5uhNbnVeDc9/cAuYWtKrMmour10cG...">
演習7.4.1.2
<問題> リスト 7.28を更新し、redirect_to user_url(@user)とredirect_to @userが同じ結果になることを確認してみましょう。
<解答> 動作確認のみなので省略。
演習7.4.2
演習7.4.2.1
<問題> コンソールに移り、文字列内の式展開 (4.2.2) でシンボルを呼び出してみましょう。例えば"#{:success}"といったコードを実行すると、どんな値が返ってきますか? 確認してみてください。
<解答>
>> "#{:success}" => "success"
演習7.4.2.2
<問題> 先ほどの演習で試した結果を参考に、リスト 7.30のflashはどのような結果になるか考えてみてください。
<解答>
>> flash = { success: "It worked!", danger: "It failed." } => {:success=>"It worked!", :danger=>"It failed."} >> "#{flash[:success]}" => "It worked!" >> "#{flash[:danger]}" => "It failed."
演習7.4.3
演習7.4.3.1
<問題> Railsコンソールを使って、新しいユーザーが本当に作成されたのかもう一度チェックしてみましょう。結果は、リスト 7.32のようになるはずです。
<解答> 動作確認のみなので省略。
演習7.4.3.2
<問題> 自分のメールアドレスでユーザー登録を試してみましょう。既にGravatarに登録している場合、適切な画像が表示されているか確認してみてください。
<解答> 動作確認のみなので省略。
演習7.4.4
演習7.4.4.1
<問題> 7.4.2で実装したflashに対するテストを書いてみてください。どのくらい細かくテストするかはお任せします。リスト 7.34に最小限のテンプレートを用意しておいたので、参考にしてください (FILL_INの部分を適切なコードに置き換えると完成します)。ちなみに、テキストに対するテストは壊れやすいです。文量の少ないflashのキーであっても、それは同じです。筆者の場合、flashが空でないかをテストするだけの場合が多いです。
<解答>
[users_signup_test.rb] require 'test_helper' class UsersSignupTest < ActionDispatch::IntegrationTest (中略) test "valid signup information" do (中略) assert_not flash.empty? end end
演習7.4.4.2
<問題> 本文中でも指摘しましたが、flash用のHTML (リスト 7.31) は読みにくいです。より読みやすくしたリスト 7.35のコードに変更してみましょう。変更が終わったらテストスイートを実行し、正常に動作することを確認してください。なお、このコードでは、Railsのcontent_tagというヘルパーを使っています。
<解答> 動作確認のみなので省略。
演習7.4.4.3
<問題> リスト 7.28のリダイレクトの行をコメントアウトすると、テストが失敗することを確認してみましょう。
<解答> 動作確認のみなので省略。
演習7.4.4.4
<問題> リスト 7.28で、@user.saveの部分をfalseに置き換えたとしましょう (バグを埋め込んでしまったと仮定してください)。このとき、assert_differenceのテストではどのようにしてこのバグを検知するでしょうか? テストコードを追って考えてみてください。
<解答> 以下のテストでは、assert_differenceブロック内の処理を実行する直前と、実行した直後のUser.countの値を比較し、1増えた際にtrueとなる。@user.saveが実行されないことによって、User.countは増えないためerrorとなる。
assert_difference 'User.count', 1 do post users_path, ... end
演習7.5.3
演習7.5.3.1
<問題> ブラウザから本番環境 (Heroku) にアクセスし、SSLの鍵マークがかかっているか、URLがhttpsになっているかどうかを確認してみましょう。
<解答> 動作確認のみなので省略。
演習7.5.3.1
<問題> 本番環境でユーザーを作成してみましょう。Gravatarの画像は正しく表示されているでしょうか?
<解答> 動作確認のみなので省略。
GEEKLYのIT・WEB・ソーシャルゲーム業界への転職支援サービス
あわせて読みたい記事
mochikichi.hatenablog.com mochikichi.hatenablog.com mochikichi.hatenablog.com mochikichi.hatenablog.com