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

Rails 7: 数値バリデータにonly_numericオプションが追加(翻訳)

概要

元サイトの許諾を得て翻訳・公開いたします。

なお、この改修はRails 7.0.0〜7.0.2には入っていません。現時点ではmainブランチに入っています。

Rails 7: 数値バリデータにonly_numericオプションが追加(翻訳)

Active Recordには、クラス定義内で直接利用できる多くのバリデーションヘルパーが事前定義されています。これらのヘルパーが提供するバリデーションルールは共通しています。つまり、バリデーションが失敗すればオブジェクトのエラーコレクションにエラーが追加され、このエラーはバリデーションが行われる属性に関連しています。

また、コードを1行書けば、さまざまな属性に同じバリデーションを追加できます。これが可能なのは、どのバリデーションヘルパーにも任意の個数の属性名を渡せるからです。

さまざまなヘルパーの中にはnumericalityヘルパーもあります。このヘルパーは、属性が数値であることを保証します。

numericalityヘルパーの動作を理解するために、最初にJSONカラムを持つテーブルを生成します。

create_table :cricket do |t|
  t.jsonb :points
end

変更前のバリデーション

従来のRailsでは、numericalityバリデータに整数ですらでない値を渡してもエラーにならずにパスしていました。以下の例を見ていただければおわかりかと思います。

class Cricketer < ApplicationRecord
  store_accessor :points, %i[scores]
end

>> Cricketer.create!(scores: "30")
#<Cricketer id: 1, points: {"scores" => "30"}, created_at: Sun, 04 Feb 2022 14:09:43.045301000 UTC +00:00, updated_at: Sun, 04 Feb 2022 14:09:43.045301000 UTC >

訳注

上のコード例はたとえば以下のようにonly_integerを指定するつもりだったと思われます。

class Cricketer < ApplicationRecord
  store_accessor :points, %i[scores]
  validates_numericality_of :scores, only_integer: true, allow_nil: true
end

Rails 7.0.2.2 + Ruby 3.1.1で試すと、たとえば"a"Validation failed: Scores is not a number (ActiveRecord::RecordInvalid)エラーになりますが、"30"という文字列はたしかに記事にあるようにパスしてしまいました。

変更後のバリデーション

先ごろRails 7.0にonly_numericオプションが導入され、属性の値に数値のみを許可できるようになりました(#43914)。つまり、数字の文字列を値として渡すと、ちゃんと解析してエラーを発生するようになります。

以下の例を見ていただければおわかりかと思います。

class Cricketer < ApplicationRecord
  store_accessor :points, %i[scores]
  validates_numericality_of :scores, only_numeric: true, allow_nil: true
end
>> Cricketer.create!(scores: "30")
'raise_validation_error': Validation failed: Scores is not a number (ActiveRecord::RecordInvalid)

>> Cricketer.create!(scores: 30)
#<Cricketer id: 1, points: {"scores" => 30}, created_at: Sun, 04 Feb 2022 14:09:43.045301000 UTC +00:00, updated_at: Sun, 04 Feb 2022 14:09:43.045301000 UTC >

通常、JSONカラムのデータは自動的にはシリアライズされないので、データを正しくシリアライズできるよう、このオプションがRailsのnumericalityバリデータに追加されました。

詳しくは#43914をご覧ください。

訳注

Railsのmainブランチ(74ba52e)でonly_numericを使うことで上記の動作を確認できました。

なお、従来からあるonly_integerの挙動は変わっていません。整数は数値の30でも文字列の"30"でもパスし、30なら整数で、"30"なら文字列で登録されます。
"a"などの非数値文字列や小数3.14や小数の文字列"3.14"は以下のエラーになります。

Validation failed: Scores must be an integer (ActiveRecord::RecordInvalid)

関連記事

Rails 7: Active RecordのConnectionPoolsがFiberセーフになった(翻訳)


CONTACT

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