💡

【Rails】ActiveModel::Attributes の活用法

2024/06/03に公開

こんにちは!
ラブグラフエンジニアのひろです。

今回は、データベースのカラムを追加することなく、クラスに属性を持たせることができる ActiveModel::Attributes について書いていきます。

ActiveModel::Attributes の基本

ActiveModel::Attributes は、 Active Model の一部であり、任意のクラスに型付けされた属性を宣言的に追加することができます。
これにより、 Active Record モデルと同様の方法で、非永続属性を扱うことができるようになります。

schema.rb
create_table "users", force: :cascade do |t|
  t.string "name"
  t.string "email"

  t.timestamps
end
user.rb
class User
  validates :name, presence: true
  validates :email, presence: true, uniqueness: true

  attribute :is_send_welcome_mail, :boolean, default: true # この行を追加
end

上記の例では、 User クラスに is_send_welcome_mail という属性を追加しています。
これらはデータベースのカラムとは無関係に、オブジェクトの属性として機能します。

## attribute 追加前
User.new
=> #<User:0x0000aaaa0000a000
 id: nil,
 name: "",
 email: "">

## attribute 追加後
User.new
=> #<User:0x0000aaaa0000a000
 id: nil,
 name: "",
 email: "",
 is_send_welcome_mail: true>

実践例

実際に ActiveModel::Attributes をどう扱うのかやってみましょう。
ここでは 「インスタンスが作成されたときにメールを送信したいが、特定の場所から作られた場合はメールを送らない」 という要件に対応する例を示します。

attribute の追加

まずは、メール送信の可否を判断する is_send_welcome_mail 属性をモデルに追加します。

この属性は、インスタンスの生成時に設定され、それに応じてメール送信をおこなうかどうかを決定します。
is_send_welcome_mail はデフォルトで true に設定されており、メール送信がおこなわれることを意味します。

user.rb
class User < ApplicationRecord
  validates :name, presence: true
  validates :email, presence: true, uniqueness: true

  attribute :is_send_welcome_mail, :boolean, default: true

  after_commit :send_welcome_email, on: :create, if: :is_send_welcome_mail

  private

  def send_welcome_email
    UserMailer.welcome_email(self).deliver_leter
  end
end

そしてインスタンスが作成されたのち、 is_send_welcome_mailtrue の場合にのみ、 send_welcome_email メソッドが実行されるようになっています。

attribute の利用

# メール送信を行う場合
user1 = User.new(name: '太朗', email: 'taro@example.com')

# メール送信を行わない場合
user2 = User.new(name: '花子', email: 'hanako@example.com', is_send_welcome_mail: false)

この例では、 user1 のインスタンス生成時には is_send_welcome_mail がデフォルト値の true になるため、メールが送信されます。
一方で user2 では、インスタンス生成時に is_send_welcome_mailfalse に設定しているため、メールは送信されません。

このように ActiveModel::Attributes を活用することで、「モデルのコールバックに書かれている処理を特定の条件では実行したくない」といった実装を実現しました。

終わりに

ActiveModel::Attributes を使い、非永続的な値をモデルに持たせることで、より柔軟な対応ができるようになります。

「カラムを追加するほどではないんだけど、インスタンスの状態ごとに処理を分岐したい...」
というケースに出会った時、 ActiveModel::Attributes の利用を検討してみてください。

ラブグラフのエンジニアブログ

Discussion