virtualbox 0.4.3 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|