Try T.M Engineer Blog

多摩市で生息するエンジニアが「アウトプットする事は大事だ」と思って始めたブログ

【Ruby on Railsチュートリアル(第4版)】第5章 レイアウトを作成する(演習と解答)

はじめに

このブログ記事は、私(Kodak)自身のRailsの勉強記録として書いています。
Ruby on Railsチュートリアル』の演習と解答をもくもくと書いているだけの記事なので、興味の無い方は軽くスルーしてあげてください。他にも、まだ『Railsチュートリアル』の演習に挑戦していない方、これから『Railsチュートリアル』をやるぞ!という方は(演習の解答の)ネタバレになりますので、スルーしてください。

Ruby on Rails チュートリアルとは?

Ruby業界でRailsを使い始めるなら、まず最初に初めるRails入門サイトです。
電子書籍版は有料ですが、Webサイトにあるオンライン版は無料なので、誰でも読む(Railsにチャレンジする)事ができます。
railstutorial.jp
全部で14章あり、かなりのボリュームですが、これを読む事でRailsの基礎を学ぶ事ができ、ちょっとしたWebアプリケーションを作れるレベルにはなれる?とのこと。
各章毎に演習問題が複数あり、これを解いていく事でRailsへの理解を深めていく事ができる様になっています。

環境について

Ruby 2.5.0-dev
Rails 5.1.4
・バージョン管理ツール:GitHub(https://github.com/Kodak4400)

演習と解答

5.1.1 ナビゲーション<演習>
1. Webページと言ったらネコ画像、というぐらいにはWebにはネコ画像が溢れていますよね。リスト 5.4のコマンドを使って、図 5.3のネコ画像をダウンロードしてきましょう。
【解答】以下の通り。

$ curl cdn.learnenough.com/kitten.jpg

2. mvコマンドを使って、ダウンロードしたkitten.jpgファイルを適切なアセットディレクトリに移動してください (参考: 5.2.1)。
【解答】以下の通り。

$ mv kitten.jpg app/assets/images/kitten.jpg

3. image_tagを使って、kitten.jpg画像を表示してみてください (図 5.4)。
【解答】以下の通り。(実機操作は割愛)

<%= link_to image_tag("kitten.jpg", alt: "Kitten Image"),
  'http://rubyonrails.org/' %>

5.1.2 BootstrapとカスタムCSS<演習>
1. リスト 5.10を参考にして、5.1.1.1で使ったネコ画像をコメントアウトしてみてください。また、ブラウザのHTMLインスペクタ機能を使って、コメントアウトするとHTMLのソースからも消えていることを確認してみてください。
【解答】以下の通り。(実機操作は割愛)

<%#= link_to image_tag("kitten.jpg", alt: "Kitten Image"),
  'http://rubyonrails.org/' %>

2. リスト 5.11のコードをcustom.scssに追加し、すべての画像を非表示にしてみてください。うまくいけば、Railsのロゴ画像がHomeページから消えるはずです。先ほどと同様にインスペクタ機能を使って、今度はHTMLのソースコードは残ったままで、画像だけが表示されなくなっていることを確認してみてください。
【解答】以下の通り。

/* app/assets/stylesheets/custom.scss */
img {
  display: none;
}

5.1.3 パーシャル(partial)<演習> 1. Railsがデフォルトで生成するheadタグの部分を、リスト 5.18のようにrenderに置き換えてみてください。ヒント: 単純に削除してしまうと後でパーシャルを1から書き直す必要が出てくるので、削除する前にどこかに退避しておきましょう。
【解答】以下の通り。

### [app/views/layouts/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 %>
   <%= render 'layouts/footer' %>
  </div>
</body>
</html>

2. リスト 5.18のようなパーシャルはまだ作っていないので、現時点ではテストは redになっているはずです。実際にテストを実行して確認してみましょう。
【解答】以下の通り。

$rails t
Error:
StaticPagesControllerTest#test_should_get_help:
ActionView::Template::Error: Missing partial layouts/_rails_default with {:locale=>[:en], :formats=>[:html], :variants=>[], :handlers=>[:raw, :erb, :html, :builder, :ruby, :coffee, :jbuilder]}. 

3. layoutsディレクトリにheadタグ用のパーシャルを作成し、先ほど退避しておいたコードを書き込み、最後にテストが green に戻ることを確認しましょう。
【解答】以下の通り。

### [app/views/layouts/_rails_default.html.erb]
<%= csrf_meta_tags %>
<%= stylesheet_link_tag    'application', media: 'all',
  'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application',
  'data-turbolinks-track': 'reload' %>
$rails t
Finished in 4.254301s, 0.9402 runs/s, 1.8804 assertions/s.
4 runs, 8 assertions, 0 failures, 0 errors, 0 skips

5.2.2 素晴らしい構文を備えたスタイルシート<演習>
1. 5.2.2で提案したように、footerのCSSを手作業で変換してみましょう。具体的には、リスト 5.17の内容を1つずつ変換していき、リスト 5.20のようにしてみてください。
【解答】以下の通り。

/* app/assets/stylesheets/custom.scss */

/* footer */

footer {
  margin-top: 45px;
  padding-top: 5px;
  border-top: 1px solid $gray-medium-light;
  color: $gray-light;
  a {
    color: $gray;
    &:hover {
      color: $gray-darker;
    }
  }
  small {
    float: left;
  }
  ul {
    float: right;
    list-style: none;
    li {
      float: left;
      margin-left: 15px;
    }
  }
}

5.3.2 RailsのルートURL<演習>
1. 実は名前付きルートは、as:オプションを使って変更することができます。有名なFar Sideの漫画に倣って、Helpページの名前付きルートをhelfに変更してみてください (リスト 5.29)。
【解答】以下の通り。

### [config/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

2. 先ほどの変更により、テストが redになっていることを確認してください。リスト 5.28を参考にルーティングを更新して、テストを greenにして見てください。
【解答】以下の通り。

$ rails t
Error:
StaticPagesControllerTest#test_should_get_help:
NameError: undefined local variable or method `help_path' for #<StaticPagesControllerTest:0x00007fca69b1b698>
    test/controllers/static_pages_controller_test.rb:16:in `block in <class:StaticPagesControllerTest>'

3. エディタのUndo機能を使って、今回の演習で行った変更を元に戻して見てください。
【解答】以下の通り。

### [config/routes.rb]
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'
end

5.3.3 名前付きルート<演習>
1. リスト 5.29のようにhelfルーティングを作成し、レイアウトのリンクを更新してみてください。
【解答】以下の通り。

### [config/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
$ rails routes
 Prefix Verb URI Pattern        Controller#Action
   root GET  /                  static_pages#home
   helf GET  /help(.:format)    static_pages#help
  about GET  /about(.:format)   static_pages#about
contact GET  /contact(.:format) static_pages#contact
### [app/views/layouts/_header.html.erb]
<header class="navbar navbar-fixed-top navbar-inverse">
  <div class="container">
    <%= link_to "sample app", root_path, id: "logo" %>
    <nav>
      <ul class="nav navbar-nav navbar-right">
        <li><%= link_to "Home",   root_path %></li>
        <li><%= link_to "Help",   helf_path %></li>
        <li><%= link_to "Log in", '#' %></li>
      </ul>
    </nav>
  </div>
</header>

2. 前回の演習と同様に、エディタのUndo機能を使ってこの演習で行った変更を元に戻してみてください。
【解答】以下の通り。

### [config/routes.rb]
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'
end
$ rails routes
 Prefix Verb URI Pattern        Controller#Action
   root GET  /                  static_pages#home
   help GET  /help(.:format)    static_pages#help
  about GET  /about(.:format)   static_pages#about
contact GET  /contact(.:format) static_pages#contact
### [app/views/layouts/_header.html.erb]
<header class="navbar navbar-fixed-top navbar-inverse">
  <div class="container">
    <%= link_to "sample app", root_path, id: "logo" %>
    <nav>
      <ul class="nav navbar-nav navbar-right">
        <li><%= link_to "Home",   root_path %></li>
        <li><%= link_to "Help",   help_path %></li>
        <li><%= link_to "Log in", '#' %></li>
      </ul>
    </nav>
  </div>
</header>

5.3.4 リンクのテスト<演習>
1. footerパーシャルのabout_pathをcontact_pathに変更してみて、テストが正しくエラーを捕まえてくれるかどうか確認してみてください。
【解答】以下の通り。

### [app/views/layouts/_footer.html.erb]
<footer class="footer">
  <small>
    The <a href="http://railstutoril.jp/">Ruby on Rails Tutorial</a>
    by <a href="http://www.michaelhartl.com/">Michael Hartl</a>
  </small>
  <nav>
    <ul>
      <!-- <li><%#= link_to "About", about_path %></li> -->
      <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 t
Failure:
Expected at least 1 element matching "a[href="/about"]", found 0..
Expected 0 to be >= 1.

2. リスト 5.35で示すように、Applicationヘルパーで使っているfull_titleヘルパーを、test環境でも使えるようにすると便利です。こうしておくと、リスト 5.36のようなコードを使って、正しいタイトルをテストすることができます。ただし、これは完璧なテストではありません。例えばベースタイトルに「Ruby on Rails Tutoial」といった誤字があったとしても、このテストでは発見することができないでしょう。この問題を解決するためには、full_titleヘルパーに対するテストを書く必要があります。そこで、Applicationヘルパーをテストするファイルを作成し、リスト 5.37のFILL_INの部分を適切なコードに置き換えてみてください。ヒント: リスト 5.37ではassert_equal <期待される値>, <実際の値>といった形で使っていましたが、内部では==演算子で期待される値と実際の値を比較し、正しいかどうかのテストをしています。
【解答】以下の通り。

### [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

5.4.1 Usersコントローラ<演習>
1. 表 5.1を参考にしながらリスト 5.41を変更し、users_new_urlではなくsignup_pathを使えるようにしてみてください。
【解答】以下の通り。

### [test/controllers/users_controller_test.rb]
require 'test_helper'

class UsersControllerTest < ActionDispatch::IntegrationTest
  test "should get signup" do
    get signup_url
    assert_response :success
  end
end

2. 先ほどの変更を加えたことにより、テストが redになったことを確認してください。なお、この演習はテスト駆動開発 (コラム 3.3) で説明した red/green のリズムを作ることを目的としています。このテストは次の5.4.2で greenになるよう修正します。
【解答】以下の通り。

$ rails t
Error:
UsersControllerTest#test_should_get_signup:
NameError: undefined local variable or method `signup_url' for #<UsersControllerTest:0x00007ffe7dd35260>
    test/controllers/users_controller_test.rb:5:in `block in <class:UsersControllerTest>'
### [config/routes.rb]
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
Finished in 0.436722s, 16.0285 runs/s, 38.9264 assertions/s.
7 runs, 17 assertions, 0 failures, 0 errors, 0 skips

5.4.2 ユーザー登録用URL<演習>
1. もしまだ5.4.1.1の演習に取り掛かっていなければ、まずはリスト 5.41のように変更し、名前付きルートsignup_pathを使えるようにしてください。また、リスト 5.43で名前付きルートが使えるようになったので、現時点でテストが greenになっていることを確認してください。
前のテストで確認済み

2. 先ほどのテストが正しく動いていることを確認するため、signupルートの部分をコメントアウトし、テスト redになることを確認してください。確認できたら、コメントアウトを解除して greenの状態に戻してください。
【解答】以下の通り。

### [config/routes.rb]
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:
SiteLayoutTest#test_layout_links:
ActionView::Template::Error: undefined local variable or method `signup_path' for #<#<Class:0x00007fa752129e58>:0x00007fa751440ea8>
    app/views/static_pages/home.html.erb:10:in `_app_views_static_pages_home_html_erb__1372035262442598662_70178306610940'
    test/integration/site_layout_test.rb:6:in `block in <class:SiteLayoutTest>'
### [config/routes.rb]
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
Finished in 0.380467s, 18.3984 runs/s, 44.6819 assertions/s.
7 runs, 17 assertions, 0 failures, 0 errors, 0 skips

3. リスト 5.32の統合テストにsignupページにアクセスするコードを追加してください (getメソッドを使います)。コードを追加したら実際にテストを実行し、結果が正しいことを確認してください。ヒント: リスト 5.36で紹介したfull_titleヘルパーを使ってみてください。
【解答】以下の通り。

### [test/integration/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
    get contact_path
    assert_select "title", full_title("Contact")
    get signup_path
  end
end
$ rails test:integration
Finished in 0.453475s, 2.2052 runs/s, 13.2312 assertions/s.
1 runs, 6 assertions, 0 failures, 0 errors, 0 skips