module Sequel::Plugins::Dirty

  1. lib/sequel/plugins/dirty.rb

The dirty plugin makes Sequel save the initial value of a column when setting a new value for the column. This makes it easier to see what changes were made to the object:

artist.name                   # => 'Foo'
artist.name = 'Bar'
artist.initial_value(:name)   # 'Foo'
artist.column_change(:name)   # ['Foo', 'Bar']
artist.column_changes         # {:name => ['Foo', 'Bar']}
artist.column_changed?(:name) # true
artist.reset_column(:name)
artist.name                   # => 'Foo'
artist.column_changed?(:name) # false

It also makes changed_columns more accurate in that it can detect when a the column value is changed and then changed back:

artist.name                   # => 'Foo'
artist.name = 'Bar'
artist.changed_columns        # => [:name]
artist.name = 'Foo'
artist.changed_columns        # => []

It can handle situations where a column value is modified in place:

artist.will_change_column(:name)
artist.name.gsub!(/o/, 'u')
artist.changed_columns       # => [:name]
artist.initial_value(:name)  # => 'Foo'
artist.column_change(:name)  # => ['Foo', 'Fuu']

It also saves the previously changed values after an update:

artist.update(name: 'Bar')
artist.column_changes        # => {}
artist.previous_changes      # => {:name=>['Foo', 'Bar']}

artist.column_previously_was(:name)
# => 'Foo'
artist.column_previously_changed?(:name)
# => true
artist.column_previously_changed?(:name, from: 'Foo', to: 'Bar')
# => true
artist.column_previously_changed?(:name, from: 'Foo', to: 'Baz')
# => false

There is one caveat; when used with a column that also uses the serialization plugin, setting the column back to its original value after changing it is not correctly detected and will leave an entry in changed_columns.

Usage:

# Make all model subclass instances record previous values (called before loading subclasses)
Sequel::Model.plugin :dirty

# Make the Album class record previous values
Album.plugin :dirty