Module: VirtualBox::AbstractModel::Attributable
- Includes:
- VersionMatcher
- Included in:
- VirtualBox::AbstractModel
- Defined in:
- lib/virtualbox/abstract_model/attributable.rb
Overview
Module which can be included into any other class which allows that class to have attributes using the "attribute" class method. This creates the reader/writer for the attribute but also provides other useful options such as readonly attributes, default values, and more.
Make sure to also see the ClassMethods.
Defining a Basic Attribute
attribute :name
The example above would put the "name" attribute on the class. This would give the class the following abilities
instance.name = "Harry!"
puts instance.name # => "Harry!"
Basic attributes alone are not different than ruby's built-in
attr_* methods.
Defining a Readonly Attribute
attribute :age, :readonly => true
The example above allows age to be read, but not written to via the
age= method. The attribute is still able to written using
#write_attribute but this is generally only for
inter-class use, and not for users of it.
Defining Default Values
attribute :format, :default => "VDI"
The example above applies a default value to format. So if no value
is ever given to it, format would return VDI.
Attributes for a Specific VirtualBox Version
Attributes change with different VirtualBox versions. One way to provide version-specific behavior is to specify the version which the attribute applies to.
attribute :name, :version => "3.2"
attribute :age, :version => "3.1"
These versions only apply to major and minor releases of VirtualBox. Patch releases can't be specified (such as "3.2.4")
Populating Multiple Attributes
Attributes can be mass populated using #populate_attributes. Below is an example of the use.
class Person
include Attributable
attribute :name
attribute :age, :readonly => true
def initialize
populate_attributes({
:name => "Steven",
:age => 27
})
end
end
Note: Populating attributes is not the same as mass-updating attributes. #populate_attributes is meant to do initial population only. There is currently no method for mass assignment for updating.
Custom Populate Keys
Sometimes the attribute names don't match the keys of the hash that will be
used to populate it. For this purpose, you can define a custom
populate_key. Example:
attribute :path, :populate_key => :location
def initialize
populate_attributes(:location => "Home")
puts path # => "Home"
end
Lazy Loading Attributes
While most attributes are fairly trivial to calculate and populate, sometimes
attributes may have an expensive cost to populate, and are generally not worth
populating unless a user of the class requests that attribute. This is known as
lazy loading the attributes. This is possibly by specifying the :lazy option
on the attribute. In this case, the first time (and only the first time) the
attribute is requested, load_attribute will be called with the name of the
attribute as the parameter. This method is then expected to call write_attribute
on that attribute to give it a value.
class ExpensiveAttributeModel
include VirtualBox::AbstractModel::Attributable
attribute :expensive_attribute, :lazy => true
def load_attribute(name)
if name == :expensive_attribute
write_attribute(name, perform_expensive_calculation)
end
end
end
Using the above definition, we could use the class like so:
# Initializing is fast, since no attribute population is done
model = ExpensiveAttributeModel.new
# But this is slow, since it has to calculate.
puts model.expensive_attribute
# But ONLY THE FIRST TIME. This time is FAST!
puts model.expensive_attribute
In addition to calling load_attribute on initial read, write_attribute
when performed on a lazy loaded attribute will mark it as "loaded" so there
will be no load called on the first request. Example, using the above class
once again:
model = ExpensiveAttributeModel.new
model.write_attribute(:expensive_attribute, 42)
# This is FAST, since "load_attribute" is not called
puts model.expensive_attribute # => 42
Defined Under Namespace
Modules: ClassMethods
Class Method Summary collapse
Instance Method Summary collapse
-
#attributes ⇒ Object
Returns a hash of all attributes and their options.
-
#has_attribute?(name) ⇒ Boolean
Returns boolean value denoting if an attribute exists.
-
#lazy_attribute?(name) ⇒ Boolean
Returns boolean value denoting if an attribute is "lazy loaded".
-
#loaded_attribute?(name) ⇒ Boolean
Returns boolean value denoting if an attribute has been loaded yet.
-
#populate_attributes(attribs) ⇒ Object
Does the initial population of the various attributes.
-
#read_attribute(name) ⇒ Object
Reads an attribute.
-
#readonly_attribute?(name) ⇒ Boolean
Returns a boolean value denoting if an attribute is readonly.
-
#reset_attributes ⇒ Object
Reset attributes which basically causes all lazy attributes to be reset.
-
#write_attribute(name, value) ⇒ Object
Writes an attribute.
Methods included from VersionMatcher
#assert_version_match, #split_version, #version_match?
Class Method Details
.included(base) ⇒ Object
137 138 139 |
# File 'lib/virtualbox/abstract_model/attributable.rb', line 137 def self.included(base) base.extend ClassMethods end |
Instance Method Details
#attributes ⇒ Object
Returns a hash of all attributes and their options.
274 275 276 |
# File 'lib/virtualbox/abstract_model/attributable.rb', line 274 def attributes @attribute_values ||= {} end |
#has_attribute?(name) ⇒ Boolean
Returns boolean value denoting if an attribute exists.
279 280 281 |
# File 'lib/virtualbox/abstract_model/attributable.rb', line 279 def has_attribute?(name) self.class.attributes.has_key?(name.to_sym) end |
#lazy_attribute?(name) ⇒ Boolean
Returns boolean value denoting if an attribute is "lazy loaded"
284 285 286 |
# File 'lib/virtualbox/abstract_model/attributable.rb', line 284 def lazy_attribute?(name) has_attribute?(name) && self.class.attributes[name.to_sym][:lazy] end |
#loaded_attribute?(name) ⇒ Boolean
Returns boolean value denoting if an attribute has been loaded yet.
290 291 292 |
# File 'lib/virtualbox/abstract_model/attributable.rb', line 290 def loaded_attribute?(name) attributes.has_key?(name) end |
#populate_attributes(attribs) ⇒ Object
Does the initial population of the various attributes. It will ignore attributes which are not defined or have no value in the hash.
Population uses the attributes populate_key if present to
determine which value to take. Example:
attribute :name, :populate_key => :namae
attribute :age
def initialize
populate_attributes(:namae => "Henry", :age => 27)
end
The above example would set name to Henry since that is
the populate_key. If a populate_key is not present, the
attribute name is used.
221 222 223 224 225 226 |
# File 'lib/virtualbox/abstract_model/attributable.rb', line 221 def populate_attributes(attribs) self.class.attributes.each do |key, | value_key = [:populate_key] || key write_attribute(key, attribs[value_key]) if attribs.has_key?(value_key) end end |
#read_attribute(name) ⇒ Object
Reads an attribute. This method will return nil if the
attribute doesn't exist. If the attribute does exist but
doesn't have a value set, it'll use the default value
if specified.
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 |
# File 'lib/virtualbox/abstract_model/attributable.rb', line 255 def read_attribute(name) if has_attribute?(name) = self.class.attributes[name] assert_version_match([:version], VirtualBox.version) if [:version] if lazy_attribute?(name) && !loaded_attribute?(name) # Load the lazy attribute attributes[name] = load_attribute(name.to_sym) end if attributes[name].nil? [:default] else attributes[name] end end end |
#readonly_attribute?(name) ⇒ Boolean
Returns a boolean value denoting if an attribute is readonly. This method also returns false for nonexistent attributes so it should be used in conjunction with #has_attribute? if existence is important.
298 299 300 301 |
# File 'lib/virtualbox/abstract_model/attributable.rb', line 298 def readonly_attribute?(name) name = name.to_sym has_attribute?(name) && self.class.attributes[name][:readonly] end |
#reset_attributes ⇒ Object
Reset attributes which basically causes all lazy attributes to be reset. This forces them to reload when they're accessed.
230 231 232 233 234 235 236 237 238 239 |
# File 'lib/virtualbox/abstract_model/attributable.rb', line 230 def reset_attributes # Find the lazy attributes to_delete = [] attributes.each do |name, value| to_delete << name if lazy_attribute?(name) end # Delete them! to_delete.each { |key| attributes.delete(key) } end |
#write_attribute(name, value) ⇒ Object
Writes an attribute. This method ignores the readonly option
on attribute definitions. This method is mostly meant for
internal use on setting attributes (including readonly
attributes), whereas users of a class which includes this
module should use the accessor methods, such as name=.
247 248 249 |
# File 'lib/virtualbox/abstract_model/attributable.rb', line 247 def write_attribute(name, value) attributes[name] = value end |