Module: VirtualBox::AbstractModel::Dirty

Included in:
VirtualBox::AbstractModel, ExtraData
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 collapse

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(meth, *args) ⇒ Object

Method missing is used to implement the “magic” methods of ‘field_changed`, `field_change`, and `field_was`.



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

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

#changed?(attribute = nil) ⇒ Boolean

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:

  • attribute (Symbol) (defaults to: nil)

    The attribute to check, or if nil, all fields checked.

Returns:

  • (Boolean)


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

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

#changesHash

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

Returns:

  • (Hash)


151
152
153
# File 'lib/virtualbox/abstract_model/dirty.rb', line 151

def changes
  @changed_attributes ||= {}
end

#clear_dirty!(key = nil) ⇒ Object

Clears dirty state for a field.

Parameters:

  • key (Symbol) (defaults to: nil)

    The field to clear dirty state.



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

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

#ignore_dirty {|_self| ... } ⇒ Object

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:



127
128
129
130
131
# File 'lib/virtualbox/abstract_model/dirty.rb', line 127

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

#set_dirty!(name, current, value) ⇒ Object

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:

  • name (Symbol)

    Name of field

  • current (Object)

    Current value (not necessarilly the original value, but the current value)

  • value (Object)

    The new value being set



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

def set_dirty!(name, current, value)
  if current != value
    name = name.to_sym

    # 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