#第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]-->
・navbar
、navbar-fixed-top
、navbar-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を下記のコードに変更
<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/" %>
・この一文はには、Bootstrap
のbtn-lg
やbtn-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
属性は、画像がなかったら表示される文字列で、聴覚障害のあるユーザーが使う際に、属性内の文字が読まれる。
###BootstrapとカスタムCSS
先ほどHTMLに多くのCSSクラスを付けたが、Bootstrapを使うとより簡単にきれいなWebデザインが作れる。また、Bootstrapを使うとレスポンシブデザインに対応できるのが良い点。これは、端末ごとに画面のサイズが異なっても、見やすいようにデザインを変えてくれるという優れもの。
なので、Bootstrapをインストールする
①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を追加
@import "bootstrap-sprockets";
@import "bootstrap";
rails serverを起動しなおし、ブラウザを立ち上げ
さっきよりはいい感じ
スタイルをCSSに追加
/* universal */
body {
padding-top: 60px;
}
section {
overflow: auto;
}
textarea {
resize: vertical;
}
.center {
text-align: center;
}
.center h1 {
margin-bottom: 10px;
}
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を色々と追加した結果が下の図
ロゴとか文字サイズ、位置、ボタン等各種見れるレベルになってきた。
###パーシャル(partial)
先ほど、HTML shim対応ように書いたコードを隠せたらよりコードがすっきりするということで、パーシャルという機能を使って、一か所にまとめる方法がある。
各ファイル名の前にアンダースコアを入れる。(ディレクトリの中のパーシャルを識別しやすくするための命名規則)
→_shim.html.erb
や_header.html.erb
呼び出し元は、render
メソッドで呼び出すが、その際にアンダースコアは含めない。
<%= render 'layouts/shim' %>
や<%= render 'layouts/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' %>
</head>
<body>
<%= render 'layouts/header' %>
<div class="container">
<%= yield %>
</div>
</body>
</html>
同様にフッターも作成する。
<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を追加する。
/* 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
メソッドで置き換えたところも問題ない。
演習
<%= render 'layouts/rails_default' %>
を追加し、パーシャル作成した。
<!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には適用されるが、画像ファイルには適用されないというところ。
*= require_tree .
アセットディレクトリにあるすべてのCSSファイルをアプリケーション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
は下のコードになる。
@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 %>
###Contactページ
ここは第3章でやってたので省略
###RailsのルートURL
rootURLを定義することにより、root_path
やroot_url
といったヘルパーメソッドが使えるようになる。
root_pathとroot_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_path
やhelp_url
の名前付きルートが使えるようになる。
help_path -> '/help'
help_url -> 'https://www.example.com/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
これでテストは成功。
因みに名前付きルートは、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
メソッドを使用して、ビューが正しく描画されているか確かめる。
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
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.エラーが出たことを確認
エラーを解決するため、テストに変更を加える。
require 'test_helper'
class UsersControllerTest < ActionDispatch::IntegrationTest
test "should get new" do
get signup_path
assert_response :success
end
end
get signup_path
に変更。
ユーザー登録ページにリンクを追加
<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/" %>
登録ページ(スタブ)を作成
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>
<p>This will be a signup page for new users.</p>
演習
サインアップにもテストを追加した。
get signup_path
assert_select "title", full_title("Sign up")
#最後に
最後にコミットして、Githubにあげれば終了
レイアウトもかなり奥が深い分野だと思う。実際にユーザーが使うのは画面に表示されているところなのでしっかりと作っていきたいところである。