Rails 6.1 → 7.0 アップグレードと Webpacker から jsbundling-rails + Webpack 移行
Rails 6.1.7 から 7.0.8.7 へのアップデートを行い、併せて Webpacker から sprockets-rails + jsbundling-rails + Webpack への移行も実施したため、その手順を記録しておきます。
参考:Rails アップグレードガイド – Railsガイド
前提
Ruby 3.1 から 3.2 へのアップグレード時のトラブルと対応策
Rails 7 にアップデートする前に、Ruby のバージョンを 3.2.6 に更新しました。
Railsバージョンアップ
- gem 'rails', '~> 6.1.7'
+ gem 'rails', '~> 7.0.8.7'
$ bundle update rails
アップデートタスク
$ rails app:update
差分を確認して必要な設定を戻します。
またrailsdiff.orgを参考にして、新しく追加された設定を確認します。
不要なファイルを削除
config/initializersの下記を削除。
application_controller_renderer.rb
backtrace_silencers.rb
cookies_serializer.rb
mime_types.rb
wrap_parameters.rb
参考:rails 7.0ではconfig/initializers
配下のファイルが少なくなっている
Gemバージョンアップ
railsdiff.orgを参考にgemを更新。
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
ruby '3.2.6'
- # Bundle edge Rails instead: gem "rails", github: "rails/rails"
+ # Bundle edge Rails instead: gem 'rails', github: 'rails/rails', branch: 'main'
gem 'rails', '~> 7.0.8.7'
+ # The original asset pipeline for Rails [https://github.com/rails/sprockets-rails]
+ gem 'sprockets-rails'
# Use postgresql as the database for Active Record
gem 'pg'
gem 'psych', '5.1.2'
# Use Puma as the app server
gem 'puma', '~> 6.5'
# Use SCSS for stylesheets
gem 'sassc-rails'
# Use Uglifier as compressor for JavaScript assets
gem 'uglifier', '>= 1.3.0'
- # Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker
- gem 'webpacker', '~> 5.4'
- # Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
- gem 'turbolinks', '~> 5'
- # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
- gem 'jbuilder', '~> 2.13'
+ # Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails]
+ # gem 'importmap-rails'
+ gem 'jsbundling-rails'
+ # Hotwire's SPA-like page accelerator [https://turbo.hotwired.dev]
+ gem 'turbo-rails'
+ # Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev]
+ gem 'stimulus-rails'
+ # Build JSON APIs with ease [https://github.com/rails/jbuilder]
+ gem 'jbuilder'
# Use Redis adapter to run Action Cable in production
gem 'redis', '~> 5.3'
- # Use Active Model has_secure_password
- # gem "bcrypt", "~> 3.1.7"
- # Use Active Storage variant
- # gem "image_processing", "~> 1.2"
+ # Use Kredis to get higher-level data types in Redis [https://github.com/rails/kredis]
+ # gem 'kredis'
+ # Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword]
+ # gem 'bcrypt', '~> 3.1.7'
+ # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
+ gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby]
# Reduces boot times through caching; required in config/boot.rb
- gem 'bootsnap', '~> 1.18.3', require: false
+ gem 'bootsnap', require: false
+ # Use Sass to process CSS
+ # gem 'sassc-rails'
+ # Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images]
+ # gem 'image_processing', '~> 1.2'
# Autoload dotenv in Rails.
gem 'dotenv-rails', '~> 2.8'
gem 'enum_help'
# Provides the generator settings required for Rails 3+ to use Slim
gem 'slim-rails', '~> 3.6'
# Configurable tool for writing clean and consistent Slim templates
gem 'slim_lint', '~> 0.27.0'
# A gem that provides a client interface for the Sentry error logger
gem 'sentry-rails', '~> 5.17'
gem 'sentry-ruby', '~> 5.20'
gem 'sentry-sidekiq', '~> 5.18'
gem 'stackprof'
# Simple, efficient background processing for Ruby.
gem 'sidekiq', '~> 6.5'
# Enables to set jobs to be run in specified time (using CRON notation)
gem 'sidekiq-cron', '~> 1.12'
gem 'faraday', '~> 1.10.3'
# A simple wrapper to send notifications to Slack webhooks.
gem 'slack-notifier'
gem 'slack-ruby-client', '~> 1.0.0'
# SAML toolkit for Ruby on Rails
gem 'ruby-saml', '~> 1.17'
# Banken provides a set of helpers which restricts what resources a given user is allowed to access.
gem 'banken', '~> 1.0', '>= 1.0.3'
# Salesforce
gem 'restforce', '~> 7.5.0'
# Ridgepole is a tool to manage DB schema. It defines DB schema using Rails DSL, and updates DB schema according to DSL.
gem 'ridgepole', '~> 2.0.3'
# Flexible authentication solution for Rails with Warden
gem 'devise', '~> 4.9'
# Easiest way to manage multi-environment settings in any ruby project or framework: Rails, Sinatra, Pandrino and others
gem 'config', '~> 5.5'
gem 'kaminari'
gem 'rails-i18n'
gem 'annotate'
# for google spread sheet
gem 'googleauth', '~> 0.17.1'
gem 'google_drive'
gem 'aws-sdk-s3'
gem 'mini_magick'
# jsRoute for path helper on JS
gem 'js-routes'
# Composite Primary Keys for ActiveRecords
- gem 'composite_primary_keys', '~> 13.0'
- gem 'tzinfo-data'
+ gem 'composite_primary_keys', '~> 14.0'
# Record versions
gem 'paper_trail'
gem 'net-imap', require: false
gem 'net-pop', require: false
gem 'net-smtp', require: false
group :development, :test do
- # Call "byebug" anywhere in the code to stop execution and get a debugger console
+ # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem
+ gem 'debug', platforms: %i[mri mingw x64_mingw]
gem 'awesome_print'
- gem 'byebug', platforms: %i[mri mingw x64_mingw]
gem 'parallel_tests' # rspecとminitestのDBを分けるために利用. ローカル環境でのparallel実行は想定していない
# Use RSpec
gem 'rspec-rails'
gem 'factory_bot_rails'
# Use RuboCop
gem 'rubocop'
gem 'rubocop-rails'
gem 'rubocop-rspec'
gem 'rubocop-performance'
# Use Brakeman
gem 'brakeman'
# Use Pry
gem 'pry-byebug'
gem 'pry-doc'
gem 'pry-rails'
gem 'pry-stack_explorer'
end
gem 'faker' # NOTE: テスト以外でも使えるように
group :development do
- # Access an interactive console on exception pages or by calling "console" anywhere in the code.
+ # Use console on exceptions pages [https://github.com/rails/web-console]
+ gem 'web-console'
+ # Add speed badges [https://github.com/MiniProfiler/rack-mini-profiler]
+ # gem 'rack-mini-profiler'
+ # Speed up commands on slow machines / big apps [https://github.com/rails/spring]
+ # gem 'spring'
gem 'listen', '>= 3.0.5', '< 3.10'
- gem 'web-console', '>= 3.3.0'
# Use Better Errors
gem 'better_errors'
gem 'binding_of_caller'
# Use Bullet
gem 'bullet'
end
group :test do
- # Adds support for Capybara system testing and selenium driver
- gem 'capybara', '>= 2.15'
+ # Use system testing [https://guides.rubyonrails.org/testing.html#system-testing]
+ gem 'capybara'
gem 'selenium-webdriver'
# This gem brings back assigns to your controller tests as well as assert_template to both controller and integration tests.
gem 'rails-controller-testing' # TODO: 削除したい -> assigns( -> オミット後に消せそう
# Use SimpleCov
gem 'simplecov'
# Use Ruby Tests Profiling Toolbox
gem 'test-prof'
# Use WebMock
gem 'webmock'
end
gem 'google-cloud-bigquery'
gem 'nokogiri'
gem 'rack-cors'
$ bundle update
Rails 7.0 への移行で発生した問題とその解決策
プロジェクトで Rails 6.1.7 から 7.0.8.7 へアップデートした際に、Webpacker や DB 関連の問題など、いくつかの問題が発生しました。以下にそれらの問題と解決策についてまとめます。
1. Integer#to_s(:delimited) の非推奨化
発生した問題
Integer#to_s(:delimited)
が Rails 7.0 で非推奨になり、以下の警告が発生しました。
DEPRECATION WARNING: Integer#to_s(:delimited) is deprecated. Please use Integer#to_fs(:delimited) instead.
解決策
to_s(:delimited)
を to_fs(:delimited)
に変更。
- to_s(:delimited)
+ to_fs(:delimited)
参考:Deprecate to_s(format) in favor of to_formatted_s(format)
2. render './path' の非推奨化
発生した問題
render './api/v2/success'
のように ./
を使ったパス指定が動作しなくなり、以下のエラーが発生しました。
ActionView::Template::Error: Missing partial ./api/v2/success
解決策
./
を /
に変更。
- render './api/v2/success'
+ render '/api/v2/success'
参考:Deprecate rendering templates with . in the name
3. rails db:structure:load の廃止
発生した問題
rails db:structure:load
を実行した際に、以下のエラーが発生しました。
rails aborted!
Don't know how to build task 'db:structure:load' (See the list of available tasks with `rails --tasks`)
Rails 7.0 から rails db:structure:load
が廃止され、代わりに rails db:schema:load
を使用する仕様になりました。
解決策
コマンドを以下のように変更。
- rails db:structure:load
+ rails db:schema:load
参考:Combine and deprecate rails db:structure:{dump,load} tasks into rails db:schema:{dump,load}
4. rails db:fixtures:load の外部キー制約エラー
発生した問題
bundle exec rails db:fixtures:load RAILS_ENV=test
を実行した際に、以下のエラーが発生しました。
Foreign key violations found in your fixture data. Ensure you aren't referring to labels that don't exist on associations.
Rails 7.0 からデフォルトで config.active_record.verify_foreign_keys_for_fixtures = true
になり、外部キー制約に違反するデータがあるとエラーになる仕様に変更されました。
解決策
外部キー制約に違反するデータを特定し、Fixture ファイルを修正。
※ 暫定対応として、 config/application.rb
に以下を追加も可。
config.active_record.verify_foreign_keys_for_fixtures = false
参考:Verify foreign keys after loading fixtures
5. datetime 型の precision の挙動変更
発生した問題
Rails 7.0 では datetime
型の precision
のデフォルト値が変更されました。
以前は無指定だと DB のデフォルトだったが、Rails 7 からは precision: 6
がデフォルトに、また nil
を指定すると DB デフォルトになるという仕様になりました。
このため、ridgepole を使用している場合、スキーマ定義の見直しが必要になりました。
解決策
スキーマの定義を以下のように変更。
# before
t.datetime :column_a # DB のデフォルト precision
t.datetime :column_b, precision: 6
# after
t.datetime :column_a, precision: nil # DB のデフォルト precision
t.datetime :column_b # precision == 6(デフォルト)
6. Turbo による影響と form_with の修正
Rails7では、Turbolinks
に代わりデフォルトでturbo-rails
というgemが用意されました。
発生した問題
Turbolinks
から Turbo
に変更した影響でボタンがクリックできない問題が発生しました。
form_with
の local: true
は、フォーム送信時に JavaScript による Ajax リクエスト (XHR) を抑制し、通常の HTTP リクエストとして送信するためのオプションでした。しかし、Rails 7 では Turbo が導入され、Ajax リクエストの管理方法が変わったため、代わりに data: { turbo: false }
を明示的に設定する必要がありました。
解決策
フォームの local: true
オプションを data: { turbo: false }
に置換。
- = form_with model: @user, local: true do |form|
+ = form_with model: @user, data: { turbo: false } do |form|
参考:Rails 7: local (non-XHR) request with a form_with form
7. CSP(コンテンツセキュリティポリシー)の変更
発生した問題
登録や変更ボタンをクリックするとエラーが発生しました。
Refused to execute inline event handler because it violates the following
Content Security Policy directive: "script-src 'self' https: 'unsafe-inline'
'nonce-607e9c9008282476644e0115c597a741'".
Note that 'unsafe-inline' is ignored
if either a hash or nonce value is present in the source list.
このエラーは CSP(Content Security Policy) によって script-src
の制約が厳しく設定されているため、インラインスクリプト(onclick
など)が実行できなくなっていることが原因です。
解決策
config/initializers/content_security_policy.rb
に :blob
を追加することで解決。
Rails.application.config.content_security_policy do |policy|
- policy.script_src :self, :https, :unsafe_inline
+ policy.script_src :self, :https, :unsafe_inline, :blob
end
8. Sprockets::ArgumentErrorエラー
発生した問題
Rails 7 へアップデートした際に、Sprockets::ArgumentError: link_tree argument must be a directory
というエラーが発生する場合があります。
このエラーは、app/assets/config/manifest.js
に 存在しないディレクトリ を link_tree
で参照していることが原因です。
Sprockets::ArgumentError: link_tree argument must be a directory
解決策
app/assets/config/manifest.js
の該当コードを削除。
- //= link_tree ../../../vendor/javascript .js
Sprockets の link_tree
は、指定したディレクトリ以下のすべてのファイルをプリコンパイル対象にするため、誤ったパスを指定するとエラーになります。
JavaScript 管理の移行
Rails 6.1 から 7.0 へのアップグレードに伴い、JavaScript の管理方法を Webpacker から sprockets-rails
+ jsbundling-rails
+ Webpack に移行しました。
参考:jsbundling-rails/docs/switch_from_webpacker.md
なぜ Webpacker から移行したのか?
Webpacker の開発が終了し、Rails 7 ではデフォルトで JavaScript の管理に Importmap が採用されました。
しかし、Vue.js などのモダンな JavaScript フレームワークを使う場合は Importmap だけでは不十分なため、jsbundling-rails
+ Webpack を利用する形にしました。
また、Sprockets も引き続きアセット管理に使用することで、Rails の標準的なアセット管理と組み合わせる形にしました。
- Sprockets (
sprockets-rails
) → CSS・画像・一部の JavaScript(非モジュール)を管理 - jsbundling-rails + Webpack → モダンな JavaScript(Vue.js など)を管理
1. Webpacker等削除
Webpacker gemを削除。
- gem 'webpacker'
Webpackerパッケージを削除。
$ yarn remove @rails/webpacker webpack-dev-server
下記ファイルが不要になったので削除。
.browserslistrc
babel.config.js
bin/webpack
bin/webpack-dev-server
config/webpack/development.js
config/webpack/environment.js
config/webpack/production.js
config/webpack/staging.js
config/webpack/test.js
config/webpack/loaders/erb.js
config/webpack/loaders/vue.js
config/webpacker.yml
postcss.config.js
public/packs
public/packs-test
2. Webpackerのコンフィグを削除
- # Add Yarn node_modules folder to the asset load path.
- Rails.application.config.assets.paths << Rails.root.join('node_modules')
- # # If you are using webpack-dev-server then specify webpack-dev-server host
- # policy.connect_src :self, :https, "http://localhost:3035", "ws://localhost:3035" if Rails.env.development?
3. jsbundling-rails の導入
インストール
$ bundle add jsbundling-rails
$ rails javascript:install:webpack
このコマンドにより、以下の設定が行われます。
-
bin/dev
に Webpack を実行するスクリプトが追加される -
package.json
に Webpack の設定が追加される -
app/javascript/application.js
が作成される(ここに Vue.js などを import する)
4. packs 配下の Vue.js ファイルと application.js の移動
変更前(Webpacker 使用時の構成)
Webpacker を利用していた場合、Vue.js のエントリーポイント (application.js
) や Vue コンポーネントは packs/
配下に配置されていました。
app/javascript/
├── packs/
│ ├── application.js # Webpacker のエントリーポイント
│ ├── components/ # Vue.js のコンポーネント
│ │ ├── app_message.vue
│ │ ├── app_header.vue
│ │ ├── app_footer.vue
│ │ └── ...
│ └── ...
└── ...
変更後(Webpack + Sprockets へ移行後の構成)
Webpacker を廃止し、Vue.js のエントリーポイントとコンポーネントを整理します。
app/javascript/
├── application.js # メインエントリーポイント
├── components/ # Vue.js のコンポーネント(packs/components/ から移動)
│ ├── app_message.vue
│ ├── app_header.vue
│ ├── app_footer.vue
│ └── ...
└── ...
主な変更点
-
app/javascript/packs/application.js
→app/javascript/application.js
に移動 -
app/javascript/packs/components/
→app/javascript/components
に移動
5. webpackの設定
const path = require("path")
const webpack = require("webpack")
const { VueLoaderPlugin } = require("vue-loader")
module.exports = {
mode: "development",
devtool: "source-map",
entry: {
application: "./app/javascript/application.js",
},
output: {
filename: "[name].js",
sourceMapFilename: "[file].map",
chunkFormat: "module",
path: path.resolve(__dirname, "app/assets/builds"),
},
module: {
rules: [
{ test: /\.vue$/, loader: "vue-loader" },
{ test: /\.scss$/, use: ["vue-style-loader", "css-loader", "sass-loader"] },
{ test: /\.erb$/, use: "rails-erb-loader" },
{ test: /\.css$/, use: ["vue-style-loader", "css-loader"] },
],
},
resolve: {
alias: { components: path.resolve(__dirname, "app/javascript/components") },
extensions: [".js", ".vue", ".json"],
modules: ["node_modules", path.resolve(__dirname, "app/javascript")],
},
plugins: [
new VueLoaderPlugin(),
new webpack.EnvironmentPlugin(Object.keys(process.env)),
// Webpack5ではprocessはundefinedになる()ので、processを使う場合は以下のプラグインを追加する
new webpack.ProvidePlugin({ process: "process/browser" }),
new webpack.optimize.LimitChunkCountPlugin({ maxChunks: 1 }),
],
}
参考:webpack でビルドするときの環境変数を読み込む方法の整理と、読み込み方法の切り替え
6. javascript_pack_tag を javascript_include_tag に変更
- = javascript_pack_tag "application", "data-turbolinks-track": "reload"
+ = javascript_include_tag "application", "data-turbolinks-track": "reload"
まとめ
Rails 6.1.7 -> 7.0.8.7 へのアップデートでは、フレームワークの進化に伴ういくつかの変更に対応する必要がありました。
主なポイントとしては、新しいデフォルト設定の適用、非推奨機能の移行、依存関係の調整などが挙げられます。特に Webpacker から sprockets-rails + jsbundling-rails + Webpack への移行により、アセット管理の構成を見直し、一部のコードに修正が必要となりました。
アップデートを進める際は、変更点を理解しながら慎重に適用し、テストを通じて影響を確認することが重要でした。
Discussion