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

Rails API: ActiveModel::Dirty(翻訳)

概要

MITライセンスに基づいて翻訳・公開いたします。

Rails API: ActiveModel::Dirty(翻訳)

ActiveModel::Dirtyは、Active Recordと同じ方法でオブジェクトの変更をトラッキングする方法を提供します。

ActiveModel::Dirtyを実装するうえで守るべき要件は以下のとおりです。

  • 自分のオブジェクト内にinclude ActiveModel::Dirtyを書く

  • define_attribute_methodsを呼び出して、トラッキングしたい個別のメソッドを渡す

  • トラッキング対象属性を変更する前には属性名_will_change!を呼び出す

  • 変更が永続化された後は、changes_appliedを呼び出す

  • 変更情報をリセットしたい場合は、clear_changes_informationを呼び出す

  • 直前のデータを復元したい場合はrestore_attributesを呼び出す

最小限の実装は以下のようになります。

class Person
  include ActiveModel::Dirty

  define_attribute_methods :name

  def initialize
    @name = nil
  end

  def name
    @name
  end

  def name=(val)
    name_will_change! unless val == @name
    @name = val
  end

  def save
    # 永続化を行う

    changes_applied
  end

  def reload!
    # 永続化層から値を取得する

    clear_changes_information
  end

  def rollback!
    restore_attributes
  end
end

このPersonオブジェクトを新しくインスタンス化したときは「変更なし」の状態になります。

person = Person.new
person.changed? # => false
  • name属性の変更をチェックする:
person.name = 'Bob'
person.changed?       # => true
person.name_changed?  # => true
person.name_changed?(from: nil, to: "Bob") # => true
person.name_was       # => nil
person.name_change    # => [nil, "Bob"]
person.name = 'Bill'
person.name_change    # => [nil, "Bill"]
  • 変更を保存する:
person.save
person.changed?      # => false
person.name_changed? # => false
  • 変更をリセットする:
person.previous_changes         # => {"name" => [nil, "Bill"]}
person.name_previously_changed? # => true
person.name_previously_changed?(from: nil, to: "Bill") # => true
person.name_previous_change     # => [nil, "Bill"]
person.name_previously_was      # => nil
person.reload!
person.previous_changes         # => {}
  • 変更をロールバックする:
person.name = "Uncle Bob"
person.rollback!
person.name          # => "Bill"
person.name_changed? # => false
  • 属性に同じ値を代入した場合は「変更なし」とする:
person.name = 'Bill'
person.name_changed? # => false
person.name_change   # => nil
  • どの属性が変更されたかを調べる:
person.name = 'Bob'
person.changed # => ["name"]
person.changes # => {"name" => ["Bill", "Bob"]}

Active Modelで属性の値を破壊的に変更する場合は、属性名_will_change!を呼び出してその属性が変更されることをマーキングしてください。そうしないと、属性の変更をActive Modelでトラッキングできなくなります。
ただし、Active Recordではそうした変更は自動的に検出されるので、Active Modelモデルでは属性名_will_change!を呼び出す必要はありません。

person.name_will_change!
person.name_change # => ["Bill", "Bill"]
person.name << 'y'
person.name_change # => ["Bill", "Billy"]

name属性のメソッドは、name_changed?のように呼び出すことも、汎用のattribute_changed?("name")に引数"name"を渡して呼び出すことも可能です。

🔗 メソッド

A

C

P

R

includeされるモジュール

🔗 publicインスタンスメソッド

🔗 属性名_change

このメソッドは属性ごとに生成されます。

属性の古い値と新しい値を返します。

person = Person.new
person.name = 'Nick'
person.name_change # => [nil, 'Nick']

🔗 属性名_changed?

このメソッドは属性ごとに生成されます。

属性に未保存の変更が存在する場合にtrueを返します。

person = Person.new
person.name = 'Andrew'
person.name_changed? # => true

🔗 属性名_previous_change

このメソッドは属性ごとに生成されます。

最後に保存したときの値とその直前の値を返します。

person = Person.new
person.name = 'Emmanuel'
person.save
person.name_previous_change # => [nil, 'Emmanuel']

🔗 属性名_previously_changed?(**options)

このメソッドは属性ごとに生成されます。

その属性を保存する前に未保存の変更が存在していた場合にtrueを返します。

person = Person.new
person.name = 'Britanny'
person.save
person.name_previously_changed? # => true
person.name_previously_changed?(from: nil, to: 'Britanny') # => true

🔗 属性名_previously_was

このメソッドは属性ごとに生成されます。

最後に保存したときの直前の値を返します。

person = Person.new
person.name = 'Sage'
person.save
person.name_previously_was  # => nil

🔗 属性名_was

このメソッドは属性ごとに生成されます。

その属性の古い値を返します。

person = Person.new(name: 'Steph')
person.name = 'Stephanie'
person.name_was # => 'Steph'

🔗 属性名_will_change!

このメソッドは属性ごとに生成されます。

Active Modelで属性を破壊的に変更する場合は、属性名_will_change!を呼び出してその属性が変更されることをマーキングしてください。そうしないと、属性の変更をActive Modelのモデルでトラッキングできなくなります。
ただし、Active Recordではそうした変更は自動的に検出されるので、Active Modelモデルでは*_will_change!を呼び出す必要はありません。

person = Person.new('Sandy')
person.name_will_change!
person.name_change # => ['Sandy', 'Sandy']

🔗 attribute_changed?(attr_name, **options)

属性名_changed?メソッドと同じですが、属性名を指定できます。

GitHubソースコード

🔗 attribute_previously_changed?(attr_name, **options)

属性名_previously_changed?メソッドと同じですが、属性名を指定できます。

GitHubソースコード

🔗 attribute_previously_was(attr_name)

属性名_previously_wasメソッドと同じですが、属性名を指定できます。

GitHubソースコード

🔗 attribute_was(attr_name)

属性名_wasメソッドと同じですが、属性名を指定できます。

GitHubソースコード

🔗 changed()

変更が未保存の属性名リストを配列として返します。

person.changed # => []
person.name = 'bob'
person.changed # => ["name"]

🔗 changed?

変更が未保存の属性が1個以上存在する場合はtrueを返し、それ以外の場合はfalseを返します。

person.changed? # => false
person.name = 'bob'
person.changed? # => true

GitHubソースコード

🔗 changed_attributes()

変更が未保存の属性名と元の値のリストをハッシュとして返します(例: 属性名 => 元の値)。

person.name # => "bob"
person.name = 'robert'
person.changed_attributes # => {"name" => "bob"}

GitHubソースコード

🔗 changes()

変更された属性名と「元の値と新しい値」のリストをハッシュとして返します(例: 属性名 => [元の値, 新しい値])。

person.changes # => {}
person.name = 'bob'
person.changes # => { "name" => ["bill", "bob"] }

GitHubソースコード

🔗 changes_applied()

未保存のダーティデータをクリアし、以下を行います。

  • changes をprevious_changesに移動
  • mutations_from_databasemutations_before_last_saveに移動

🔗 clear_属性名_change

このメソッドは属性ごとに生成されます。

その属性のダーティデータ(現在の変更や直前の変更)をすべてクリアします。

person = Person.new(name: 'Chris')
person.name = 'Jason'
person.name_change # => ['Chris', 'Jason']
person.clear_name_change
person.name_change # => nil

🔗 clear_attribute_changes(attr_names)

🔗 clear_changes_information()

すべてのダーティデータ(現在の変更や直前の変更)をクリアします。

🔗 previous_changes()

モデルが保存される前に変更された属性のリストをハッシュとして返します。

person.name # => "bob"
person.name = 'robert'
person.save
person.previous_changes # => {"name" => ["bob", "robert"]}

🔗 restore_属性名!

このメソッドは属性ごとに生成されます。

その属性を古い値に戻します。

person = Person.new
person.name = 'Amanda'
person.restore_name!
person.name # => nil

🔗 restore_attributes(attr_names = changed)

指定したすべての属性を古い値に戻します。

関連記事

Rails: ActiveRecord標準のattributes APIドキュメント(翻訳)

Rails: ActiveRecord標準のattributes APIドキュメント(翻訳)

Rails API: ActiveRecord::NestedAttributes(翻訳)


CONTACT

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