新米パパの育児留学

読者です 読者をやめる 読者になる 読者になる

新米パパの育児留学

『育児留学』とは、育児を通して異なる視点を得たり新しいことに挑戦して自己成長に繋げること。育児奮闘中の新米パパが育児を通して得た気づきや感じたこと、育休中に習得したプログラミングに関する話題を発信していきます。

【第10章】Ruby on Rails チュートリアル 5.0(第4版)演習と解答まとめ

プログラミング プログラミング-Rails_チュートリアル

Ruby on Rails Tutorial最新版の演習と解答です。

f:id:mochikichi321:20170206212242p:plain

目的

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版)に関しては検索しても出てきませんでした。

プログラミングを学習し始めたきっかけについてはこちら

mochikichi.hatenablog.com

演習問題と解答

演習10.1.1

演習10.1.1.1

<問題> 先ほど触れたように、target=“_blank"で新しいページを開くときには、セキュリティ上の小さな問題があります。それは、リンク先のサイトがHTMLドキュメントのwindowオブジェクトを扱えてしまう、という点です。具体的には、フィッシング (Phising) サイトのような、悪意のあるコンテンツを導入させられてしまう可能性があります。Gravatarのような著名なサイトではこのような事態は起こらないと思いますが、念のため、このセキュリティ上のリスクも排除しておきましょう。対処方法は、リンク用のaタグのrel (relationship) 属性に、"noopener"と設定するだけです。早速、リスト 10.2で使ったGravatarの編集ページへのリンクにこの設定をしてみましょう。

<解答> 指示通り実行するのみなので省略。

演習10.1.1.2

<問題> リスト 10.5のパーシャルを使って、new.html.erbビュー (リスト 10.6) とedit.html.erbビュー (リスト 10.7) をリファクタリングしてみましょう (コードの重複を取り除いてみましょう)。ヒント: 3.4.3で使ったprovideメソッドを使うと、重複を取り除けます3。(関連するリスト 7.27の演習課題を既に解いている場合、この演習課題をうまく解けない可能性があります。うまく解けない場合は、既存のコードのどこに差異があるのか考えながらこの課題に取り組んでみましょう。例えば筆者であれば、リスト 10.5のテクニックをリスト 10.6に適用してみたり、リスト 10.7のテクニックをリスト 10.5に適用してみたりするでしょう。)

<解答> リスト10.5,10.6,10.7参照。

リスト 7.27の演習課題を既に解いている場合は以下。

[new.html.erb]の以下の",url: signup_path"が[edit.html.erb]と異なるため、provideで指定。

<%= form_for(@user,url: signup_path) do |f| %>

[_form.html.erb]

<%= form_for(@user, url: yield(:url)) do |f| %>   <---注目
  <%= render 'shared/error_messages', object: @user %>

  <%= f.label :name %>
  <%= f.text_field :name, class: 'form-control' %>

  <%= f.label :email %>
  <%= f.email_field :email, class: 'form-control' %>

  <%= f.label :password %>
  <%= f.password_field :password, class: 'form-control' %>

  <%= f.label :password_confirmation %>
  <%= f.password_field :password_confirmation, class: 'form-control' %>

  <%= f.submit yield(:button_text), class: "btn btn-primary" %>
<% end %>

[new.html.erb]

<% provide(:title, 'Sign up') %>
<% provide(:url, signup_path) %>   <---注目
<% provide(:button_text, 'Create my account') %>
<h1>Sign up</h1>
<div class="row">
  <div class="col-md-6 col-md-offset-3">
      <%= render 'form' %>
  </div>
</div>

[edit.html.erb]
<% provide(:title, "Edit user") %>
<% provide(:url, user_path) %>   <---注目
<% provide(:button_text, 'Save changes') %>
<h1>Update your profile</h1>
<div class="row">
  <div class="col-md-6 col-md-offset-3">
      <%= render 'form' %>
    <div class="gravatar_edit">
      <%= gravatar_for @user %>
      <a href="http://gravatar.com/emails" target="_blank" rel="noopener">change</a>
    </div>
  </div>
</div>

演習10.1.2

演習10.1.2.1

<問題> 編集フォームから有効でないユーザー名やメールアドレス、パスワードを使って送信した場合、編集に失敗することを確認してみましょう。

<解答> 動作確認のみなので省略。

演習10.1.3

演習10.1.3.1

<問題> リスト 10.9のテストに1行追加し、正しい数のエラーメッセージが表示されているかテストしてみてましょう。ヒント: 表 5.2で紹介したassert_selectを使ってalertクラスのdivタグを探しだし、「The form contains 4 errors.」というテキストを精査してみましょう。

<解答>

[users_edit_test.rb]

require 'test_helper'

class UsersEditTest < ActionDispatch::IntegrationTest

   (中略)

  test "unsuccessful edit" do
 
   (中略)

    assert_template 'users/edit'
    assert_select "div.alert", "The form contains 4 errors."   <---注目
  end
end

演習10.1.4

演習10.1.4.1

<問題> 実際に編集が成功するかどうか、有効な情報を送信して確かめてみましょう。

<解答> 動作確認のみなので省略。

演習10.1.4.2

<問題> もしGravatarと紐付いていない適当なメールアドレス (foobar@example.comなど) に変更した場合、プロフィール画像はどのように表示されるでしょうか? 実際に編集フォームからメールアドレスを変更して、確認してみてましょう。

<解答> 動作確認のみなので省略。

演習10.2.1

演習10.2.1.1

<問題> デフォルトのbeforeフィルターは、すべてのアクションに対して制限を加えます。今回のケースだと、ログインページやユーザー登録ページにも制限の範囲が及んでしまうはずです (結果としてテストも失敗するはずです)。リスト 10.15のonly:オプションをコメントアウトしてみて、テストスイートがそのエラーを検知できるかどうか (テストが失敗するかどうか) 確かめてみましょう。

<解答> 動作確認のみなので省略。

演習10.2.2

演習10.2.2.1

<問題> 何故editアクションとupdateアクションを両方とも保護する必要があるのでしょうか? 考えてみてください。

<解答> それぞれのパスが異なるため。 edit_user GET /users/:id/edit(.:format) users#edit user PATCH /users/:id(.:format) users#update

演習10.2.2.2

<問題> 上記のアクションのうち、どちらがブラウザで簡単にテストできるアクションでしょうか?

<解答> editアクション。 GETメソッドのeditの方がURLを打ち込んでアクセスするのみなので簡単。

演習10.2.3

演習10.2.3.1

<問題> フレンドリーフォワーディングで、最初に渡されたURLにのみ確実に転送されていることを確認するテストを作成してみましょう。続けて、ログインを行った後、転送先のURLはデフォルト (プロフィール画面) に戻る必要もありますので、これもテストで確認してみてください。ヒント: リスト10.29のsession[:forwarding_url]が正しい値かどうかを確認するテストを追加してみましょう。

<解答>

[users_edit_test.rb]

require 'test_helper'

class UsersEditTest < ActionDispatch::IntegrationTest

(中略)

  test "successful edit with friendly forwarding" do
    get edit_user_path(@user)
    assert session[:forwarding_url]   <---注目
    log_in_as(@user)
    assert_redirected_to edit_user_url(@user) || default   <---注目
    name  = "Foo Bar"
    email = "foo@bar.com"
    patch user_path(@user), params: { user: { name:  name,
                                              email: email,
                                              password:              "",
                                              password_confirmation: "" } }
    assert_not flash.empty?
    assert_redirected_to @user
    @user.reload
    assert_equal name,  @user.name
    assert_equal email, @user.email
  end
end

演習10.2.3.2

<問題> 7.1.3で紹介したdebuggerメソッドをSessionsコントローラのnewアクションに置いてみましょう。その後、ログアウトして /users/1/edit にアクセスしてみてください (デバッガーが途中で処理を止めるはずです)。ここでコンソールに移り、session[:forwarding_url]の値が正しいかどうか確認してみましょう。また、newアクションにアクセスしたときのrequest.get?の値も確認してみましょう (デバッガーを使っていると、ときどき予期せぬ箇所でターミナルが止まったり、おかしい挙動を見せたりします。熟練の開発者になった気になって (コラム 1.1)、落ち着いて対処してみましょう)。

<解答>

(byebug) session[:forwarding_url]
"https://rails-tutorial-******.c9users.io/users/1/edit"

正しくない。ルートが正。

(byebug) request.get?
true

演習10.3.1

演習10.3.1.1

<問題> レイアウトにあるすべてのリンクに対して統合テストを書いてみましょう。ログイン済みユーザーとそうでないユーザーのそれぞれに対して、正しい振る舞いを考えてください。ヒント: log_in_asヘルパーを使ってリスト 5.32にテストを追加してみましょう。

<解答>

[site_layout_test.rb]
require 'test_helper'

class SiteLayoutTest < ActionDispatch::IntegrationTest
  test "layout links" do
    get root_path
    assert_template 'static_pages/home'
    assert_select "a[href=?]", root_path, count: 2
    assert_select "a[href=?]", help_path
    assert_select "a[href=?]", about_path
    assert_select "a[href=?]", contact_path
    assert_select "a[href=?]", login_path
    get contact_path
    assert_select "title", full_title("Contact")
    get signup_path
    assert_select "title", full_title("Sign up")
  end
  
  def setup
    @user       = users(:michael)
  end
  
  test "layout links when logged in" do
    log_in_as(@user)
    get root_path
    assert_template 'static_pages/home'
    assert_select "a[href=?]", users_path
    assert_select "a[href=?]", user_path(@user)
    assert_select "a[href=?]", edit_user_path(@user)    
    assert_select "a[href=?]", logout_path
  end
end

演習10.3.2

演習10.3.2.1

<問題> 試しに他人の編集ページにアクセスしてみて、10.2.2で実装したようにリダイレクトされるかどうかを確かめてみましょう。

<解答> 動作確認のみなので省略。

演習10.3.3

演習10.3.3.1

<問題> Railsコンソールを開き、pageオプションにnilをセットして実行すると、1ページ目のユーザーが取得できることを確認してみましょう。

<解答>

演習10.3.3.2

<問題> 先ほどの演習課題で取得したpaginationオブジェクトは、何クラスでしょうか? また、User.allのクラスとどこが違うでしょうか? 比較してみてください。

<解答> class: User::ActiveRecord_Relation User.allと同じ。

演習10.3.4

演習10.3.4.1

<問題> 試しにリスト 10.45にあるページネーションのリンク (will_paginateの部分) を2つともコメントアウトしてみて、リスト 10.48のテストが redに変わるかどうか確かめてみましょう。

<解答> 動作確認のみなので省略。

演習10.3.4.2

<問題> 先ほどは2つともコメントアウトしましたが、1つだけコメントアウトした場合、テストが greenのままであることを確認してみましょう。will_paginateのリンクが2つとも存在していることをテストしたい場合は、どのようなテストを追加すれば良いでしょうか? ヒント: 表 5.2を参考にして、数をカウントするテストを追加してみましょう。

<解答>

[users_index_test.rb]

require 'test_helper'

class UsersIndexTest < ActionDispatch::IntegrationTest

  def setup
    @user = users(:michael)
  end

  test "index including pagination" do
    log_in_as(@user)
    get users_path
    assert_template 'users/index'
    assert_select 'div.pagination', count:2  <---注目
    User.paginate(page: 1).each do |user|
      assert_select 'a[href=?]', user_path(user), text: user.name
    end
  end
end

演習10.3.5

演習10.3.5.1

<問題> リスト 10.52にあるrenderの行をコメントアウトし、テストの結果が redに変わることを確認してみましょう。

<解答> 動作確認のみなので省略。

演習10.4.1

演習10.4.1.1

<問題> Web経由でadmin属性を変更できないことを確認してみましょう。具体的には、リスト 10.56に示したように、PATCHを直接ユーザーのURL (/users/:id) に送信するテストを作成してみてください。テストが正しい振る舞いをしているかどうか確信を得るために、まずはadminをuser_paramsメソッド内の許可されたパラメータ一覧に追加するところから始めてみましょう。最初のテストの結果は redになるはずです。

<解答>

[users_controller_test.rb]

require 'test_helper'

class UsersControllerTest < ActionDispatch::IntegrationTest

(中略)

  test "should not allow the admin attribute to be edited via the web" do
    log_in_as(@other_user)
    assert_not @other_user.admin?
    patch user_path(@other_user), params: {
                                    user: { password:              @other_user.password,
                                            password_confirmation: @other_user.password,
                                            admin: true } }
    assert_not @other_user.reload.admin?
  end
end

演習10.4.2

演習10.4.2.1

<問題> 管理者ユーザーとしてログインし、試しにサンプルユーザを2〜3人削除してみましょう。ユーザーを削除すると、Railsサーバーのログにはどのような情報が表示されるでしょうか?

<解答> 動作確認のみなので省略。

演習10.4.3

演習10.4.3.1

<問題> 試しにリスト 10.59にある管理者ユーザーのbeforeフィルターをコメントアウトしてみて、テストの結果が redに変わることを確認してみましょう

<解答> 動作確認のみなので省略。

関連記事

【第1章】Ruby on Rails チュートリアル 5.0(第4版)演習と解答まとめ - 新米パパの育児留学

【第2章】Ruby on Rails チュートリアル 5.0(第4版)演習と解答まとめ - 新米パパの育児留学

【第3章】Ruby on Rails チュートリアル 5.0(第4版)演習と解答まとめ - 新米パパの育児留学

【第4章】Ruby on Rails チュートリアル 5.0(第4版)演習と解答まとめ - 新米パパの育児留学

【第5章】Ruby on Rails チュートリアル 5.0(第4版)演習と解答まとめ - 新米パパの育児留学

【第6章】Ruby on Rails チュートリアル 5.0(第4版)演習と解答まとめ - 新米パパの育児留学

【第7章】Ruby on Rails チュートリアル 5.0(第4版)演習と解答まとめ - 新米パパの育児留学

【第8章】Ruby on Rails チュートリアル 5.0(第4版)演習と解答まとめ - 新米パパの育児留学

【第9章】Ruby on Rails チュートリアル 5.0(第4版)演習と解答まとめ - 新米パパの育児留学

【第11章】Ruby on Rails チュートリアル 5.0(第4版)演習と解答まとめ - 新米パパの育児留学

【第12章】Ruby on Rails チュートリアル 5.0(第4版)演習と解答まとめ - 新米パパの育児留学

【第13章】Ruby on Rails チュートリアル 5.0(第4版)演習と解答まとめ - 新米パパの育児留学

【第14章】Ruby on Rails チュートリアル 5.0(第4版)演習と解答まとめ - 新米パパの育児留学

Rails 検索機能拡張 (rails tutorial) - 新米パパの育児留学

プログラミング未経験から2か月でエンジニアへ転職したノウハウを知りたい方の相談も受け付けております。