Hisakeyのブログ

エンジニアが色々呟くブログです。

Rails:非カラム属性(attribute)にenumを使う方法 ― 仕組みと実例

はじめに

Rails 7.1.2 で 非カラム属性(non-column-backed attribute)に対して enum を再び使えるようになりました。ところが、この変更はブログ等であまり解説されておらず、理解に時間がかかったため、自分用の整理も兼ねて記事化します。

単なる使い方だけでなく、attribute/enum を どのクラス(モジュール)が提供しているのか まで掘り下げ、ActiveRecord と ActiveModel の役割もあわせて整理します。

背景・動機

実務コードで「非カラム属性に enum」のパターンを見かけ、意味が掴めず調査に時間がかかりました。実際のコードは出せないため、CHANGELOGのサンプルで説明します。

参照:Rails 7.1.2 CHANGELOGenum を非カラム属性で再サポート。「明示型の attribute 宣言が必須」)

class Post < ApplicationRecord
  attribute :topic, :string
  enum topic: %i[science tech engineering math]
end

このコードで一番の疑問は「attribute とは何者か?」でした。attribute を使うと DBカラムを持たない仮想属性にも型を与えられる ため、7.1.2 以降は その仮想属性に enum を定義できる、というのがポイントです。

実例・やってみたこと

まずは最小例です(posts テーブルに topic カラムは無い想定)。

class Post < ApplicationRecord
  attribute :topic, :string, default: "science"  # 非カラム属性に明示型
  enum :topic, %i[science tech engineering math], _prefix: :topic
end

使い勝手(抜粋):

判定: post.topic_science?, post.topic_tech?

変更: post.topic_science!, post.topic = "tech"

列挙: Post.topics → { "science" => "science", ... }(文字列マッピング

重要:これは仮想属性なので このままではDBに保存されません。確認例:

Post.attribute_types.keys.include?("topic")  # => true  (仮想属性も型表に載る)
Post.column_names.include?("topic")          # => false (DBカラムではない)
post = Post.new
post.topic # => "science"

注意点

既存のDBカラムに対しては attribute を追加する必要は基本ありません。 enum が生成するスコープ(例:Post.science)は、DBカラムが無いと検索に使えません。

学び・気付き・解釈

  1. 「attribute を先に明示」するのが前提

非カラム属性に enum を使う場合は、必ず attribute :name, :type を先に宣言する必要があります。これが 7.1.2 の再サポート条件です。

  1. ActiveRecord と ActiveModel の役割(どこが何を提供している?)

    • attribute

      土台は ActiveModel::Attributes(型付き属性の仕組み)。

      ActiveRecord 側にも ActiveRecord::Attributes があり、ActiveModel の仕組みを ARモデルの世界(永続化・クエリ)へ統合しています。

      参考:ActiveRecord 側の実装文脈

    • enum

      ActiveRecord::Enum の機能。ActiveModel(ActiveModel::Model など)には enum は無い点に注意。

ActiveRecord と ActiveModel の関係

継承関係ではありません。ActiveRecord::Base が ActiveModel の複数モジュール(Validations / Dirty / Attributes など)を取り込んでいます。

ActiveModel は「モデル機能の部品置き場」、ActiveRecord は「DB永続化を含む機能を提供するクラス」という感じでしょうか?

まとめ

  • Rails 7.1.2 で、非カラム属性 + 明示型 attribute に対して enum を再び使えるようになった。

  • フォーム一時値や外部API値など仮想属性の状態管理に enum を活用できる。

  • attribute は ActiveModel が土台、enumActiveRecord の機能――この切り分けを押さえると理解がスッキリします。

あまり馴染みのないコードだったので、深堀りしてみました。

そのおかげで、Railsの内部処理についても知ることができとても良い機会でした!

また、見たことないなと思うコードがあったときは、深堀りしてみようと思います。