virtualbox 0.4.3 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +3 -3
- data/Gemfile +8 -8
- data/Rakefile +2 -0
- data/Readme.md +11 -2
- data/VERSION +1 -1
- data/docs/WhatsNew.md +40 -24
- data/lib/virtualbox.rb +9 -0
- data/lib/virtualbox/abstract_model.rb +80 -10
- data/lib/virtualbox/abstract_model/attributable.rb +61 -1
- data/lib/virtualbox/abstract_model/relatable.rb +88 -6
- data/lib/virtualbox/attached_device.rb +18 -10
- data/lib/virtualbox/command.rb +13 -0
- data/lib/virtualbox/dvd.rb +35 -0
- data/lib/virtualbox/exceptions.rb +1 -0
- data/lib/virtualbox/extra_data.rb +10 -21
- data/lib/virtualbox/global.rb +126 -0
- data/lib/virtualbox/hard_drive.rb +33 -9
- data/lib/virtualbox/image.rb +2 -3
- data/lib/virtualbox/media.rb +19 -0
- data/lib/virtualbox/nic.rb +28 -67
- data/lib/virtualbox/shared_folder.rb +7 -12
- data/lib/virtualbox/storage_controller.rb +8 -36
- data/lib/virtualbox/system_property.rb +55 -0
- data/lib/virtualbox/usb.rb +72 -0
- data/lib/virtualbox/vm.rb +126 -25
- data/test/test_helper.rb +118 -12
- data/test/virtualbox/abstract_model/attributable_test.rb +55 -5
- data/test/virtualbox/abstract_model/relatable_test.rb +66 -4
- data/test/virtualbox/abstract_model_test.rb +140 -8
- data/test/virtualbox/attached_device_test.rb +10 -7
- data/test/virtualbox/command_test.rb +13 -0
- data/test/virtualbox/dvd_test.rb +50 -28
- data/test/virtualbox/extra_data_test.rb +11 -51
- data/test/virtualbox/global_test.rb +78 -0
- data/test/virtualbox/hard_drive_test.rb +34 -57
- data/test/virtualbox/image_test.rb +0 -5
- data/test/virtualbox/nic_test.rb +11 -64
- data/test/virtualbox/shared_folder_test.rb +5 -5
- data/test/virtualbox/storage_controller_test.rb +15 -30
- data/test/virtualbox/system_property_test.rb +71 -0
- data/test/virtualbox/usb_test.rb +35 -0
- data/test/virtualbox/vm_test.rb +62 -121
- data/virtualbox.gemspec +15 -2
- metadata +23 -4
data/.gitignore
CHANGED
data/Gemfile
CHANGED
@@ -1,12 +1,12 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
source :gemcutter
|
2
|
+
|
3
|
+
# External Dependencies
|
4
|
+
gem "nokogiri", "1.4.1"
|
5
|
+
|
6
|
+
# Gems required for testing only.
|
7
|
+
group :test do
|
4
8
|
gem "contest", ">= 0.1.2"
|
5
9
|
gem "mocha"
|
6
10
|
gem "ruby-debug", ">= 0.10.3" if RUBY_VERSION < '1.9'
|
7
11
|
gem "ruby-debug19", ">= 0.11.6" if RUBY_VERSION >= '1.9'
|
8
|
-
end
|
9
|
-
|
10
|
-
# Makes sure that our code doesn't request gems outside
|
11
|
-
# of our dependency list.
|
12
|
-
disable_system_gems
|
12
|
+
end
|
data/Rakefile
CHANGED
data/Readme.md
CHANGED
@@ -69,10 +69,19 @@ Please use the [issue tracker](https://github.com/mitchellh/virtualbox/issues).
|
|
69
69
|
## Contributing
|
70
70
|
|
71
71
|
If you'd like to contribute to VirtualBox, the first step to developing is to
|
72
|
-
clone this repo, get [
|
72
|
+
clone this repo, get [bundler](http://github.com/carlhuda/bundler) if you
|
73
73
|
don't have it already, and do the following:
|
74
74
|
|
75
|
-
|
75
|
+
bundle install
|
76
|
+
bundle lock
|
76
77
|
rake
|
77
78
|
|
78
79
|
This will run the test suite, which should come back all green! Then you're good to go!
|
80
|
+
|
81
|
+
## Special Thanks
|
82
|
+
|
83
|
+
These folks went above and beyond with contributions to the virtualbox gem, and
|
84
|
+
for that, I have to say "thanks!"
|
85
|
+
|
86
|
+
* [Kieran Pilkington](http://github.com/KieranP)
|
87
|
+
* [Aleksey Palazhchenko](http://github.com/AlekSi)
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.5.0
|
data/docs/WhatsNew.md
CHANGED
@@ -1,34 +1,50 @@
|
|
1
|
-
# What's New in 0.
|
1
|
+
# What's New in 0.5.x?
|
2
2
|
|
3
|
-
##
|
3
|
+
## HUGE Speed Boost! Very few system calls!
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
Most of the data retrieved by the virtualbox library now comes via XML parsing, rather
|
6
|
+
than making calls to `VBoxManage`. This results in a drastic speedup. The few relationships or
|
7
|
+
attributes which require a system call are typically _lazy loaded_ (covered below), so they
|
8
|
+
don't incur a performance penalty unless they're used.
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
10
|
+
The one caveat is that you now need to set the path to the global VirtualBox configuration
|
11
|
+
XML. The virtualbox library will do its best to guess this path based on the operating
|
12
|
+
system, but this is hardly foolproof. To set the virtualbox config path, it is a simple
|
13
|
+
one-liner:
|
12
14
|
|
13
|
-
|
15
|
+
# Remember, this won't be necessary MOST of the time
|
16
|
+
VirtualBox::Global.vboxconfig = "~/path/to/VirtualBox.xml"
|
14
17
|
|
15
|
-
##
|
18
|
+
## Lazy Loading of Attributes and Relationships
|
16
19
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
+
Although still not widely used (will be in future patch releases), some attributes and
|
21
|
+
relationships are now _lazy loaded_. This means that since they're probably expensive
|
22
|
+
to load (many system calls, heavy parsing, etc.) they aren't loaded initially. Instead,
|
23
|
+
they are only loaded if they're used. This means that you don't incur the penalty cost
|
24
|
+
of loading them unless you use it! Fantastic!
|
20
25
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
port.hostport = 8080
|
26
|
-
vm.forwarded_ports << port
|
27
|
-
vm.save
|
26
|
+
There is no real "example code" for this feature since to the casual user, it happens
|
27
|
+
transparently in the background and generally "just works." If you're _really_ curious,
|
28
|
+
then feel free to check out any class which derives from {VirtualBox::AbstractModel}
|
29
|
+
and any attribute or relationship with the `:lazy => true` option is lazy loaded!
|
28
30
|
|
29
|
-
|
31
|
+
## System Properties
|
30
32
|
|
31
|
-
|
33
|
+
A small but meaningful update is the ability to view the system properties for the
|
34
|
+
host system which VirtualBox is running. This is done via the {VirtualBox::SystemProperty}
|
35
|
+
class, which is simply a `Hash`. System properties are immutable properties defined
|
36
|
+
by the host system, which typically are limits imposed upon VirtualBox, such as
|
37
|
+
maximum RAM size or default path to machine files. Retrieving the system properties
|
38
|
+
is quite easy:
|
32
39
|
|
33
|
-
|
34
|
-
|
40
|
+
properties = VirtualBox::SystemProperty.all
|
41
|
+
properties.each do |key, value|
|
42
|
+
puts "#{key} = #{value}"
|
43
|
+
end
|
44
|
+
|
45
|
+
## USB Device Relationship on VMs
|
46
|
+
|
47
|
+
Previously, {VirtualBox::VM VM} object would only be able to tell you if there
|
48
|
+
were USB devices enabled or not. Now, `usbs` is a full-fledged relationship
|
49
|
+
on VM. This relationship is access just like any other. For more information
|
50
|
+
view the {VirtualBox::USB USB} class.
|
data/lib/virtualbox.rb
CHANGED
@@ -1,4 +1,9 @@
|
|
1
1
|
$:.unshift(File.expand_path(File.dirname(__FILE__)))
|
2
|
+
|
3
|
+
# External Dependencies
|
4
|
+
require 'nokogiri'
|
5
|
+
|
6
|
+
# Internal Dependencies
|
2
7
|
require 'virtualbox/exceptions'
|
3
8
|
require 'virtualbox/command'
|
4
9
|
require 'virtualbox/abstract_model'
|
@@ -10,9 +15,13 @@ require 'virtualbox/extra_data'
|
|
10
15
|
require 'virtualbox/forwarded_port'
|
11
16
|
require 'virtualbox/hard_drive'
|
12
17
|
require 'virtualbox/nic'
|
18
|
+
require 'virtualbox/usb'
|
13
19
|
require 'virtualbox/shared_folder'
|
14
20
|
require 'virtualbox/storage_controller'
|
15
21
|
require 'virtualbox/vm'
|
22
|
+
require 'virtualbox/media'
|
23
|
+
require 'virtualbox/global'
|
24
|
+
require 'virtualbox/system_property'
|
16
25
|
|
17
26
|
module VirtualBox
|
18
27
|
class <<self
|
@@ -14,6 +14,30 @@ module VirtualBox
|
|
14
14
|
include Relatable
|
15
15
|
include Validatable
|
16
16
|
|
17
|
+
class <<self
|
18
|
+
# Returns whether or not the class should be reloaded.
|
19
|
+
#
|
20
|
+
# @return [Boolean]
|
21
|
+
def reload?
|
22
|
+
!!@_reload
|
23
|
+
end
|
24
|
+
|
25
|
+
def reload!
|
26
|
+
@_reload = true
|
27
|
+
end
|
28
|
+
|
29
|
+
def reloaded!
|
30
|
+
@_reload = false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Signals to the class that it should be reloaded. This simply toggles
|
35
|
+
# a boolean value to true. It is up to the subclass to implement functionality
|
36
|
+
# around it. See {reload?}
|
37
|
+
def reload!
|
38
|
+
self.class.reload!
|
39
|
+
end
|
40
|
+
|
17
41
|
# Returns a boolean denoting if the record is new or existing. This
|
18
42
|
# method is provided for subclasses to use to differentiate between
|
19
43
|
# creating a new object or saving an existing one. An example of this
|
@@ -78,7 +102,12 @@ module VirtualBox
|
|
78
102
|
save_attribute(key, values[1], *args)
|
79
103
|
end
|
80
104
|
|
81
|
-
|
105
|
+
# Go through and only save the loaded relationships, since
|
106
|
+
# only those would be modified.
|
107
|
+
self.class.relationships.each do |name, options|
|
108
|
+
next if lazy_relationship?(name) && !loaded_relationship?(name)
|
109
|
+
save_relationship(name, *args)
|
110
|
+
end
|
82
111
|
|
83
112
|
# No longer a new record
|
84
113
|
@new_record = false
|
@@ -93,6 +122,20 @@ module VirtualBox
|
|
93
122
|
clear_dirty!(key)
|
94
123
|
end
|
95
124
|
|
125
|
+
# Overriding {Attributable#lazy_attribute?} to always return false for
|
126
|
+
# new records, since new records shouldn't load lazy data.
|
127
|
+
def lazy_attribute?(*args)
|
128
|
+
return false if new_record?
|
129
|
+
super
|
130
|
+
end
|
131
|
+
|
132
|
+
# Overriding {Relatable#lazy_relationship?} to always return false for
|
133
|
+
# new records, since new records shouldn't load lazy data.
|
134
|
+
def lazy_relationship?(*args)
|
135
|
+
return false if new_record?
|
136
|
+
super
|
137
|
+
end
|
138
|
+
|
96
139
|
# Sets the initial attributes from a hash. This method is meant to be used
|
97
140
|
# once to initially setup the attributes. It is **not a mass-assignment**
|
98
141
|
# method for updating attributes.
|
@@ -108,21 +151,39 @@ module VirtualBox
|
|
108
151
|
#
|
109
152
|
# Calling this method will also cause the model to assume that it is not
|
110
153
|
# a new record (see {#new_record?}).
|
111
|
-
def populate_attributes(attribs)
|
112
|
-
# No longer a new record
|
113
|
-
@new_record = false
|
114
|
-
|
154
|
+
def populate_attributes(attribs, opts={})
|
115
155
|
ignore_dirty do
|
116
|
-
super
|
156
|
+
super(attribs)
|
117
157
|
|
118
|
-
populate_relationships(attribs)
|
158
|
+
populate_relationships(attribs) unless opts[:ignore_relationships]
|
119
159
|
end
|
160
|
+
|
161
|
+
# No longer a new record
|
162
|
+
existing_record!
|
163
|
+
end
|
164
|
+
|
165
|
+
# Loads and populates the relationships with the given data. This method
|
166
|
+
# is meant to be used once to initially setup the relatoinships.
|
167
|
+
#
|
168
|
+
# This methods does **not** affect dirtiness, but also does not clear it.
|
169
|
+
#
|
170
|
+
# Calling this method will also cuase the model to assume that it is not
|
171
|
+
# a new record (see {#new_record?})
|
172
|
+
def populate_relationships(data)
|
173
|
+
existing_record!
|
174
|
+
ignore_dirty { super }
|
175
|
+
end
|
176
|
+
|
177
|
+
# Populates a single relationship with the given data.
|
178
|
+
def populate_relationship(name, data)
|
179
|
+
existing_record!
|
180
|
+
ignore_dirty { super }
|
120
181
|
end
|
121
182
|
|
122
183
|
# Overwrites {Attributable#write_attribute} to set the dirty state of
|
123
184
|
# the written attribute. See {Dirty#set_dirty!} as well.
|
124
185
|
def write_attribute(name, value)
|
125
|
-
set_dirty!(name, read_attribute(name), value)
|
186
|
+
set_dirty!(name, read_attribute(name), value) unless lazy_attribute?(name) && !loaded_attribute?(name)
|
126
187
|
super
|
127
188
|
end
|
128
189
|
|
@@ -148,12 +209,21 @@ module VirtualBox
|
|
148
209
|
# Creates a human-readable format for this model. This method overrides the
|
149
210
|
# default `#<class>` syntax since this doesn't work well for AbstractModels.
|
150
211
|
# Instead, it abbreviates it, instead showing all the attributes and their
|
151
|
-
# values, and `...` for relationships.
|
212
|
+
# values, and `...` for relationships. For attributes which are themselves
|
213
|
+
# AbstractModels, it shows the class name to avoid extremely verbose inspections
|
214
|
+
# and infinite loops.
|
152
215
|
def inspect
|
153
216
|
values = []
|
154
217
|
|
155
218
|
self.class.attributes.each do |name, options|
|
156
|
-
|
219
|
+
value = read_attribute(name)
|
220
|
+
value = if value.is_a?(AbstractModel)
|
221
|
+
"#<#{value.class.name}>"
|
222
|
+
else
|
223
|
+
value.inspect
|
224
|
+
end
|
225
|
+
|
226
|
+
values.push("#{name.inspect}=#{value}")
|
157
227
|
end
|
158
228
|
|
159
229
|
self.class.relationships.each do |name, options|
|
@@ -73,6 +73,50 @@ module VirtualBox
|
|
73
73
|
# puts path # => "Home"
|
74
74
|
# end
|
75
75
|
#
|
76
|
+
# ## Lazy Loading Attributes
|
77
|
+
#
|
78
|
+
# While most attributes are fairly trivial to calculate and populate, sometimes
|
79
|
+
# attributes may have an expensive cost to populate, and are generally not worth
|
80
|
+
# populating unless a user of the class requests that attribute. This is known as
|
81
|
+
# _lazy loading_ the attributes. This is possibly by specifying the `:lazy` option
|
82
|
+
# on the attribute. In this case, the first time (and _only_ the first time) the
|
83
|
+
# attribute is requested, `load_attribute` will be called with the name of the
|
84
|
+
# attribute as the parameter. This method is then expected to call `write_attribute`
|
85
|
+
# on that attribute to give it a value.
|
86
|
+
#
|
87
|
+
# class ExpensiveAttributeModel
|
88
|
+
# include VirtualBox::AbstractModel::Attributable
|
89
|
+
# attribute :expensive_attribute, :lazy => true
|
90
|
+
#
|
91
|
+
# def load_attribute(name)
|
92
|
+
# if name == :expensive_attribute
|
93
|
+
# write_attribute(name, perform_expensive_calculation)
|
94
|
+
# end
|
95
|
+
# end
|
96
|
+
# end
|
97
|
+
#
|
98
|
+
# Using the above definition, we could use the class like so:
|
99
|
+
#
|
100
|
+
# # Initializing is fast, since no attribute population is done
|
101
|
+
# model = ExpensiveAttributeModel.new
|
102
|
+
#
|
103
|
+
# # But this is slow, since it has to calculate.
|
104
|
+
# puts model.expensive_attribute
|
105
|
+
#
|
106
|
+
# # But ONLY THE FIRST TIME. This time is FAST!
|
107
|
+
# puts model.expensive_attribute
|
108
|
+
#
|
109
|
+
# In addition to calling `load_attribute` on initial read, `write_attribute`
|
110
|
+
# when performed on a lazy loaded attribute will mark it as "loaded" so there
|
111
|
+
# will be no load called on the first request. Example, using the above class
|
112
|
+
# once again:
|
113
|
+
#
|
114
|
+
# model = ExpensiveAttributeModel.new
|
115
|
+
# model.write_attribute(:expensive_attribute, 42)
|
116
|
+
#
|
117
|
+
# # This is FAST, since "load_attribute" is not called
|
118
|
+
# puts model.expensive_attribute # => 42
|
119
|
+
#
|
76
120
|
module Attributable
|
77
121
|
def self.included(base)
|
78
122
|
base.extend ClassMethods
|
@@ -146,7 +190,7 @@ module VirtualBox
|
|
146
190
|
def populate_attributes(attribs)
|
147
191
|
self.class.attributes.each do |key, options|
|
148
192
|
value_key = options[:populate_key] || key
|
149
|
-
write_attribute(key, attribs[value_key])
|
193
|
+
write_attribute(key, attribs[value_key]) if attribs.has_key?(value_key)
|
150
194
|
end
|
151
195
|
end
|
152
196
|
|
@@ -165,6 +209,11 @@ module VirtualBox
|
|
165
209
|
# if specified.
|
166
210
|
def read_attribute(name)
|
167
211
|
if has_attribute?(name)
|
212
|
+
if lazy_attribute?(name) && !loaded_attribute?(name)
|
213
|
+
# Load the lazy attribute
|
214
|
+
load_attribute(name.to_sym)
|
215
|
+
end
|
216
|
+
|
168
217
|
attributes[name] || self.class.attributes[name][:default]
|
169
218
|
end
|
170
219
|
end
|
@@ -179,6 +228,17 @@ module VirtualBox
|
|
179
228
|
self.class.attributes.has_key?(name.to_sym)
|
180
229
|
end
|
181
230
|
|
231
|
+
# Returns boolean value denoting if an attribute is "lazy loaded"
|
232
|
+
def lazy_attribute?(name)
|
233
|
+
has_attribute?(name) && self.class.attributes[name.to_sym][:lazy]
|
234
|
+
end
|
235
|
+
|
236
|
+
# Returns boolean value denoting if an attribute has been loaded
|
237
|
+
# yet.
|
238
|
+
def loaded_attribute?(name)
|
239
|
+
attributes.has_key?(name)
|
240
|
+
end
|
241
|
+
|
182
242
|
# Returns a boolean value denoting if an attribute is readonly.
|
183
243
|
# This method also returns false for **nonexistent attributes**
|
184
244
|
# so it should be used in conjunction with {#has_attribute?} if
|
@@ -78,6 +78,46 @@ module VirtualBox
|
|
78
78
|
#
|
79
79
|
# This is not a feature built-in to Relatable but figured it should be
|
80
80
|
# mentioned here.
|
81
|
+
#
|
82
|
+
# # Lazy Relationships
|
83
|
+
#
|
84
|
+
# Often, relationships are pretty heavy things to load. Data may have to be
|
85
|
+
# retrieved, classes instantiated, etc. If a class has many relationships, or
|
86
|
+
# many relationships within many relationships, the time and memory required
|
87
|
+
# for relationships really begins to add up. To address this issue, _lazy relationships_
|
88
|
+
# are available. Lazy relationships defer loading their content until the
|
89
|
+
# last possible moment, or rather, when a user requests the data. By specifing
|
90
|
+
# the `:lazy => true` option to relationships, relationships will not be loaded
|
91
|
+
# immediately. Instead, when they're first requested, `load_relationship` will
|
92
|
+
# be called on the model, with the name of the relationship given as a
|
93
|
+
# parameter. It is up to this method to call {#populate_relationship} at some
|
94
|
+
# point with the data to setup the relationship. An example follows:
|
95
|
+
#
|
96
|
+
# class SomeModel
|
97
|
+
# include VirtualBox::AbstractModel::Relatable
|
98
|
+
#
|
99
|
+
# relationship :foos, Foo, :lazy => true
|
100
|
+
#
|
101
|
+
# def load_relationship(name)
|
102
|
+
# if name == :foos
|
103
|
+
# populate_relationship(name, get_data_for_a_long_time)
|
104
|
+
# end
|
105
|
+
# end
|
106
|
+
# end
|
107
|
+
#
|
108
|
+
# Using the above class, we can use it like so:
|
109
|
+
#
|
110
|
+
# model = SomeModel.new
|
111
|
+
#
|
112
|
+
# # This initial load takes awhile as it loads...
|
113
|
+
# model.foos
|
114
|
+
#
|
115
|
+
# # Instant! (Just a hash lookup. No load necessary)
|
116
|
+
# model.foos
|
117
|
+
#
|
118
|
+
# One catch: If a model attempts to {#destroy_relationship destroy} a lazy
|
119
|
+
# relationship, it will first load the relationship, since destroy typically
|
120
|
+
# depends on some data of the relationship.
|
81
121
|
module Relatable
|
82
122
|
def self.included(base)
|
83
123
|
base.extend ClassMethods
|
@@ -99,7 +139,7 @@ module VirtualBox
|
|
99
139
|
relationships << [name, { :klass => klass }.merge(options)]
|
100
140
|
|
101
141
|
# Define the method to read the relationship
|
102
|
-
define_method(name) {
|
142
|
+
define_method(name) { read_relationship(name) }
|
103
143
|
|
104
144
|
# Define the method to set the relationship
|
105
145
|
define_method("#{name}=") { |*args| set_relationship(name, *args) }
|
@@ -138,9 +178,19 @@ module VirtualBox
|
|
138
178
|
end
|
139
179
|
end
|
140
180
|
|
181
|
+
# Reads a relationship. This is equivalent to {Attributable#read_attribute},
|
182
|
+
# but for relationships.
|
183
|
+
def read_relationship(name)
|
184
|
+
if lazy_relationship?(name) && !loaded_relationship?(name)
|
185
|
+
load_relationship(name)
|
186
|
+
end
|
187
|
+
|
188
|
+
relationship_data[name.to_sym]
|
189
|
+
end
|
190
|
+
|
141
191
|
# Saves the model, calls save_relationship on all relations. It is up to
|
142
192
|
# the relation to determine whether anything changed, etc. Simply
|
143
|
-
# calls `save_relationship` on each
|
193
|
+
# calls `save_relationship` on each relationship class passing in the
|
144
194
|
# following parameters:
|
145
195
|
#
|
146
196
|
# * **caller** - The class which is calling save
|
@@ -150,20 +200,34 @@ module VirtualBox
|
|
150
200
|
# end and they'll be pushed through to the `save_relationship` method.
|
151
201
|
def save_relationships(*args)
|
152
202
|
self.class.relationships.each do |name, options|
|
153
|
-
|
154
|
-
options[:klass].save_relationship(self, relationship_data[name], *args)
|
203
|
+
save_relationship(name, *args)
|
155
204
|
end
|
156
205
|
end
|
157
206
|
|
207
|
+
# Saves a single relationship. It is up to the relationship class to
|
208
|
+
# determine whether anything changed and how saving is implemented. Simply
|
209
|
+
# calls `save_relationship` on the relationship class.
|
210
|
+
def save_relationship(name, *args)
|
211
|
+
options = self.class.relationships_hash[name]
|
212
|
+
return unless options[:klass].respond_to?(:save_relationship)
|
213
|
+
options[:klass].save_relationship(self, relationship_data[name], *args)
|
214
|
+
end
|
215
|
+
|
158
216
|
# The equivalent to {Attributable#populate_attributes}, but with
|
159
217
|
# relationships.
|
160
218
|
def populate_relationships(data)
|
161
219
|
self.class.relationships.each do |name, options|
|
162
|
-
|
163
|
-
relationship_data[name] = options[:klass].populate_relationship(self, data)
|
220
|
+
populate_relationship(name, data) unless lazy_relationship?(name)
|
164
221
|
end
|
165
222
|
end
|
166
223
|
|
224
|
+
# Populate a single relationship.
|
225
|
+
def populate_relationship(name, data)
|
226
|
+
options = self.class.relationships_hash[name]
|
227
|
+
return unless options[:klass].respond_to?(:populate_relationship)
|
228
|
+
relationship_data[name] = options[:klass].populate_relationship(self, data)
|
229
|
+
end
|
230
|
+
|
167
231
|
# Calls `destroy_relationship` on each of the relationships. Any
|
168
232
|
# arbitrary args may be added and they will be forarded to the
|
169
233
|
# relationship's `destroy_relationship` method.
|
@@ -181,6 +245,11 @@ module VirtualBox
|
|
181
245
|
def destroy_relationship(name, *args)
|
182
246
|
options = self.class.relationships_hash[name]
|
183
247
|
return unless options && options[:klass].respond_to?(:destroy_relationship)
|
248
|
+
|
249
|
+
# Read relationship, which forces lazy relationships to load, which is
|
250
|
+
# probably necessary for destroying
|
251
|
+
read_relationship(name)
|
252
|
+
|
184
253
|
options[:klass].destroy_relationship(self, relationship_data[name], *args)
|
185
254
|
end
|
186
255
|
|
@@ -199,6 +268,19 @@ module VirtualBox
|
|
199
268
|
self.class.has_relationship?(key.to_sym)
|
200
269
|
end
|
201
270
|
|
271
|
+
# Returns boolean denoting if a relationship is to be lazy loaded.
|
272
|
+
#
|
273
|
+
# @return [Boolean]
|
274
|
+
def lazy_relationship?(key)
|
275
|
+
options = self.class.relationships_hash[key.to_sym]
|
276
|
+
!options.nil? && options[:lazy]
|
277
|
+
end
|
278
|
+
|
279
|
+
# Returns boolean denoting if a relationship has been loaded.
|
280
|
+
def loaded_relationship?(key)
|
281
|
+
relationship_data.has_key?(key)
|
282
|
+
end
|
283
|
+
|
202
284
|
# Sets a relationship to the given value. This is not guaranteed to
|
203
285
|
# do anything, since "set_relationship" will be called on the class
|
204
286
|
# that the relationship is associated with and its expected to return
|