新米パパの育児留学

新米パパの育児留学

未経験からエンジニアへの転職体験談など "リアル" な情報を発信

IT/WEBエンジニアの転職(未経験/30代可あり)エージェント, サービス比較(p.s.侍の炎上を見て)
全くの未経験からIT/Webエンジニアに転職した私(30代)のロードマップ
30代未経験からIT / Webエンジニアへのリアルな転職体験談4 ”2度目の転職活動から入社へ”
本当に使えるものだけ!出産準備品・ベビー用品で実際に買ってよかったおすすめ10選
クロスバイク  LIG(リグ) MOVE 700Cの組み立て手順まとめ
Ruby初心者におすすめの学習方法「プロを目指す人のためのRuby入門」
Ruby on Rails チュートリアル 完全攻略 概要と演習解答総まとめ

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

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

GEEKLYのIT・WEB・ソーシャルゲーム業界への転職支援サービス

目的

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

演習問題と解答

演習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_equal session[:forwarding_url], edit_user_url(@user)  <---注目
    log_in_as(@user)
    assert_nil session[:forwarding_url]   <---注目
    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に変わることを確認してみましょう

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

GEEKLYのIT・WEB・ソーシャルゲーム業界への転職支援サービス

あわせて読みたい記事

mochikichi.hatenablog.com

mochikichi.hatenablog.com

mochikichi.hatenablog.com

おすすめの本

この本は、初心者にもわかりやすいとエンジニア界隈ではかなり著名な"伊藤 淳一"さんの本です。 私もいつもQiitaやブログでお世話になっており、わかりやすい解説に信頼を寄せていたので購入しました。

期待通り、いや、期待以上にわかりやすく丁寧な解説でしたのでかなり理解が深まったと思います。 私は参考書を読むのは退屈で頭に入ってこないタイプなのですが、わかりやすい解説に加えて実際に手を動かして理解を深められる例題が用意されているので楽しくサクサク進めていけます。

Rubyの基礎はProgateやドットインストールなどで理解した上で読まれると更に理解が深まると思います。 RailsスキルをレベルアップしていくためにもRubyの理解を深めることは非常に効果的です。 Railsチュートリアルを完了した方や、進めている途中の方で"Rubyのステップアップとしての次の一冊"をお探しの方におすすめです。