7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Rails Tutorialsで勉強のメモ その5(レイアウトの作成)

Posted at

やること

  • アプリケーションにBootstrapフレームワークを組み込み、カスタムスタイルを追加
  • 作成するページへのリンクをレイアウトに追加
  • パーシャルRailsのルーティング、Asset PipelineSass
  • RSpecテクニックを用いたリファクタリング

構造を追加する

  • Bootstrap
    • Twitter社のオープンソースのWebデザインフレームワーク
  • 画面の変更
    • リンク等のレイアウト変更
    • 各種ページへのリンクを追加
    • サインアップページへのリンクを追加
レイアウト変更のためのブランチを作成
$ git checkout -b filling-in-layout

ナビゲーションの追加

  • リンクとスタイルを追加
    • レイアウトファイルのapplication.html.erbにHTML構造を追加
    • <div>タグの追加、CSSクラスの追加、サイトナビゲーションの追加
app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title><%= full_title(yield(:title)) %></title>
    <%= stylesheet_link_tag    "application", media: "all", "data-turbolinks-track" => true %>
    <%= javascript_include_tag "application", "data-turbolinks-track" => true %>
    <%= csrf_meta_tags %>
    <!--[if lt IE 9]>
    <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
    <![endif]-->
  </head>
  <body>
    <header class="navbar navbar-fixed-top navbar-inverse">
      <div class="navbar-inner">
        <div class="container">
          <%= link_to "sample app", '#', id: "logo" %>
          <nav>
            <ul class="nav pull-right">
              <li><%= link_to "Home",    '#' %></li>
              <li><%= link_to "Help",    '#' %></li>
              <li><%= link_to "Sign in", '#' %></li>
            </ul>
          </nav>
        </div>
      </div>
    </header>
    <div class="container">
      <%= yield %>
    </div>
  </body>
</html>
  • <!--[if lt IE 9]> - <!--[endif]>
    • IEのバージョンが9未満の場合にのみ、囲まれている部分の行を実行
    • HTMLの機能で、条件付コメントと呼ばれる
  • <header>タグ
    • ページのトップに来るべき要素
      • 3つのCSSクラスをスペース区切りで与えている
    • Bootstrapにおいて特別な意味を持つ
  • <div>タグ
    • ドキュメントを別々のパーツに分けるのに利用
    • 設定しているclassはBootstrapにおいて特別な意味を持つ
      • navbar-innercontainer
  • <%= link_to "Home", '#' %>
    • リンクを生成するヘルパー
    • 第1引数:リンクテキスト
    • 第2引数:URL
    • 第3引数(オプション):オプションハッシュ。HTMLオプションを柔軟に追加できる
  • <nav>タグ
    • 内部がナビゲーションリンクであることを表す
    • <ul>タグのnavpull-rightクラスはBootstrapにおいて特別な意味を持つ
navタグの埋め込みRuby評価後のソース
<nav>
  <ul class="nav pull-right">
    <li><a href="#">Home</a></li>
    <li><a href="#">Help</a></li>
    <li><a href="#">Sign in</a></li>
  </ul>
</nav>
  • レイアウト修正後の画面
    5-1-1-1.png

  • Home画面の修正

app/views/static_pages/home.html.erb
<div class="center hero-unit">
  <h1>Welcome to the Sample App</h1>

  <h2>
    This is the home page for the
    <a href="http://railstutorial.jp/">Ruby on Rails Tutorial</a>
    sample application.
  </h2>

  <%= link_to "Sign up now!", '#', class: "btn btn-large btn-primary" %>
</div>

<%= link_to image_tag("rails.png", alt: "Rails"), 'http://rubyonrails.org/' %>

5-1-1-2.png

  • hero-unitbtnbtn-primaryクラス
    • Bootstrapにおいて特別な意味を持つ
  • image_tagヘルパー
    • imgタグの生成
    • 第1引数:画像ファイル名
    • 第2引数:ハッシュ
image_tagの埋め込みRuby評価後のソース
<img alt="Rails" src="/assets/rails.png" />

BootstrapとカスタムCSS

  • Bootstrapを使用するメリット

    • 洗練されたWebデザインとUI要素を簡単にHTML5アプリに追加できる
    • カスタムCSSルールとBootstrapを組合わせて、サンプルアプリにスタイルを追加
  • Bootstrapの導入

    • bootstrap-sass gemを使用してRailsアプリに導入
  • Bootstrapの言語

    • LESS CSS言語
    • RailsのAsset PipelineSass言語をサポート
    • bootstrap-sassLESSSassに変換する
Gemfileにbootstrap-sassを追加
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '4.0.4'
gem 'bootstrap-sass', '2.3.2.0'
gem 'sprockets', '2.11.0'

(中略)
config/application.rb ※Rails4.0以降の場合のみ必要
require File.expand_path('../boot', __FILE__)
require "active_record/railtie"
require "action_controller/railtie"
require "action_mailer/railtie"
require "sprockets/railtie"
Bundler.require(*Rails.groups)

module SampleApp
  class Application < Rails::Application
    config.assets.precompile += %w(*.png *.jpg *.jpeg *.gif)
  end
end
Bootstrapの組み込み
# Bootstrapのインストール
$ bundle install

# Webサーバの再起動
# Ctrl+CなどでRailsサーバを停止
$ rails s &
  • カスタムCSS用のファイル
    • app/assets/stylesheets/custom.css.scssを作成
    • Asset Pipelineの一部であり、上記ディレクトリに格納されたスタイルシートはapplication.cssの一部として自動的にWebサイトのレイアウトにインクルードされる
    • .cssが含まれているのでCSSファイル
    • .scssが含まれているのでSassを記述できるCSSファイル(Sassy CSS: Scss)
    • @importを使って、Bootstrapをインクルードする
app/assets/stylesheets/custom.css.scss
@import "bootstrap";
  • この時点での画面表示
    5-1-2-1.png

  • custom.css.scssにスタイルを定義

app/assets/stylesheets/custom.css.scssにスタイルを追加
@import "bootstrap";

html {
  overflow-y: scroll;
}

/* 
 ページ上部に60ピクセルの余白を追加
 headerタグにnavbar-fixed-topクラスが与えられているので、
 これに従いBootstrapはナビゲーションバーをページ上部に固定し、
 ナビゲーションバーの下に余白を置いて主要部分から分離する
 */
body {
  padding-top: 60px;
}

section {
  overflow: auto;
}

textarea {
  resize: vertical;
}

/*
 centerクラスにtext-align: centerプロパティを関連付けている
 centerクラスに属しているタグの内側にある要素は全てセンタリングされる
 */
.center {
  text-align: center;
}

.center h1 {
  margin-bottom: 10px;
}
  • 画面表示の変更を確認
    5-1-2-2.png

  • サイトのテキストの外観を変更

    • 洗練されたタイポグラフィーを利用
app/assets/stylesheets/custom.css.scssにタイポグラフィーの設定を追加
@import "bootstrap";

中略

/* typography */

h1, h2, h3, h4, h5, h6 {
  line-height: 1;
}

h1 {
  font-size: 3em;
  letter-spacing: -2px;
  margin-bottom: 30px;
  text-align: center;
}

h2 {
  font-size: 1.2em;
  letter-spacing: -1px;
  margin-bottom: 30px;
  text-align: center;
  font-weight: normal;
  color: #999;
}

p {
  font-size: 1.1em;
  line-height: 1.7em;
}
  • サイトのテキストを変更した後の画像
    5-1-2-3.png

  • サイトロゴのスタイル変更

    • テキストを大文字に変換
    • サイズ、色、配置の変更
app/assets/stylesheets/custom.css.scssにサイトロゴの設定を追加
@import "bootstrap";

(中略)

/* header */

# logo {
  float: left;
  margin-right: 10px;
  font-size: 1.7em;
  color: #fff;
  text-transform: uppercase;
  letter-spacing: -1px;
  padding-top: 9px;
  font-weight: bold;
  line-height: 1;
}

# logo:hover {
  color: #fff;
  text-decoration: none;
}
  • サイトロゴ修正後の画像

5-1-2-4.png

パーシャル(partial)

  • 現状の問題点

    • HTML shimだけで3行使ってしまっている
      • IE特有の文法を使用してしまっている
    • HTMLヘッダーは論理的な単位を形成するため、一箇所にまとめる必要がある
  • パーシャル機能によって整理する

    • renderを使い、IE固有の設定およびヘッダー部分の記述を変更
app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title><%= full_title(yield(:title)) %></title>
    <%= stylesheet_link_tag    "application", media: "all", "data-turbolinks-track" => true %>
    <%= javascript_include_tag "application", "data-turbolinks-track" => true %>
    <%= csrf_meta_tags %>
    <%= render 'layouts/shim' %>
  </head>
  <body>
    <%= render 'layouts/header' %>
    <div class="container">
      <%= yield %>
    </div>
  </body>
</html>
  • <%= render 'layouts/shim' %>
    • app/views/layouts/_shim.html.erbというファイルを検索してその内容を評価し、結果をビューに挿入
    • パーシャルの命名規則として、ファイル名に_をつける
app/views/layouts/_shim.html.erbでパーシャルを作成
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
app/views/layouts/_header.html.erbでパーシャルを作成
<header class="navbar navbar-fixed-top navbar-inverse">
  <div class="navbar-inner">
    <div class="container">
      <%= link_to "sample app", '#', id: "logo" %>
      <nav>
        <ul class="nav pull-right">
          <li><%= link_to "Home", '#' %></li>
          <li><%= link_to "Help", '#' %></li>
          <li><%= link_to "Sign in", '#' %></li>
        </ul>
      </nav>
    </div>
  </div>
</header>
  • フッターの作成
app/views/layouts/_footer.html.erbでパーシャルを作成
<footer class="footer">
  <small>
    <a href="http://railstutorial.jp/">Rails Tutorial</a>
    by Michael Hartl
  </small>
  <nav>
    <ul>
      <li><%= link_to "About",   '#' %></li>
      <li><%= link_to "Contact", '#' %></li>
      <li><a href="http://news.railstutorial.jp/">News</a></li>
    </ul>
  </nav>
</footer>
app/views/layouts/application.html.erbにフッターのパーシャルを組み込む
<!DOCTYPE html>
<html>
  <head>
    <title><%= full_title(yield(:title)) %></title>
    <%= stylesheet_link_tag    "application", media: "all", "data-turbolinks-track" => true %>
    <%= javascript_include_tag "application", "data-turbolinks-track" => true %>
    <%= csrf_meta_tags %>
    <%= render 'layouts/shim' %>
  </head>
  <body>
    <%= render 'layouts/header' %>
    <div class="container">
      <%= yield %>
      <%= render 'layouts/footer' %>
    </div>
  </body>
</html>
  • フッターを追加した画面を確認

5-1-3-1.png

  • フッターのスタイルを追加
app/assets/stylesheets/custom.css.scss
@import "bootstrap";

(中略)

/* footer */

footer {
  margin-top: 45px;
  padding-top: 5px;
  border-top: 1px solid #eaeaea;
  color: #999;
}

footer a {
  color: #555;
}

footer a:hover {
  color: #222;
}

footer small {
  float: left;
}

footer ul {
  float: right;
  list-style: none;
}

footer ul li {
  float: left;
  margin-left: 10px;
}
  • フッターのスタイル設定後の画面を確認

5-1-3-2.png

SassとAsset Pipeline

  • Asset Pipeline
    • CSS、Javascript、画像などの静的コンテンツの生産性と管理を大幅に強化する機能
  • Sass
    • CSS生成ツール

Asset Pipeline

  • 主要な機能
    1. アセットディレクトリ
    2. マニフェストファイル
    3. プリプロセッサエンジン

  • アセットディレクトリ
    • Rails3.0以前のバージョン
      • public/stylesheets
      • public/javascripts
      • public/images
    • Rails3.1以降のバージョン - 目的別に静的ファイルを分類。それぞれにstylesheetsjavascriptsimagesディレクトリが存在する
      • app/assets:現在のアプリケーション固有のアセット
      • lib/assets:自身の開発チームによって作成されたライブラリ用のアセット
      • vendor/assets:サードパーティーのアセット

  • マニフェストファイル

    • アセットディレクトリのファイルをどのように1つのファイルにまとめるかをRailsに指示
    • 実際にまとめるのはSprockets gem
    • CSSとJavaScriptには適用されるが、画像ファイルには適用されない
  • *= require_tree .

    • app/assets/stylesheetsディレクトリの全てのCSSをアプリケーションCSSにインクルードすることを指す
  • *= require_self

    • CSSの読み込みの際に、application.css自身もインクルードする
  • 今回は特に変更しない

マニフェストファイルapp/assets/stylesheets/application.css
/*
 * This is a manifest file that'll be compiled into application.css, which will include all the files
 * listed below.
 *
 * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
 * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
 *
 * You're free to add application-wide styles to this file and they'll appear at the top of the
 * compiled file, but it's generally better to create a new file per style scope.
 *
 *= require_self
 *= require_tree .
 */
  • プリプロセッサエンジン
    • 必要なアセットをディレクトリに配置してまとめた後、Railsは様々なプリプロセッサエンジンを介してそれらを実行
    • ブラウザに配信できるようにマニフェストファイルを用いて結合し、際とテンプレート用に準備する
  • Railsでどのプリプロセッサを使用するかの判定
    • ファイルの拡張子で判断
      • .scss:Sass
      • .coffee:CoffeeScript
      • .erb:埋め込みRuby(ERb)
    • 繋げて実行することができる
      • foobar.js.coffee:CoffeeScriptプロセッサ経由で実行される
      • foobar.js.erb.coffee:CoffeeScriptとERbの両方で実行される
        • 右から左に実行されるので、CoffeeScript => ERbの順に実行される

  • Asset Pipelineのメリット
    • 本番のアプリケーションで効率的になるように最適化されたアセットも自動的に生成される
  • 従来のCSSとJavaScriptを整理する手法
    • 機能を個別のファイルに分割し、読みやすいフォーマットに整形
    • プログラマにとっては可読性があるが、圧縮されていないので本番環境では非効率
  • Asset Pipeline
    • 全てのスタイルシートを1つのCSSファイル(application.css)にまとめる
    • 全てのJavaScriptファイルを1つのJSファイル(javascripts.js)にまとめる
    • それらのファイル全てに対して不要な空白を取り除く
    • ファイルサイズを最小化する
  • プログラマにとっては見やすく分割されたフォーマットのファイルを提供
  • 本番環境に対しては最適化された1つのファイルを提供

Sass

  • Sassとは
    • スタイルシートを記述するための言語
    • CSSよりも多くの点で強化されている
    • 本節ではネストと変数について説明
      • ミックスインについては第7章で説明
    • SCSSフォーマットに対応
      • CSS本体を抽象化したフォーマット
      • CSSに新しい機能を追加しただけで、新しい構文を定義したものではない
    • 拡張子は.scss

ネスト

  • ネスト
    • スタイルシート内に共通のパターンがある場合、要素をネストすることができる
通常のCSSの表記:.centerへのルール
.center {
  text-align: center;
}
.center h1 {
  margin-bottom: 10px;
}
Sassでの表記:.centerをネストして記載
.center {
  text-align: center;
  h1 {
    margin-bottom: 10px;
  }
}
通常のCSSでの表記:#logoというidが重複
// #logoの定義
# logo {
  float: left;
  margin-right: 10px;
  font-size: 1.7em;
  color: #fff;
  text-transform: uppercase;
  letter-spacing: -1px;
  padding-top: 9px;
  font-weight: bold;
  line-height: 1;
}

# logo:hover {
  color: #fff;
  text-decoration: none;
}
Sassでの表記:親属性を参照するのに&を使用
# logo {
  float: left;
  margin-right: 10px;
  font-size: 1.7em;
  color: #fff;
  text-transform: uppercase;
  letter-spacing: -1px;
  padding-top: 9px;
  font-weight: bold;
  line-height: 1;
  &:hover {
    color: #fff;
    text-decoration: none;
  }
}
フッターのCSS
footer {
  margin-top: 45px;
  padding-top: 5px;
  border-top: 1px solid #eaeaea;
  color: #999;
}

footer a {
  color: #555;
}

footer a:hover {
  color: #222;
}

footer small {
  float: left;
}

footer ul {
  float: right;
  list-style: none;
}

footer ul li {
  float: left;
  margin-left: 10px;
}
フッターのCSSをSCSSで記載
footer {
  margin-top: 45px;
  padding-top: 5px;
  border-top: 1px solid #eaeaea;
  color: #999;
  a {
    color: #555;
    &:hover {
      color: #222;
    }
  }
  small {
    float: left;
  }
  ul {
    float: right;
    list-style: none;
    li {
      float: left;
      margin-left: 10px;
    }
  }
}

変数

  • 変数
    • 同じ値を変数として定義・参照することで、冗長なコードを削除し、より自由な表現を可能とする
    • 繰り返し使わないとしても、可読性がよくなるので定義すべき
    • Bootstrapフレームワークにおいても多くの色などに対して変数名を定義している
      • LESS変数一覧を参照
      • bootstrap-sassというgemを使用することで、Sassでも同様の変数が使える
通常のCSS:#999を複数個所で使用
h2 {
  color: #999;
}
footer {
  color: #999;
}
SCSS:#999を変数として定義
$lightGray: #999;
h2 {
  color: $lightGray;
}
footer {
  color: $lightGray;
}
SCSS:Bootstrapフレームワークで定義している変数を利用
h2 {
  color: $grayLight;
}
footer {
  color: $grayLight;
}
  • ネスト・変数を使用して、SCSSファイルを書き換える
app/assets/stylesheets/custom.css.scss
@import "bootstrap";

/* mixins, variables, etc. */
$grayMediumLight: #eaeaea;

/* universal */

html {
  overflow-y: scroll;
}

body {
  padding-top: 60px;
}

section {
  overflow: auto;
}

textarea {
  resize: vertical;
}

.center {
  text-align: center;
  h1 {
    margin-bottom: 10px;
  }
}

/* typography */

h1, h2, h3, h4, h5, h6 {
  line-height: 1;
}

h1 {
  font-size: 3em;
  letter-spacing: -2px;
  margin-bottom: 30px;
  text-align: center;
}

h2 {
  font-size: 1.2em;
  letter-spacing: -1px;
  margin-bottom: 30px;
  text-align: center;
  font-weight: normal;
  color: $grayLight;
}

p {
  font-size: 1.1em;
  line-height: 1.7em;
}

/* header */

# logo {
  float: left;
  margin-right: 10px;
  font-size: 1.7em;
  color: $white;
  text-transform: uppercase;
  letter-spacing: -1px;
  padding-top: 9px;
  font-weight: bold;
  line-height: 1;
  &:hover {
    color: $white;
    text-decoration: none;
  }
}

/* footer */

footer {
  margin-top: 45px;
  padding-top: 5px;
  border-top: 1px solid #eaeaea;
  color: $grayLight;
  a {
    color: $gray;
  }
  &:hover {
    color: $grayDarker;
  }
  small {
    float: left;
  }
  ul {
    float: right;
    list-style: none;
    li {
      float: left;
      margin-left: 10px;
    }
  }
}

レイアウトのリンク

  • レイアウトのリンク

    • 現時点では全て#
    • <a href="/static_pages/about">About</a>はいけてない
      • Railsでは/aboutとなるようにすべき
    • Railsの場合、<%= link_to "About", about_path %>などとする
      • 可読性が増す
      • about_pathに定義しておくことで、URLを変更したい場合にも容易に変更できる
  • サンプルアプリケーションの全てのリンクリスト

ページ URL 名前付きルート
Home / root_path
About /about about_path
Help /help help_path
Contact /contact contact_path
Sign up /signup signup_path
Sign in /signin signin_path
  • Contactは既に第3章で作成済み

名前付きルートのテスト

  • テストコードの修正
    • visit '/static_pages/about'となっているような部分をvisit about_pathに置き換える
  • テストを実行するとエラーになる
    • bundle exec rspec spec/requests/static_pages_spec.rb
spec/requests/static_pages_spec.rb:アドレスを名前付きルートに変更
require 'spec_helper'

describe "StaticPages" do

  let(:base_title) { "Ruby on Rails Tutorial Sample App" }

  describe "Home page" do
    it "should have the content 'Sample App'" do
      visit root_path
      expect(page).to have_content('Sample App')
    end

    it "should have the title 'Home'" do
      visit root_path
      expect(page).to have_title("#{base_title} - Home")
    end
  end

  describe "Help page" do
    it "should have the content 'Help'" do
      visit help_path
      expect(page).to have_content('Help')
    end

    it "should have the title 'Help'" do
      visit help_path
      expect(page).to have_title("#{base_title} - Help")
    end
  end

  describe "About page" do
    it "should have the content 'About Us'" do
      visit about_path
      expect(page).to have_content('About Us')
    end

    it "should have the title 'About'" do
      visit about_path
      expect(page).to have_title("#{base_title} - About Us")
    end
  end

  describe "Contact page" do
    it "should have the content 'Contact'" do
      visit contact_path
      expect(page).to have_content('Contact')
    end

    it "should have the title 'Contact'" do
      visit contact_path
      expect(page).to have_title("#{base_title} - Contact")
    end
  end
end

Railsのルート

  • RailsがURLマッピングに使用するファイル
    • config/routes.rb
  • 現在のURLマッピングの設定
    • get 'static_pages/help'
  • 名前付きルートに変更した場合
    • match '/help', to: 'static_pages#help', via: 'get'
      • /helpへのGETリクエストにマッチ
      • StaticPagesコントローラのhelpアクションにルーティングされる
    • match '/help'というコードにより、下記の名前付きルートが自動生成される
      • about_path -> '/about'
      • about_url -> 'http://localhost:3000/about'
config/routes.rb:名前付きルートの定義
SampleApp::Application.routes.draw do
  match '/help', to: 'static_pages#help', via: 'get'
  match '/about', to: 'static_pages#about', via: 'get'
  match '/contact', to: 'static_pages#contact', via: 'get'
end
  • 上記変更後にテストを再実行
    • home以外はOKになる
  • homeconfig/routes.rbに記載するには
    • root 'static_pages#home'と書く
    • match '/', to: 'static_pages#home', via: 'get'でも書けるが、上記が推奨
    • root 'static_pages#home'というコードにより、下記の名前付きルートが自動生成される
      • root_path -> '/'
      • root_url -> 'http://localhost:3000/'
  • rootの設定を追加後にテストを実行すると全てOKになる
config/routes.rb:homeの設定を追加
SampleApp::Application.routes.draw do
  root 'static_pages#home'
  match '/help', to: 'static_pages#help', via: 'get'
  match '/about', to: 'static_pages#about', via: 'get'
  match '/contact', to: 'static_pages#contact', via: 'get'
end

名前付きルート

  • link_toメソッドの変更
    • ヘッダーのパーシャルとフッターのパーシャルを修正
    • 当変更後、HomeHelpAboutContactへのリンクが有効になる
app/views/layouts/_header.html.erb
<header class="navbar navbar-fixed-top navbar-inverse">
  <div class="navbar-inner">
    <div class="container">
      <%= link_to "sample app", root_path, id: "logo" %>
      <nav>
        <ul class="nav pull-right">
          <li><%= link_to "Home", root_path %></li>
          <li><%= link_to "Help", help_path %></li>
          <li><%= link_to "Sign in", '#' %></li>
        </ul>
      </nav>
    </div>
  </div>
</header>
app/views/layouts/_footer.html.erb
<footer class="footer">
  <small>
    <a href="http://railstutorial.jp/">Rails Tutorial</a>
    by Michael Hartl
  </small>
  <nav>
    <ul>
      <li><%= link_to "About",   about_path %></li>
      <li><%= link_to "Contact", contact_path %></li>
      <li><a href="http://news.railstutorial.jp/">News</a></li>
    </ul>
  </nav>
</footer>

RSpecを洗練させる

  • RSpecのテスト内容が冗長で繰り返しが多くなってきている
spec/requests/static_pages_spec.rbの抜粋
  describe "Home page" do
    it "should have the content 'Sample App'" do
      visit root_path
      expect(page).to have_content('Sample App')
    end

    it "should have the title 'Home'" do
      visit root_path
      expect(page).to have_title("#{base_title} - Home")
    end
  end
  • visitが重複している
    • beforeブロックを使用して冗長箇所を除去する
spec/requests/static_pages_spec.rb:beforeブロックに変更
  describe "Home page" do
    before { visit root_path }
    it "should have the content 'Sample App'" do
      expect(page).to have_content('Sample App')
    end

    it "should have the title 'Home'" do
      expect(page).to have_title("#{base_title} - Home")
    end
  end
  • どっちのテストもpage変数を使用する
    • subjectブロックを使用し、テストの主題がpageであることを定義する
  • it "should have the content 'Sample App!'" doexpect(page).to have_content('Sample App')は同じことを言っている
    • itメソッドの別のものを使用
spec/requests/static_pages_spec.rb:subjectブロックの使用とitメソッドの変更
  subject { page }
  describe "Home page" do
    before { visit root_path }

    it { should have_content('Sample App') }
    it { should have_title("#{base_title} - Home") }
  end
  • letブロックでbase_titleを定義しているが、ユーティリティクラスに持たせたい
    • spec/supportディレクトリにutilities.rbファイルを作成し、full_titleメソッドを定義
    • spec/supportディレクトリはRSpecによって自動的に読み込まれる
spec/support/utilities.rb
def full_title(page_title)
  base_title = "Ruby on Rails Tutorial Sample App"
  if page_title.empty?
    base_title
  else
    "#{base_title} - #{page_title}"
  end
end
spec/requests/static_pages_spec.rb:ユーティリティクラスを参照
  subject { page }
  describe "Home page" do
    before { visit root_path }

    it { should have_content('Sample App') }
    it { should have_title(full_title('Home')) }
  end
  • 上記を踏まえて、テストクラスをリファクタリング
    • 修正後にテストを実行して、正常終了することを確認
spec/requests/static_pages_spec.rb:リファクタリング
require 'spec_helper'

describe "StaticPages" do
  subject { page }

  describe "Home page" do
    before { visit root_path }

    it { should have_content('Sample App') }
    it { should have_title(full_title('Home')) }
  end

  describe "Help page" do
    before { visit help_path }

    it { should have_content('Help') }
    it { should have_title(full_title('Help')) }
  end

  describe "About page" do
    before { visit about_path }

    it { should have_content('About Us') }
    it { should have_title(full_title('About Us')) }
  end

  describe "Contact page" do
    before { visit contact_path }

    it { should have_content('Contact') }
    it { should have_title(full_title('Contact')) }
  end
end

ユーザー登録

  • ユーザー登録ページへのルーティングを作成
    • 2番目のコントローラを作成する必要がある

Usersコントローラ

Usersコントローラの生成(newアクションを追加)
$ rails generate controller Users new --no-test-framework
      create  app/controllers/users_controller.rb
       route  get "users/new"
      invoke  erb
      create    app/views/users
      create    app/views/users/new.html.erb
      invoke  helper
      create    app/helpers/users_helper.rb
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/users.js.coffee
      invoke    scss
      create      app/assets/stylesheets/users.css.scss

ユーザー登録用URL

  • テスト用ファイルの作成
テスト用ファイルの作成
$ rails generate integration_test user_pages
      invoke  rspec
      create    spec/requests/user_pages_spec.rb
spec/requests/user_pages_spec.rb
require 'spec_helper'

describe "User pages" do
  subject { page }

  describe "signup page" do
    before { visit signup_path }

    it { should have_content('Sign up') }
    it { should have_title(full_title('Sign up')) }
  end
end
  • StaticPagesも含めてテストを実施

    • bundle exec rspec spec/
    • user_pages_spec.rb部分がエラーになる
  • ルーティングの設定

    • /signupのルートを追加
config/routes.rb
SampleApp::Application.routes.draw do
  get "users/new"
  root 'static_pages#home'
  match '/signup',  to: 'users#new',            via: 'get'
  match '/help',    to: 'static_pages#help',    via: 'get'
  match '/about',   to: 'static_pages#about',   via: 'get'
  match '/contact', to: 'static_pages#contact', via: 'get'
end
  • ユーザ登録ページのスタブを作成
    • テストを実施し、正常終了することを確認
app/views/users/new.html.erb
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>
<p>Find me in app/views/users/new.html.erb</p>
  • homeのページからのSign upへのリンクを修正
app/views/static_pages/home.html.erb
<% provide(:title, 'Home') %>
<div class="center hero-unit">
  <h1>Welcome to the Sample App</h1>

  <h2>
    This is the home page for the
    <a href="http://railstutorial.jp/">Ruby on Rails Tutorial</a>
    sample application.
  </h2>

  <%= link_to "Sign up now!", signup_path, class: "btn btn-large btn-primary" %>
</div>

<%= link_to image_tag("rails.png", alt: "Rails"), 'http://rubyonrails.org/' %>

コミット

コミット、デプロイ
$ git add .
$ git commit -m "Finish layout and routes"
$ git checkout master
$ git merge filling-in-layout

$ git push
$ git push heroku

$ heroku open

# エラーが発生する場合はログを確認
$ heroku logs

演習問題

Shared Examples

  • Shared Examples
    • 同じようなテストを複数箇所で実施する場合に使用
    • テンプレートのテストケースを記載し、変数部分だけを各メソッドでletにて設定することで、同じようなテストケースの記載を省くことができる
spec/requests/static_pages_spec.rbの修正前
describe "StaticPages" do
  subject { page }

  describe "Home page" do
    before { visit root_path }

    it { should have_content('Sample App') }
    it { should have_title(full_title('Home')) }
  end
end
  • Shared Examples適用後
    • Helpなど、他のページにも同様に適用
spec/requests/static_pages_spec.rbの修正後
describe "StaticPages" do
  subject { page }

  shared_examples_for "all static pages" do
    it { should have_content(heading) }
    it { should have_title(full_title(page_title)) }
  end

  describe "Home page" do
    before { visit root_path }
    let(:heading)    { 'Sample App' }
    let(:page_title) { '' }

    it_should_behave_like "all static pages"
  end
end

click_linkによるレイアウトのリンクのテスト

  • click_link
    • リンクが実際に正しいページへのリンクになっているかをテスト
spec/requests/static_pages_spec.rbにリンクのテストを追加
describe "StaticPages" do
  it "should have the right links on the layout" do
    visit root_path

    click_link "About"
    expect(page).to have_title(full_title('About Us'))

    click_link "Help"
    expect(page).to have_title(full_title('Help'))

    click_link "Contact"
    expect(page).to have_title(full_title('Contact'))

    click_link "Home"
    expect(page).to have_title(full_title(''))

    click_link "Sign up now!"
    expect(page).to have_title(full_title(''))

    click_link "sample app"
    expect(page).to have_title(full_title(''))
  end

end

ヘルパーメソッドのテストを作成

spec/helpers/application_helper_spec.rb
require 'spec_helper'

describe ApplicationHelper do
  it "should include the page title" do
    expect(full_title("foo")).to match(/foo/)
  end

  it "should include the base title" do
    expect(full_title("")).to match(/^Ruby on Rails Tutorial Sample App/)
  end

  it "should not include a bar for the home page" do
    expect(full_title("")).not_to match(/-/)
  end
end
spec/support/utilities.rbの中身を変更(ApplicationHelperをincludeするだけに変更)
include ApplicationHelper
7
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?