#この章でやること
- アプリケーションにBootstrapフレームワークを組み込み、そして、カスタムスタイルを追加する
- 作成したページ(HomeやAboutなど)へのリンクを追加
- パーシャル
- Railsのルーティング
- Asset Pipeline
- Sass
###5.1 構造を追加する
5.1章では最小限のスタイルを追加する
CSSと、Bootstrapも利用し、レイアウトのコードを、パーシャル(Partial)機能を使って整えていく
モックアップ(Webではよくワイヤーフレームと呼ばれます)を使って実装後のアプリケーションの外観をスケッチしておく
ユーザーインターフェイスの概要をできるだけ早いうちに把握しておくことが有効
5.1.1 ナビゲーション
まずはレイアウトを以下のコードに整える (ここでは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>
上記のコードでおそらく不思議なところは以下のコードかと思います。
これはHTML5がIEではサポートが不完全な場合があるため、JavaScriptのコード(通称: HTML5 shim(shivとも呼ばれます))を使って回避する
<!--[if lt IE 9]> #IEのバージョンが9より小さい場合(if lt IE 9)にのみ、囲まれている行を実行
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/r29/html5.min.js">
</script>
<![endif]-->
コメントアウトして記述があるが、これはrailsの機能ではなく、IEのサポートでIEのバージョンが古い場合だけ、コメントアウトの行を読み込む仕様になっている
次はHomeのページにサインアップページへのリンクを追加する
<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/" %>
<a href="#" class="btn btn-lg btn-primary">Sign up now!</a>
については
divタグのCSSクラスjumbotronや、signupボタンのbtnクラス、btn-lgクラス、btn-primaryクラスはすべて、後で追加するBootstrapでCSSを追加するために大事なポイントです。
<%= link_to image_tag("rails.svg", alt: "Rails logo", width: "200px")
image_tagヘルパーは引数として画像ファイルのパスと任意のオプションハッシュをとる
image_tagヘルパは、シンボルを使ってalt属性やwidth属性などを設定可能
###5.1.2 BootstrapとカスタムCSS
Bootstrapを追加する
Bootstrap自体が不明な人は、たにぐちまことさんの
https://www.youtube.com/watch?v=FtkRIuWTf0E&list=PLh6V6_7fbbo9sHm8E3F7lZuDDxDJkheKD&index=2
で勉強されるといいと思います。
railsでBootstrapを使うために、bootstrap-sass gemをインストールする
source 'https://rubygems.org'
gem 'rails', '6.0.3'
gem 'bootstrap-sass', '3.4.1'
gem 'puma', '4.3.6'
.
.
$ bundle install
を実行
コントローラーを生成するたびにCSSファイルが自動生成されるが、今回はすべてのCSSを1つにまとめる方針ですすむ
そのためにcustom.scss
を作る
$ touch app/assets/stylesheets/custom.scss
SCSSはCSSを書きやすくした言語で、railsのアセットパイプラインがSCSSを処理できるようにしてくれる
※アセットパイプラインとは、JavaScriptやCSSのアセットを最小化 (minify: スペースや改行を詰めるなど) または圧縮して連結するためのフレームワーク
カスタムCSSに@importを使って、Bootstrap(とそれに関連するSprockets)を読み込む
@import "bootstrap-sprockets";
@import "bootstrap";
記述後、Webサーバーを再起動させると、アプリケーションに反映させることができる
※ブラウザの更新機能だけでは、反映されないので注意
###スタイルをCSSに追加する
CSSの記述について解説されているんですが、プロゲートとかで学んだことも多いので割愛します
@import "bootstrap-sprockets";
@import "bootstrap";
/* universal */
body {
padding-top: 60px;
}
section {
overflow: auto;
}
textarea {
resize: vertical;
}
.center {
text-align: center;
}
.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: #777;
}
p {
font-size: 1.1em;
line-height: 1.7em;
}
/* 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;
}
###5.1.3 パーシャル(partial)
railsでは各部品をファイルごと切り離して別のファイルから呼び出しすることができる
###パーシャルの基本ルール
- ファイル名はアンダースコアから始まる
- render '{ファイル名}'で呼びだす
- パーシャルが同じディレクトリにある時はrender '{ファイル名}'、違うディレクトリの時はrender '{ディレクトリ名/ファイル名}'で呼びだす。(拡張子不要)
shimとheaderパーシャルを追加する
<!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' %> #shimを呼び出している
</head>
<body>
<%= render 'layouts/header' %>
<div class="container">
<%= yield %>
</div>
</body>
</html>
呼び出される側のパーシャルを作成
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/r29/html5.min.js">
</script>
<![endif]-->
headerのパーシャル
<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>
同様にfooterパーシャル
<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>
<!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' %> #headerパーシャル呼び出し
<div class="container">
<%= yield %>
<%= render 'layouts/footer' %> #footerパーシャル呼び出し
</div>
</body>
</html>
次はfooterにもスタイルを足します。
.
.
.
/* 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;
}
headタグをrenderに置き換える
<!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>
##5.2 Sassとアセットパイプライン
このセクションでは、アセットパイプラインの概要と、CSS生成ツールである「Sass」の使い方について解説
###5.2.1 アセットパイプライン
アセットディレクトリ、マニフェストファイル、プリプロセッサエンジンの3つの主要な機能を解説する
Railsのアセットパイプラインでは、静的ファイルを目的別に分類する、標準的な3つのディレクトリが使われています。
- app/assets: 現在のアプリケーション固有のアセット
- lib/assets: あなたの開発チームによって作成されたライブラリ用のアセット
- vendor/assets: サードパーティのアセット(デフォルトでは存在しません)
それぞれのディレクトリには、2つのアセットクラス用のサブディレクトリがあり、例えばapp/assetsの場合、次のような画像用とCSS用のサブディレクトリがあります。
$ ls app/assets/ #lsはそのディレクトリの中身を表示するコマンド
config images stylesheets
上記の説明から、5.1.2で取り上げたカスタムCSSが配置された場所と、その理由について理解することができると思います。つまり、custom.scssはサンプルアプリケーション固有のアセットなので、app/assets/stylesheetsに配置されていたのです。
####マニフェストファイル
静的ファイル(アセット)を上記の場所へそれぞれ配置すれば、マニフェストファイルを使って、それらをどのように1つのファイルにまとめるのかをRailsに指示することができます。なお、実際にアセットをまとめる処理を行うのはSprocketsというgemです。また、マニフェストファイルはCSSとJavaScriptには適用されますが、画像ファイルには適用されません。
1つの具体例として、アプリケーションのCSS用マニフェストファイルを見てみましょう(リスト 5.19)。
リスト 5.19: アプリケーション固有の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, or any
* plugin's vendor/assets/stylesheets directory 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 bottom of the compiled file so the styles you add here take precedence
* over styles defined in any other CSS/SCSS files in this directory. Styles in
* this file should be added after the last require_* statement.
* It is generally better to create a new file per style scope.
*
*= require_tree . #app/assets/stylesheetsディレクトリ中のすべてのCSSファイルが、CSSに含まれるようにする
*= require_self #CSSの読み込みシーケンスの中で、application.css自身もその対象に含める
*/
Railsには実用的なデフォルトでマニフェストファイルが付属しているので、Railsチュートリアルでは変更を加える必要がないが、もし必要な場合は、Railsガイドの「アセットパイプライン」で確認します
####プリプロセッサエンジン
必要なアセットをディレクトリに配置してまとめたあとは、
プリプロセッサエンジンを介してブラウザに配信できるようにそれらをマニフェストファイルを用いて統合し、サイトテンプレート用に準備する。
Railsではどのプリプロセッサを使うのか、ファイルの拡張子で判断する。
- .scss
- .coffee
- .erb
の三つが一般的な拡張子である。
プリプロセッサエンジンは、繋げて実行する(chain)ことができる
foobar.js.erb.coffee
この拡張子だと、erbとcoffee両方のプロセッサ経由で実行される。
なお、コードは右から左に実行されるので、coffee→erbという実行手順となる。
####本番環境での効率性
Asset Pipelineの最大のメリットは開発環境と本番環境それぞれで最高の環境が用意できることである。
具体的には、開発環境ではCSSやJavaScriptを個別のファイルで用意し、本番環境ではAsset PipelineでそれぞれのCSSファイルを1つのCSSファイルapplication.cssにまとめ、全てのJavaScriptファイルを一つのJSファイルjavascripts.jsにまとめる。
さらに、それら全てのファイルのインデントや空白を取り除く処理を行い、ファイルを最小化する。
Asset Pipelineの結果、ユーザーから見て素早い動きでありながら開発環境ではプログラマに取って書きやすい形態を取ることができる。
###5.2.2 素晴らしい構文を備えたスタイルシート
Sassの基礎知識はProgateの「Sass」コースで学べます。
機能や書き方だけ簡単に紹介して終わります。
- ネスト
- 変数
###ネスト
これを
.center {
text-align: center;
}
.center h1 {
margin-bottom: 10px;
}
SASSでは、こうかける
.center {
text-align: center;
h1 {
margin-bottom: 10px;
}
}
A:hoverなども&:でネストできる
#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;
}
}
###変数
Sassでは、変数が定義できる
h2 {
color: #777;
}
footer {
color: #777;
}
上のcolor:777;
を$light-gray: #777;
と定義してしまい、シンプルに記載できる
$light-gray: #777;
.
h2 {
color: $light-gray;
}
footer {
color: $light-gray;
}
Bootstrapフレームワークでは、多くの色に対して変数名を定義してくれているので、Bootstrapページの「LESS変数一覧」で参照することができる。
「LESS変数一覧」では、SassではなくLESSを使って変数が定義されていますが、少し変更すれば問題ない
例えばLESSではアットマーク@を使っているのに対して、Sassはドルマーク$を使っているので
@gray-light: #777;
を$gray-light: #777;
で使用可能
ネスト、変数を使って実際にスタイルを書き換える
@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;
}
}
}
##5.3 レイアウトのリンク
仮置きしていた'#'のリンクを書き換えていく
Railsでは以下のような名前付きルートを使うのが慣例となっている
<%= link_to "About", about_path %>
今後使う予定のURLとルーティング(route)とのマッピングを以下に表示
ページ名 | URL | 名前付きルート |
---|---|---|
Home | / | root_path |
About | /about | about_path |
Help | /help | help_path |
Contact | /contact | contact_path |
Sign up | /signup | signup_path |
Log in | /login | login_path |
###5.3.1 Contactページ
まずは、cotactページを追加する(3章演習でやっていればここは不要)
require 'test_helper'
.
.
.
test "should get contact" do
get static_pages_contact_url
assert_response :success
assert_select "title", "Contact | Ruby on Rails Tutorial Sample App"
end
end
Contactページのルートを追加
Rails.application.routes.draw do
root 'static_pages#home'
get 'static_pages/home'
get 'static_pages/help'
get 'static_pages/about'
get 'static_pages/contact'
end
Contactページ用のアクションを追加
class StaticPagesController < ApplicationController
.
.
.
def contact
end
end
Contactページのビューを追加
<% provide(:title, 'Contact') %>
<h1>Contact</h1>
<p>
Contact the Ruby on Rails Tutorial about the sample app at the
<a href="https://railstutorial.jp/contact">contact page</a>.
</p>
###5.3.2 RailsのルートURL
現在のhomeのルーティングは
root 'static_pages#home'
rootメソッドを使って、ルートURLをコントローラのアクションに紐付けている。
このようなルーティングを定義することは、ブラウザからアクセスしやすくすることだけでなく、名前付きルートを使ってURLを参照することができるようになる。
ルートURLにはroot_pathやroot_urlといったメソッドを通してURLを参照できる。
_pathと_urlの違いは
root_path -> '/' #ルートURL以下の文字列を返す
root_url -> 'https://www.example.com/' #完全なURLを返す
Railsチュートリアルでは基本的にroot_path書式を使う。
リダイレクトの場合のみroot_url書式を使う。(HTTPの標準としてリダイレクト時に完全なURLが要求される為)
ここでデフォルトのルーティングを名前付きルートに変更する。
例えば、
get `static_pages/help`
↓↓
get '/help', to: 'static_pages#help'
という書き方に変更する。
他のルーティングも以下のように変更する
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
さらに、テストも古くなったので、変更する
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
###5.3.3 名前付きルート
_headerパーシャルから名前付きルートのリンクに変更していく
ロゴにもリンクをつける
<header class="navbar navbar-fixed-top navbar-inverse">
<div class="container">
<%= link_to "sample app", root_path, id: "logo" %> #root_pathへリンク
<nav>
<ul class="nav navbar-nav navbar-right">
<li><%= link_to "Home", root_path %></li> #root_pathへリンク
<li><%= link_to "Help", help_path %></li> #help_pathへリンク
<li><%= link_to "Log in", '#' %></li>
</ul>
</nav>
</div>
</header>
※注意
help_pathなどは不要 私は
つけていてエラーになりました
footerパーシャル もリンク追加
<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", about_path %></li> #about_pathへリンク
<li><%= link_to "Contact", contact_path %></li> #contact_pathへリンク
<li><a href="https://news.railstutorial.org/">News</a></li>
</ul>
</nav>
</footer>
###5.3.4 リンクのテスト
これらのリンクが正しく動いているかどうかチェックするテストを書く
「統合テスト(Integration Test)」を使って一連の作業を自動化する
統合テストを使うと、アプリケーションの動作を端から端まで(end-to-end)シミュレートしてテストすることができる。
まずは、site_layoutというテストのテンプレートを生成する
$ rails generate integration_test site_layout
invoke test_unit
create test/integration/site_layout_test.rb
ここで、integration_test
と言うのは統合テストのことである点に注目。
その後ろにsite_layoutというテンプレートを生成している。
今回のテストの目的は、アプリケーションのHTML構造を調べて、レイアウトの各リンクが正しく動くかどうかチェックすること
- ルートURL(Homeページ)にGETリクエストを送る。
- 正しいページテンプレートが描画されているかどうか確かめる。
- Home,Help,About,Contactの各ページへのリンクが正しく動くか確かめる。
Railsの統合テストでは、上の三つのステップをコードに落とし込んで行く
これを確認するために、assert_templateメソッドを使ってHomeページが正しいビューを描画しているかどうか確かめる。
require 'test_helper'
class SiteLayoutTest < ActionDispatch::IntegrationTest
test "layout links" do
get root_path #root_pathを取得
assert_template 'static_pages/home' #homeのビューが表示されているか確認
assert_select "a[href=?]", root_path, count: 2 #root_pathへのaタグが2つあるか確認
assert_select "a[href=?]", help_path #help_pathへのaタグがあるか確認 以下同じ
assert_select "a[href=?]", about_path
assert_select "a[href=?]", contact_path
end
end
assert_selectメソッドは特定のリンクが存在するかどうかを、aタグとhref属性をオプションで指定して調べている。
例えば
assert_select "a[href=?]", about_path
このコードでは、?部分を自動的にabout_pathに置換している。
この時、about_path内に特殊記号があればエスケープ処理される。
これにより、次のようなHTMLがあるかどうかをチェックすることができる。
<a href="/about">...</a>
そして、ルートURLへのリンクは二つあるので、root_pathへの確認では
assert_select "a[href=?]", root_path, count: 2
とcount: 2で二つのカウントを取る(Homeページのリンクの個数を調べる)
assert_selectには様々な指定方法がある。
ここでは、頻繁に使われる一部のコードを紹介する。
Code | マッチするHTML |
---|---|
assert_select "div" | <div>foobar</div> |
assert_select "div", "foobar" | <div>foobar</div> |
assert_select "div.nav" | <div class="nav">foobar</div> |
assert_select "div#profile" | <div id="profile">foobar</div> |
assert_select "div[name=yo]" | <div name="yo">hey</div> |
assert_select "a[href=?]", '/', count: 1 | <a href="/">foo</a> |
assert_select "a[href=?]", '/', text: "foo" | <a href="/">foo</a> |
統合テストを走らせる
$ rails test:integration
統合テストが成功したら、今度はすべてのテストを流す
$ rails test
##5.4 ユーザー登録: 最初のステップ
ユーザー登録ページへのルーティングを作成する
そのために2番目のコントローラを作成する
###5.4.1 Usersコントローラ
2番目のコントローラであるUsersコントローラを作成
$ rails generate controller Users new
create app/controllers/users_controller.rb
route get 'users/new'
invoke erb
create app/views/users
...
生成されたビュー、ルーティングを名前付きルートでできるように変更していく
まずはtestから
require 'test_helper'
class UsersControllerTest < ActionDispatch::IntegrationTest
test "should get new" do
get signup_path #signup_pathへ
assert_response :success
end
end
ビューにリンクを追加
<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" %>
# signup_pathを追加
</div>
<%= link_to image_tag("rails.svg", alt: "Rails logo", width: "200px"),
"https://rubyonrails.org/" %>
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>
<p>This will be a signup page for new users.</p>
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
あとはgitへcommitして5章はこれで終わり