Ruby
Rails
webpack
React
Rails5

ReactやwebpackもサポートしたRails 5.1の新機能・変更点


はじめに

2016年のバージョン5.0のリリースに引き続き、2017年の春にRuby on Rails(以下Rails)のバージョン5.1がリリースされました。

Rails 5.0は、APIモードやAction Cableなど、APIやWebSocket周りの新機能を中心としたリリースでしたが、Rails 5.1は、React / Vue.js / webpack / Yarn(npmパッケージ)のサポートなど、フロントエンド周りの新機能を中心としたリリースとなりました。

本記事では、Rails 5.0の記事に引き続いて、GitHubのRailsプロジェクトIssuesPull Requestsの履歴、Railsの公式ブログの記事をもとに、Rails 5.1の主要な新機能・変更点の紹介を行います。


新機能


Reactとwebpackのサポート

Webpackerというパッケージを使用することで、JavaScriptファイルの依存関係の処理や圧縮・結合をwebpack経由で行えるようになりました。

Webpacker自体は既存のアセットパイプラインと共存し、比較的規模の大きいシングルページアプリケーションのようなJavaScriptの管理を行います。

(Webpackerが管理するJavaScriptのパスは、app/javascript/packs/になっています。)

webpackのサポートを有効にするには、rails newコマンドのオプションで--webpackを使用するか、Gemfileのgem 'webpacker'の部分を有効にして、bin/rails webpacker:installを実行します。

標準でReactもサポートされており、新しいアプリケーションの場合はrails new myapp --webpack=reactを、既存のアプリケーションの場合は、rails webpacker:install:reactをそれぞれ実行することですぐに利用することができます。


Yarnとnpmパッケージのサポート

rails newコマンドを実行した際にデフォルトでYarnとnpmパッケージのサポートを有効にするようにしました。これによって、package.jsonがアプリケーションに追加され、node_modulesがアセットパイプラインのパスに追加されます。

rails newコマンド実行時にYarnのサポートを無効にするには、--skip-yarnを指定します。


機能追加


form_with

これまで似たような使い方がされてきたform_tagform_forの代わりにform_withメソッドを使用することができるようになりました。

form_tagform_forは受け取る引数やオプション、フォーム内部のフィールド用のタグを出力するメソッドなどが異なっていましたが、form_withによって共通の方法でフォームを作成することができるようになります。

(Rails 6ではform_withに完全に統合され、form_tagform_forは非推奨になる可能性があります。)

また、form_withでは、以下のような変更が加えられています。


  1. 標準では出力されるHTMLタグにDOMのid属性やclass属性をつけないようにしました。これは特にid属性の重複をなくすためで、必要な場合は簡単に追加することもできます。

  2. id属性やclass属性を追加する際に、オプションのhtml: {}の値に入れる必要がなくなりました。これは、idclassといった名前が他のキーの名前とほぼ重複しないためです。

  3. モデルの属性以外のフィールドを許可するようにしました。これによって、登録確認のチェックボックスなど、モデルに含まれないフィールドをフォームに入れるのが容易になりました。


  4. remote: trueオプションがデフォルトになりました。フォーム送信後は、全く新しいページに遷移するのではなく、Turbolinks.visit()か、サーバー側で生成されたレスポンス(のJavaScript)を使用します。

例)



  • form_withurlを指定した場合

<%= form_with url: posts_path do |form| %>

<%= form.text_field :title %>
<% end %>

<form action="/posts" method="post" data-remote="true">

<input type="text" name="title">
</form>



  • form_withmodelを指定した場合

<%= form_with model: Post.new do |form| %>

<%= form.text_field :title %>
<% end %>

<form action="/posts" method="post" data-remote="true">

<input type="text" name="post[title]">
</form>


Action Mailerでパラメータを利用できるように

コントローラと同様にメーラでもパラメータを利用できるようになりました。

メーラに使用するパラメータはwithメソッドの引数で指定します。

たとえば、以下のようになります。

InvitationsMailer.with(inviter: '喜井太郎').account_invitation.deliver_now!

(パラメータを使用しない場合は、withメソッドの呼び出しがなくなるだけなので、後方互換姓が保たれています。)

メーラのアクション内では、以下のようにしてparamsにアクセスできるようになります。

class InvitationsMailer < ApplicationMailer

def account_invitation
@inviter = params[:inviter]
mail(subject: "#{@inviter}さんから招待が来ています。")
end
end

また、before_actionなどのコールバックを使用して、各アクションに共通なパラメータの処理を定義することができます。

class InvitationsMailer < ApplicationMailer

before_action { @inviter = params[:inviter] }

def account_invitation
mail(subject: "#{@inviter}さんから招待が来ています。")
end

def project_invitation
@project = params[:project]
mail(subject: "#{@inviter}さんから#{@project}への招待が来ています。")
end
end


tagヘルパの記法改善

ActionView::Helpers::TagHelperで提供されている#tag#content_tagの引数を減らし、HTML5をサポートするようにしました。

(下の例に示すように、これまで、上記のメソッドは多くのパラメータを渡す必要があり、デフォルトとしてXTHMLを想定していたため冗長な記述が必要でした。)

これにより、新しい記法では、より短くシンプルにヘルパメソッドを用いてタグを出力することができるようになります。

古い記法

tag(:br, nil, true)

content_tag(:div, content_tag(:p, "Hello world!"), class: "strong")

<%= content_tag :div, class: "strong" do -%>
Hello world!
<% end -%>

新しい記法

tag.br

tag.div tag.p("Hello world!"), class: "strong"

<%= tag.div class: "strong" do %>
Hello world!
<% end %>


ActiveJob::Baseにretry_on / discard_onを追加

Active Jobで例外が発生した場合のジョブの再試行と破棄の動作を指定できるようになりました。


再試行 (retry_on)

例外をキャッチして、ジョブの再試行を指定した試行回数分だけ実行します。試行回数を超えた場合は、例外はキャッチされません。

再試行が失敗した場合は、ブロックを渡して独自の処理を行うこともできます。

オプション



  • wait: 再試行の間隔を秒単位で指定することができます。(デフォルト: 3秒)


  • exponentially_longer: 再試行の間隔に指数アルゴリズムを使用します。
    (回数**4 + 2,となり、3秒後、18秒後、83秒後、…に再試行を行います。)


  • attempts: 指定した回数だけ再試行を行います。(デフォルト: 5回)


  • queue: 再試行を指定した別のキューで行います。


  • prioriry: 再試行を別の優先度で行います。

例)

class RemoteServiceJob < ActiveJob::Base

retry_on CustomAppException
retry_on ActiveRecord::StatementInvalid, wait: 5.seconds, attempts: 3
retry_on Net::OpenTimeout, wait: :exponentially_longer, attempts: 10
..
end


破棄

例外が発生した場合は再試行を行わずにジョブを破棄します。これは対象となるモデルのデータが存在しない場合などで使用することができます。

例)

class SearchIndexingJob < ActiveJob::Base

discard_on ActiveJob::DeserializationError
..
end


単一リソースのパス生成方法の指定が可能に

ルーティングの記述でresolveを用いることによって、モデルのインスタンスを指定した場合のURLヘルパの生成方法をカスタマイズすることができるようになりました。

例えば、以下のようなコードで、@current_userUserモデルのインスタンスを引数として用いた場合に、URLとして/profileを生成できるようにする場合は、

link_to 'プロフィール', @current_user

ルーティングで以下のような記述を行います。

resource :profile

resolve("User") { [:profile] }

※上記のパス生成方法の指定に関しては、現在は1つのモデルインスタンスを使用した単一リソースの場合のみ有効で、複数リソースの場合や、namespaceを使用した場合、リソースのネストを行った場合は使用できません。


カスタムURLヘルパ

ルーティングの記述でdirectを用いることによって、独自に定義したapple_urlのようなURLヘルパを使用できるようになりました。

direct :apple do

"http://www.apple.com/"
end

カスタムURLヘルパに引数を取るようにできるようにするには、ブロック引数を使用します。

たとえば、commentable_path(article)みたいなURLヘルパを使用したい場合(引数articleはモデルのインスタンス)、以下のようにルーティングを定義するとarticleがブロック引数のmodelとなりURLの生成が可能になります。(ブロックが返す値はurl_forの引数となります。)

direct :commentable do |model|

[ model, anchor: model.dom_id ]
end


Capybaraによるシステムテスト

標準でChromeなどのブラウザを用いたテストが実行できるようになりました。内部的には、Capybaraを使用しています。

テストコードの記述には、以下の例のように、ApplicationSystemTestCaseを継承したクラスを用います。

require 'application_system_test_case'

class Users::CreateTest < ApplicationSystemTestCase
test "adding a new user" do
visit users_path
click_on 'New User'

fill_in 'Name', with: 'Arya'
click_on 'Create User'
assert_text 'Arya'
end
end

Railsアプリケーションを作成した場合、標準で以下のようなapplication_system_test_case.rbという名前のファイルが生成され、そこにApplicationSystemTestCaseクラスが定義されています。

require "test_helper"

class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
driven_by :selenium, using: :chrome, screen_size: [1400, 1400]
end

このファイルの内容を変更することで、Capybaraのドライバや、ブラウザ、画面解像度の設定が可能です。


config/secrets.ymlの暗号化

これまでconfig/secrets.ymlファイルにアプリケーションで使用するsecret_key_baseや、外部APIのトークンなどの記述を平文で行っていましたが、Rails 5.1ではこの内容を暗号化したファイルに保存したり、編集することができるようになりました。


鍵と暗号化されたファイルの生成

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

bin/rails secrets:setup

config/secrets.yml.key(鍵)とconfig/secrets.yml.enc(暗号化されたconfig/secrets.yml)の2つのファイルが生成されます。

※ 絶対に、config/secrets.yml.keyをリポジトリにコミットしないようにしてください。また、その中身を失わないようにしてください。第三者に悪用されたり、config/secrets.yml.encの内容を読み書きすることができなくなったりしてしまいます。

RailsはENV["RAILS_MASTER_KEY"]からも鍵を参照するので、config/secrets.yml.keyの内容を環境変数RAILS_MASTER_KEYに入れておくのが簡単です。


暗号化されたファイルの編集

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

bin/rails secrets:edit

これによって、$EDITOR環境変数で指定されたエディタが立ち上がり、一時ファイルに書き出された平文化されたconfig/secrets.yml.encを編集することができます。エディタで保存を行うと、一時ファイルは破棄され、config/secrets.yml.encに書き出しが行われます。 


変更点


jQuery依存の廃止

これまで使用されていたRailsのJavaScriptのjquery-ujsがjQueryではなく素のJavaScriptで書き直されてrails-ujsとなりました。


PostgreSQLとMySQLのプライマリキーがBIGINTに

PostgreSQLとMySQLの両方でデフォルトのプライマリキーがIntegerからBIGINTになりました。(SQLiteはBIGINTのプライマリキーをサポートしていないので、Integerのままです。)

既存のテーブルはIntegerのままで、新しいテーブルでBIGINTのプライマリキーが有効になります。


ActiveRecord::NotNullViolation

NOT NULL制約の為にモデルの保存に失敗した場合は、ActiveRecord::StatementInvalidの代わりにActiveRecord::NotNullViolationを例外として発生するようにしました。


ActiveRecord::DeadlockError

トランザクション処理でシリアライズに失敗した場合や、デッドロックが検出された場合に、RDBMSのエラーコードを元に、ActiveRecord::DeadlockErrorを例外として発生するようにしました。


Time形式のカラムがタイムゾーンを考慮するように

マイグレーションで:datetimeを指定したカラムだけでなく、:timeを指定したカラムも、標準でタイムゾーンを考慮して(具体的には、Time.zoneとして)処理が行われることになりました。

以前の挙動に戻すには以下のように設定を行う必要があります。

config/application.rb

config.active_record.time_zone_aware_types = [:datetime]


Active Record (Active Model)のコールバックの停止条件の変更

これまで、Active RecordやActive Modelで、before_save / before_validationのコールバックのメソッドの返り値がfalseの場合は実行を停止してましたが、この挙動が変更になり、Rails 5.0で導入されたthrow :abortの呼び出しによってのみ停止するようになりました。

(特にRails 4からアプリケーションをアップグレードした場合はfalseで停止を行っていないかチェックが必要です。)


廃止


ActionController::TestCaseをgemとして分離

Rails 5.0ではActionDispatch::IntegrationTestをActionController::TestCaseの代わりに標準で使用するようになっており、ActionController::TestCaseは非推奨になっていました。

Rails 5.1ではActionController::TestCaseはgemとしてRailsとは別のリポジトリに切り離されて、標準では利用できなくなります。


コントローラのコールバックの名称をactionを用いたものに統一

before_filter / after_filterなどのfilterを用いた名称が廃止され、before_action / after_actionなどactionを用いた名称に統一されます。


redirect_to :backの廃止

コントローラ内で以前のアクセスしたURLに戻る際に用いられてきた、redirect_to :backのメソッド呼び出しが廃止され、代わりにredirect_back(fallback_location: fallback_location)を使うようにする必要があります。ここで、fallback_locationとしてはリクエストにHTTPリファラの情報がない場合に戻る場所(URL)を指定します。


render :nothingの廃止

コントローラ内でステータスコードのみを返す場合に利用されていたrender :nothingのメソッド呼び出しが廃止されました。

Rails 5.1以降では、単純にステータスコードを返す場合、head :createdのように、headメソッドを使用する必要があります。


params(コントローラ内のパラメータ)とハッシュの比較ができないように

Rails 5でActionController::ParametersがHashから継承しなくなったのに伴い、Rails 5.1ではparamsとハッシュの比較ができなくなりました。今後比較を行う場合には、ActionController::Parameters#newでハッシュを変換する必要があります。また、ActionController::ParametersからHashにあるメソッドの一部が廃止されています。


まとめ

Rails 5.1はReact / webpack / Yarnのサポートなどフロントエンド周りの新機能と共に、form_withタグやtagヘルパの文法変更など、主にJavaScriptアセットやView部分の変更が大きいリリースとなりそうです。将来的な変更に備え、今からアップグレードの準備しておきましょう。


参考サイト