Module: VirtualBox::AbstractModel::Dirty

Defined in:
lib/virtualbox/abstract_model/dirty.rb

Overview

Tracks "dirtiness" of values for a class. Its not tied to AbstractModel in any way other than the namespace.

Checking if a Value was Changed

Dynamic methods allow functionality for checking if values changed:

obj.foo_changed?

Previous Value

Can also view the previous value of an attribute:

obj.foo # => "foo" initially
obj.foo = "bar"
obj.foo_was # => "foo"

Previous and Current Value

Using the _change dynamic method, can view the changes of a field.

obj.foo # => "foo" initially
obj.foo = "bar"
obj.foo_change # => ["foo", "bar"]

All Changes

Can also view all changes for a class with the changes method.

obj.foo # => "foo" initially
obj.bar # => "bar" initially
obj.foo = "far"
obj.bar = "baz"
obj.changes # => { :foo => ["foo", "far"], :bar => ["bar", "baz"]}

Setting Dirty

Dirtiness tracking only occurs for values which the implementor explicitly sets as dirty. This is done with the #set_dirty! method. Example implementation below:

class Person
  include VirtualBox::AbstractModel::Dirty

  attr_reader :name

  def name=(value)
    set_dirty!(:name, @name, value)
    @name = value
  end
end

The above example has all the changes necessary to track changes on an attribute.

Ignoring Dirtiness Tracking

Sometimes, for features such as mass assignment, dirtiness tracking should be disabled. This can be done with the ignore_dirty method.

ignore_dirty do |obj|
  obj.name = "Foo"
end

obj.changed? # => false

Clearing Dirty State

Sometimes, such as after saving a model, dirty states should be cleared. This can be done with the clear_dirty! method.

obj.clear_dirty!(:name)
obj.name_changed? # => false

If no specific field is speciied, clear_dirty! will clear the dirty status on the entire model.

obj.changed? # => assume true
obj.clear_dirty!
obj.changed? # => false

Instance Method Summary

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

- (Object) method_missing(meth, *args)

Method missing is used to implement the "magic" methods of field_changed, field_change, and field_was.



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/virtualbox/abstract_model/dirty.rb', line 155

def method_missing(meth, *args)
  meth_string = meth.to_s

  if meth_string =~ /^(.+?)_changed\?$/
    changed?($1.to_sym)
  elsif meth_string =~ /^(.+?)_change$/
    changes[$1.to_sym]
  elsif meth_string =~ /^(.+?)_was$/
    change = changes[$1.to_sym]
    if change.nil?
      nil
    else
      change[0]
    end
  else
    super
  end
end

Instance Method Details

- (Boolean) changed?(attribute = nil)

Returns boolean denoting if field changed or not. If no attribute is specified, returns true of false showing whether the model changed at all.

Parameters:

  • (Symbol) attribute (defaults to: nil) — The attribute to check, or if nil, all fields checked.

Returns:

  • (Boolean)


137
138
139
140
141
142
143
# File 'lib/virtualbox/abstract_model/dirty.rb', line 137

def changed?(attribute = nil)
  if attribute.nil?
    !changes.empty?
  else
    changes.has_key?(attribute)
  end
end

- (Hash) changes

Returns hash of changes. Keys are fields, values are an array of the original value and the current value.

Returns:

  • (Hash)


149
150
151
# File 'lib/virtualbox/abstract_model/dirty.rb', line 149

def changes
  @changed_attributes ||= {}
end

- (Object) clear_dirty!(key = nil)

Clears dirty state for a field.

Parameters:

  • (Symbol) key (defaults to: nil) — The field to clear dirty state.


114
115
116
117
118
119
120
# File 'lib/virtualbox/abstract_model/dirty.rb', line 114

def clear_dirty!(key=nil)
  if key.nil?
    @changed_attributes = {}
  else
    changes.delete(key)
  end
end

- (Object) ignore_dirty(&block) {|_self| ... }

Ignores any dirty changes during the duration of the block. Guarantees the dirty state will be the same before and after the method call, but not within the block itself.

Yields:

  • (_self)

Yield Parameters:



125
126
127
128
129
# File 'lib/virtualbox/abstract_model/dirty.rb', line 125

def ignore_dirty(&block)
  current_changes = @changed_attributes.dup rescue nil
  yield self
  @changed_attributes = current_changes
end

- (Object) set_dirty!(name, current, value)

Manages dirty state for an attribute. This method will handle setting the dirty state of an attribute (or even clearing it if the old value is reset). Any implementors of this mixin should call this for any fields they want tracked.

Parameters:

  • (Symbol) name — Name of field
  • (Object) current — Current value (not necessarilly the original value, but the current value)
  • (Object) value — The new value being set


94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/virtualbox/abstract_model/dirty.rb', line 94

def set_dirty!(name, current, value)
  if current != value
    # If its the first time this attribute has changed, store the
    # original value in the first field
    changes[name] ||= [current, nil]

    # Then store the changed value
    changes[name][1] = value

    # If the value changed back to the original value, remove from the
    # dirty hash
    if changes[name][0] == changes[name][1]
      changes.delete(name)
    end
  end
end