とりあえずコード書けよ

技術的なことの備忘録。

Railsのindex_errorsでi18nを適用する際にハマったのと、それを調べる過程について

要約

  • index_errorsでi18nを使うときは config.active_model.i18n_customize_full_message = true を指定すること
  • 一次情報が大事。Qiita等を見るのはそのあと。
  • 困ったときはとりあえずprintデバッグから始めてみる。順を追って行けばそれなりに読める。

本文

Railsで1対多の関係(has_many)において、どの子要素でエラーが出たのかを載せるindex_errorsという機能がある。
index_errorsについては下記参照。
github.com

qiita.com

しかし、この機能困ったことにエラーのキーに上記を見ると分かるが、エラーのキーにindexが入ってくるため、i18nでエラーメッセージを変換するのがなかなか難しいのが個人的に難点だった。

だが、Rails6.0ではこれに対し、indexを除いた値をi18nで参照するような修正がなされているということで、おお素晴らしいとアップデートの日を心待ちにしていた。
github.com

しかし、アップデートしたもののどうだろうか、エラー表示は変わらなかった。
しかも、特に調べてみてもその辺りに関する言及は出てこなかった。
(accepts_nested_attributes_forが市民権を得てないからかな?笑)

どうやらデフォルトでONになるわけではないようである。

仕方ないので、binding.pryしながらコードを読み進めていくと、下記のコードでindexを削るか否かの判定をしていることが分かった。

if self.class.i18n_customize_full_message && @base.class.respond_to?(:i18n_scope)

https://github.com/rails/rails/blob/66cabeda2c46c582d19738e1318be8d59584cc5b/activemodel/lib/active_model/errors.rb#L416

@base.class.respond_to?はtrueだったが、self.class.i18n_customize_full_messageはfalseだったので、処理が走らないという状況だった。
(ちなみに、self.classはActiveModel::Errors、@base.classはフォームで操作しているオブジェクト)

では、このi18n_customize_full_messageをどこで操作しているのか。
まずActiveModel::Errors内でi18n_customize_full_messageを見ると、デフォルトでfalseがセットされているっぽいことが分かった。

class << self
  attr_accessor :i18n_customize_full_message # :nodoc:
end
self.i18n_customize_full_message = false

https://github.com/rails/rails/blob/66cabeda2c46c582d19738e1318be8d59584cc5b/activemodel/lib/active_model/errors.rb#L65-L68

そこで次はrailsリポジトリ内でi18n_customize_full_messageを検索してみる。 そうすると、testファイルを除いてrailtie.rbとconfigure.mdがヒットした。

railtie.rbを見ると、どうやらconfigで何か指定しなくてはならないことが読み解ける。

 initializer "active_model.i18n_customize_full_message" do
   ActiveModel::Error.i18n_customize_full_message = config.active_model.delete(:i18n_customize_full_message) || false
 end

そして、configure.mdを見るとバッチリ書いてあった。

config.active_model.i18n_customize_full_message is a boolean value which controls whether the full_message error format can be overridden at the attribute or model level in the locale files. This is false by default.

https://github.com/rails/rails/blob/439d4995c1dab475b576fcb19ea95ae37e0ed222/guides/source/configuring.md#configuring-active-model

というわけで、config/application.rbに下記の内容を追加する。

module Aftshop
  class Application < Rails::Application
    # other config..
    config.active_model.i18n_customize_full_message = true
  end
end

すると、indexを除いたi18n_keyの内容を無事、参照してくれるようになった。

教訓

一次情報をよく見るべし、とつよつよエンジニアの方々はよく言うが、今回は身を以て経験した感じ。
そして、改めて大事だなと思ったのは2つ。
1. 公式リポジトリを見る習慣 2. rails等のフレームワークのコードも、順に追っていけば案外読める

Railsのコードも、一つの巨大な塊と考えてしまうと、どこからアプローチしていけばいいのか分かりにくい。
でも、binding.pry等を駆使して、順に処理を追って行けば、意外と読めるなという感覚。

どんなに優れたコードとて、所詮は同じ人間が書いたもの。
そんな感じで気楽に構えたらいいのかなーと思った。