【Rails】ActiveModel::Attributes の活用法
こんにちは!
ラブグラフエンジニアのひろです。
今回は、データベースのカラムを追加することなく、クラスに属性を持たせることができる ActiveModel::Attributes について書いていきます。
ActiveModel::Attributes の基本
ActiveModel::Attributes は、 Active Model の一部であり、任意のクラスに型付けされた属性を宣言的に追加することができます。
これにより、 Active Record モデルと同様の方法で、非永続属性を扱うことができるようになります。
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.timestamps
end
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
に設定されており、メール送信がおこなわれることを意味します。
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_mail
が true
の場合にのみ、 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_mail
を false
に設定しているため、メールは送信されません。
このように ActiveModel::Attributes
を活用することで、「モデルのコールバックに書かれている処理を特定の条件では実行したくない」といった実装を実現しました。
終わりに
ActiveModel::Attributes
を使い、非永続的な値をモデルに持たせることで、より柔軟な対応ができるようになります。
「カラムを追加するほどではないんだけど、インスタンスの状態ごとに処理を分岐したい...」
というケースに出会った時、 ActiveModel::Attributes
の利用を検討してみてください。
Discussion