はじめに
第4回目は、Static pages(静的なページ)を作ることにチャレンジしてみましょう!
そもそも静的なページとは、アクセスするユーザーや時間帯に関係なく同じコンテンツが表示されるページのことです。対義語は動的なページは例えばマイページのようなユーザーごとにコンテンツが変わってくるようなページのことですね。
静的なページのみで構築されているサイトを『Webサイト』、動的なページも存在するサイトを『Webアプリケーション』と呼び分けたりします。
今回は、静的なトップページを作っていきますよ!
前回のソースコード
前回のソースコードはこちらに格納してます。今回のだけやりたい場合はこちらからダウンロードしてください。
Railsの初期設定
とその前に。
今回は日本で使われ日本で運用するアプリを想定するのですが、Railsはデフォルトでは英語が使われていたりUTC(協定世界時)が使われていたりと、そのまま日本でサービス展開しようとすると面倒な部分があります。
最初にこの設定をローカライズ(日本化)しておきましょう!
Timezoneの設定
Dockerfile
やdocker-compose.yml
でコンテナのタイムゾーンは日本に設定していましたね(TZ=Asia/Tokyo
)。Railsアプリも同じように設定してあげます。
...
module App
class Application < Rails::Application
config.load_defaults 6.0
config.generators.system_tests = nil
+
+ # Timezone
+ config.time_zone = 'Tokyo'
+ config.active_record.default_timezone = :local
end
end
+
の行が追加の行です。
time_zone
はRailsアプリが時間を扱うときにどのtimezoneで動くかを指定する設定でデフォルトだとUTCになってます。例えばTime.current
で現在の時間を表示できたりするのですが、この結果をどのtimezoneで表示するかがこの設定で決まります。この後、Modelを保存したりするときなどもその作成日時などがこのtimezoneで設定されます。
active_record.default_timezone
はDBに書き込まれている時間をどのtimezoneとして扱うかの設定です。
言語の設定
Railsではエラーメッセージなどが準備されているのですが、デフォルトでは英語で定義されています。日本でサービス展開する場合「?」となってしまうので、日本語化します。
module App
class Application < Rails::Application
config.load_defaults 6.0
config.generators.system_tests = nil
# Timezone
config.time_zone = 'Tokyo'
config.active_record.default_timezone = :local
+
+ # Language
+ config.i18n.default_locale = :ja
end
end
この設定で、config/locales/
にあるyamlファイルの中からja:
で定義されている文字列が表示されるようになるんです。日本語版のファイルを作ってくださっている方がいらっしゃいますのでベースとして利用させていただきましょう。
rails-i18n/ja.yml at master · svenfuchs/rails-i18n
こちらのファイルをconfig/locales/ja.yml
として保存することで日本語化完了です!
ちなみに『i18n』は『internationalization』のことで『国際化』と訳されます。アクセスする国によって時間や言語を変えたりすることです(今回は日本オンリーに対応ですが...笑い)。頭の『i』とお尻の『n』の間に18文字あるので『i18n』と表現します。技術系だとこういうの最近多いっすよね?
Bootstrap
まだまだ静的なページの作成には入りませんよ!次は、Bootstrapを使えるようにしていきましょう。
BootstrapはTwitter社が開発したCSSフレームワークです。
レスポンシブデザインに標準で対応されており、CSSだけでなくJavascript(jQuery)も含まれています。
多くの人に使われているためインターネット上に情報があふれていますので初心者にも安心です。一方で、多くの人に使われているため似通ったデザインになってしまうので少し慣れてくると敬遠されがちな印象です。
Boostrapをyarnでインストール
Rails5まではgemでインストールするのが主流だったと思うのですが、Rails6ではWebpackerが必須になったこともありパッケージマネージャー経由でインストールするのが主流になっていくと思われますのでその方法で。
まだコンテナを立ち上げていなかったですね。それではコンテナを立ち上げてコンテナの中でコマンドを実行していきましょう!
$ docker-compose up -d
$ docker-compose exec web ash
# yarn add bootstrap jquery popper.js
これでBootstrap関連のライブラリをインストールできたのでRailsアプリで使えるように設定していきます。
まずapplication.jsでBootstrapを読み込みます。
require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")
+ require("bootstrap")
次にBootstrapをSCSSで読み込むためにapplication.css
をapplication.scss
にリネームします。
# mv app/assets/stylesheets/application.css app/assets/stylesheets/application.scss
ファイルの中身はCSSの記法で書かれているものなので一度全て削除して以下のように書き換えましょう。
@import 'bootstrap/scss/bootstrap';
最後にBootstrapと依存関係にあるjQuery
とpopper.js
の設定を追加してあげます。
const { environment } = require('@rails/webpack')
+
+ const webpack = require('webpack')
+
+ environment.plugins.append('Provide', new webpack.ProvidePlugin({
+ $: 'jquery/src/jquery',
+ jQuery: 'jquery/src/jquery',
+ Popper: ['popper.js', 'default']
+ }))
module.exports = environment
たったこれだけでBootstrapの準備が整いました。Bootstrapで用意されているあらゆるスタイルシートやjavascriptが利用可能になっちゃいました。
BootstrapがどんなことができるかはBootstrap公式のDocumentationを見るのが一番いいと思っているのですが、これだけの表現が上の設定でできるようになってしまったのです。
Topページを作成する
いよいよ静的なページを作っていきます。
静的なページではModelのような動的なものはいらないのでMVCのVCがあればOKですね。
VC(ルーティングも!)はrails generate controller
コマンドで作成することができます。今回はstatic_pages
controllerでhome
actionを作ってみましょう。
# rails generate controller static_pages home
rails generate controller NAME [action action]
の形式でコマンドを実行できます。actionは複数指定可能です。
慣習的にNAME
は複数形を用います。
このコマンドだけですでにhttp://localhost:3000/static_pages/home
へのRouting, Controller#Action, Viewが出来上がっているのでアクセスできるようになっていますね。
このコマンドで作成・更新されたファイルの中身をちょっとみていきましょう!
Routing
Rails.application.routes.draw do
get 'static_pages/home'
end
get 'static_pages/home'
の行が追加されてます。前回はresouces
メソッドを使ったルーティングの指定の仕方でしたが、このように一つ一つのルートを定義することもできるのです。
これだけで「static_pages/home
のパスにget
メソッドできたリクエストをstatic_pages
controllerのhome
actionにルーティングする」ことを定義しています。
Controller
class StaticPagesController < ApplicationController
def home
end
end
home
アクションが定義されていますね。特段処理はないので、app/views/static_pages/home.html.erb
をレンダリングするのみです。
View
<h1>StaticPages#home</h1>
<p>Find me in app/views/static_pages/home.html.erb</p>
app/views/static_pages/home.html.erb
ファイルが生成されています。
中身も先ほどhttp://localhost:3000/static_pages/home
と同じものが表示されていることがわかりますね。
ルートパスをTopページにする
現状ルートパス(http://localhost:3000
)にアクセスするとHello worldのページが表示されています。本当はこのURLにアクセスしたときにTopページを表示させたいのでルーティングの設定を更新していきます。
Rails.application.routes.draw do
- get 'static_pages/home'
+ root 'static_pages#home'
end
ルートパスの設定の仕方は`root '[controller_name]#[action_name]'とするだけです!
試しにhttp://localhost:3000/
にアクセスしてみてください。Topページが表示されるようになりましたね。
またhttp://localhost:3000/static_pages/home
にもう一度アクセスしてみましょう。get 'static_pages/home'
を削除してるので以下のようにそんなルートないよとエラーになります。
Bootstrapでページを装飾する
にしても味気ないページですよね。ということでBootstrapのCSSを使いながらいい感じのページに装飾していきましょう!
CSSはHTMLのスタイル(色とか大きさとか)をまとめた変数みたいなものです。HTMLタグ(<h1>とか<p>とか)にclass属性をつけることでそのスタイルが適用されます。SCSSはCSSを書きやすくしたものです(なので本質はCSS)。
<h1 class="hoge">Hello.</h1>
.hoge {
font-size: 32px;
color: red;
}
これで32pxの赤文字で"Hello."が表示されるようになるってイメージですね。
Topページのコンテンツを装飾する
ではまず、app/views/static_pages/home.html.erb
を更新していい感じのTopページを作っていきましょう。
<div class="jumbotron mb-0">
<div class="container text-center">
<h1>Welcome to Sample App.</h1>
<h2>Twitterみたいなアプリです。</h2>
<%= link_to "Sign up now!", "#", class: "btn btn-lg btn-primary mt-5" %>
</div>
</div>
ここに出てくるjumbotron
, container
, text-center
, btn
, btn-lg
, btn-primary
, mt-5
はすべてBootstrapのclassです。Bootstrapのclassの使い方はしっかりと公式のDocumantationがあるのでそちらをみてみましょう。実際に自分で何かを作るとなるとこういう公式ドキュメントと向き合うことが一番の近道だったりするのでここでは説明しないっす。
1つだけ、ERBコードがありますね。
<%= link_to "Sing up now!", "#", class: "btn btn-lg btn-primary mt-5" %>
link_to
はリンクを作るためのメソッドです。HTML的にいえば<a>
タグを作ってくれるということです。
link_to [表示文字], [リンク先], [options]
の構文になってまして、今回の例だと以下のような<a>
タグが出来上がります。
<a href="#" class="btn btn-lg btn-primary mt-5">Sign up now!</a>
リンク文字列やリンク先などに変数を指定でき(例えばルートパスはroot_pathが変数として割り当てられている)るのでERBファイルでリンクを作成する場合多用するコードなので覚えておきましょう。
リンク先として#を指定していますが、まだ遷移先のページを作成していないので仮置きしているだけです(#を指定しておくとリンクを押しても今いるページから遷移しないです。)
編集ができたらトップページをリロードしてみましょう。以下のページになっていれば成功です!
ふへー。ぽくなってきましたね。
ただHeaderとかFooterとかも欲しくなってきました。
ヘッダーを装飾する
突然ですが、ブラウザでトップページのソースコードをみてみましょう。(右クリックで「ページのソースを表示する」を選択するとみれます。)
app/views/static_pages/home.html.erb
に全く書いた記憶がないもの、例えば<head>
タグとかがありますよね。
<!DOCTYPE html>
<html>
<head>
<title>App</title>
<meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="3702TRDf2wiVe1ErXV1NWoksR1sWw1+LO31miZuonos9K8zOmMwJ+VotF3ZXCPRP+g9IiKoZovoA8HpIjLjqmA==" />
<link rel="stylesheet" media="all" href="/assets/application.debug-9519f0a34796e622c941135a612890361ca8a44f05d715927aaa1575ce29c235.css" data-turbolinks-track="reload" />
<script src="/packs/js/application-923746675d9ea6d857bd.js" data-turbolinks-track="reload"></script>
</head>
<body>
<div class="jumbotron">
<div class="container text-center">
<h1>Welcome to Sample App.</h1>
<h2>Twitterみたいなアプリです。</h2>
<a class="btn btn-lg btn-primary mt-5" href="#">Sign up now!</a>
</div>
</div>
</body>
</html>
これはなんでしょう?
実はRailsアプリケーションではViewファイルの中でもLayoutと呼ばれるやつがいます。こいつはなにかというと、ページで共通な部分を表現してくれるものです。デフォルトでapp/views/layouts/application.html.erb
のレイアウトファイルが利用されています。ちょっと中を覗いてみましょう。
<!DOCTYPE html>
<html>
<head>
<title>App</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' %>
</head>
<body>
<%= yield %>
</body>
</html>
ERBコードが入っているので少し表現は違いますが、Topページのソースコードと似た構文をしていることがわかります。実は最終的に表示されているHTMLはapp/views/static_pages/home.html.erb
がこのapp/views/layouts/application.html.erb
の<%= yield %>
に代入されたものです。
少しだけapp/views/layouts/application.html.erb
の中身をご紹介。
まず全体の構文ですが、HTMLの基本の構文が記述されていますね。
<!DOCTYPE html>
<html>
<head>
...
</head>
<body>
...
</body>
</html>
<body>
の中のyeild
については上で話した通りで、actionのviewファイルの中身が代入されるようになっています。
<head>
の中はどうでしょうか?
title
title
はブラウザのタブのところに表示される文字列です。今だとApp
と指定されているのでそれが表示されていますね。
scrf_meta_tags
CSRF(Cross-Site Request Forgery)対策のための記述です。今回のTopページではあまり活躍しませんが、POSTリクエストを飛ばすページで活躍します。簡単にいえば、正しいユーザーからのリクエストなのかというリクエストの真正性(Authenticity)を検証するために必要なことを開発者が意識せずともRailsがカバーしてくれるようになるとのこと。
これを有効にするためにはApplicationControllerでprotect_from_forgery
メソッドを有効にする必要があります。
class ApplicationController < ActionController::Base
+ protect_from_forgery with: :exception
end
csp_meta_tag
CSP(Content Security Policy)に必要なタグを生成してくれるコードです。CSPはXSS(Cross Site Scripting)を防ぐためのもので、コンテンツの提供元や取得方法を制限する手法です。別途、制限を設定することで動作するようになります。ここでは紹介程度。
stylesheet_link_tag
stylesheetの読み込み。app/assets/stylesheets/application.css[scss]
を読み込んでくれている。
javascript_pack_tag
javascriptの読み込み。app/javascript/packs/application.js
を読み込んでくれている。
ってな具合です。
さて、長々と話してしまいましたが、本当に言いたかったことは『ヘッダーやフッターは各ページ個別のものではなく、サイトで共通のものであるはずなので、レイアウトファイルに記述するんだよ』ということです。
では、ヘッダーをapp/views/layouts/application.html.erb
に記述します!
...
<body>
+ <header class="navbar navbar-dark navbar-expand bg-dark">
+ <div class="container">
+ <%= link_to "sample app", root_path, class: "navbar-brand" %>
+ <ul class="navbar-nav">
+ <li class="nav-item"><%= link_to "Home", root_path, class: "nav-link" %></li>
+ <li class="nav-item"><%= link_to "Sign in", "#", class: "nav-link" %></li>
+ </ul>
+ </div>
+ </header>
+
<%= yield %>
</body>
...
このあたりもBootstrapの公式ドキュメントを参考に記述してみました => Navbar · Bootstrap
Topページにアクセスしてみましょう。以下のようになっていれば成功です!
おー、どんどんぽくなってきましたね!
フッターを装飾する
最後にフッターも付けちゃいましょう!今回はコピーライトを書いているくらいのフッターをば。
フッターは<footer>
タグを使います。
...
<body>
<header class="navbar navbar-dark navbar-expand bg-dark">
<div class="container">
<%= link_to "sample app", root_path, class: "navbar-brand" %>
<ul class="navbar-nav">
<li class="nav-item"><%= link_to "Home", root_path, class: "nav-link" %></li>
<li class="nav-item"><%= link_to "Sign in", "#", class: "nav-link" %></li>
</ul>
</div>
</header>
<%= yield %>
+
+ <footer class="bg-dark">
+ <p class="text-center text-white py-2 mb-0">(c) Hoge Inc. All Rights Reserved.</p>
+ </footer>
</body>
...
特別難しいところはありませんね。再度Topページにアクセスします。
おー、ぽい。ぽいのですが、ブラウザの下の方に余白ができているのが気になりますね...
表示するコンテンツがブラウザの縦サイズに合わない場合は、フッターは画面の最下部に表示されるようにしたい...
ということで少しCSSをいじってみましょう。
@import 'bootstrap/scss/bootstrap';
+
+ body {
+ display: flex;
+ flex-direction: column;
+ min-height: 100vh;
+ }
+
+ footer {
+ margin-top: auto;
+ }
本当はfooter用のファイルを作ってappliction.scss
で@import
する方が整理されるのでいいのですが、今回は分量も少ないのでひとまずapplication.scss
に記述します。
これで何をしているかというと、まずbody
タグに対してdisplay: flex;
をあててます。これはFlexboxという要素をきれいに横並びや縦並びでレイアウトしてくれるレイアウトモジュールです。
flex-direction: column;
と定義しているので縦方向に要素を並べてくれます。
そして、min-height: 100vh;
を定義しているのでbody
タグで囲まれた要素は最低ブラウザいっぱいの高さを持つ要素に指定されたことになります。
今、body
タグの中にはheader
, <%= yeild %>の中のdiv
, footer
の3つの要素が同じレベルに存在しています。これを縦方向に並べていることになるんですね。
この時、Flexboxの親要素(今回だとbody
)をコンテナ、子要素(今回だとheader
, div
, footer
)をアイテムと呼びます。
footer
にはmargin-top: auto;
が定義されています。これは親要素に対して、この要素の上部に最大限のmargin(余白)を付与することを表していて、簡単に言うと親要素の一番下に配置するってことになります。
今、親要素のbody
は縦に最小で100vhの高さを持ちます。コンテンツが足りない場合はbody
は100vhの高さになるのでfooter
は100vhの一番下に位置します。コンテンツが100vh以上の場合はbody
もheader
, div
, footer
の3要素の高さの合計が高さになるので、footer
は自然な形でdiv
のすぐ下に配置されることになります。
さてさて、ではTopページをリロードしてみてください!
とてもいい感じになりましたね!
スマートフォンに対応する
PCだといい感じに表示されるのですが、スマートフォンだと実はとても見にくい状態になっています。ChromeのDeveloper toolsなどで確かめてみてください(Chrome DevTools での Device Mode によるモバイル端末のシミュレート)
BootstrapはもともとResponsive Design(ブラウザの横幅に合わせてCSSが変わる)に対応しているので、Viewport設定の1行をhead
タグ内に挿入するだけでいい感じのデザインになります。
...
<head>
<title>App</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
+ <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
</head>
...
呪文みたいな感じで書いちゃうことが多いのですが、意味は「もう逃げない。HTMLのviewportをちゃんと理解する - Qiita」の記事が参考になりました。
スマホモードでリロードしてみましょう。
文字の大きさもいい感じになりましたね。
Google Fontsでフォントを変える
んー。デザインはよくなってきたけどフォントがデフォルトっぽくて気になるなぁ...
フォントはこのアプリを使うユーザーの端末にフォントがインストールされていないと使えなかったりするのであんまり冒険できないところだったりします。アプリケーションの中でフォントを配布してもいいのですが、まぁ若干面倒ですね。
そこで便利なのがGoogle Fontsです。『Webフォント』と呼ばれますが、インターネット上ですでに公開されているため端末のインストールなしで利用でき、どの端末からでも同じフォントで表現ができるようになります。
app/assets/stylesheets/application.scss
でGoogle Fontsの中からNoto Sans JP
をインポートしてbody
のfont-family
に指定するだけなんですね簡単です。
@import 'bootstrap/scss/bootstrap';
+ @import url('https://fonts.googleapis.com/css?family=Noto+Sans+JP&display=swap');
body {
+ font-family: 'Noto Sans JP', sans-serif!important;
display: flex;
flex-direction: column;
min-height: 100vh;
}
footer {
margin-top: auto;
}
少し特別な書き方として、font-family
のところで!important
と末尾に記述してます。
!important
はCSSの適用優先度を決めるための記述で、最優先で反映させるものに使います。つまり、他にfont-family
を記述したとしてもこいつが適用されるってことです。
Bootstrapでもfont-family
を定義してくれていたりするので、こっちを最優先にするために記述してます。
Google Fontsのサイトでお好きなフォントを探してみて適用してみてください!日本語フォントの場合は、『Language』で『Japanese』を選択すれば日本語フォントありに絞られます。『+』で使用フォントを追加するとインポートの仕方とかも教えてくれるので気軽に使えます。もっと詳しくはこちらの記事から↓
【2019年版】Google Fontsの使い方:初心者向けに解説!
後片付け
最後にコンテナを停止しておきましょう。
# exit
$ docker-compose down
まとめ
今回は、Railsの初期設定とBootstrapのインストール、Topページのデザインをやってみました。
静的なページではありますが、コーディングした内容が反映されてどんどん変わっていくのを実感する楽しみを感じてもらえたんじゃないかと思います。
次回は今回触れていなかったModelが主役です。Modelの作成からModelに用意されているメソッドを使ってデータの作成や更新を遊んでみようと思います。
では、次回も乞うご期待!ここまでお読みいただきありがとうございました!
Next: コーディング未経験のPO/PdMのためのRails on Dockerハンズオン vol.5 - Model and CRUD - - Qiita
本日のソースコード
Reference
- Ruby on Rails チュートリアル:実例を使って Rails を学ぼう
- Railsタイムゾーンまとめ - Qiita
- rails-i18n/ja.yml at master · svenfuchs/rails-i18n
- もう迷わない!CSS Flexboxの使い方を徹底解説 | Web Design Trends
- 【2019年版】Google Fontsの使い方:初心者向けに解説!