1
0

More than 3 years have passed since last update.

Railsチュートリアル(第6版) 第5章 レイアウトを作成する

Last updated at Posted at 2021-05-23

第5章

第5章はレイアウトに関する事柄が大半。
具体的には、HTML、CSS、Bootstrap(Twitter社が公開しているやつ)、Asset Pipeline等を用いて、レイアウトを良い感じにしていく。
また、最後に統合テストについての紹介がある。

構造を追加

Twitter社のWebデザインフレームワークのBootstrapを利用する。また、パーシャル機能を使ってコードを整えていく。
Viewの画面だけではなく、コードも可読性を上げるというのが目標。

Webアプリケーションを作るときは、モックアップといってユーザーインターフェースの概要を作成しておくと良い。

ナビゲーション

まずは、リンクとスタイルを追加するためにapplication.html.erbにHTML構造を追加する。

<!DOCTYPE html>
<html>
  <head>
    <title><%= full_title(yield(:title)) %></title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>
    <%= stylesheet_link_tag    'application', media: 'all',
                               'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag 'application',
                               'data-turbolinks-track': 'reload' %>
    <!--[if lt IE 9]>
      <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/r29/html5.min.js">
      </script>
    <![endif]-->
  </head>
  <body>
    <header class="navbar navbar-fixed-top navbar-inverse">
      <div class="container">
        <%= link_to "sample app", '#', id: "logo" %>
        <nav>
          <ul class="nav navbar-nav navbar-right">
            <li><%= link_to "Home",   '#' %></li>
            <li><%= link_to "Help",   '#' %></li>
            <li><%= link_to "Log in", '#' %></li>
          </ul>
        </nav>
      </div>
    </header>
    <div class="container">
      <%= yield %>
    </div>
  </body>
</html>

上記のコードのポイント
<!DOCTYPE html>は、HTML5を宣言するためのもの。

・HTML5未対応のブラウザの際には、下記のコードでHTML5 slimに対応させる。IEでサポートされる条件付きコメントと言われる書き方。IEがバージョン9未満の時のみ対応。

<!--[if lt IE 9]>
  <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/r29/html5.min.js">
  </script>
<![endif]-->

navbarnavbar-fixed-topnavbar-inverse等はBootstrapによって与えられたクラス。

<header class="navbar navbar-fixed-top navbar-inverse">
  <div class="container">
    <%= link_to "sample app", '#', id: "logo" %>
    <nav>
      <ul class="nav navbar-nav navbar-right">
        <li><%= link_to "Home",   '#' %></li>
        <li><%= link_to "Help",   '#' %></li>
        <li><%= link_to "Log in", '#' %></li>
      </ul>
    </nav>
  </div>
</header>

・埋め込みRuby。link_toはRailsヘルパー機能で、第一引数はリンクテキスト、第二引数はURLとなる。このURLは後々名前付きルートに置き換える。
<%= link_to "リンクテキスト", 'URL' %>

<%= link_to "sample app", '#', id: "logo" %>
<nav>
  <ul class="nav navbar-nav navbar-right">
    <li><%= link_to "Home",   '#' %></li>
    <li><%= link_to "Help",   '#' %></li>
    <li><%= link_to "Log in", '#' %></li>
  </ul>
</nav>

navタグは「その内側がナビゲーションリンク」であるということを明示的に示している。なので、上記のコードはレイアウトを描画する時下のように置き換わる。

<nav>
  <ul class="nav navbar-nav navbar-right">
    <li><a href="#">Home</a></li>
    <li><a href="#">Help</a></li>
    <li><a href="#">Log in</a></li>
  </ul>
</nav>

・HomeページのHTMLを下記のコードに変更

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

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

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

<%= link_to image_tag("rails.svg", alt: "Rails logo", width: "200px"),
                      "https://rubyonrails.org/" %>

・この一文はには、Bootstrapbtn-lgbtn-primary等を使用している。

<a href="#" class="btn btn-lg btn-primary">Sign up now!</a>

・下のコードのいくつのポイント

<img alt="Rails logo" width="200px" src="/assets/rails-<long string>.svg">

Railsはロード時間を高速化するため、assetsディレクトリ直下にある画像をapp/assets/imagesと結びつけているので、より高速にブラウザにデータを渡せる。
また、alt属性は、画像がなかったら表示される文字列で、聴覚障害のあるユーザーが使う際に、属性内の文字が読まれる。

演習の猫画像を表示
image.png

BootstrapとカスタムCSS

先ほどHTMLに多くのCSSクラスを付けたが、Bootstrapを使うとより簡単にきれいなWebデザインが作れる。また、Bootstrapを使うとレスポンシブデザインに対応できるのが良い点。これは、端末ごとに画面のサイズが異なっても、見やすいようにデザインを変えてくれるという優れもの。

なので、Bootstrapをインストールする

①Gemfileに追加

Gemfile
gem 'bootstrap-sass', '3.4.1'

②インストール

$ bundle install

このアプリを作り始めてから、rails generateコマンドを実行してきたが、このコマンドを実行するとコントローラーごとにCSSファイルが自動で生成される。ファイルを正しく読み込ませるのは難しいので、全てのCSSを一か所にまとめる。

$ touch app/assets/stylesheets/custom.scss

custom.scssの.scssという拡張子は、「Sass(Sassy CSS」と呼べれるCSS
を拡張した言語。
アセットパイプラインによって、Sassを処理できるようにしている。

Bootstrap CSSを追加

app/assets/stylesheets/custom.scss
@import "bootstrap-sprockets";
@import "bootstrap";

rails serverを起動しなおし、ブラウザを立ち上げ
image.png
さっきよりはいい感じ

スタイルをCSSに追加

/* universal */

body {
  padding-top: 60px;
}

section {
  overflow: auto;
}

textarea {
  resize: vertical;
}

.center {
  text-align: center;
}

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

よりバランスが良くなった。
image.png

CSSの解説

body {
  padding-top: 60px;
}

body*タグ*に対して、上部の余白60px追加

.center {
  text-align: center;
}

テキストを中央ぞろえにしている。なお、.center「.」がついているので、クラスが付与されたタグ対して適用している。

/* 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: #777;
}

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

文字サイズや色等の体裁を整えるCSS

/* 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;
}

#logo:hover {
  color: #fff;
  text-decoration: none;
}

ロゴ周りのCSSを追加。ここで#logoのように「#」を付けたものが出てきているが、「.(ドット)」と違って、こちらはIDに対してCSSを適用するもの。

CSSを色々と追加した結果が下の図
image.png
ロゴとか文字サイズ、位置、ボタン等各種見れるレベルになってきた。

演習をやった結果
image.png
railsと猫の画像を非表示にした。

パーシャル(partial)

先ほど、HTML shim対応ように書いたコードを隠せたらよりコードがすっきりするということで、パーシャルという機能を使って、一か所にまとめる方法がある。

各ファイル名の前にアンダースコアを入れる。(ディレクトリの中のパーシャルを識別しやすくするための命名規則)
_shim.html.erb_header.html.erb

呼び出し元は、renderメソッドで呼び出すが、その際にアンダースコアは含めない。
<%= render 'layouts/shim' %><%= render 'layouts/header' %>

そうすると以下のコードになる。

app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title><%= full_title(yield(:title)) %></title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>
    <%= stylesheet_link_tag    'application', media: 'all',
                               'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag 'application',
                               'data-turbolinks-track': 'reload' %>
    <%= render 'layouts/shim' %>
  </head>
  <body>
    <%= render 'layouts/header' %>
    <div class="container">
      <%= yield %>
    </div>
  </body>
</html>

同様にフッターも作成する。

app/views/layouts/_footer.html.erb
<footer class="footer">
  <small>
    The <a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a>
    by <a href="https://www.michaelhartl.com/">Michael Hartl</a>
  </small>
  <nav>
    <ul>
      <li><%= link_to "About",   '#' %></li>
      <li><%= link_to "Contact", '#' %></li>
      <li><a href="https://news.railstutorial.org/">News</a></li>
    </ul>
  </nav>
</footer>

そしたら、application.html.erb<%= render 'layouts/footer' %>を追加する。

また、footer用のCSSを追加する。

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

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

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: 15px;
}

フッターも追加されたページができた。また、renderメソッドで置き換えたところも問題ない。
image.png

演習

<%= render 'layouts/rails_default' %>を追加し、パーシャル作成した。

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>

Sassとアセットパイプライン

Railsにある機能として、CSS、JavaScript、画像等の静的コンテンツの生産性と管理を強化してくれる「アセットパイプライン」というものがある。

これには、3つの主要な機能が理解の対象となる。

・アセットディレクトリ
・マニフェストファイル
・プリプロセッサエンジン

アセットディレクトリ
目的別に静的ファイルを分類する3つのディレクトリ
 ・app/assets: 現在のアプリケーション固有のアセット
 ・lib/assets: あなたの開発チームによって作成されたライブラリ用のアセット
 ・vendor/assets: サードパーティのアセット(デフォルトでは存在しません)

マニフェストファイル
アセットディレクトリにある静的ファイルを一つにまとめるためのもの。
アセットをまとめる処理をするのは、SproketsというGem
注意点として、CSSとJavaScriptには適用されるが、画像ファイルには適用されないというところ。

app/assets/stylesheets/application.css
 *= require_tree .

アセットディレクトリにあるすべてのCSSファイルをアプリケーションCSSに含まれるようにしたもの

app/assets/stylesheets/application.css
 *= require_self

application.cssもCSS読み込みに含める。

プリプロセッサエンジン
アセットをディレクトリに配置、まとめた後にプロプロセッサエンジンを介して、ブラウザに配信できるようにマニフェストファイルを用いて結合、サイトテンプレート用に準備する。

Railsはどのプリプロセッサエンジンを使うか、ファイル名の拡張子から判断する。

・.scss
・.coffee
・.erb

プリプロセッサエンジンは繋げて実行できる。

foobar.js.erb.coffee

右から左へ順番に実行される。

本番環境での効率性
なぜアセットパイプラインを使うのか?は、本番環境と開発環境それぞれに最適化させるためである。
開発者にとっては、機能ごとにファイルを分割し、可読性を高めていたが本番環境で多数に分割されたファイルを読み込むには、速度低下が起こりユーザーの体験の質が下がる。

しかし、アセットパイプラインを使えば、分割されたファイルを一つのCSSにまとめて(最小化)、読み込み速度の向上と快適な開発環境を作れるという訳。

Sassの機能

SassはCSSと違って、様々な点で強化されている。
それは、ネストと変数だ。(ミックインは別で紹介)

ネスト
例えば下のようなコード。.center h1.centerのクラスに属している。

.center {
  text-align: center;
}

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

なので、下のようにネスト化してしまえば、すっきりとする。要は入れ子の関係

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

h1.centerのルールを継承している。

hooverに対しても使える。下はCSS記法

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

#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;
  &:hover {
    color: #fff;
    text-decoration: none;
  }
}

#logo:hover&:hoverとすることで使えるようにしている。

変数
Sassでは、変数が定義できる。(変数が使えるってすごく便利)

h2 {
  .
  .
  .
  color: #777;
}
.
.
.
footer {
  .
  .
  .
  color: #777;
}

color: #777は二か所で使われている。これを変数でできたらどうか?

$light-gray: #777;

変数の定義はこのように、$変数名: 値;としてあげる。
そして変数を呼び出すときは、$変数名とすればOK。下のコードのような感じになる。

$light-gray: #777;
.
.
.
h2 {
  .
  .
  .
  color: $light-gray;
}
.
.
.
footer {
  .
  .
  .
  color: $light-gray;
}

因みに、Bootstrapフレームワークでは、多くの色に対して様々な変数名を定義している。

ネストと変数を使って書き直したcustom.scssは下のコードになる。

app/assets/stylesheets/custom.scss
@import "bootstrap-sprockets";
@import "bootstrap";

/* mixins, variables, etc. */

$gray-medium-light: #eaeaea;

/* universal */

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: $gray-light;
}

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;
  &:hover {
    color: white;
    text-decoration: none;
  }
}

/* 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;
    }
  }
}

レイアウトのリンク

リンクを使うには、HTMLに書く方法とRailsの埋め込みコードを使って行う方法がある。

HTMLの場合

<a href="/static_pages/about">About</a>

Rubyの埋め込み(Rails流 名前付きルート)

<%= link_to "About", about_path %>

ルーティングとURL
image.png
参照:railsチュートリアル

Contactページ

ここは第3章でやってたので省略

RailsのルートURL

rootURLを定義することにより、root_pathroot_urlといったヘルパーメソッドが使えるようになる。

root_pathroot_urlの違い

root_path:相対参照 「/」とか
root_url:絶対参照 「https://www.aaaaa.jp/」

注意点として、**リダイレクトの場合は、絶対パス「root_url」書式を使用する。理由として、リダイレクトは、完全なURLを要求されるから。(どちらでも動作するが、絶対パスを使おう)
それ以外は、相対パスでOK。

名前付きルートの定義

現在はこのようにルーティングしている。

get  '/help', to: 'static_pages#help'

下のように変換する。

get  '/help', to: 'static_pages#help'

こうすることで、help_pathhelp_urlの名前付きルートが使えるようになる。

help_path -> '/help'
help_url  -> 'https://www.example.com/help'

ルーティングを書き換える。

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

名前付きルートにしたため、テストも書き換える。

test/controllers/static_pages_controller_test.rb
require 'test_helper'

class StaticPagesControllerTest < ActionDispatch::IntegrationTest

  test "should get home" do
    get root_path
    assert_response :success
    assert_select "title", "Ruby on Rails Tutorial Sample App"
  end

  test "should get help" do
    get help_path
    assert_response :success
    assert_select "title", "Help | Ruby on Rails Tutorial Sample App"
  end

  test "should get about" do
    get about_path
    assert_response :success
    assert_select "title", "About | Ruby on Rails Tutorial Sample App"
  end

  test "should get contact" do
    get contact_path
    assert_response :success
    assert_select "title", "Contact | Ruby on Rails Tutorial Sample App"
  end
end

これでテストは成功。

因みに名前付きルートは、as:オプションを使えば変更できる。

名前付きルート

今まで、link_toメソッドの引数にURLは何も与えていなかったので与える。

変更前

<%= link_to "About", '#' %>

変更後

<%= link_to "About", about_path %>

各ページのリンクに名前付きルートを入れてあげれば、ここでやる事はOK

リンクのテスト

リンクを設定したので、そのリンクがきちんと動作しているかのテストをしなければならない。ブラウザを立ち上げテストしてもいいのだが、ページが多くなったりすれば大変手間のかかる作業になる。
そこで、「統合テスト(Integration test)」を使用し、自動化する。これは、アプリケーションの動作を端から端までテストできるもの。

$ rails generate integration_test site_layout

site_layoutというテストのテンプレートを生成
テストを生成した時、ファイル名の末尾に_testという文字列が追加される。

テストの目的は、HTML構造を調べ、レイアウトのリンクが正しい動作をするかどうかの確認

1.ルートURL(Homeページ)にGETリクエストを送る.
2.正しいページテンプレートが描画されているかどうか確かめる.
3.Home、Help、About、Contactの各ページへのリンクが正しく動くか確かめる.

上記3点のステップをコードにしていく。
その際、assert_templateメソッドを使用して、ビューが正しく描画されているか確かめる。

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
  end
end

上記のコードの説明

assert_select "a[href=?]", about_path

特定のリンクがあるか調べている。?の部分には、about_pathが渡される。
これは、下のようなHTMLが正しいかをチェックしている。

<a href="/about">...</a>

そして、ルートURLへのリンクが2つあるので、count: 2とすることで、そのリンクの個数も調べられる。下のコード

assert_select "a[href=?]", root_path, count: 2

ここまで追加したらrails testをすればOK
また、テストする項目を指定もできる。

$ rails test:integration

上のようにやれば、統合テストのみ実行される。

演習
1.footerパーシャルのabout_pathをcontact_pathに変更してみて、テストが正しくエラーを捕まえてくれるかどうか確認

Failure:
SiteLayoutTest#test_layout_links [/home/ubuntu/environment/sample_app/test/integration/site_layout_test.rb:10]:
Expected at least 1 element matching "a[href="/about"]", found 0..
Expected 0 to be >= 1.

2

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
6 runs, 15 assertions, 0 failures, 0 errors, 0 skips

テストしたらOKだった。

ユーザー登録

ユーザー登録の機能を実装する。

まずは、Usersコントローラ作成

$ rails generate controller Users new

generateコマンドを実行したので、コントローラー、newアクションページが作られたので、テストする。

7 runs, 16 assertions, 0 failures, 0 errors, 0 skips

問題なし。

演習
1.get '/signup', to: 'users#new'に書き換えた
2.エラーが出たことを確認

エラーを解決するため、テストに変更を加える。

test/controllers/users_controller_test.rb
require 'test_helper'

class UsersControllerTest < ActionDispatch::IntegrationTest

  test "should get new" do
    get signup_path
    assert_response :success
  end
end

get signup_pathに変更。

ユーザー登録ページにリンクを追加

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

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

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

<%= link_to image_tag("rails.svg", alt: "Rails logo", width: "200px"),
                      "https://rubyonrails.org/" %>

登録ページ(スタブ)を作成

app/views/users/new.html.erb
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>
<p>This will be a signup page for new users.</p>

登録ページ(スタブ)できた。
image.png

演習

サインアップにもテストを追加した。

    get signup_path
    assert_select "title", full_title("Sign up")

最後に

最後にコミットして、Githubにあげれば終了

レイアウトもかなり奥が深い分野だと思う。実際にユーザーが使うのは画面に表示されているところなのでしっかりと作っていきたいところである。

1
0
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
1
0