Tech Racho エンジニアの「?」を「!」に。
  • 開発

Rails5「中級」チュートリアル(2)レイアウト(翻訳)

概要

原著者の許諾を得て翻訳・公開いたします。

Rails5中級チュートリアルはセットアップが短めで、RDBMSにはPostgreSQL、テストにはRSpecを用います。
原文が非常に長いので分割します。章ごとのリンクは順次追加します。

注意: Rails中級チュートリアルは、Ruby on Railsチュートリアル(https://railstutorial.jp/)(Railsチュートリアル)とは著者も対象読者も異なります。

目次

Rails5「中級」チュートリアル(2)レイアウト(翻訳)

それではコーディングを始めます。どこから始めてもよいのですが、私はWebサイトを新しく作るときは基本的な表示構造を最初に作ってから他のものを作るのが好みなので、この方法で進めることにしましょう。

2-1 Homeページ

今はhttp://localhost:3000を開くとRailsのwelcomeページが表示されるので、これを独自のデフォルトページに変えることにします。そのためには、Pagesコントローラを生成します。Railsのコントローラに慣れていない方は、Action Controllerを読んでRailsのコントローラの概要を理解しておいてください。コマンドプロンプトで以下を実行して、新しいコントローラを生成します。

rails g controller pages

このRailsジェネレータによっていくつかのファイルが作成されます。コマンドプロンプトの出力は以下のような感じになります。

このPagesControllerを使って、特殊な静的ページを制御します。テキストエディタでCollabfieldプロジェクトを開きましょう。私はSublime Textを使っていますが、お好みのエディタで構いません。

pages_controller.rbファイルを開きます。

app/controllers/pages_controller.rb

homeページの定義はここで行います。もちろん、homeページを別の方法で他のコントローラに定義することも可能ですが、私はhomeページをPagesControllerの内部で定義するのが好みです。

pages_controller.rbを開くと以下のようになっています(Gist)。

# app/controllers/pages_controller.rb
class PagesController < ApplicationController
end

これはPagesControllerという名前の空のクラスになっており、ApplicationControllerクラスを継承しています。ApplicationControllerのソースコードはapp/controllers/application_controller.rbにあります。

私たちが今後作成するコントローラは、すべてApplicationControllerクラスから継承します。つまり、ApplicationControllerクラスで定義したメソッドはすべてのコントローラで利用できるようになります。

indexという名前のpublicメソッドを定義しましょう。これはアクションとして呼び出せます(Gist)。

# controllers/pages_controller.rb
class PagesController < ApplicationController

  def index
  end

end

Action Controllerの概要をお読みになった方ならおわかりのように、呼び出されるコントローラとそのpublicメソッド(アクション)は「ルーティング」によって決定されます。それではルーティングを定義して、Webサイトのrootページを開いたときに呼び出されるコントローラとアクションをRailsに認識させてみましょう。app/config/routes.rbファイルを開きます。

Railsのルーティングがわからない方は、この機会にガイドのRailsのルーティングをじっくり読んでルーティングに慣れておきましょう。

ルーティングに以下のコードを追加します。

root to: 'pages#index'

追加後のroutes.rbは以下のようになります(Gist)。

# app/config/routes.rb
Rails.application.routes.draw do
  root to: 'pages#index'
end

Rubyのハッシュシンボル#はメソッドを文章の中で表記するときに使います。アクションは単なるpublicメソッドなので、pages#indexは「PagesControllerのpublicメソッド(アクション)であるindexを呼び出す」という意味です。

rootパスhttp://localhost:3000を開くと、このindexアクションが呼び出されます。しかしレンダリングするテンプレートが未定義のままなので、indexアクションに対応するテンプレートを新しく作ってみましょう。app/views/pagesディレクトリに移動してindex.html.erbファイルを開きます。このファイルには、通常のHTMLの他にERB(Embedded Ruby)コードを書けます。このファイルに以下のように書けば、テンプレートがブラウザに表示されるようになります。

<h1>Home page</h1>

http://localhost:3000を開くと、デフォルトのRails情報ページの代わりに以下のような感じで表示されるはずです。

これでやっと基本的な出発点にたどり着きました。ここからWebサイトに新しい要素を導入していきます。このあたりでgitの最初のcommitを行うのがよいでしょう。

コマンドプロンプトで以下を実行します。

git status

以下のような結果が出力されます。

参考までに、新しいRailsアプリを生成すると、新しいローカルgitリポジトリも初期化されます。

以下を実行して、現在の変更内容をすべて追加します。

git add -A

続いて以下を実行し、変更内容をすべてcommitします。

git commit -m "Generate PagesController. Initialize Home page"

試しに以下を実行してみましょう。

git status

変更点がすべてcommitされたので、「nothing to commit」と表示されます。

2-2 Bootstrap

ナビゲーションバーやレスポンシブなグリッドシステムを使えるよう、Bootstrapライブラリを使うことにします。Bootstrapを使うには、エディタでGemfileにbootstrap-sass gemを追加する必要があります。エディタでGemfileを開きます。

collabfield/Gemfile

bootstrap-sass gemをGemfileに追加します。ドキュメントにも書かれているように、sass-rails gemが存在していることも確認する必要があります。

...
gem 'bootstrap-sass', '~> 3.3.6'
gem 'sass-rails', '>= 3.2'
...

ファイルを保存して以下を実行し、追加したgemをインストールします。

bundle install

アプリを実行中の場合は、Railsサーバーを再起動して新しいgemを利用できるようにします。サーバーを再起動するには、Ctrl + Cでサーバーを停止し、rails sコマンドを再度実行してサーバーを起動するだけで完了します。

assetsディレクトリに移動してapplication.cssファイルを開きます。

app/assets/stylesheets/application.css

コメントアウトされている行の下に以下を追加します。

...
@import "bootstrap-sprockets";
@import "bootstrap";

続いてapplication.cssapplication.scssにリネームします。この変更はRailsでBootstrapライブラリを用いるのに必要です。また、これによってSassの機能を使えるようになります。

今後Sass変数を作成する場合に備えて、すべての.scssファイルがレンダリングされるよう順序を変更する必要があります。Sass変数が使われる前に定義されるよう、順序を変更したいと思います。

これを行うには、application.scssファイルの以下の2行を削除します。

*= require_self
*= require_tree .

Bootstrapライブラリが使えるようになるまであと少しです。もうひとつやっておかなければならないことがあります。bootstrap-sassドキュメントに記載されているとおり、BootstrapのJavaScriptはjQueryライブラリに依存しています。RailsでjQueryを使えるようにするには、jquery-rails gemを追加する必要があります。

訳注: Rails 5.1以降でjQueryを使う場合、gemよりもWebpackとyarnでインストールする方法が標準になりつつあります。この時点でWebpackとyarnでjQueryをインストールする場合、gem 'jquery-rails'の代わりに以下の方法が使えます。
1. Gemfileにgem 'webpacker', github: 'rails/webpacker'を追加し、bundle installを実行する
2. rails webpacker:installを実行し、Webpackerをインストールする
3. yarn install jqueryを実行し、jQueryをインストールする
サーバー再起動後の手順は同じです。

gem 'jquery-rails'

以下を実行します。

bundle install

再度サーバーを再起動します。

最後は、アプリのJavaScriptファイルでBootstrapとjQueryをrequireする手順です。application.jsファイルを開きます。

app/assets/javascripts/application.js

以下の行を追加します(訳注: require_tree .の前の行に追加します)。

//= require jquery
//= require bootstrap-sprockets

変更をgitにcommitします。

git add -A
git commit -m "Add and configure bootstrap gem"

2-3 ナビゲーションバー

ナビゲーションバーは、Bootstrapのnavbarコンポーネントを元に今後いろいろ変更を加えます。ナビゲーションバーはパーシャルテンプレートに保存します。

この時点でこれを行う理由は、アプリのあらゆるコンポーネントを別ファイルに分割するのが望ましいためです。コンポーネントを分割することで、アプリのコードのテストや管理がずっとやりやすくなりますし、コードを複製せずにコンポーネントをアプリの別の箇所で再利用することもできます。

以下のディレクトリに移動します。

views/layouts

以下のファイルを作成します。

_navigation.html.erb

パーシャルファイルの先頭にはアンダースコア_を付け、Railsフレームワークがこのファイルをパーシャルとして認識できるようにします。これを行うには、Bootstrapドキュメントのnavbarコンポーネントのコードをこのファイルにコピペして保存します。このパーシャルをWebサイトで表示するには、コードのどこかでレンダリングする必要があります。views/layouts/application.html.erbファイルを開きます。このファイルの内容は、デフォルトで常にレンダリングされます。

application.html.erbファイルには以下のメソッドがあります。

<%= yield %>

リクエストされたテンプレートはここでレンダリングされます。HTMLファイル内でRuby構文を使うには、<% %>(ERBの書式)で囲む必要があります。ERB構文の違いについて手っ取り早く知りたい方は、StackOverflowの回答をご覧ください。

2-1 Homeページセクションでは、ルーティングを設定することでroot URLが認識されるようにしました。これにより、GETリクエストを送信してrootページに移動すると、常にPagesController#indexアクションが呼び出されます。ルーティングに対応するアクション(ここではindex)は、yieldメソッドでレンダリングされるテンプレートを用いてレスポンスを返します。homeページのテンプレートがapp/views/pages/index.html.erbにあることを思い出しましょう。

ナビゲーションバーはすべてのページに表示したいので、ナビゲーションファイルのレンダリングはデフォルトのapplication.html.erbファイル内で行います。パーシャルファイルをレンダリングするには、パーシャルへのパスを引数に与えてrenderメソッドを呼び出します。以下のようにyieldメソッドの上にrenderを追加します。

...
<%= render 'layouts/navigation' %>
<%= yield %>
...

これで、http://localhost:3000をブラウザで開くと以下のようにナビゲーションバーが表示されます。

予告のとおり、このナビゲーションバーに変更を加えることにします。最初に、<li>要素と<form>要素をすべて削除します。今後ここには独自の要素を作成します。変更後の_navigation.html.erbファイルは次のようになります。

<!-- views/layouts/_navigation.html.erb -->
<nav class="navbar navbar-default">
  <div class="container-fluid">
    <!-- ブランド表示と、モバイル表示切り替え用のグループ化 -->
    <div class="navbar-header">
      <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
        <span class="sr-only">Toggle navigation</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
      <a class="navbar-brand" href="#">Brand</a>
    </div>

    <!-- ナビゲーションリンク/フォームなどのコンテンツをここにまとめて表示をオンオフできるようにする -->
    <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
      <ul class="nav navbar-nav">
      </ul>

      <ul class="nav navbar-nav navbar-right">
      </ul>
    </div><!-- /.navbar-collapse -->
  </div><!-- /.container-fluid -->
</nav>

これでレスポンシブなナビゲーションバーの基本部分ができました。ここでgitに新しくcommitするのがよいでしょう。コマンドプロンプトで以下を実行します。

git add -A
git commit -m "Add a basic navigation bar"

今度はナビゲーションバーの名前をBrandからcollabfieldに変更します。Brandはlink要素なので、リンクの生成には[link_to](https://apidock.com/rails/ActionView/Helpers/UrlHelper/link_to)メソッドを使うべきです。その理由は、link_toメソッドを使う方がURIパスを簡単に生成できるからです。コマンドプロンプトでプロジェクトディレクトリに移動し、以下のコマンドを実行します。

rails routes

このコマンドは、routes.rbファイルで生成される利用可能なルーティングを表示します。以下が出力されます。

この時点のルーティングは先ほど定義した1つだけです。出力結果にPrefixカラムがあることにご注目ください。このprefixを使って、表示したいページへのパスを生成できます。パスの生成は、このprefix名の後ろに_pathを追加するだけでできます。たとえばroot_pathと書けば、rootページへのパスが生成されます。それでは、このlink_toメソッドとルーティングの力を借りてやってみましょう。

<a class="navbar-brand" href="#">Brand</a>

上のコードを以下に置き換えます。

<%= link_to 'collabfield', root_path, class: 'navbar-brand' %>

メソッドの使い方がよくわからなくても、適当にググればたいてい解説ドキュメントを見つけられることを覚えておきましょう。たまにハズレのドキュメントもあるので、その場合はもう少し丁寧にキーワードを指定してググれば、有用なブログ記事やStackOverflowの回答が見つかることもあります。

第1引数で渡す文字列は、<a>要素の値を追加します。第2引数はパスの指定に必要で、ここでパスを生成するのにルーティングが役立ちます。第3引数はオプションで、ここで渡したものはhtml_optionsハッシュに組み込まれます。ここでは、ナビゲーションバーにBootstrapを効かせたいので、navbar-brandクラスの追加が必要です。

ただいまの小さな変更をgitにcommitしましょう。この後のセクションでアプリのデザインに手を加える予定ですが、このナビゲーションバーから変更を開始します。

git add -A
git commit -m "Change navigation bar's brand name from Brand to collabfield"

2-4 スタイルシート

スタイルシートファイルの構成方法をご紹介します。Railsには、スタイルシートの構成方法についての厳密な規則はなく、人それぞれ少しずつ違っています。

ここでは、私が普段用いている構成方法をご紹介します。

  • baseディレクトリ: アプリ全体で使われるSass変数やスタイルはここに置いています(デフォルトのフォントサイズやデフォルト要素のスタイルなど)。
  • partialsディレクトリ: ほとんどのスタイルはここに置いています。このディレクトリでは、コンポーネントやページごとにスタイルを分割するようにしています。
  • responsiveディレクトリ: ここでは、異なる画面サイズごとに、異なるスタイルルールを定義しています(デスクトップ画面のスタイル、タブレット画面のスタイル、スマートフォン画面のスタイルなど)。

最初に、以下を実行してgitリポジトリで新しいブランチを切ります。

git checkout -b "styles"

これにより、新しいgit branchが作成され、自動的にそのブランチに切り替わります。今後、新たなコード変更を別ブランチ上で実装する場合はこのようにします。

別ブランチを切る理由は、現在動作しているバージョン(masterブランチ)から、プロジェクトに追加する新しいコードを切り離し、変更によってmasterブランチに悪影響が生じることのないようにするためです。

実装が終わったら、変更をmasterブランチにmergeできます。

最初にディレクトリをいくつか作成します。

  • app/assets/stylesheets/partials/layout

このlayoutディレクトリでnavigation.scssというファイルを作成し、以下のコードを追加します(Gist)。

//app/assets/stylesheets/partials/layout/navigation.scss
.navbar-default, .navbar-toggle:focus, .collapsed, button.navbar-toggle {
  background: $navbarColor !important;
  border: none;
  a {
    color: white !important;
  }
}

上のScssコードでは、navbarの背景色とリンクの色を変更しています。既にお気づきのように、aセレクタが別の宣言ブロックの内部でネストしていますが、これはSassの機能です。!importantは、デフォルトのBootstrapスタイルを強制的にオーバーライドするのに使います。最後に、この部分では色名の代わりにSass変数が使われていることにお気づきかと思います。Sass変数を使う理由は、アプリ全体で色を変更できるようにするためです。このSass変数を定義しましょう。

最初に以下のディレクトリを作成します。

app/assets/stylesheets/base

baseディレクトリの下にvariables.scssファイルを作成し、以下を定義します。

$navbarColor: #323738;

試しにこの時点でhttp://localhost:3000をブラウザで開いてみると、スタイルはまだ何も変更されていません。スタイルが反映されない理由は、2-2 Bootstrapセクションでapplication.scssファイルから以下を削除したためです。

*= require_self
*= require_tree .

上を削除したのは、すべてのスタイルが自動的にimportされることのないようにするためでした。

つまり、スタイルシートのファイルを新しく作成するときは、メインのapplication.scssファイルで(明示的に)importしなければならないということです。importを追加したapplication.scssファイルは次のようになります(Gist)。

//app/assets/stylesheets/application.scss
// ...デフォルトのコメント

// Bootstrap
@import "bootstrap-sprockets";
@import "bootstrap";

// Variables
@import "base/variables";

// Partials - メインのcssファイル
@import "partials/layout/*";

variables.scssをpartialよりも先にimportする理由は、partialで使われるSass変数より先にSass変数が定義されるようにするためです。

navigation.scssファイルのコードの冒頭に、さらに以下のCSSを追加します。

//app/assets/stylesheets/partials/layout/navigation.scss
nav {
  .navbar-header {
    width: 100%;
    button, .navbar-brand {
      transition: opacity 0.15s;
    }
    button {
      margin-right: 0;
    }
    button:hover, .navbar-brand:hover {
      opacity: 0.8;
    }
  }
}

好みに応じて、上のコードを冒頭に追加する代わりに末尾に追加しても構いません。個人的には、CSSセレクタの「詳細度」の順でCSSコードを配置およびグループ化するようにしています。繰り返しますが、CSSファイルの構成方法は人それぞれ少しずつ違っています。私の場合、詳細度の小さい(大雑把な)セレクタを上に、詳細度の大きいセレクタを下に置くようにしています。たとえば、要素型セレクタはクラスセレクタより上になり、クラスセレクタはIDセレクタより上になるといった具合です。

ここで変更をgitにcommitしましょう。

git add -A
git commit -m "Add CSS to the navigation bar"

今度は、画面を下にスクロールしてもナビゲーションバーが常に最上部に表示されるようにしたいと思います。現時点でスクロールするほどのコンテンツがありませんが、コンテンツは今後増えます。この段階でナビゲーションバーを固定しておくのがよいとは思いませんか?

これを行うには、Bootstrapのnavbar-fixed-topクラスを使います。次のように、nav要素にこのクラスを追加します。

<!-- views/layouts/_navigation.html.erb -->
<nav class="navbar navbar-default navbar-fixed-top">

ついでに、Bootstrap Grid Systemの左側の境界にcollabfieldを配置したいと思います。現在のクラスがcontainer-fluidになっているので、現時点のcollabfieldはviewportの左側境界に配置されています。これを変更するには、(2行目の)このクラスをcontainerに変更します。

変更後の_navigation.html.erbファイルは次のようになります。

<!-- views/layouts/_navigation.html.erb -->
<div class="container">

変更をcommitします。

git add -A
git commit -m "
- in _navigation.html.erb add navbar-fixed-top class to nav.
- Replace container-fluid class with container"

http://localhost:3000をブラウザで開くと、Home pageというテキストがナビゲーションバーの下に隠れてしまいました。これはnavbar-fixed-topクラスのせいです。この問題を解決するには、navigation.scssに以下を追加して<body>要素を下に下げます。

//app/assets/stylesheets/partials/layout/navigation.scss
body {
 margin-top: 50px;
}

これで、アプリは以下のように正常に表示されるはずです。

変更をcommitします。

git add -A
git commit -m "Add margin-top 50px to the body"

先ほど、新しいブランチを切ってそこに切り替えて作業していたのを思い出しましょう。ここでmasterブランチに戻ることにします。

以下のコマンドを実行します。

git branch

以下のようにブランチのリストが表示されています。現在のブランチはstylesになっています。

masterブランチに切り替えるには、以下を実行します。

git checkout master

以下を実行すれば、stylesブランチで行った変更をすべてmergeできます。

git merge styles

このコマンドによって、2つのブランチがmergeされ、変更の概要が以下のように表示されます。

styleブランチが不要になったので、以下を実行して削除します。

git branch -D styles

関連記事

新しいRailsフロントエンド開発(1)Asset PipelineからWebpackへ(翻訳)


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。