Rails 5.1を使うならSprocketsは捨ててwebpackを使おう


## やりたいこと

* es6を使う。coffee scriptとはサヨナラ〜
* bootstrapを使う。
* bootstrap datetimepickerを使う。（追記）
* font-awesomeを使う。
* webpackでjs、css（scss、sass)ファイルを管理する。

bootstrapはjqueryに依存しているので、jqueryも設置します。
jqueryに依存してないbootstrap.nativeもあるが、既存の資産でjqueryを使うところが多いので、しばらくはjqueryとes6を共存した方がいいと思いました。
作業者がjqueryには慣れているが、es6には慣れてないことも１つの理由です。

## bootstrap datetimepicker（追記：2017年12月20日）

bootstrap datetimepickerを使う場合は、Eonasdanのbootstrap datetimepickerではなくこれをforkしてbootstrap4用に直したbootstrap4-datetimepickerを使います。

```sh
% yarn add bootstrap4-datetimepicker
```

そして、application.jsやapplication.cssに追加します。

```javascript:app/javascript/packs/application.js
... snip ...
import 'bootstrap/dist/js/bootstrap'
import 'moment/moment'
import 'bootstrap4-datetimepicker/src/js/bootstrap-datetimepicker'
import 'moment/locale/ja'
```

Eonasdanのbootstrap datetimepickerはbootstrap3に合わせたものでbootstrap4で使うとカレンダーは表示されませんでした。時間設定の部分は表示されます。
moment/locale/jaはカレンダーを日本語化してくれるものです。

```html:app/javascript/stylesheets/application.css
... snip ...
@import '~bootstrap4-datetimepicker/build/css/bootstrap-datetimepicker';
```

jsとcssを読み込む経路が異なります。気をつけてください。

## rails-ujs（追記：2017年12月20日）

link_toでmethod: :deleteが効かない場合があります。
ruby on rails 5.0まではjqueryに依存していてこれはjquery-ujsで処理してくれるものでしたが、5.1以降ではもうjqueryに依存していないので、純粋なjavascriptで書いたrails-ujsを使います。

```sh
% yarn add rails-ujs
```

application.jsにも追加します。

```javascript:app/javascript/packs/application.js
... snip ...
import Rails from 'rails-ujs'
Rails.start()
```

## プロジェクトの生成

```sh
% rails new sample -B -C -S -T --skip-coffee --skip-turbolinks --webpack -d mysql
```

オプションの説明はrailsだけを叩くと表示されるので、簡単に説明します。

-Bはプロジェクト生成と共にbundle installすることをスキップします。
-CはAction Cableをスキップします。
-SはSprocketsをスキップします。
-Tはminitestをスキップします。私はRSpecの方なので、スキップしますが、minitestが良いならスルーしてください。
--skip-coffeeはcoffee scriptをスキップします。es6を使います。
--skip-turbolinksはturbolinksをスキップします。最初から使ったことないです。
--webpackはSprocketsの代わりにwebpackを使います。
-d mysqlはデータベースとしてmysql（mariadb）を使います。

## ライブラリの設置

```ruby:Gemfile
group :development do
  ... snip ...
  gem 'pry-rails'
  gem 'annotation'
  gem 'foreman'
end
```

pry-railsとannotationはあった方がいいですね。
webpackを使う場合、railsのアプリケーションサーバーだけではなくwebpack-dev-serverなども起動しなければなりません。
開発環境でそれぞれ起動することは面倒なので、一緒に起動するためにforemanを使います。個人的には5年ぶりに使いますが、まだ残されていました。
この以外にも開発に役に立つgemはもっとありますが、この記事では「やりたいこと」に集中したいと思うので、この位で始めます。

```sh
% cd sample
% bundle install --path vendor/bundle
```

私はrbenvとrbenv-gemsetを使っていますが、開発環境ではvendor/bundleにgemを設置しています。
多数のrailsバージョンやgemを設置することが多く、一種の習慣みたいになりました。
vimとctagsでomni補完機能を使っていてGemfile.tagsのファイルサイズを最小化するためです。
プロジェクトごとに使っているgemが異なるので、管理的な面でもこうした方がいいです。
vendor/bundleは容量が大きくなりやすいので、バージョン管理の対象外にしましょう。

次はwebpackerを設置します。webpackerはwebpackをrailsで利用するために作られました。
これからjsライブラリの管理にはwebpackを使います。

```sh
% rails webpacker:install
```

npmの代わりにyarnを利用してbootstrapとfont-awesome、bootstrapの依存パッケージであるjqueryとtether、popper.jsなどを設置します。
bootstrapの場合、バージョンを明示しないとbetaバージョンが設置されますが、色々と異なる部分がありましてややこしくなるので、alphaバージョンの最新バージョンを設置します。

```sh
% yarn add bootstrap@4.0.0-alpha.6 font-awesome jquery tether popper.js
```

## 設定

Sprocketsを使う場合はAsset pipelineを使う時と同じ手順なので、assetsの配下でjsやcssを管理しますが、webpackの場合はjavascriptの配下で管理することになります。

```text
app
├── assets
│   ├── config
│   │   └── manifest.js
│   ├── images
│   ├── javascripts
│   │   └── application.js
│   └── stylesheets
│       └── application.css
├── javascript
│   ├── packs
│   │   └── application.js
│   └── stylesheets
│       └── application.scss
├
```

「app/javascript/stylesheets」は私が改めて追加したことなので、好みのディレクトリ名で作って使ってください。

```html:app/javascript/stylesheets/application.scss
@import '~bootstrap/dist/css/bootstrap';
$fa-font-path: "~font-awesome/fonts";
@import '~font-awesome/scss/font-awesome';
```

「$fa-font-path: "~font-awesome/fonts";」がない場合、fontを見つかりませんというエラーが表示されます。
githubのwebpackerのissuesで見つけました。

https://github.com/rails/webpacker/issues/619

次はapplication.jsです。

```javascript:app/javascript/packs/application.js
/* eslint no-console:0 */
// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.
//
// To reference this file, add <%= javascript_pack_tag 'application' %> to the appropriate
// layout file, like app/views/layouts/application.html.erb

console.log('Hello World from Webpacker')
import 'stylesheets/application'
import 'bootstrap/dist/js/bootstrap'
```

webpackの場合は「webpack.config.js」設定ファイルを使っていますが、webpackerでは「config/webpack/environment.js」になります。
環境ごとにdevelopment.js、test.js、production.jsファイルが用意されていますが、これらのファイルはenvironment.jsファイルを読み込みます。

```javascript:config/webpack/environment.js
const { environment } = require('@rails/webpacker')
const webpack = require('webpack')

environment.plugins.prepend(
  'Provide',
  new webpack.ProvidePlugin({
    $: 'jquery',
    jQuery: 'jquery',
    jquery: 'jquery',
    Tether: 'tether',
    'window.Tether': 'tether',
    Popper: ['popper.js', 'default']
  })
)

module.exports = environment
```

__$やjQueryなどよく使われるaliasを全部指定する必要があります。__
__Tetherの場合も同様です。__

webpackの処理によるoutputはapplication.jsとapplication.cssです。
これらのoutputでレイアウトファイルを修正しましょう。

```html+erb:app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>Sample</title>
    <meta name='viewport' content='width=device-width, initial-scale=1'>
    <%= csrf_meta_tags %>

    <%= stylesheet_pack_tag 'application', media: 'all' %>
    <%= javascript_pack_tag 'application' %>
  </head>

  <body>
    <%= yield %>
  </body>
</html>
```

stylesheet_link_tagとjavascript_include_tagの代わりにstylesheet_pack_tagとjavascript_pack_tagを使っていることに注目してください。

## 起動

railsのアプリケーションサーバーとwebpack-dev-serverをまとめて起動するためにforemanを利用します。
foremanを利用するためには設定ファイルが必要で、デフォルトの設定ファイルの名前がProcfileです。
他の名前にする場合は、オプションで指定する必要があります。

```sh:Procfile
rails: rails s -p 3000
webpack: bin/webpack-dev-server
```

次のコマンドで実行すると、puma（railsのアプリケーションサーバー）とwebpack-dev-serverが一緒に起動されます。

```sh
% bundle exec foreman start
```

因みに、以下のようにする場合は起動できませんでした。

```sh
% bundle binstubs foreman
% bin/foreman start
```

## 確認

bootstrapがちゃんと表示されるのか確認してみましょう。
まず適当なcontrollerとviewファイルを作ります。

```sh
% rails g controller hello world
```

generatorで生成したworld.html.erbを開いて、下記のように入力してください。

```html+erb:app/views/hello/world.html.erb
<div class='container-fluid'>
  <div class='row'>
    <i class="fa fa-exclamation-triangle fa-3x"></i>
  </div>
  <div class="alert alert-success" role="alert">
    <strong>Well done!</strong> You successfully read this important alert message.
  </div>
  <div class="alert alert-info" role="alert">
    <strong>Heads up!</strong> This alert needs your attention, but it's not super important.
  </div>
  <div class="alert alert-warning" role="alert">
    <strong>Warning!</strong> Better check yourself, you're not looking too good.
  </div>
  <div class="alert alert-danger" role="alert">
    <strong>Oh snap!</strong> Change a few things up and try submitting again.
  </div>

  <div class='row'>
    <i class="fa fa-list fa-3x"></i>
  </div>
  <!-- Provides extra visual weight and identifies the primary action in a set of buttons -->
  <button type="button" class="btn btn-primary">Primary</button>

  <!-- Secondary, outline button -->
  <button type="button" class="btn btn-secondary">Secondary</button>

  <!-- Indicates a successful or positive action -->
  <button type="button" class="btn btn-success">Success</button>

  <!-- Contextual button for informational alert messages -->
  <button type="button" class="btn btn-info">Info</button>

  <!-- Indicates caution should be taken with this action -->
  <button type="button" class="btn btn-warning">Warning</button>

  <!-- Indicates a dangerous or potentially negative action -->
  <button type="button" class="btn btn-danger">Danger</button>

  <!-- Deemphasize a button by making it look like a link while maintaining button behavior -->
  <button type="button" class="btn btn-link">Link</button>
</div>
```

タイピングする必要はありません。
bootstrapのサイトでexampleが用意されているので、コピペしてください。

* https://v4-alpha.getbootstrap.com/components/alerts/#examples
* https://v4-alpha.getbootstrap.com/components/buttons/#examples

![スクリーンショット 2017-12-14 15.56.30.png](https://qiita-image-store.s3.amazonaws.com/0/93752/6d2945e3-6eb0-d6be-1fba-74a071103a09.png "スクリーンショット 2017-12-14 15.56.30.png")

font-awesomeのサイトにもexampleが用意されているので、ご参照ください。

http://fontawesome.io/icons/

綺麗に表示されました。

![スクリーンショット 2017-12-14 16.41.23.png](https://qiita-image-store.s3.amazonaws.com/0/93752/1f83032f-e969-095b-fe85-337eb15fbcba.png "スクリーンショット 2017-12-14 16.41.23.png")

画面サイズを変更して見ましょう。

![スクリーンショット 2017-12-14 16.41.55.png](https://qiita-image-store.s3.amazonaws.com/0/93752/42fd9fd0-9ab9-b8aa-2adb-2a2046f4fd7b.png "スクリーンショット 2017-12-14 16.41.55.png")

Developer Toolsのconsoleにてエラーがないことも確認してください。

![スクリーンショット 2017-12-14 15.57.37.png](https://qiita-image-store.s3.amazonaws.com/0/93752/5707823e-46ed-c3d1-b6b0-a7bfbda10916.png "スクリーンショット 2017-12-14 15.57.37.png")