はじめに
2019年8月16日に約3年ぶりのメジャーバージョンアップである Rails 6 がリリースされました。今回は Rails 5 から Rails 6 にかけての変更点と新しく追加されたコンポーネントについて紹介します。
概要
Rails 6 がリリースされたのは2019年8月15日で2016年6月30日にリリースされた Rails 5 から約3年2ヶ月ぶりのメジャーバージョンアップです。今回のバージョンアップでは15,000以上ものコミットが行われました。
変更点について全てを紹介することはできませんので、今回は以下の変更点について触れていきます。詳細についてはリリースノートをご覧ください。
- Rubyのサポートバージョン
- Webpacker
- 複数データベース接続
- 並列テスト
Rubyのサポートバージョン
Rails がメジャーバージョンアップする際はサポートする Ruby のバージョンが大幅に上がります。実際に、Rails 5系 では Ruby 2.2 以上をサポートしていましたが、Rails 6系 では Ruby 2.5 以上となりました。
Ruby 2系 以降では毎年マイナーバージョンアップが行われており、バージョンアップから3〜4年ほどでサポートが切れます。それに合わせて Rails は約3年毎にメジャーバージョンアップを行っています。
Webpacker
もともと Rails では JavaScript や CSS などのビルドに Sprockets を使用していましたが、近年のフロントエンド開発ツールの進化に伴い Sprockets の代わりに webpack を使用するケースが増えていきました。
そこで、DHH は webpack のラッパライブラリである Webpacker を開発しました。これにより、Rails から webpack のビルドができるようになり、webpack で生成したライブラリを使用することができるようになりました。
Webpacker 自体は Rails 5.1 から使用することができましたが、Rails 6 では Webpacker がデフォルトになりました。これによる変更点は以下のとおりです。
- 新規Railsアプリケーションの作成時にwebpackのインストールが自動で行われるようになった
- Action Cableのジェネレータが生成するコードがCoffeeScriptからJavaScriptに変わった
- scaffoldのジェネレータがJavaScriptのコードを生成しなくなった
- rails-ujsやAction Cableなどで使われているRails内のJavaScriptライブラリがnpmで公開されているものになった
今回の変更により Rails 6 では CoffeeScript が一切使われなくなりました。
Webpackerの設定
Webpacker の設定は config/webpacker.yml で行います。ここでは、生成されたファイルの出力先やビルド対象ファイルの指定などを行います。プロジェクト作成時の webpacker.yml の内容は以下のとおりです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | # Note: You must restart bin/webpack-dev-server for changes to take effect default: &default source_path: app/javascript source_entry_path: packs public_root_path: public public_output_path: packs cache_path: tmp/cache/webpacker check_yarn_integrity: false webpack_compile_output: false # Additional paths webpack should lookup modules # ['app/assets', 'engine/foo/app/assets'] resolved_paths: [] # Reload manifest.json on all requests so we reload latest compiled packs cache_manifest: false # Extract and emit a css file extract_css: false static_assets_extensions: - .jpg - .jpeg - .png - .gif - .tiff - .ico - .svg - .eot - .otf - .ttf - .woff - .woff2 extensions: - .mjs - .js - .sass - .scss - .css - .module.sass - .module.scss - .module.css - .png - .svg - .gif - .jpeg - .jpg development: <<: *default compile: true # Verifies that correct packages and versions are installed by inspecting package.json, yarn.lock, and node_modules check_yarn_integrity: true # Reference: https://webpack.js.org/configuration/dev-server/ dev_server: https: false host: localhost port: 3035 public: localhost:3035 hmr: false # Inline should be set to true if using HMR inline: true overlay: true compress: true disable_host_check: true use_local_ip: false quiet: false headers: 'Access-Control-Allow-Origin': '*' watch_options: ignored: '**/node_modules/**' test: <<: *default compile: true # Compile test packs to a separate directory public_output_path: packs-test production: <<: *default # Production depends on precompilation of packs prior to booting for performance. compile: false # Extract and emit a css file extract_css: true # Cache manifest.json for performance cache_manifest: true |
また、 webpack 自体の設定は config/webpack/ ディレクトリの環境ごとの JavaScript ファイルで行います。こちらもプロジェクト作成時の development.js の内容を表示します。
1 2 3 4 5 | process.env.NODE_ENV = process.env.NODE_ENV || 'development' const environment = require('./environment') module.exports = environment.toWebpackConfig() |
Webpackerでのビルド
Webpacker では webpack を操作するための binstub が提供されています。代表的なコマンドをいくつか紹介します。
1 | bin/webpack --debug |
上記はデバッグモードでビルドする場合のコマンドです。
1 | bin/webpack-dev-server |
上記は webpack-dev-server のラッパコマンドです。このコマンドではファイルの更新時に自動的にビルドされるだけでなくブラウザがリロードされるため、開発中はこちらのコマンドを使うのが良いでしょう。
1 | bin/rails webpacker:compile |
上記は本番環境向けのビルドタスクです。 webpack が正しくインストールされているかのチェックと webpack でのビルドを行います。
ビルドしたJavaScriptファイルを読み込む
Webpacker でビルドした JavaScript ファイルを読み込むには javascript_pack_tag を使用します。例えば、application.js を読み込むには以下のように記述します。
1 | <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %> |
ほかにも、スタイルシートの読み込み用には stylesheet_pack_tag が、画像ファイルの読み込み用には image_pack_tag があります。
split chunksを使用する場合
split chunks を使用する場合は javascript_pack_tag が使用できません。その場合は代わりに javascript_packs_with_chunks_tag を使用します。
JavaScript以外のアセット
Webpacker のスタイルシートや画像ファイルなどの JavaScript 以外のアセットの管理は発展途上であり、その点は Sprockets に軍配が上がります。そのため、デフォルトでは Webpacker と Sprockets が併用して使用されています。もちろん、 Webpacker のみを使用するように変更することができます。
複数データベース接続
Rails 6 以前は複数のデータベースに接続するには establish_connection メソッドを明示的に呼び出すか、Octopus などのサードパーティ製のコンポーネントに頼る必要がありました。Rails 6 では Active Record に複数データベースへの接続機能が実装され、これにより気軽に2つ以上のデータベースにコネクションを張ることができるようになりました。
データベースの設定は引き続き config/database.yml に記述しますが、Rails 6 からは1階層増えて接続情報に名前をつけられるようになりました。ここでつけた名前はリードレプリカ構成にする場合や異なるデータベースに接続する際に使います。
database.yml の設定例を以下に記載します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | default: &default adapter: mysql2 encoding: utf8mb4 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root password: host: localhost development: primary: <<: *default database: rails6_development another_db: <<: *default database: rails6_development_another migrations_paths: db/another_db_migrate |
データベース毎にマイグレーションファイルを分けたい場合は another_db のように migrations_paths を指定します。
※ mysql2 のインストールに失敗する場合はこちらが参考になります。
マイグレーションの実行
マイグレーションファイル作成のコマンドは Rails 6 以前と基本的に変わりません。メインのデータベース以外のデータベースのマイグレーションファイルを作成するには –db オプションを指定します。
1 | bin/rails g migration create_another_users_table --db another_db |
このようにすると –db オプションで指定したディレクトリにマイグレーションファイルが作成されます。
リードレプリカ構成におけるデータベースの設定
リードレプリカ構成にするにはスレーブ側のデータベースの定義に replica: true を指定します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | default: &default adapter: mysql2 encoding: utf8mb4 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root password: host: localhost development: primary: <<: *default database: rails6_development readonly: <<: *default database: rails6_development replica: true |
こうすることでアプリケーションからの更新系のクエリを受付なくなります。
リードレプリカ構成におけるモデルの設定
リードレプリカの設定はモデルで行います。例えば、database.yml で定義した readonly をスレーブデータベースとして接続するには aplication_record.rb に以下のように connects_to を指定します。
1 2 3 4 5 | class ApplicationRecord < ActiveRecord::Base self.abstract_class = true connects_to database: { writing: :primary, reading: :readonly } end |
接続先データベースの変更
特定の処理で接続するデータベースを変える場合は connected_to メソッドを使用します。database.yml で定義した another_db に接続するには以下のようにします。
1 2 3 | ActiveRecord::Base.connected_to(database: :another_db) do AnotherUser.create(name: 'yamada') end |
並列テスト
2014年以降、Rails ではユニットテストよりもE2Eテストを強化する方針に転換しました。しかし、E2Eテストでは内部でブラウザエンジンを実行するためオーバーヘッドが大きくなり、その結果テストの実行時間が長くなる傾向にありました。
そこで、Rails 6 ではテストを並列に実行する機能が実装されました。この並列テストでは複数のワーカを起動し、それぞれのワーカで並列にテストを行うようになっています。
ワーカの実行
ワーカはプロセス、もしくはスレッドで実行できます。Linux や macOS などの fork が使用できる環境では fork でプロセスを生成します。逆に、Windows などの fork が使用できない環境ではスレッドを使用します。
データベースの作成
ワーカをプロセスとして実行した場合はワーカ毎に前処理でデータベースが作成されます。そのためワーカ間でのデータベースへの競合は発生しません。しかし、ワーカをスレッドとして実行した場合は前処理でデータベースの作成が行われず、複数のスレッドが同じデータベースを使用するため競合が発生する可能性があります。そのため、スレッドを使用する場合はアプリケーション側で排他制御を意識する必要があります。
Rails 6で追加されたコンポーネント
Rails 6 では新たに Action Text と Action Mailbox というコンポーネントが追加されました。これらのコンポーネントはたしかに便利ですが使わないサービスも多いのではないかと思います。なので、どのようなサービスなのかを簡単に紹介するに留めます。
今回追加されたコンポーネントは Rails 5.2 で追加された Active Storage と密接な関係にあります。そこで、まず始めに Active Storage について簡単におさらいし、その後で新しいコンポーネントについて触れていきます。
Active Storage
Active Storage はファイルアップロードのためのコンポーネントです。ローカルへのアップロードはもちろん、Amazon S3 や Google Cloud Storage などへのアップロードも可能です。
carrierwave などのコンポーネントでは既存のテーブルにファイル情報を保持するカラムを追加する必要がありましたが Active Storage ではファイル情報を管理するテーブルが作られ、Active Record のモデルと紐付けられます。
Action Text
Action Text は WYSIWYG エディタと Rails アプリケーションを連携させるためのコンポーネントです。このコンポーネントでは主に以下の機能を提供します。
- 入力した内容を保存するカラムの追加
- テキスト内で入力されたファイルの保存
- サニタイズ処理
Action Mailbox
Action Mailbox はメール受信処理のコンポーネントです。主にメールの受信、メールごとの処理、処理が終わったメールの削除を行います。メールの受信に関しては SendGrid や Mailgan などのクラウドサービスからのメール受信をサポートしています。
メールを受信してからの処理の流れは以下の通りです。
- 各ルートでメールを受信する
- 受信したメールを保存する
- 振り分け処理用のジョブを登録する
- 振り分け処理を実行する
- 振り分けたメールを処理する
- メール削除用のジョブを登録する
これらの内、4と5についてはユーザが実装する必要があります。
さいごに
今回のバージョンアップでは webpack との連携や複数データベースのサポートなど実際のサービス開発に大きな影響を与える内容が多いのではないでしょうか。今現在 Rails はあまり人気がない印象ですがバージョンアップを機に盛り上がってくれればと思います。