ゼネットの土屋です。
先日Rails 7.2 Beta 1について投稿させていただきました。
その際、大きな変更点については触れましたが、非推奨への変更については触れませんでした。
media.zenet-web.co.jp
今回はRails 7.2で非推奨となる「enumでキーワード引数を使用した列挙型の定義」について触れていきたいと思います。
非推奨警告の内容
modelのenum定義がキーワード引数を使用した列挙型の場合、下記のような非推奨警告が出力されます。
DEPRECATION WARNING: Defining enums with keyword arguments is deprecated and will be removed in Rails 7.3. Positional arguments should be used instead: enum :type, {:unknown=>0, :legacy=>1, :store=>2} (called from <class:Agent> at /app/app/models/agent.rb:14)
翻訳すると、
キーワード引数を使用した列挙型の定義は非推奨となり、Rails 7.3では削除される予定です。代わりに位置引数を使用する必要があります。
NG例
問題となるコードを確認してみると下記のようになっていました。
enum type: {:unknown=>0, :legacy=>1, :store=>2}, _prefix: true
非推奨警告に記載されているコード例と比べてみると、些細な違いのようです。
enum type: {:unknown ... # × enum :type, {:unknown ... # ○
修正
コード例に倣って下記のように修正してみましたが、別のエラーが出力されました。
enum :type, {:unknown=>0, :legacy=>1, :store=>2}, _prefix: true
Failure/Error: enum :type, { unknown: 0, legacy: 1, store: 2 }, _prefix: true ArgumentError: You tried to define an enum named "type" on the model "Agent", but this will generate a instance method "unknown?", which is already defined by another enum. # /usr/local/bundle/gems/activerecord-7.2.0.beta1/lib/active_record/enum.rb:394:in `raise_conflict_error' # /usr/local/bundle/gems/activerecord-7.2.0.beta1/lib/active_record/enum.rb:389:in `detect_enum_conflict!' # /usr/local/bundle/gems/activerecord-7.2.0.beta1/lib/active_record/enum.rb:317:in `define_enum_methods' # /usr/local/bundle/gems/activerecord-7.2.0.beta1/lib/active_record/enum.rb:285:in `block (2 levels) in _enum' # /usr/local/bundle/gems/activerecord-7.2.0.beta1/lib/active_record/enum.rb:279:in `each_pair' # /usr/local/bundle/gems/activerecord-7.2.0.beta1/lib/active_record/enum.rb:279:in `each' # /usr/local/bundle/gems/activerecord-7.2.0.beta1/lib/active_record/enum.rb:279:in `block in _enum' # /usr/local/bundle/gems/activerecord-7.2.0.beta1/lib/active_record/enum.rb:269:in `module_eval' # /usr/local/bundle/gems/activerecord-7.2.0.beta1/lib/active_record/enum.rb:269:in `_enum' # /usr/local/bundle/gems/activerecord-7.2.0.beta1/lib/active_record/enum.rb:219:in `enum' # ./app/models/agent.rb:14:in `<class:Agent>'
調べてみると、オプションの記述が正しくありませんでした。
キーワード引数での定義の場合は、オプションの先頭に_
(アンダースコア)が付きます。(例:_prefix
, _default
)
参考:https://api.rubyonrails.org/v5.1/classes/ActiveRecord/Enum.html
位置引数での定義の場合は、オプションの先頭に_
(アンダースコア)は付けません。(例:prefix
, default
)
参考:https://api.rubyonrails.org/v7.1.3/classes/ActiveRecord/Enum.html
オプションも修正することで無事に非推奨警告が出力されなくなりました。
enum :type, {:unknown=>0, :legacy=>1, :store=>2}, prefix: true
enumのコードを確認する
enumのコードを読むと分かりますが、第一引数が位置引数かキーワード引数かで処理が分かれていたのですね。
def enum(name = nil, values = nil, **options) if name values, options = options, {} unless values return _enum(name, values, **options) end definitions = options.slice!(:_prefix, :_suffix, :_scopes, :_default, :_instance_methods) options.transform_keys! { |key| :"#{key[1..-1]}" } definitions.each { |name, values| _enum(name, values, **options) } ActiveRecord.deprecator.warn(<<~MSG) Defining enums with keyword arguments is deprecated and will be removed in Rails 7.3. Positional arguments should be used instead: #{definitions.map { |name, values| "enum :#{name}, #{values}" }.join("\n")} MSG end
https://github.com/rails/rails/blob/v7.2.0.beta1/activerecord/lib/active_record/enum.rb#L216-L233
なぜこのような分岐が入ったのかコミット履歴を追ってみると、どうやら前述でエラー解決したオプションについて、先頭に_
が付いているのをどうにかしたかったようです。
I propose new syntax for
enum
to avoid leading_
from reserved options, by allowingenum(attr_name, ..., **options)
more Attribute API like syntax. github.com
おわりに
enumに関する非推奨警告を対応しました。
非推奨警告が出力されると「なんで今までOKだったのにダメにするんだよ!?」とネガティブな気持ちになりますが、コミット履歴やissueなどから経緯を確認すると納得できるのかなと思います。
また非推奨警告は放置してしまうと、バージョンを上げた際に多量のエラーの原因になったり、はたまた、エラーも吐かずに異なる結果になったりと、よくないことに繋がってしまうので早めの対応をお勧めします。