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.
-
#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.
260 261 262 |
# File 'lib/virtualbox/abstract_model/attributable.rb', line 260 def attributes @attribute_values ||= {} end |
#has_attribute?(name) ⇒ Boolean
Returns boolean value denoting if an attribute exists.
265 266 267 |
# File 'lib/virtualbox/abstract_model/attributable.rb', line 265 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”
270 271 272 |
# File 'lib/virtualbox/abstract_model/attributable.rb', line 270 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.
276 277 278 |
# File 'lib/virtualbox/abstract_model/attributable.rb', line 276 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.
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 |
# File 'lib/virtualbox/abstract_model/attributable.rb', line 241 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 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.
284 285 286 287 |
# File 'lib/virtualbox/abstract_model/attributable.rb', line 284 def readonly_attribute?(name) name = name.to_sym has_attribute?(name) && self.class.attributes[name][:readonly] 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=`.
233 234 235 |
# File 'lib/virtualbox/abstract_model/attributable.rb', line 233 def write_attribute(name, value) attributes[name] = value end |