Rails Tutorial 第4版 第5章 演習 解答例
この文書はRails Tutorial 第4版 第5章 レイアウトを作成するの演習に対する個人の解答例です。解答には誤りや不適切な表現が含まれていることがありますが、もし誤謬を見つけたらコメント頂けると嬉しいです。
それでは、やっていきましょう!
- 5.1 構造を追加する
- 5.2 Sassとアセットパイプライン
- 5.3 レイアウトのリンク
- 5.3.1 Contactページ
- 5.3.2 RailsのルートURL
- 5.3.3 名前付きルート
- 5.3.4 リンクのテスト
- 1. footerパーシャルのabout_pathをcontact_pathに変更してみて、テストが正しくエラーを捕まえてくれるかどうか確認してみてください。
- 2. リスト 5.35で示すように、Applicationヘルパーで使っているfull_titleヘルパーを、test環境でも使えるようにすると便利です。こうしておくと、リスト 5.36のようなコードを使って、正しいタイトルをテストすることができます。ただし、これは完璧なテストではありません。例えばベースタイトルに「Ruby on Rails Tutoial」といった誤字があったとしても、このテストでは発見することができないでしょう。この問題を解決するためには、full_titleヘルパーに対するテストを書く必要があります。そこで、Applicationヘルパーをテストするファイルを作成し、リスト 5.37のFILL_INの部分を適切なコードに置き換えてみてください。ヒント: リスト 5.37ではassert_equal <期待される値>, <実際の値>といった形で使っていましたが、内部では==演算子で期待される値と実際の値を比較し、正しいかどうかのテストをしています。
- 5.4 ユーザー登録: 最初のステップ
- 5.4.1 Usersコントローラ
- 5.4.2
- 1. もしまだ5.4.1.1の演習に取り掛かっていなければ、まずはリスト 5.41のように変更し、名前付きルートsignup_pathを使えるようにしてください。また、リスト 5.43で名前付きルートが使えるようになったので、現時点でテストが greenになっていることを確認してください。
- 2. 先ほどのテストが正しく動いていることを確認するため、signupルートの部分をコメントアウトし、テストredになることを確認してください。確認できたら、コメントアウトを解除してgreenの状態に戻してください。
- 3. リスト 5.32の統合テストにsignupページにアクセスするコードを追加してください (getメソッドを使います)。コードを追加したら実際にテストを実行し、結果が正しいことを確認してください。ヒント: リスト 5.36で紹介したfull_titleヘルパーを使ってみてください。
- 5.5 最後に
5.1 構造を追加する
5.1.1 ナビゲーション
1. Webページと言ったらネコ画像、というぐらいにはWebにはネコ画像が溢れていますよね。リスト 5.4のコマンドを使って、図 5.3のネコ画像をダウンロードしてきましょう。
リスト 5.4のコマンドを実行してネコ画像をダウンロードします。
$ curl -OL cdn.learnenough.com/kitten.jpg
2. mvコマンドを使って、ダウンロードしたkitten.jpgファイルを適切なアセットディレクトリに移動してください (参考: 5.2.1)。
$ mv kitten.jpg app/assets/images
3. image_tagを使って、kitten.jpg画像を表示してみてください (図 5.4)。
home.html.erbに次のコードを追加します。
<%= link_to image_tag("kitten.jpg", alt: "Kitten image"), '/assets/kitten-44dde42a7808d66c6f314c6587e2c0faabb03d8a3c2c47e4d07e09c08e3c1c6b.jpg' %>
5.1.2 BootstrapとカスタムCSS
1. リスト 5.10を参考にして、5.1.1.1で使ったネコ画像をコメントアウトしてみてください。また、ブラウザのHTMLインスペクタ機能を使って、コメントアウトするとHTMLのソースからも消えていることを確認してみてください。
home.html.erbのkitten.jpgに関する記述をコメントアウトします。
<%#= link_to image_tag("kitten.jpg", alt: "Kitten image"), '/assets/kitten-44dde42a7808d66c6f314c6587e2c0faabb03d8a3c2c47e4d07e09c08e3c1c6b.jpg' %>
ネコ画像が画面とHTMLソースの両方から消えていることを確認できました。
2. リスト 5.11のコードをcustom.scssに追加し、すべての画像を非表示にしてみてください。うまくいけば、Railsのロゴ画像がHomeページから消えるはずです。先ほどと同様にインスペクタ機能を使って、今度はHTMLのソースコードは残ったままで、画像だけが表示されなくなっていることを確認してみてください。
リスト 5.11: すべての画像を非表示にするCSS
img { display: none; }
HTMLのソースコードは残ったままで、Railsのロゴ画像だけが表示されなくなっています。
5.1.3 パーシャル (partial)
1. Railsがデフォルトで生成するheadタグの部分を、リスト 5.18のようにrenderに置き換えてみてください。ヒント: 単純に削除してしまうと後でパーシャルを1から書き直す必要が出てくるので、削除する前にどこかに退避しておきましょう。
リスト5.18のようにapplication.html.erbを編集します。
<!DOCTYPE html> <html> <head> <title><%= full_title(yield(:title)) %></title> <%= render 'layouts/rails_default'%> <%= render 'layouts/shim' %> </head> <body> <%= render 'layouts/header' %> <div class="container"> <%= yield %> </div> <%= render 'layouts/footer' %> </body> </html>
2. リスト 5.18のようなパーシャルはまだ作っていないので、現時点ではテストはredになっているはずです。実際にテストを実行して確認してみましょう。
$ rails t Running via Spring preloader in process 5524 ... /0: [] 0% Time: 00:00:00, ETERROR["test_should_get_about", #<Minitest::Reporters::Suite:0x00007fa0e6bbae20 @name="StaticPagesControllerTest">, 0.14099599997280166] test_should_get_about#StaticPagesControllerTest (0.14s) ActionView::Template::Error: ActionView::Template::Error: Missing partial layouts/_rails_default with {:locale=>[:en], :formats=>[:html], :variants=>[], :handlers=>[:raw, :erb, :html, :builder, :ruby, :coffee, :jbuilder]}. Searched in: * "/Users/Username/Documents/Programming/Ruby/Rails/workspace/Rails-Tutorial/sample_app/app/views" app/views/layouts/application.html.erb:6:in `_app_views_layouts_application_html_erb___2719366376557442984_70164521289380' test/controllers/static_pages_controller_test.rb:16:in `block in <class:StaticPagesControllerTest>' 3/0: [] 0% Time: 00:00:00, EERROR["test_should_get_home", #<Minitest::Reporters::Suite:0x00007fa0ebb6d648 @name="StaticPagesControllerTest">, 0.359660999965854] test_should_get_home#StaticPagesControllerTest (0.36s) ActionView::Template::Error: ActionView::Template::Error: Missing partial layouts/_rails_default with {:locale=>[:en], :formats=>[:html], :variants=>[], :handlers=>[:raw, :erb, :html, :builder, :ruby, :coffee, :jbuilder]}. Searched in: * "/Users/Username/Documents/Programming/Ruby/Rails/workspace/Rails-Tutorial/sample_app/app/views" app/views/layouts/application.html.erb:6:in `_app_views_layouts_application_html_erb___2719366376557442984_70164521289380' test/controllers/static_pages_controller_test.rb:5:in `block in <class:StaticPagesControllerTest>' 3/1: [] 33% Time: 00:00:00, ERROR["test_should_get_help", #<Minitest::Reporters::Suite:0x00007fa0ebb9ad00 @name="StaticPagesControllerTest">, 0.37114999996265396] test_should_get_help#StaticPagesControllerTest (0.37s) ActionView::Template::Error: ActionView::Template::Error: Missing partial layouts/_rails_default with {:locale=>[:en], :formats=>[:html], :variants=>[], :handlers=>[:raw, :erb, :html, :builder, :ruby, :coffee, :jbuilder]}. Searched in: * "/Users/Username/Documents/Programming/Ruby/Rails/workspace/Rails-Tutorial/sample_app/app/views" app/views/layouts/application.html.erb:6:in `_app_views_layouts_application_html_erb___2719366376557442984_70164521289380' test/controllers/static_pages_controller_test.rb:11:in `block in <class:StaticPagesControllerTest>' 3/2: [] 66% Time: 00:00:00, 3/3: [] 100% Time: 00:00:00, Time: 00:00:00 Finished in 0.37221s 3 tests, 0 assertions, 0 failures, 3 errors, 0 skips
redになることが確認できました。
3. layoutsディレクトリにheadタグ用のパーシャルを作成し、先ほど退避しておいたコードを書き込み、最後にテストが green に戻ることを確認しましょう。
退避しておいたコードを/app/views/layouts/_rails_default.html.erbとして保存します。
<%= csrf_meta_tags %> <%= csp_meta_tag %> <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
続いてテストを実行します。
$ rails t ... 3/3: [==============] 100% Time: 00:00:00, Time: 00:00:00 Finished in 0.64040s 3 tests, 4 assertions, 0 failures, 0 errors, 0 skips
greenに戻ることが確認できました。
5.2 Sassとアセットパイプライン
5.2.1 アセットパイプライン
演習なし。
5.2.2 素晴らしい構文を備えたスタイルシート
1. 5.2.2で提案したように、footerのCSSを手作業で変換してみましょう。具体的には、リスト 5.17の内容を1つずつ変換していき、リスト 5.20のようにしてみてください。
リスト5.20と同じなので省略。
5.3 レイアウトのリンク
5.3.1 Contactページ
演習なし。
5.3.2 RailsのルートURL
1. 実は名前付きルートは、as:オプションを使って変更することができます。有名なFar Sideの漫画に倣って、Helpページの名前付きルートをhelfに変更してみてください (リスト 5.29)。
リスト5.29と同じなので省略。
2. 先ほどの変更により、テストが redになっていることを確認してください。リスト 5.28を参考にルーティングを更新して、テストを greenにして見てください。
$ rails t ... ERROR["test_should_get_help", #<Minitest::Reporters::Suite:0x00007ff4edb0d710 @name="StaticPagesControllerTest">, 0.5129970000125468] test_should_get_help#StaticPagesControllerTest (0.51s) NameError: NameError: undefined local variable or method `help_path' for #<StaticPagesControllerTest:0x00007ff4edee9578> test/controllers/static_pages_controller_test.rb:12:in `block in <class:StaticPagesControllerTest>' 4/4: [==============] 100% Time: 00:00:00, Time: 00:00:00 Finished in 0.56098s 4 tests, 6 assertions, 0 failures, 1 errors, 0 skips
redになることが確認できました。次にhelpページに関するテストで扱うパスをhelf_path
に変更します。
... test "should get help" do get helf_path assert_response :success assert_select "title", "Help | Ruby on Rails Tutorial Sample App" end ...
テストを実行すると、
$ rails t ... 4/4: [==============] 100% Time: 00:00:00, Time: 00:00:00 Finished in 0.58349s 4 tests, 8 assertions, 0 failures, 0 errors, 0 skips
greenになることが確認できました。
3. エディタのUndo機能を使って、今回の演習で行った変更を元に戻して見てください。
戻します。
5.3.3 名前付きルート
1. リスト 5.29のようにhelfルーティングを作成し、レイアウトのリンクを更新してみてください。
routes.rb
Rails.application.routes.draw do root 'static_pages#home' get '/help', to: 'static_pages#help', as: 'helf' get '/about', to: 'static_pages#about' get '/contact', to: 'static_pages#contact' end
help.html.erb
<% provide(:title, "Helf") %> <h1>Helf</h1> <p> Get helf on the Ruby on Rails Tutorial at the <a href="https://railstutorial.jp/helf">Rails Tutorial helf page</a>. To get helf on this sample app, see the <a href="https://railstutorial.jp/#ebook"> <em>Ruby on Rails Tutorial</em> book</a>. </p>
2. 前回の演習と同様に、エディタのUndo機能を使ってこの演習で行った変更を元に戻してみてください。
戻します。
5.3.4 リンクのテスト
1. footerパーシャルのabout_pathをcontact_pathに変更してみて、テストが正しくエラーを捕まえてくれるかどうか確認してみてください。
_footer.html.erbのabout_pathをcontact_pathに変更します。
<footer class="footer"> <small> The <a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a> by <a href="http://www.michaelhartl.com/">Michael Hartl</a> </small> <nav> <ul> <li><%= link_to "About", contact_path %></li> <li><%= link_to "Contact", contact_path %></li> <li><a href="http://news.railstutorial.org/">News</a></li> </ul> </nav> </footer>
統合テストを実行します。
$ rails test:integration ... FAIL["test_layout_links", #<Minitest::Reporters::Suite:0x00007faf629ad120 @name="SiteLayoutTest">, 0.5238879999378696] test_layout_links#SiteLayoutTest (0.52s) Expected at least 1 element matching "a[href="/about"]", found 0.. Expected 0 to be >= 1. test/integration/site_layout_test.rb:9:in `block in <class:SiteLayoutTest>' 1/1: [==============] 100% Time: 00:00:00, Time: 00:00:00 Finished in 0.52591s 1 tests, 4 assertions, 1 failures, 0 errors, 0 skips
エラーを捕まえてくれました。
2. リスト 5.35で示すように、Applicationヘルパーで使っているfull_titleヘルパーを、test環境でも使えるようにすると便利です。こうしておくと、リスト 5.36のようなコードを使って、正しいタイトルをテストすることができます。ただし、これは完璧なテストではありません。例えばベースタイトルに「Ruby on Rails Tutoial」といった誤字があったとしても、このテストでは発見することができないでしょう。この問題を解決するためには、full_titleヘルパーに対するテストを書く必要があります。そこで、Applicationヘルパーをテストするファイルを作成し、リスト 5.37のFILL_INの部分を適切なコードに置き換えてみてください。ヒント: リスト 5.37ではassert_equal <期待される値>, <実際の値>といった形で使っていましたが、内部では==演算子で期待される値と実際の値を比較し、正しいかどうかのテストをしています。
リスト5.35、リスト5.36を記述した上で、次の内容を持つtest/helpers/application_helper_test.rbを作成します。
require 'test_helper' class ApplicationHelperTest < ActionView::TestCase test "full title helper" do assert_equal full_title, "Ruby on Rails Tutorial Sample App" assert_equal full_title("Help"), "Help | Ruby on Rails Tutorial Sample App" end end
統合テストを実施するとgreenになることを確認できます。
$ rails test:integration ... 1/1: [==============] 100% Time: 00:00:00, Time: 00:00:00 Finished in 0.53891s 1 tests, 6 assertions, 0 failures, 0 errors, 0 skips
5.4 ユーザー登録: 最初のステップ
5.4.1 Usersコントローラ
1. 表 5.1を参考にしながらリスト 5.41を変更し、users_new_urlではなくsignup_pathを使えるようにしてみてください
users_controller_test.rb
require 'test_helper' class UsersControllerTest < ActionDispatch::IntegrationTest test "should get new" do get signup_path assert_response :success end end
2. 先ほどの変更を加えたことにより、テストが redになったことを確認してください。なお、この演習はテスト駆動開発 (コラム 3.3) で説明した red/green のリズムを作ることを目的としています。このテストは次の5.4.2で greenになるよう修正します。
$ rails t ... ERROR["test_should_get_new", #<Minitest::Reporters::Suite:0x00007fa652327990 @name="UsersControllerTest">, 0.5956730000907555] test_should_get_new#UsersControllerTest (0.60s) NameError: NameError: undefined local variable or method `signup_path' for #<UsersControllerTest:0x00007fa65231c2e8> test/controllers/users_controller_test.rb:5:in `block in <class:UsersControllerTest>' 6/6: [==============] 100% Time: 00:00:00, Time: 00:00:00 Finished in 0.66642s 6 tests, 13 assertions, 0 failures, 1 errors, 0 skips
redになることが確認できました。
5.4.2
1. もしまだ5.4.1.1の演習に取り掛かっていなければ、まずはリスト 5.41のように変更し、名前付きルートsignup_pathを使えるようにしてください。また、リスト 5.43で名前付きルートが使えるようになったので、現時点でテストが greenになっていることを確認してください。
$ rails t ... 6/6: [==============] 100% Time: 00:00:00, Time: 00:00:00 Finished in 0.60987s 6 tests, 14 assertions, 0 failures, 0 errors, 0 skips
greenになっていることを確認しました。
2. 先ほどのテストが正しく動いていることを確認するため、signupルートの部分をコメントアウトし、テストredになることを確認してください。確認できたら、コメントアウトを解除してgreenの状態に戻してください。
routes.rbにおいてsignupルートの部分をコメントアウトします。
Rails.application.routes.draw do root 'static_pages#home' get '/help', to: 'static_pages#help' get '/about', to: 'static_pages#about' get '/contact', to: 'static_pages#contact' # get '/signup', to:'users#new' end
テストを実行します。
$ rails t ... ERROR["test_should_get_new", #<Minitest::Reporters::Suite:0x00007fd483473420 @name="UsersControllerTest">, 0.1346680000424385] test_should_get_new#UsersControllerTest (0.13s) NameError: NameError: undefined local variable or method `signup_path' for #<UsersControllerTest:0x00007fd483632a18> test/controllers/users_controller_test.rb:5:in `block in <class:UsersControllerTest>' ERROR["test_layout_links", #<Minitest::Reporters::Suite:0x00007fd4843036e8 @name="SiteLayoutTest">, 0.25056299997959286] test_layout_links#SiteLayoutTest (0.25s) ActionView::Template::Error: ActionView::Template::Error: undefined local variable or method `signup_path' for #<#<Class:0x00007fd483629cd8>:0x00007fd487089b30> app/views/static_pages/home.html.erb:10:in `_app_views_static_pages_home_html_erb___2879737312358609488_70275357037440' test/integration/site_layout_test.rb:5:in `block in <class:SiteLayoutTest>' ERROR["test_should_get_home", #<Minitest::Reporters::Suite:0x00007fd482c67e20 @name="StaticPagesControllerTest">, 0.5597980000311509] test_should_get_home#StaticPagesControllerTest (0.56s) ActionView::Template::Error: ActionView::Template::Error: undefined local variable or method `signup_path' for #<#<Class:0x00007fd483629cd8>:0x00007fd489029288> app/views/static_pages/home.html.erb:10:in `_app_views_static_pages_home_html_erb___2879737312358609488_70275357037440' test/controllers/static_pages_controller_test.rb:6:in `block in <class:StaticPagesControllerTest>' 6/6: [==============] 100% Time: 00:00:00, Time: 00:00:00 Finished in 0.57788s 6 tests, 6 assertions, 0 failures, 3 errors, 0 skips
redになることが確認できました。コメントアウトを解除してgreenの状態に戻しておきます。
3. リスト 5.32の統合テストにsignupページにアクセスするコードを追加してください (getメソッドを使います)。コードを追加したら実際にテストを実行し、結果が正しいことを確認してください。ヒント: リスト 5.36で紹介したfull_titleヘルパーを使ってみてください。
テストでfull_titleヘルパーを使えるようにするため/test/test_helper.rbにApplicationHelperを含める記述をしておきます。
ENV['RAILS_ENV'] ||= 'test' require_relative '../config/environment' require 'rails/test_help' require "minitest/reporters" Minitest::Reporters.use! class ActiveSupport::TestCase # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. fixtures :all # Add more helper methods to be used by all tests here... include ApplicationHelper end
test/integration/site_layout_test.rbにおいてsignup_pathにアクセスできること、さらにそのページに"Sign up"という文字が含まれていることをテストします。
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 get signup_path assert_select "title", full_title("Sign up") end end
テストを実行します。
$ rails t ... 6/6: [==============] 100% Time: 00:00:00, Time: 00:00:00 Finished in 0.60866s 6 tests, 15 assertions, 0 failures, 0 errors, 0 skips
greenになることが確認できました。
5.5 最後に
5.5.1 本章のまとめ
演習なし。
完