Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

Rails 6+Webpacker開発環境をJS強者ががっつりセットアップしてみた(翻訳)

概要

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

日本語タイトルは内容に即したものにしました。画像はすべて元記事からの引用です。
原文の目次は省略しました。原文の乱れは訳文で修正してあります。以下のRailsガイドもどうぞ。

更新履歴:

  • 2019/11/28: 初版公開
  • 2021/05/06: 更新

Rails 6+Webpacker開発環境をJS強者ががっつりセットアップしてみた(翻訳)

👋皆さんこんにちは。本記事では私がRubyやRuby on Railsを学んだ一環として、ドキュメントに書かれていないことを全部盛りしました。RubyやRailsのエコシステムは初めてなので、皆さまからのフィードバックを心よりお待ちしております。お気づきの点がありましたら元記事にコメントをどうぞ。

Ruby on Railsのためのチュートリアルの大半はAPIの利用方法にフォーカスしていますが、これは理にかなっています(例: Rails をはじめよう - Rails ガイド)。

しかしrails new blogしてコントローラを編集し、git push herokuを何度かやってみるまではいいとして、そこから先はどうすればいいのでしょうか?私はその先に進みたかったので、以下についてのベストプラクティスが欲しかったのです。

  • 開発用セットアップ
  • RubyやRuby on Railsのコードエディタの設定
  • アセットの設定やオートリロード(Webpacker)
  • 再現可能なビルド、安全な環境
  • もう少し学べる書籍をいくつか

自分はこれらの点について相当じたばたしました。というのもオンライン上での議論がほとんど見当たらないからです。つまりこれらの点については「実際に体当たりで使って学ぶのが普通」ということです。

私はRailsアプリケーションを学んでデプロイするときに、自分がハマった小さなワナや、上述の課題をすべて解決するために追加しなければならなかったコード片を、以下のようにすべてREADMEファイルに盛り込むところから始めました。

ある時点からこのリストはどんどん膨れ上がってきたので、次にRailsアプリケーションを始めなければならなくなったときに「自分が使える資料にいつでも手が届く」状態にすべく書き直す必要を感じました。

このメモ書きを公開することで私の知見を共有し、(願わくば)皆さまのお役に立てられればと思います。

エディタとVS Codeのセットアップ

訳注:

参考: 原文の「エディタとVS Codeのセットアップ」セクションは、後に以下の記事に切り出されました。

私はJavaScript開発者だったので、それもあってVisual Studio Codeを使っています。JetBrainsの「Ruby 2019 State of the developer ecosystem」↓を見ると、RubyMineのようにもっとよさげなエディタがあるようですね。お使いの方はご感想を私の記事のコメントまでお知らせください。

エディタの設定

シンプルな割には忘れがちですが、VS CodeはEditorConfigをサポートしています。プロジェクトのルートディレクトリに.editorconfigファイルを作成しておけば、VS Codeを使っているどのユーザーでもインデント設定や改行コードをマスター設定にできます(訳注: VS Codeの場合はEditorConfig拡張のインストールが必要です)。

最小限の.editorconfigファイルを作成します。

# editorconfig.org
root = true

[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true

これでプロジェクトの他のコントリビューターもEditorConfigを使えるようになります。

VS Codeの拡張

VS CodeでのRailsやRubyを気持ちよく編集するために、以下の拡張を使っています。

これらの拡張を楽にインストールできるよう以下のフォルダとファイルを作成します。これでRailsプロジェクトのメンバー全員に拡張がおすすめされ、インストールをいちいち依頼しなくてもよくなります。

  • .vscode/extensions.json:
{
  "recommendations": [
    "rebornix.Ruby",
    "kaiwood.endwise",
    "vortizhe.simple-ruby-erb",
    "bung87.rails",
    "ninoseki.vscode-gem-lens"
  ]
}

これで、プロジェクトをVS Codeで開けばこれらのチーム用拡張をインストールするようヒントが表示されます。

VS Codeの設定

さらに、RubyやRailsプロジェクトの開発効率を最大限に高めるため、以下のプロジェクト設定を使っています。

  • .vscode/settings.json
{
  "ruby.useLanguageServer": true,
  "ruby.useBundler": true,
  "ruby.intellisense": "rubyLocate",
  "ruby.format": "rufo",
  "files.associations": {
    "*.html.erb": "html"
  },
  "[html]": {
    "editor.defaultFormatter": "vscode.html-language-features"
  },
  "editor.formatOnSave": true
}

これで、.rbファイルや.erb.htmlをエディタで保存するとRubyやRailsのコードが自動的にフォーマットされるようになります。

これは現時点で私が見出したベストのセットアップです。最終的にはPrettierとそれ用のRubyプラグインがベストなオプションになるかと思いますが、記事執筆時点では.erb.htmlのフォーマットがサポートされていません(#371)。

  • フォーマットがよく効くように以下をGemfileに追加しておきます。
group :development do
  # [...]
  gem 'rufo', '~> 0.7.0', require: false
end

これで、.rbファイルや.erb.htmlファイルを保存するたびに自動フォーマットされるようになります。

Webpackerの使い方

このセクションでは[S]CSSについて説明します。この説明はすべてのSCSSファイルとCSSファイルに適用できます。Webpackerは複数のフォーマットをサポートしています(webpacker/css.md at master · rails/webpacker)。

当初、Webpackerについて以下の理由で混乱しました。

Rails 6からRailsアプリケーションにWebpackが同梱されて組み込めるようになりました。これはWebpacker経由で行われます。Webpackerは、事前に設定されたWebpackにビューヘルパーを加えたものを提供することで、生成されたアセット(JavaScriptや[S]CSSファイルなど)を簡単に対応付けられるようにします。

2019年11月の時点では、デフォルトのRailsアプリケーションのerbテンプレートファイルには以下が含まれています。

  • app/views/layout/application.erb.html
  <%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track": "reload" %>
  <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>

2行目は、app/javascript/packs/application.jsというエントリポイントから生成されたコンパイル済みJavaScriptファイルパスをWebpackerから取得します。

しかし1行目は、現在のアセットからのファイル生成を片っ端からアセットパイプラインに指示します。

このようにシステムが二重になっていることで混乱しました。何か理由はあるのかもしれませんが、ここではWebpackerのみを用いてアセットを生成するよう修正しましょう。

[S]CSSファイルをWebpackerで扱う

[S]CSSファイルをWebpackerで扱う(Rails 6でBootstrap 4を使うなど)には、CSSをインクルードする行を以下のように変更する必要があります。

  • app/views/layout/application.erb.html
  <%= stylesheet_pack_tag "application", media: "all", "data-turbolinks-track": "reload" %>

これにより、「application」という名前のパックについてWebpackerがスタイルシートアセットの正しいURLを提供するようになります。現在のメインのパックのファイル名をアプリケーションが実際に参照するときの名前はapp/javascript/packs/application.jsです。

stylesheet_link_tagstylesheet_pack_tagに変更されていることにお気づきでしょうか。

次に、メインのSCSSファイルを作成します(実際のファイル名や置き場所は何でも構いません)。

  • app/javascript/src/style.scss
@import "~bootstrap/scss/bootstrap"; // Bootstrapを使う予定なら

次にJavaScriptのimport文を書きます。

  • app/javascript/packs/application.js
// [...]
import "../src/style.scss";

このスタイルシートは、実際にはこのJavaScriptファイルでは読み込まれません。この記述は、単にWebpackにコンパイルを指示して、Railsアプリケーションのテンプレートでrequireのパスを指定できるようにするためのものです。

Rails 6でBootstrap 4を使う

訳注

現在(2021/05/06)はBootstrap 5がリリースされているので、yarn add bootstrapを実行するとBootstrap 5がインストールされます。

また、Bootstrap 5と併用するpopper.jsについてはyarn add @popperjs/coreでインストールするようです。

先ほどの最後の部分を@import "~bootstrap/scss/bootstrap";に変えて動かすには、Bootstrapをインストールする必要があります。ただしGemfileに書くのではなく、Yarnを使います。~を付けた~bootstrapという記述は、node_modules/bootstrap/...以下からファイルを探すようWebpackに指示するためのものです。それでは追加しましょう。

  • ターミナルで以下を実行します
yarn add bootstrap

BootstrapのJavaScriptを動かすには、JavaScriptの依存関係もインストールする必要があります。

  • ターミナルで以下を実行します
yarn add jquery popper.js

インストールしたファイルをJavaScriptアプリケーションでrequireします。

  • app/javascript/packs/application.js
require("jquery");
require("bootstrap");

[S]CSSやJavaScriptファイルのオートリロード

私にとってWebpackの強みのひとつといえばオートリロード機能ですが、デフォルトのRails 6では無効になっています。オートリロードでは、developmentモードですべてのページの更新が常に読み込まれます。オートリロードを有効にするにはwebpack-dev-serverを使う必要があります。ありがたいことに、webpack-dev-serverは新しいRails 6アプリケーションには既に同梱されているので、これをRailsサーバーと一緒に起動するだけで使えます。

  • ターミナルで以下を実行します
./bin/webpack-dev-server

私はdevelopment用サーバーでは1つのコマンドで実行できるようにするのが好みです。rails server./bin/webpack-dev-serverをパラレルに起動できれば理想的です。昨今ならProcfile.devとovermindで今風にやると楽にできそうです。

DarthSim/overmind - GitHub

overmindは事実上foremanですが、「よくできています」。私はインターネット上のツールを信頼しているのでこれを使っています。必要な作業は以下のとおりです。

  • Procfile.devを作成します
web: rails server
webpacker:  ./bin/webpack-dev-server
  • .envを作成または更新します
OVERMIND_PROCFILE=Procfile.dev

これで、rails serverrails sをローカルで起動する代わりに、以下のようにovermindでやれます。

  • ターミナルで以下を実行します
brew install overmind
overmind start
# またはovermind s

おめでとう、これでhttp://localhost:5000にアクセスすればWebサーバーが実行されます。組み込みのオートリロードも高速です。

(以前はよりシンプルなhivemindをおすすめしていましたが、Railsサーバーをデバッグする必要が生じたときは使えません)


1つ問題があったのは、WebpackerがYarn integrityチェックを頻繁に行うことでdevelopment環境で遅くなる点でした。私のYarn依存関係は最新かつ常に同じに保たれていることを確認してあるので(次の「再現可能な環境」を参照)、Yarn integrityは無効にしました。

Webpackerを使えばいろんなことができるのですが、メインのRailsドキュメントには情報がありません。Webpackerについて学ぶには以下がおすすめです。私はWebpackerについて必要なこと(特にフォルダ構造webpack-dev-server、 CSS)はすべてここで学びました。

訳注

現在はWebpackerガイドがありますので、先にこちらをどうぞ。

以下の記事も参考までにどうぞ。

【保存版】Rails 5 Webpacker公式ドキュメントの歩き方+追加情報

自動テストとデスクトップ通知

私はJavaScript方面から来たので、JestというJavaScript例外テストツールを使っていました。これは今やJavaScriptアプリケーションのテストでは定番です(他のものを使っても別に構いません)が、RubyやRuby on Railsのテストにはさまざまな方法があります。ほとんどの場合自分に必要なのは、テストの自動実行と、テストがパスまたは失敗した場合のデスクトップ通知です。

テストの自動実行ツールは皆さんも既にネットでいろいろ見つけていることでしょう。たとえば「Automating Minitest in Rails 6」はよい出発点です。

でも自分は以下のようなデスクトップ通知がどうしても必要欲しいのです。

これならエディタを切り替えずにテストの状態をチェックできます。

最も簡単に通知を実現する方法はterminal-notifierとterminal-notifier-guardを使うことだとわかってきました。

julienXX/terminal-notifier - GitHub
Codaisseur/terminal-notifier-guard - GitHub

必要な作業は、Gemfileのdevelopmentグループとtestグループに以下を追加することだけです。

gem 'terminal-notifier', '2.0.0'
gem 'terminal-notifier-guard', '1.7.0'

これでbundleを実行すれば、guardがこれらのgemを自動検出してナイスな通知を表示してくれます。


本記事では当初Growlを推していましたが、理由はterminal-notifierがその時点でうまく動かなかったためでした。しかしコメントで再挑戦を勧められて、今度はうまくいきました。

もちろんGrowlでも問題なくやれますので、既にお使いの方はそのままどうぞ。

再現可能な環境

私はNode.jsやJavaScript方面の出身なので、「再現可能な環境」に必要な正しい習慣を身に付けてきました。

再現可能な環境(reproducible environments)についてご存知ですか?Travis CIで動かそうがHerokuで動かそうが、はたまたMacbookで動かそうが、常に同一の依存関係、プラットフォーム、言語バージョンで実行するべきです。MacbookではYarn 1.9にRuby 2.6にNode.js 12.3.4という依存関係で動かしつつ、HerokuではYarn 1.1にRuby 2.4にNode.js 12.1.0という依存関係で動かしたいことなどありえません。理想は、どのバージョンについてもインストールやメンテナンスやアップグレードを簡単に行えることです。

bundler、Rails、Ruby、Node.js、Yarnもこれに該当します。次のセクションで方法を説明します。

再現可能な環境: Ruby編

  1. rbenvでさまざまなバージョンのRubyを簡単にインストールできるようにする
  2. .ruby-versionファイルでRubyバージョンを全プラットフォームで強制的に設定する
  3. Gemfileを変更して、bundler実行時にRubyのバージョンを強制的に設定する
  4. 依存関係のバージョンを抽出する(いわゆる依存関係のピニング(pinning)

1. rbenvをインストールします

2. .ruby-versionファイルを以下の内容で作成します

2.6.5
# 訳注: 2021/05/06時点の最新安定版は3.0.1です

3. GemfileのRubyバージョンを、Bundlerで使うRubyバージョンと合わせます

ruby '2.6.5'
# 訳注: 2021/05/06時点の最新安定版は3.0.1です

4. GemfileのRuby依存関係で正確なバージョンを指定します

gem 'webpacker', '~> 4.0'
# 訳注: 2021/05/06時点では以下のようになります。
# gem 'webpacker', '~> 5.0'

具体的には、上を以下のように変更します。

gem 'webpacker', '4.2.0'
# 訳注: 2021/05/06時点のWebpackerは5.3.0なので以下のように書きます。
# gem 'webpacker', '5.3.0'

ありがたいことにVS Codeにインストールした拡張のおかげで、Ruby依存関係の最新版を簡単に確認できます。Gemfileの該当行にマウスオーバーするだけで以下のように表示されます。

依存関係のピニングはデリケートな問題です。ありがたいことに、Gemfileやbundlerでバージョンを指定するときのさまざまな戦略のメリットやデメリットについてうまくまとめた良記事がありますのでご覧ください。

再現可能な環境: Node.jsとYarn編

RailsがWebpackに依存するようになったことで、Node.jsとYarnにも依存するようになりました。これは驚きですね。つまり、それぞれのバイナリで使うバージョンも正確に指定する必要があります。

以下の要領で進めます。

  1. インストールして使う.Node.jsのバージョンは.nvmrcファイルで指定する。HerokuもTravis CIも、利用するNode.jsのバージョンをこれで推測する。
  2. インストールして使うYarnのバージョンはYarnポリシーで指定する。これはYarn 1.13.0以降であればどこでも使えます。Yarn policyは誕生して1年ですが、安全です。
  3. (Webpacker経由のWebpackなどのように)Node.jsスクリプトを実行するYarnやNode.jsのバージョンは、package.jsonのenginesフィールドで検証される。
  4. 依存関係のバージョンを抽出する

1. nvmをインストールします

続いて以下の内容で.nvmrcファイルを作成します。

12.13.1
# 訳注: 2021/05/06時点のLTS版Node.jsは14.17.0です

本記事をお読みの方は、Node.jsの最新バージョンをNode.jsサイトで確認してお使いください。

2. ターミナルで以下のYarnポリシーを設定します

yarn policies set-version 1.19.1
# 訳注: 2021/05/06時点のYarnは1.22.5です

これでYarn 1.19.1がダウンロードされて.yarnrcファイルが作成され、Yarn 1.19.1を使うようYarnのバイナリに指示されます。いいですね〜❤️。繰り返しますが、本記事をお読みの方は、Yarnの最新バージョンをYarnサイトのリリース情報(およびリリース一覧)で確認してお使いください。

最新のLTS版Node.jsも利用できます。

3. package.jsonを以下のように更新します

{
  // [...],
  "engines": {
    "node": "12.13.1",
    "yarn": "1.19.1"
  }
}

4. package.jsonの依存関係をすべて正確なバージョンにピニングします

"@rails/webpacker": "^4.2.0",

具体的には、上のバージョンを以下のように変更します。

"@rails/webpacker": "4.2.0",

次のセクションでは、依存関係を実際にピニングできるツールについて説明します。

これでマスターとなるNode.jsとYarnの環境ができました!

依存関係の自動アップデート

ご覧のように、再現可能な環境づくりの一環として、私はいついかなるときでも依存関係のバージョンはピンポイントで指定しています。なにかのはずみで、環境やプラットフォームの依存関係バージョンがちょっと違ってしまうという事態になって嬉しい人などいません。そんなことになれば時間をドブに捨てるはめになります。

もちろん、Gemfile.lockやyarn.lockのようなロックファイルのおかげで大半の問題は解決できますが、それだけでは不十分です。ロックファイルを使っても失敗する可能性がある(参考)というだけでなく、私はいついかなる場合でも現在の直接的な依存関係も正確に知っておかないと気が済まないのです。今使える機能や今あるバグの両方について知りたいときに、package.jsonやGemfileの"~2.x"みたいなバージョン表記の背後で何が起きているのかを解明するのに謎のコマンドを調べまくるはめになりたくないのです。

しかし、たまには依存関係のアップグレードもやりたいのです。それも一切つらい目に遭わずに。JavaScriptやRubyの依存関係の自動アップデートについては、Renovateを一度チェックしてみることをおすすめします。

Herokuと、Yarnインストールかぶり現象

Herokuにデプロイする場合、Herokuの以下のガイドがよくできているので、これに従いましょう。

私の場合、Herokuにデプロイ中にYarnインストールが複数回トリガされるというおかしな動作に遭遇しました。ひとつはHerokuでpackage.jsonファイルが検出されたとき、もうひとつはWebpackerでアセットをバンドルしているときです。これでは困るので、これらのタスクを以下のように分割したいと思いました。

  1. Yarn依存関係をインストール
  2. Ruby依存関係をインストール
  3. アセットのコンパイル

タスク1.と2.はHerokuで自動検出されますが、自分で順序を指定することもできます。これを行うには、buildpacksとbuildpacksの順序を自分で設定し、最初にNode.jsのを、次にRubyのを使うようにしなければなりません。

  • ターミナルで以下を実行します
heroku buildpacks:clear
heroku buildpacks:add heroku/nodejs
heroku buildpacks:add heroku/ruby

今度はWebpackerによるYarnインストールをすべて無効にしなければなりません。Rakefileに以下を追加します。

Rake::Task['yarn:install'].clear
namespace :yarn do
  desc "Disabling internal yarn install from Rails"
  task :install => [:environment] do
    puts "Disabling internal yarn install from Rails"
  end
end

Rake::Task['webpacker:yarn_install'].clear
namespace :webpacker do
  desc "Disabling internal yarn install from Rails"
  task :yarn_install => [:environment] do
    puts "Disabling internal yarn install from Rails"
  end
end

この問題(#744)を調べてくれたRebecca Lynn Cremonaに感謝いたします。

設定管理

credential、設定ファイル、環境変数ついては以下の別記事をご覧ください。

RubyやRailsを学ぶのに最適なリソース一覧

これについては別記事に切り出しました。

解説は以上です。

Node.jsの経験を元にRails世界に飛び込んでみましたが、全般的に楽しめました。本記事で指摘した問題がいずれ解消しますように。そのときには本記事を更新いたします。本記事で取り上げたトピックについてもっと詳しい方がいらっしゃいましたら、ぜひ元記事にコメントいただくかvincent@codeagain.comまでお知らせください。

本記事がどこかでお役に立ちましたら、他の方にもおすすめください。

お読みいただいた皆さまに感謝いたします。

おたより発掘

関連記事

Rails 6.1のDocker開発環境構築をEvil Martians流にやってみた(更新)


CONTACT

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