Module: VirtualBox::AbstractModel::Attributable
- 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
.
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
Instance Method Summary
- - (Object) attributes Returns a hash of all attributes and their options.
- - (Boolean) has_attribute?(name) Returns boolean value denoting if an attribute exists.
- - (Boolean) lazy_attribute?(name) Returns boolean value denoting if an attribute is "lazy loaded".
- - (Boolean) loaded_attribute?(name) Returns boolean value denoting if an attribute has been loaded yet.
- - (Object) populate_attributes(attribs) Does the initial population of the various attributes.
- - (Object) read_attribute(name) Reads an attribute.
- - (Boolean) readonly_attribute?(name) Returns a boolean value denoting if an attribute is readonly.
- - (Object) write_attribute(name, value) Writes an attribute.
Class Method Details
+ (Object) included(base)
121 122 123 |
# File 'lib/virtualbox/abstract_model/attributable.rb', line 121 def self.included(base) base.extend ClassMethods end |
Instance Method Details
- (Object) attributes
Returns a hash of all attributes and their options.
222 223 224 |
# File 'lib/virtualbox/abstract_model/attributable.rb', line 222 def attributes @attribute_values ||= {} end |
- (Boolean) has_attribute?(name)
Returns boolean value denoting if an attribute exists.
227 228 229 |
# File 'lib/virtualbox/abstract_model/attributable.rb', line 227 def has_attribute?(name) self.class.attributes.has_key?(name.to_sym) end |
- (Boolean) lazy_attribute?(name)
Returns boolean value denoting if an attribute is "lazy loaded"
232 233 234 |
# File 'lib/virtualbox/abstract_model/attributable.rb', line 232 def lazy_attribute?(name) has_attribute?(name) && self.class.attributes[name.to_sym][:lazy] end |
- (Boolean) loaded_attribute?(name)
Returns boolean value denoting if an attribute has been loaded yet.
238 239 240 |
# File 'lib/virtualbox/abstract_model/attributable.rb', line 238 def loaded_attribute?(name) attributes.has_key?(name) end |
- (Object) populate_attributes(attribs)
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.
190 191 192 193 194 195 |
# File 'lib/virtualbox/abstract_model/attributable.rb', line 190 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 |
- (Object) read_attribute(name)
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.
210 211 212 213 214 215 216 217 218 219 |
# File 'lib/virtualbox/abstract_model/attributable.rb', line 210 def read_attribute(name) if has_attribute?(name) if lazy_attribute?(name) && !loaded_attribute?(name) # Load the lazy attribute load_attribute(name.to_sym) end attributes[name] || self.class.attributes[name][:default] end end |
- (Boolean) readonly_attribute?(name)
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.
246 247 248 249 |
# File 'lib/virtualbox/abstract_model/attributable.rb', line 246 def readonly_attribute?(name) name = name.to_sym has_attribute?(name) && self.class.attributes[name][:readonly] end |
- (Object) write_attribute(name, value)
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=
.
202 203 204 |
# File 'lib/virtualbox/abstract_model/attributable.rb', line 202 def write_attribute(name, value) attributes[name] = value end |