Class: VirtualBox::AbstractModel Abstract

Inherits:
Object
  • Object
show all
Includes:
Attributable, Dirty, InterfaceAttributes, Relatable, Validatable, Logger
Defined in:
lib/virtualbox/abstract_model.rb,
lib/virtualbox/abstract_model/dirty.rb,
lib/virtualbox/abstract_model/relatable.rb,
lib/virtualbox/abstract_model/validatable.rb,
lib/virtualbox/abstract_model/attributable.rb,
lib/virtualbox/abstract_model/version_matcher.rb,
lib/virtualbox/abstract_model/interface_attributes.rb

Overview

This class is abstract.

AbstractModel is the base class used for most of virtualbox’s classes. It provides convenient ActiveRecord-style model behavior to subclasses.

Defined Under Namespace

Modules: Attributable, Dirty, InterfaceAttributes, Relatable, Validatable, VersionMatcher

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Validatable

#__validates_extract_options, #add_error, #clear_errors, #errors_on, #full_error_messages, #valid?, #validates_format_of, #validates_inclusion_of, #validates_numericality_of, #validates_presence_of

Methods included from Relatable

#destroy_relationship, #destroy_relationships, #has_relationship?, included, #loaded_relationship?, #read_relationship, #relationship_class, #relationship_data, #save_relationship, #save_relationships

Methods included from VersionMatcher

#assert_version_match, #split_version, #version_match?

Methods included from Dirty

#changed?, #changes, #clear_dirty!, #ignore_dirty, #method_missing, #set_dirty!

Methods included from InterfaceAttributes

#load_interface_attribute, #load_interface_attributes, #save_interface_attributes, #spec_to_proc

Methods included from Attributable

#attributes, #has_attribute?, included, #loaded_attribute?, #read_attribute, #readonly_attribute?

Methods included from Logger

included, #logger, #logger_output=

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class VirtualBox::AbstractModel::Dirty

Class Method Details

.errors_for_relationship(caller, data) ⇒ Object

Default errors for relationship implementation, since this is a pretty stable method.



41
42
43
44
# File 'lib/virtualbox/abstract_model.rb', line 41

def errors_for_relationship(caller, data)
  return data.errors if data.respond_to?(:errors)
  nil
end

.reload!Object



31
32
33
# File 'lib/virtualbox/abstract_model.rb', line 31

def reload!
  @_reload = true
end

.reload?Boolean

Returns whether or not the class should be reloaded.

Returns:

  • (Boolean)


27
28
29
# File 'lib/virtualbox/abstract_model.rb', line 27

def reload?
  !!@_reload
end

.reloaded!Object



35
36
37
# File 'lib/virtualbox/abstract_model.rb', line 35

def reloaded!
  @_reload = false
end

Instance Method Details

#destroy(*args) ⇒ Object

Destroys the model. The exact behaviour of this method is expected to be defined on the subclasses. This method on AbstractModel simply propagates the destroy to the dependent relationships. For more information on relationships, see Relatable.



237
238
239
240
241
242
# File 'lib/virtualbox/abstract_model.rb', line 237

def destroy(*args)
  # Destroy dependent relationships
  self.class.relationships.each do |name, options|
    destroy_relationship(name, *args) if options[:dependent] == :destroy
  end
end

#errorsObject

Returns the errors for a model.



79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/virtualbox/abstract_model.rb', line 79

def errors
  self.class.relationships.inject(super) do |acc, data|
    name, options = data

    if options && options[:klass].respond_to?(:errors_for_relationship)
      errors = options[:klass].errors_for_relationship(self, relationship_data[name])
      acc.merge!(name => errors) if errors && !errors.empty?
    end

    acc
  end
end

#existing_record!Object

Explicitly sets the model to not be a new record. If you’re using this method outside of virtualbox library core, you should really be asking yourself “why?”



74
75
76
# File 'lib/virtualbox/abstract_model.rb', line 74

def existing_record!
  @new_record = false
end

#inspectObject

Creates a human-readable format for this model. This method overrides the default ‘#<class>` syntax since this doesn’t work well for AbstractModels. Instead, it abbreviates it, instead showing all the attributes and their values, and ‘…` for relationships. For attributes which are themselves AbstractModels, it shows the class name to avoid extremely verbose inspections and infinite loops.



260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/virtualbox/abstract_model.rb', line 260

def inspect
  values = []

  self.class.attributes.each do |name, options|
    value = read_attribute(name)
    value = if value.is_a?(AbstractModel) || value.is_a?(COM::AbstractInterface) || value.is_a?(Proxies::Collection)
      "#<#{value.class.name}>"
    else
      value.inspect
    end

    values.push("#{name.inspect}=#{value}")
  end

  self.class.relationships.each do |name, options|
    values.push("#{name.inspect}=...")
  end

  "#<#{self.class} #{values.sort.join(", ")}>".strip
end

#lazy_attribute?(*args) ⇒ Boolean

Overriding VirtualBox::AbstractModel::Attributable#lazy_attribute? to always return false for new records, since new records shouldn’t load lazy data.

Returns:

  • (Boolean)


162
163
164
165
# File 'lib/virtualbox/abstract_model.rb', line 162

def lazy_attribute?(*args)
  return false if new_record?
  super
end

#lazy_relationship?(*args) ⇒ Boolean

Overriding VirtualBox::AbstractModel::Relatable#lazy_relationship? to always return false for new records, since new records shouldn’t load lazy data.

Returns:

  • (Boolean)


169
170
171
172
# File 'lib/virtualbox/abstract_model.rb', line 169

def lazy_relationship?(*args)
  return false if new_record?
  super
end

#new_record!Object

Explicitly resets the model to a new record. If you’re using this method outside of virtualbox library core, you should really be asking yourself “why?”



67
68
69
# File 'lib/virtualbox/abstract_model.rb', line 67

def new_record!
  @new_record = true
end

#new_record?Boolean

Returns a boolean denoting if the record is new or existing. This method is provided for subclasses to use to differentiate between creating a new object or saving an existing one. An example of this is HardDrive#save which will create a new hard drive if it didn’t previously exist, or save an old one if it did exist.

Returns:

  • (Boolean)


59
60
61
62
# File 'lib/virtualbox/abstract_model.rb', line 59

def new_record?
  new_record! if !defined?(@new_record) || @new_record.nil?
  @new_record
end

#parent_machineVM

Gets the root machine of an AbstractModel by traversing a ‘parent` attribute until it reaches a type of VM.

Returns:



248
249
250
251
252
# File 'lib/virtualbox/abstract_model.rb', line 248

def parent_machine
  current = parent
  current = current.parent while current && !current.is_a?(VM)
  current
end

#populate_attributes(attribs, opts = {}) ⇒ Object

Sets the initial attributes from a hash. This method is meant to be used once to initially setup the attributes. It is **not a mass-assignment** method for updating attributes.

This method does not affect dirtiness, but also does not clear it. This means that if you call populate_attributes, the same attributes that were dirty before the call will be dirty after the call (but no more and no less). This distinction is important because most subclasses of AbstractModel only save changed attributes, and ignore unchanged attributes. Attempting to change attributes through this method will cause them to not be saved, which is surely unexpected behaviour for most users.

Calling this method will also cause the model to assume that it is not a new record (see #new_record?).



189
190
191
192
193
194
195
196
197
198
# File 'lib/virtualbox/abstract_model.rb', line 189

def populate_attributes(attribs, opts={})
  ignore_dirty do
    super(attribs)

    populate_relationships(attribs) unless opts[:ignore_relationships]
  end

  # No longer a new record
  existing_record!
end

#populate_relationship(name, data) ⇒ Object

Populates a single relationship with the given data.



213
214
215
216
# File 'lib/virtualbox/abstract_model.rb', line 213

def populate_relationship(name, data)
  existing_record!
  ignore_dirty { super }
end

#populate_relationships(data) ⇒ Object

Loads and populates the relationships with the given data. This method is meant to be used once to initially setup the relatoinships.

This methods does not affect dirtiness, but also does not clear it.

Calling this method will also cuase the model to assume that it is not a new record (see #new_record?)



207
208
209
210
# File 'lib/virtualbox/abstract_model.rb', line 207

def populate_relationships(data)
  existing_record!
  ignore_dirty { super }
end

#reload!Object

Signals to the class that it should be reloaded. This simply toggles a boolean value to true. It is up to the subclass to implement functionality around it. See reload?



50
51
52
# File 'lib/virtualbox/abstract_model.rb', line 50

def reload!
  self.class.reload!
end

#save(*args) ⇒ Object

Saves the model attributes and relationships.

The method can be passed any arbitrary arguments, which are implementation specific (see VM#save, which does this).



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/virtualbox/abstract_model.rb', line 111

def save(*args)
  # Go through changed attributes and call save_attribute for
  # those only
  changes.each do |key, values|
    save_attribute(key, values[1], *args)
  end

  # Go through and only save the loaded relationships, since
  # only those would be modified.
  self.class.relationships.each do |name, options|
    save_relationship(name, *args)
  end

  # No longer a new record
  @new_record = false

  true
end

#save!(*args) ⇒ Object

Saves the model and raises an Exceptions::ValidationFailedException if the model is invalid, instead of returning false.



132
133
134
135
# File 'lib/virtualbox/abstract_model.rb', line 132

def save!(*args)
  raise Exceptions::ValidationFailedException.new(errors.inspect) if !save(*args)
  true
end

#save_attribute(key, value, *args) ⇒ Object

Saves a single attribute of the model. This method on the abstract model does nothing on its own, and is expected to be overridden by any subclasses.

This method clears the dirty status of the attribute.



142
143
144
# File 'lib/virtualbox/abstract_model.rb', line 142

def save_attribute(key, value, *args)
  clear_dirty!(key)
end

#save_changed_interface_attributes(interface) ⇒ Object

Saves only changed interface attributes.



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

def save_changed_interface_attributes(interface)
  changes.each do |key, values|
    save_interface_attribute(key, interface)
  end
end

#save_interface_attribute(key, interface) ⇒ Object

Overrides VirtualBox::AbstractModel::InterfaceAttributes#save_interface_attribute to clear the dirty state of the attribute.



155
156
157
158
# File 'lib/virtualbox/abstract_model.rb', line 155

def save_interface_attribute(key, interface)
  super
  clear_dirty!(key)
end

#set_relationship(key, value) ⇒ Object

Overwrites VirtualBox::AbstractModel::Relatable#set_relationship to set the dirty state of the relationship. See VirtualBox::AbstractModel::Dirty#set_dirty! as well.



227
228
229
230
231
# File 'lib/virtualbox/abstract_model.rb', line 227

def set_relationship(key, value)
  existing = relationship_data[key]
  new_value = super
  set_dirty!(key, existing, new_value)
end

#validate(*args) ⇒ Object

Validates the model and relationships.



93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/virtualbox/abstract_model.rb', line 93

def validate(*args)
  # First clear all previous errors
  clear_errors

  # Then do the validations
  failed = false
  self.class.relationships.each do |name, options|
    next unless options && options[:klass].respond_to?(:validate_relationship)
    failed = true if !options[:klass].validate_relationship(self, relationship_data[name], *args)
  end

  return !failed
end

#write_attribute(name, value) ⇒ Object

Overwrites VirtualBox::AbstractModel::Attributable#write_attribute to set the dirty state of the written attribute. See VirtualBox::AbstractModel::Dirty#set_dirty! as well.



220
221
222
223
# File 'lib/virtualbox/abstract_model.rb', line 220

def write_attribute(name, value)
  set_dirty!(name, read_attribute(name), value) unless lazy_attribute?(name) && !loaded_attribute?(name)
  super
end