1
1

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 3 years have passed since last update.

コーディング未経験のPO/PdMのためのRails on Dockerハンズオン vol.4 - Static pages -

Last updated at Posted at 2020-02-08

はじめに

第4回目は、Static pages(静的なページ)を作ることにチャレンジしてみましょう!
そもそも静的なページとは、アクセスするユーザーや時間帯に関係なく同じコンテンツが表示されるページのことです。対義語は動的なページは例えばマイページのようなユーザーごとにコンテンツが変わってくるようなページのことですね。
静的なページのみで構築されているサイトを『Webサイト』、動的なページも存在するサイトを『Webアプリケーション』と呼び分けたりします。

今回は、静的なトップページを作っていきますよ!

前回のソースコード

前回のソースコードはこちらに格納してます。今回のだけやりたい場合はこちらからダウンロードしてください。

Railsの初期設定

とその前に。
今回は日本で使われ日本で運用するアプリを想定するのですが、Railsはデフォルトでは英語が使われていたりUTC(協定世界時)が使われていたりと、そのまま日本でサービス展開しようとすると面倒な部分があります。
最初にこの設定をローカライズ(日本化)しておきましょう!

Timezoneの設定

Dockerfiledocker-compose.ymlでコンテナのタイムゾーンは日本に設定していましたね(TZ=Asia/Tokyo)。Railsアプリも同じように設定してあげます。

config/application.rb
  ...
  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ではエラーメッセージなどが準備されているのですが、デフォルトでは英語で定義されています。日本でサービス展開する場合「?」となってしまうので、日本語化します。

config/application.rb
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を読み込みます。

app/javascript/packs/application.js
  require("@rails/ujs").start()
  require("turbolinks").start()
  require("@rails/activestorage").start()
  require("channels")
+ require("bootstrap")

次にBootstrapをSCSSで読み込むためにapplication.cssapplication.scssにリネームします。

# mv app/assets/stylesheets/application.css app/assets/stylesheets/application.scss

ファイルの中身はCSSの記法で書かれているものなので一度全て削除して以下のように書き換えましょう。

app/assets/stylesheets/application.scss
@import 'bootstrap/scss/bootstrap';

最後にBootstrapと依存関係にあるjQuerypopper.jsの設定を追加してあげます。

config/webpack/environment.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が出来上がっているのでアクセスできるようになっていますね。
image.png

このコマンドで作成・更新されたファイルの中身をちょっとみていきましょう!

Routing

config/routes.rb
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

app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController
  def home
  end
end

homeアクションが定義されていますね。特段処理はないので、app/views/static_pages/home.html.erbをレンダリングするのみです。

View

app/views/static_pages/home.html.erb
<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ページを表示させたいのでルーティングの設定を更新していきます。

config/routes.rb
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'を削除してるので以下のようにそんなルートないよとエラーになります。
image.png

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ページを作っていきましょう。

app/views/static_pages/home.html.erb
<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コードがありますね。

app/views/static_pages/home.html.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ファイルでリンクを作成する場合多用するコードなので覚えておきましょう。
リンク先として#を指定していますが、まだ遷移先のページを作成していないので仮置きしているだけです(#を指定しておくとリンクを押しても今いるページから遷移しないです。)

編集ができたらトップページをリロードしてみましょう。以下のページになっていれば成功です!
image.png

ふへー。ぽくなってきましたね。

ただHeaderとかFooterとかも欲しくなってきました。

ヘッダーを装飾する

突然ですが、ブラウザでトップページのソースコードをみてみましょう。(右クリックで「ページのソースを表示する」を選択するとみれます。)
app/views/static_pages/home.html.erbに全く書いた記憶がないもの、例えば<head>タグとかがありますよね。

Topページ
<!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>

これはなんでしょう?:thinking:

実はRailsアプリケーションではViewファイルの中でもLayoutと呼ばれるやつがいます。こいつはなにかというと、ページで共通な部分を表現してくれるものです。デフォルトでapp/views/layouts/application.html.erbのレイアウトファイルが利用されています。ちょっと中を覗いてみましょう。

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メソッドを有効にする必要があります。

app/controllers/application_controller.rb
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に記述します!

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ページにアクセスしてみましょう。以下のようになっていれば成功です!
image.png
おー、どんどんぽくなってきましたね!

フッターを装飾する

最後にフッターも付けちゃいましょう!今回はコピーライトを書いているくらいのフッターをば。

フッターは<footer>タグを使います。

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 %>
+
+   <footer class="bg-dark">
+     <p class="text-center text-white py-2 mb-0">(c) Hoge Inc. All Rights Reserved.</p>
+   </footer>
  </body>
  ...

特別難しいところはありませんね。再度Topページにアクセスします。
image.png
おー、ぽい。ぽいのですが、ブラウザの下の方に余白ができているのが気になりますね...
表示するコンテンツがブラウザの縦サイズに合わない場合は、フッターは画面の最下部に表示されるようにしたい...

ということで少しCSSをいじってみましょう。

app/assets/stylesheets/application.scss
  @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以上の場合はbodyheader, div, footerの3要素の高さの合計が高さになるので、footerは自然な形でdivのすぐ下に配置されることになります。

さてさて、ではTopページをリロードしてみてください!
image.png
とてもいい感じになりましたね!

スマートフォンに対応する

PCだといい感じに表示されるのですが、スマートフォンだと実はとても見にくい状態になっています。ChromeのDeveloper toolsなどで確かめてみてください(Chrome DevTools での Device Mode によるモバイル端末のシミュレート

BootstrapはもともとResponsive Design(ブラウザの横幅に合わせてCSSが変わる)に対応しているので、Viewport設定の1行をheadタグ内に挿入するだけでいい感じのデザインになります。

app/views/layouts/application.html.erb
  ...
  <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」の記事が参考になりました。

スマホモードでリロードしてみましょう。
image.png
文字の大きさもいい感じになりましたね。

Google Fontsでフォントを変える

んー。デザインはよくなってきたけどフォントがデフォルトっぽくて気になるなぁ...
フォントはこのアプリを使うユーザーの端末にフォントがインストールされていないと使えなかったりするのであんまり冒険できないところだったりします。アプリケーションの中でフォントを配布してもいいのですが、まぁ若干面倒ですね。
そこで便利なのがGoogle Fontsです。『Webフォント』と呼ばれますが、インターネット上ですでに公開されているため端末のインストールなしで利用でき、どの端末からでも同じフォントで表現ができるようになります。

app/assets/stylesheets/application.scssでGoogle Fontsの中からNoto Sans JPをインポートしてbodyfont-familyに指定するだけなんですね簡単です。

app/assets/stylesheets/application.scss
  @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

Other Hands-on Links

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?