virtualbox 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.yardopts +1 -0
- data/TODO +0 -1
- data/VERSION +1 -1
- data/docs/WhatsNew.md +41 -0
- data/lib/virtualbox.rb +1 -0
- data/lib/virtualbox/abstract_model.rb +31 -0
- data/lib/virtualbox/abstract_model/validatable.rb +44 -0
- data/lib/virtualbox/attached_device.rb +15 -3
- data/lib/virtualbox/exceptions.rb +1 -2
- data/lib/virtualbox/hard_drive.rb +13 -0
- data/lib/virtualbox/shared_folder.rb +243 -0
- data/lib/virtualbox/vm.rb +2 -0
- data/test/virtualbox/abstract_model/validatable_test.rb +125 -0
- data/test/virtualbox/abstract_model_test.rb +49 -0
- data/test/virtualbox/attached_device_test.rb +44 -10
- data/test/virtualbox/hard_drive_test.rb +36 -0
- data/test/virtualbox/shared_folder_test.rb +231 -0
- data/test/virtualbox/vm_test.rb +12 -1
- data/virtualbox.gemspec +9 -2
- metadata +9 -2
data/.yardopts
CHANGED
data/TODO
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.0
|
data/docs/WhatsNew.md
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# What's New in 0.3.x?
|
2
|
+
|
3
|
+
## Shared Folders
|
4
|
+
|
5
|
+
Shared folders are a great feature of VirtualBox which allows the host system
|
6
|
+
to share data with guest systems easily using the native filesystem. Attaching,
|
7
|
+
modifying, and removing these shared folders are now supported. A quick example
|
8
|
+
below:
|
9
|
+
|
10
|
+
vm = VirtualBox::VM.find("FooVM")
|
11
|
+
folder = VirtualBox::SharedFolder.new
|
12
|
+
folder.name = "hosthome"
|
13
|
+
folder.hostpath = "/home/username"
|
14
|
+
vm.shared_folders << folder
|
15
|
+
vm.save
|
16
|
+
|
17
|
+
For full documentation on this new feature, read about them at
|
18
|
+
{VirtualBox::SharedFolder}.
|
19
|
+
|
20
|
+
## Validations
|
21
|
+
|
22
|
+
Many of the models for the virtualbox library now come complete with data
|
23
|
+
validations. These validations are performed within the library itself prior to
|
24
|
+
calling the virtualbox commands. They work very much the same was as ActiveRecord
|
25
|
+
validations:
|
26
|
+
|
27
|
+
sf = VirtualBox::SharedFolder.new(hash_of_values)
|
28
|
+
if !sf.valid?
|
29
|
+
puts "#{sf.errors.length} errors with the folder"
|
30
|
+
else
|
31
|
+
sf.save
|
32
|
+
end
|
33
|
+
|
34
|
+
In addition to `valid?` there is `errors` which returns a hash of all the errors,
|
35
|
+
including errors on relationships. There is also the `validate` method which
|
36
|
+
runs the validations, but you really shouldn't have the need to call that directly.
|
37
|
+
|
38
|
+
All validations are run automatically on `save`, which will return `false` if
|
39
|
+
they fail. If you choose to raise errors on the save, a `ValidationFailedException`
|
40
|
+
will be raised (in contrast to a `CommandFailedException`, which serves its own
|
41
|
+
role).
|
data/lib/virtualbox.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'virtualbox/abstract_model/attributable'
|
2
2
|
require 'virtualbox/abstract_model/dirty'
|
3
3
|
require 'virtualbox/abstract_model/relatable'
|
4
|
+
require 'virtualbox/abstract_model/validatable'
|
4
5
|
|
5
6
|
module VirtualBox
|
6
7
|
# AbstractModel is the base class used for most of virtualbox's classes.
|
@@ -11,6 +12,7 @@ module VirtualBox
|
|
11
12
|
include Attributable
|
12
13
|
include Dirty
|
13
14
|
include Relatable
|
15
|
+
include Validatable
|
14
16
|
|
15
17
|
# Returns a boolean denoting if the record is new or existing. This
|
16
18
|
# method is provided for subclasses to use to differentiate between
|
@@ -36,6 +38,35 @@ module VirtualBox
|
|
36
38
|
@new_record = false
|
37
39
|
end
|
38
40
|
|
41
|
+
# Returns the errors for a model.
|
42
|
+
def errors
|
43
|
+
error_hash = super
|
44
|
+
|
45
|
+
self.class.relationships.each do |name, options|
|
46
|
+
next unless options && options[:klass].respond_to?(:errors_for_relationship)
|
47
|
+
relationship_errors = options[:klass].errors_for_relationship(self, relationship_data[name])
|
48
|
+
|
49
|
+
error_hash.merge!({ :foos => relationship_errors }) if relationship_errors.length > 0
|
50
|
+
end
|
51
|
+
|
52
|
+
error_hash
|
53
|
+
end
|
54
|
+
|
55
|
+
# Validates the model and relationships.
|
56
|
+
def validate(*args)
|
57
|
+
# First clear all previous errors
|
58
|
+
clear_errors
|
59
|
+
|
60
|
+
# Then do the validations
|
61
|
+
failed = false
|
62
|
+
self.class.relationships.each do |name, options|
|
63
|
+
next unless options && options[:klass].respond_to?(:validate_relationship)
|
64
|
+
failed = true if !options[:klass].validate_relationship(self, relationship_data[name], *args)
|
65
|
+
end
|
66
|
+
|
67
|
+
return !failed
|
68
|
+
end
|
69
|
+
|
39
70
|
# Saves the model attributes and relationships.
|
40
71
|
#
|
41
72
|
# The method can be passed any arbitrary arguments, which are
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module VirtualBox
|
2
|
+
class AbstractModel
|
3
|
+
# Provides validation methods for a class. Unlike ActiveRecord,
|
4
|
+
# validations are instance-level rather than class-level.
|
5
|
+
module Validatable
|
6
|
+
def errors
|
7
|
+
@errors ||= {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_error(field, error)
|
11
|
+
errors[field] ||= []
|
12
|
+
errors[field].push(error)
|
13
|
+
end
|
14
|
+
|
15
|
+
def clear_errors
|
16
|
+
@errors = {}
|
17
|
+
end
|
18
|
+
|
19
|
+
def valid?
|
20
|
+
validate
|
21
|
+
errors.empty?
|
22
|
+
end
|
23
|
+
|
24
|
+
# Subclasses should override this method.
|
25
|
+
def validate
|
26
|
+
true
|
27
|
+
end
|
28
|
+
|
29
|
+
def validates_presence_of(field)
|
30
|
+
if field.is_a?(Array)
|
31
|
+
field.map { |v| validates_presence_of(v) }.all? { |v| v == true }
|
32
|
+
else
|
33
|
+
value = send(field)
|
34
|
+
if value.nil? || value == ""
|
35
|
+
add_error(field, "must not be blank.")
|
36
|
+
return false
|
37
|
+
else
|
38
|
+
return true
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -140,16 +140,28 @@ module VirtualBox
|
|
140
140
|
end
|
141
141
|
end
|
142
142
|
|
143
|
+
# Validates an attached device.
|
144
|
+
def validate
|
145
|
+
super
|
146
|
+
|
147
|
+
validates_presence_of :parent
|
148
|
+
validates_presence_of :image
|
149
|
+
validates_presence_of :port
|
150
|
+
end
|
151
|
+
|
143
152
|
# Saves or creates an attached device.
|
144
153
|
#
|
145
154
|
# @param [Boolean] raise_errors If true, {Exceptions::CommandFailedException}
|
146
155
|
# will be raised if the command failed.
|
147
156
|
# @return [Boolean] True if command was successful, false otherwise.
|
148
|
-
def save(raise_errors=false)
|
149
|
-
raise Exceptions::NoParentException.new if parent.nil?
|
150
|
-
raise Exceptions::InvalidObjectException.new("Image must be set") if image.nil?
|
157
|
+
def save(raise_errors=false)
|
151
158
|
return true unless changed?
|
152
159
|
|
160
|
+
if !valid?
|
161
|
+
raise Exceptions::ValidationFailedException.new(errors) if raise_errors
|
162
|
+
return false
|
163
|
+
end
|
164
|
+
|
153
165
|
# If the port changed, we have to destroy the old one, then create
|
154
166
|
# a new one
|
155
167
|
destroy({:port => port_was}, raise_errors) if port_changed? && !port_was.nil?
|
@@ -5,9 +5,8 @@ module VirtualBox
|
|
5
5
|
class Exception < ::Exception; end
|
6
6
|
|
7
7
|
class CommandFailedException < Exception; end
|
8
|
-
class InvalidObjectException < Exception; end
|
9
8
|
class InvalidRelationshipObjectException < Exception; end
|
10
9
|
class NonSettableRelationshipException < Exception; end
|
11
|
-
class
|
10
|
+
class ValidationFailedException < Exception; end
|
12
11
|
end
|
13
12
|
end
|
@@ -138,6 +138,14 @@ module VirtualBox
|
|
138
138
|
"hdd"
|
139
139
|
end
|
140
140
|
|
141
|
+
# Validates a hard drive.
|
142
|
+
def validate
|
143
|
+
super
|
144
|
+
|
145
|
+
validates_presence_of :format
|
146
|
+
validates_presence_of :size
|
147
|
+
end
|
148
|
+
|
141
149
|
# Creates a new hard drive.
|
142
150
|
#
|
143
151
|
# **This method should NEVER be called. Call {#save} instead.**
|
@@ -146,6 +154,11 @@ module VirtualBox
|
|
146
154
|
# will be raised if the command failed.
|
147
155
|
# @return [Boolean] True if command was successful, false otherwise.
|
148
156
|
def create(raise_errors=false)
|
157
|
+
if !valid?
|
158
|
+
raise Exceptions::ValidationFailedException.new(errors) if raise_errors
|
159
|
+
return false
|
160
|
+
end
|
161
|
+
|
149
162
|
raw = Command.vboxmanage("createhd --filename #{location} --size #{size} --format #{read_attribute(:format)} --remember")
|
150
163
|
return nil unless raw =~ /UUID: (.+?)$/
|
151
164
|
|
@@ -0,0 +1,243 @@
|
|
1
|
+
module VirtualBox
|
2
|
+
# Represents a shared folder in VirtualBox. In VirtualBox, shared folders are a
|
3
|
+
# method for basically "symlinking" a folder on the guest system to a folder which
|
4
|
+
# exists on the host system. This allows for sharing of files across the virtual
|
5
|
+
# machine.
|
6
|
+
#
|
7
|
+
# **Note:** Whenever modifying shared folders on a VM, the changes won't take
|
8
|
+
# effect until a _cold reboot_ occurs. This means actually closing the virtual
|
9
|
+
# machine _completely_, then restarting it. You can't just hit "Start > Restart"
|
10
|
+
# or do a `sudo reboot`. It doesn't work that way!
|
11
|
+
#
|
12
|
+
# # Getting Shared Folders
|
13
|
+
#
|
14
|
+
# All shared folders are attached to a {VM} object, by definition. Therefore, to
|
15
|
+
# get a list of the shared folders, first {VM.find find} the VM you need, then
|
16
|
+
# use the `shared_folders` relationship to access an array of the shared folders.
|
17
|
+
# With this array, you can create, modify, update, and delete the shared folders
|
18
|
+
# for that virtual machine.
|
19
|
+
#
|
20
|
+
# # Creating a Shared Folder
|
21
|
+
#
|
22
|
+
# **This whole section will assume you already looked up a {VM} and assigned it to
|
23
|
+
# a local variable named `vm`.**
|
24
|
+
#
|
25
|
+
# With a VM found, creating a shared folder is just a few lines of code:
|
26
|
+
#
|
27
|
+
# folder = VirtualBox::SharedFolder.new
|
28
|
+
# folder.name = "desktop-images"
|
29
|
+
# folder.hostpath = File.expand_path("~/Desktop/images")
|
30
|
+
# vm.shared_folders << folder
|
31
|
+
# folder.save # Or you can call vm.save, which works too!
|
32
|
+
#
|
33
|
+
# # Modifying an Existing Shared Folder
|
34
|
+
#
|
35
|
+
# **This whole section will assume you already looked up a {VM} and assigned it to
|
36
|
+
# a local variable named `vm`.**
|
37
|
+
#
|
38
|
+
# Nothing tricky here: You treat existing shared folder objects just as if they
|
39
|
+
# were new ones. Assign a new name and/or a new path, then save.
|
40
|
+
#
|
41
|
+
# folder = vm.shared_folders.first
|
42
|
+
# folder.name = "rufus"
|
43
|
+
# folder.save # Or vm.save
|
44
|
+
#
|
45
|
+
# **Note**: The VirtualBox-saavy will know that VirtualBox doesn't actually
|
46
|
+
# expose a way to edit shared folders. Under the hood, the virtualbox ruby
|
47
|
+
# library is actually deleting the old shared folder, then creating a new
|
48
|
+
# one with the new details. This shouldn't affect the way anything works for
|
49
|
+
# the VM itself.
|
50
|
+
#
|
51
|
+
# # Deleting a Shared Folder
|
52
|
+
#
|
53
|
+
# **This whole section will assume you already looked up a {VM} and assigned it to
|
54
|
+
# a local variable named `vm`.**
|
55
|
+
#
|
56
|
+
# folder = vm.shared_folder.first
|
57
|
+
# folder.destroy
|
58
|
+
#
|
59
|
+
# Poof! It'll be gone. This is usually the place where I warn you about this
|
60
|
+
# being non-reversable, but since no _data_ was actually destroyed, this is
|
61
|
+
# not too risky. You could always just recreate the shared folder with the
|
62
|
+
# same name and path and it'll be like nothing happened.
|
63
|
+
#
|
64
|
+
# # Attributes and Relationships
|
65
|
+
#
|
66
|
+
# Properties of the model are exposed using standard ruby instance
|
67
|
+
# methods which are generated on the fly. Because of this, they are not listed
|
68
|
+
# below as available instance methods.
|
69
|
+
#
|
70
|
+
# These attributes can be accessed and modified via standard ruby-style
|
71
|
+
# `instance.attribute` and `instance.attribute=` methods. The attributes are
|
72
|
+
# listed below.
|
73
|
+
#
|
74
|
+
# Relationships are also accessed like attributes but can't be set. Instead,
|
75
|
+
# they are typically references to other objects such as an {AttachedDevice} which
|
76
|
+
# in turn have their own attributes which can be modified.
|
77
|
+
#
|
78
|
+
# ## Attributes
|
79
|
+
#
|
80
|
+
# This is copied directly from the class header, but lists all available
|
81
|
+
# attributes. If you don't understand what this means, read {Attributable}.
|
82
|
+
#
|
83
|
+
# attribute :parent, :readonly => :readonly
|
84
|
+
# attribute :name
|
85
|
+
# attribute :hostpath
|
86
|
+
#
|
87
|
+
class SharedFolder < AbstractModel
|
88
|
+
attribute :parent, :readonly => :readonly
|
89
|
+
attribute :name, :populate_key => "SharedFolderNameMachineMapping"
|
90
|
+
attribute :hostpath, :populate_key => "SharedFolderPathMachineMapping"
|
91
|
+
|
92
|
+
class <<self
|
93
|
+
# Populates the shared folder relationship for anything which is related to it.
|
94
|
+
#
|
95
|
+
# **This method typically won't be used except internally.**
|
96
|
+
#
|
97
|
+
# @return [Array<SharedFolder>]
|
98
|
+
def populate_relationship(caller, data)
|
99
|
+
relation = []
|
100
|
+
|
101
|
+
counter = 1
|
102
|
+
loop do
|
103
|
+
break unless data["SharedFolderNameMachineMapping#{counter}".downcase.to_sym]
|
104
|
+
|
105
|
+
folder = new(counter, caller, data)
|
106
|
+
relation.push(folder)
|
107
|
+
counter += 1
|
108
|
+
end
|
109
|
+
|
110
|
+
relation
|
111
|
+
end
|
112
|
+
|
113
|
+
# Saves the relationship. This simply calls {#save} on every
|
114
|
+
# member of the relationship.
|
115
|
+
#
|
116
|
+
# **This method typically won't be used except internally.**
|
117
|
+
def save_relationship(caller, data)
|
118
|
+
# Just call save on each folder with the VM
|
119
|
+
data.each do |sf|
|
120
|
+
sf.save
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# @overload initialize(data={})
|
126
|
+
# Creates a new SharedFolder which is a new record. This
|
127
|
+
# should be attached to a VM and saved.
|
128
|
+
# @param [Hash] data (optional) A hash which contains initial attribute
|
129
|
+
# values for the SharedFolder.
|
130
|
+
# @overload initialize(index, caller, data)
|
131
|
+
# Creates an SharedFolder for a relationship. **This should
|
132
|
+
# never be called except internally.**
|
133
|
+
# @param [Integer] index Index of the shared folder
|
134
|
+
# @param [Object] caller The parent
|
135
|
+
# @param [Hash] data A hash of data which must be used
|
136
|
+
# to extract the relationship data.
|
137
|
+
def initialize(*args)
|
138
|
+
super()
|
139
|
+
|
140
|
+
if args.length == 3
|
141
|
+
initialize_for_relationship(*args)
|
142
|
+
elsif args.length == 1
|
143
|
+
initialize_for_data(*args)
|
144
|
+
elsif args.length == 0
|
145
|
+
return
|
146
|
+
else
|
147
|
+
raise NoMethodError.new
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# Initializes the record for use in a relationship. This
|
152
|
+
# is automatically called by {#initialize} if it has three
|
153
|
+
# parameters.
|
154
|
+
#
|
155
|
+
# **This method typically won't be used except internally.**
|
156
|
+
def initialize_for_relationship(index, caller, data)
|
157
|
+
# Setup the index specific attributes
|
158
|
+
populate_data = {}
|
159
|
+
self.class.attributes.each do |name, options|
|
160
|
+
key = options[:populate_key] || name
|
161
|
+
value = data["#{key}#{index}".downcase.to_sym]
|
162
|
+
populate_data[key] = value
|
163
|
+
end
|
164
|
+
|
165
|
+
populate_attributes(populate_data.merge({
|
166
|
+
:parent => caller
|
167
|
+
}))
|
168
|
+
end
|
169
|
+
|
170
|
+
# Initializes a record with initial data but keeping it a "new
|
171
|
+
# record." This is called automatically if {#initialize} is given
|
172
|
+
# only a single parameter. View {#initialize} for documentation.
|
173
|
+
def initialize_for_data(data)
|
174
|
+
self.class.attributes.each do |name, options|
|
175
|
+
data[options[:populate_key]] = data[name]
|
176
|
+
end
|
177
|
+
|
178
|
+
populate_attributes(data)
|
179
|
+
new_record!
|
180
|
+
end
|
181
|
+
|
182
|
+
# Validates a shared folder.
|
183
|
+
def validate
|
184
|
+
super
|
185
|
+
|
186
|
+
validates_presence_of :parent
|
187
|
+
validates_presence_of :name
|
188
|
+
validates_presence_of :hostpath
|
189
|
+
end
|
190
|
+
|
191
|
+
# Saves or creates a shared folder.
|
192
|
+
#
|
193
|
+
# @param [Boolean] raise_errors If true, {Exceptions::CommandFailedException}
|
194
|
+
# will be raised if the command failed.
|
195
|
+
# @return [Boolean] True if command was successful, false otherwise.
|
196
|
+
def save(raise_errors=false)
|
197
|
+
return true unless changed?
|
198
|
+
|
199
|
+
if !valid?
|
200
|
+
raise Exceptions::ValidationFailedException.new(errors) if raise_errors
|
201
|
+
return false
|
202
|
+
end
|
203
|
+
|
204
|
+
# If this isn't a new record, we destroy it first
|
205
|
+
destroy(raise_errors) if !new_record?
|
206
|
+
|
207
|
+
Command.vboxmanage("sharedfolder add #{Command.shell_escape(parent.name)} --name #{Command.shell_escape(name)} --hostpath #{Command.shell_escape(hostpath)}")
|
208
|
+
existing_record!
|
209
|
+
clear_dirty!
|
210
|
+
|
211
|
+
true
|
212
|
+
rescue Exceptions::CommandFailedException
|
213
|
+
raise if raise_errors
|
214
|
+
false
|
215
|
+
end
|
216
|
+
|
217
|
+
# Relationship callback when added to a collection. This is automatically
|
218
|
+
# called by any relationship collection when this object is added.
|
219
|
+
def added_to_relationship(parent)
|
220
|
+
write_attribute(:parent, parent)
|
221
|
+
end
|
222
|
+
|
223
|
+
# Destroys the shared folder. This doesn't actually delete the folder
|
224
|
+
# from the host system. Instead, it simply removes the mapping to the
|
225
|
+
# virtual machine, meaning it will no longer be possible to mount it
|
226
|
+
# from within the virtual machine.
|
227
|
+
#
|
228
|
+
# @param [Boolean] raise_errors If true, {Exceptions::CommandFailedException}
|
229
|
+
# will be raised if the command failed.
|
230
|
+
# @return [Boolean] True if command was successful, false otherwise.
|
231
|
+
def destroy(raise_errors=false)
|
232
|
+
# If the name changed, we have to be sure to use the previous
|
233
|
+
# one.
|
234
|
+
name_value = name_changed? ? name_was : name
|
235
|
+
|
236
|
+
Command.vboxmanage("sharedfolder remove #{Command.shell_escape(parent.name)} --name #{Command.shell_escape(name_value)}")
|
237
|
+
true
|
238
|
+
rescue Exceptions::CommandFailedException
|
239
|
+
raise if raise_errors
|
240
|
+
false
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
data/lib/virtualbox/vm.rb
CHANGED
@@ -74,6 +74,7 @@ module VirtualBox
|
|
74
74
|
#
|
75
75
|
# relationship :nics, Nic
|
76
76
|
# relationship :storage_controllers, StorageController, :dependent => :destroy
|
77
|
+
# relationship :shared_folders, SharedFolder
|
77
78
|
#
|
78
79
|
class VM < AbstractModel
|
79
80
|
attribute :uuid, :readonly => true
|
@@ -104,6 +105,7 @@ module VirtualBox
|
|
104
105
|
attribute :state, :populate_key => :vmstate, :readonly => true
|
105
106
|
relationship :nics, Nic
|
106
107
|
relationship :storage_controllers, StorageController, :dependent => :destroy
|
108
|
+
relationship :shared_folders, SharedFolder
|
107
109
|
|
108
110
|
class <<self
|
109
111
|
# Returns an array of all available VMs.
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', '..', 'test_helper')
|
2
|
+
|
3
|
+
class ValidatableTest < Test::Unit::TestCase
|
4
|
+
class ValidatableModel
|
5
|
+
include VirtualBox::AbstractModel::Validatable
|
6
|
+
|
7
|
+
attr_accessor :foos
|
8
|
+
attr_accessor :bars
|
9
|
+
end
|
10
|
+
|
11
|
+
context "errors" do
|
12
|
+
setup do
|
13
|
+
@model = ValidatableModel.new
|
14
|
+
end
|
15
|
+
|
16
|
+
should "have no errors by default" do
|
17
|
+
assert @model.errors.empty?
|
18
|
+
end
|
19
|
+
|
20
|
+
should "be able to add errors" do
|
21
|
+
@model.add_error(:foo, "is blank")
|
22
|
+
assert !@model.errors.empty?
|
23
|
+
assert !@model.errors[:foo].nil?
|
24
|
+
assert_equal "is blank", @model.errors[:foo].first
|
25
|
+
end
|
26
|
+
|
27
|
+
should "be able to add multiple errors" do
|
28
|
+
@model.add_error(:foo, "foo")
|
29
|
+
@model.add_error(:foo, "bar")
|
30
|
+
assert !@model.errors.empty?
|
31
|
+
assert !@model.errors[:foo].nil?
|
32
|
+
assert_equal 2, @model.errors[:foo].length
|
33
|
+
end
|
34
|
+
|
35
|
+
should "be able to clear errors" do
|
36
|
+
@model.add_error(:foo, "foo")
|
37
|
+
assert !@model.errors.empty?
|
38
|
+
@model.clear_errors
|
39
|
+
assert @model.errors.empty?
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context "validity" do
|
44
|
+
setup do
|
45
|
+
@model = ValidatableModel.new
|
46
|
+
end
|
47
|
+
|
48
|
+
should "call validate on valid?" do
|
49
|
+
@model.expects(:validate)
|
50
|
+
assert @model.valid?
|
51
|
+
end
|
52
|
+
|
53
|
+
should "be valid if there are no errors" do
|
54
|
+
assert @model.valid?
|
55
|
+
end
|
56
|
+
|
57
|
+
should "be invalid if there are any errors" do
|
58
|
+
@model.add_error(:foo, "foo")
|
59
|
+
assert !@model.valid?
|
60
|
+
end
|
61
|
+
|
62
|
+
should "have a validate method by default which returns true" do
|
63
|
+
assert @model.validate
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context "specific validations" do
|
68
|
+
setup do
|
69
|
+
@model = ValidatableModel.new
|
70
|
+
end
|
71
|
+
|
72
|
+
context "validates_presence_of" do
|
73
|
+
setup do
|
74
|
+
@model.foos = "foo"
|
75
|
+
@model.bars = "bar"
|
76
|
+
end
|
77
|
+
|
78
|
+
should "not add an error if not blank" do
|
79
|
+
@model.validates_presence_of(:foos)
|
80
|
+
assert @model.valid?
|
81
|
+
end
|
82
|
+
|
83
|
+
should "add an error if blank field" do
|
84
|
+
@model.foos = ""
|
85
|
+
@model.validates_presence_of(:foos)
|
86
|
+
assert !@model.valid?
|
87
|
+
end
|
88
|
+
|
89
|
+
should "add an error for a nil field" do
|
90
|
+
@model.foos = nil
|
91
|
+
@model.validates_presence_of(:foos)
|
92
|
+
assert !@model.valid?
|
93
|
+
end
|
94
|
+
|
95
|
+
should "validate multiple fields" do
|
96
|
+
@model.bars = nil
|
97
|
+
@model.validates_presence_of([:foos, :bars])
|
98
|
+
|
99
|
+
assert !@model.valid?
|
100
|
+
assert @model.errors[:bars]
|
101
|
+
end
|
102
|
+
|
103
|
+
should "return false on invalid" do
|
104
|
+
@model.bars = nil
|
105
|
+
assert !@model.validates_presence_of(:bars)
|
106
|
+
end
|
107
|
+
|
108
|
+
should "return true on valid" do
|
109
|
+
@model.bars = "foo"
|
110
|
+
assert @model.validates_presence_of(:bars)
|
111
|
+
end
|
112
|
+
|
113
|
+
should "return false if any are invalid on multiple fields" do
|
114
|
+
@model.bars = nil
|
115
|
+
assert !@model.validates_presence_of([:foos, :bars])
|
116
|
+
end
|
117
|
+
|
118
|
+
should "return true if all fields are valid" do
|
119
|
+
@model.foos = "foo"
|
120
|
+
@model.bars = "bar"
|
121
|
+
assert @model.validates_presence_of([:foos, :bars])
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -5,6 +5,9 @@ class AbstractModelTest < Test::Unit::TestCase
|
|
5
5
|
def self.set_relationship(caller, old_value, new_value)
|
6
6
|
new_value
|
7
7
|
end
|
8
|
+
|
9
|
+
def self.validate_relationship(caller, data)
|
10
|
+
end
|
8
11
|
end
|
9
12
|
|
10
13
|
class Bar; end
|
@@ -16,6 +19,52 @@ class AbstractModelTest < Test::Unit::TestCase
|
|
16
19
|
relationship :bars, Bar, :dependent => :destroy
|
17
20
|
end
|
18
21
|
|
22
|
+
context "validation" do
|
23
|
+
setup do
|
24
|
+
@model = FakeModel.new
|
25
|
+
end
|
26
|
+
|
27
|
+
should "clear all previous errors" do
|
28
|
+
@model.expects(:clear_errors).once
|
29
|
+
@model.validate
|
30
|
+
end
|
31
|
+
|
32
|
+
should "call validate_relationship on each relationship class" do
|
33
|
+
Foo.expects(:validate_relationship).once.with(@model, nil)
|
34
|
+
@model.validate
|
35
|
+
end
|
36
|
+
|
37
|
+
should "forward arguments to validate_relationship" do
|
38
|
+
Foo.expects(:validate_relationship).once.with(@model, nil, "HELLO")
|
39
|
+
@model.validate("HELLO")
|
40
|
+
end
|
41
|
+
|
42
|
+
should "succeed if all relationships succeeded" do
|
43
|
+
Foo.expects(:validate_relationship).returns(true)
|
44
|
+
assert @model.validate
|
45
|
+
end
|
46
|
+
|
47
|
+
should "fail if one relationship failed to validate" do
|
48
|
+
Foo.expects(:validate_relationship).returns(true)
|
49
|
+
Bar.expects(:validate_relationship).returns(false)
|
50
|
+
assert !@model.validate
|
51
|
+
end
|
52
|
+
|
53
|
+
context "errors" do
|
54
|
+
should "return the errors of the relationships, as well as the model itself" do
|
55
|
+
@model.foo = nil
|
56
|
+
assert !@model.validate
|
57
|
+
|
58
|
+
@model.validates_presence_of(:foo)
|
59
|
+
Foo.expects(:errors_for_relationship).with(@model, nil).returns("BAD")
|
60
|
+
errors = @model.errors
|
61
|
+
assert errors.has_key?(:foos)
|
62
|
+
assert_equal "BAD", errors[:foos]
|
63
|
+
assert errors.has_key?(:foo)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
19
68
|
context "new/existing records" do
|
20
69
|
setup do
|
21
70
|
@model = FakeModel.new
|
@@ -19,6 +19,34 @@ class AttachedDeviceTest < Test::Unit::TestCase
|
|
19
19
|
VirtualBox::Command.stubs(:execute).returns('')
|
20
20
|
end
|
21
21
|
|
22
|
+
context "validations" do
|
23
|
+
setup do
|
24
|
+
@ad = VirtualBox::AttachedDevice.new
|
25
|
+
@ad.image = VirtualBox::DVD.empty_drive
|
26
|
+
@ad.port = 7
|
27
|
+
@ad.added_to_relationship(@caller)
|
28
|
+
end
|
29
|
+
|
30
|
+
should "be valid with all fields" do
|
31
|
+
assert @ad.valid?
|
32
|
+
end
|
33
|
+
|
34
|
+
should "be invalid with no image" do
|
35
|
+
@ad.image = nil
|
36
|
+
assert !@ad.valid?
|
37
|
+
end
|
38
|
+
|
39
|
+
should "be invalid with no port" do
|
40
|
+
@ad.port = nil
|
41
|
+
assert !@ad.valid?
|
42
|
+
end
|
43
|
+
|
44
|
+
should "be invalid if not in a relationship" do
|
45
|
+
@ad.write_attribute(:parent, nil)
|
46
|
+
assert !@ad.valid?
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
22
50
|
context "medium" do
|
23
51
|
setup do
|
24
52
|
@ad = VirtualBox::AttachedDevice.new
|
@@ -62,6 +90,12 @@ class AttachedDeviceTest < Test::Unit::TestCase
|
|
62
90
|
@value.save
|
63
91
|
end
|
64
92
|
|
93
|
+
should "return false and not call vboxmanage if invalid" do
|
94
|
+
VirtualBox::Command.expects(:vboxmanage).never
|
95
|
+
@value.expects(:valid?).returns(false)
|
96
|
+
assert !@value.save
|
97
|
+
end
|
98
|
+
|
65
99
|
should "not call destroy if the port didn't change" do
|
66
100
|
@value.expects(:destroy).never
|
67
101
|
assert !@value.port_changed?
|
@@ -90,9 +124,16 @@ class AttachedDeviceTest < Test::Unit::TestCase
|
|
90
124
|
@ad.port = 3
|
91
125
|
end
|
92
126
|
|
93
|
-
should "
|
94
|
-
|
95
|
-
|
127
|
+
should "return false and not call vboxmanage if invalid" do
|
128
|
+
VirtualBox::Command.expects(:vboxmanage).never
|
129
|
+
@ad.expects(:valid?).returns(false)
|
130
|
+
assert !@ad.save
|
131
|
+
end
|
132
|
+
|
133
|
+
should "raise a ValidationFailedException if invalid and raise_errors is true" do
|
134
|
+
@ad.expects(:valid?).returns(false)
|
135
|
+
assert_raises(VirtualBox::Exceptions::ValidationFailedException) {
|
136
|
+
@ad.save(true)
|
96
137
|
}
|
97
138
|
end
|
98
139
|
|
@@ -106,13 +147,6 @@ class AttachedDeviceTest < Test::Unit::TestCase
|
|
106
147
|
@ad.expects(:destroy).never
|
107
148
|
assert @ad.save
|
108
149
|
end
|
109
|
-
|
110
|
-
should "not raise an InvalidObjectException if no image is set" do
|
111
|
-
@ad.image = nil
|
112
|
-
assert_raises(VirtualBox::Exceptions::InvalidObjectException) {
|
113
|
-
@ad.save
|
114
|
-
}
|
115
|
-
end
|
116
150
|
|
117
151
|
should "call the proper vboxcommand" do
|
118
152
|
VirtualBox::Command.expects(:vboxmanage).with("storageattach #{@vm.name} --storagectl #{VirtualBox::Command.shell_escape(@caller.name)} --port #{@ad.port} --device 0 --type #{@image.image_type} --medium #{@ad.medium}")
|
@@ -23,6 +23,29 @@ raw
|
|
23
23
|
VirtualBox::Command.stubs(:vboxmanage).with("showhdinfo #{@name}").returns(@find_raw)
|
24
24
|
end
|
25
25
|
|
26
|
+
context "validations" do
|
27
|
+
setup do
|
28
|
+
@hd = VirtualBox::HardDrive.new
|
29
|
+
@hd.size = 2000
|
30
|
+
end
|
31
|
+
|
32
|
+
should "be valid with a size and format" do
|
33
|
+
assert @hd.valid?
|
34
|
+
end
|
35
|
+
|
36
|
+
should "be invalid if size is nil" do
|
37
|
+
@hd.size = nil
|
38
|
+
assert !@hd.valid?
|
39
|
+
end
|
40
|
+
|
41
|
+
should "clear validations when rechecking" do
|
42
|
+
@hd.size = nil
|
43
|
+
assert !@hd.valid?
|
44
|
+
@hd.size = 700
|
45
|
+
assert @hd.valid?
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
26
49
|
context "destroying a hard drive" do
|
27
50
|
setup do
|
28
51
|
@hd = VirtualBox::HardDrive.find(@name)
|
@@ -136,6 +159,19 @@ raw
|
|
136
159
|
@hd.save(true)
|
137
160
|
}
|
138
161
|
end
|
162
|
+
|
163
|
+
should "not run if invalid" do
|
164
|
+
@hd.expects(:valid?).returns(false)
|
165
|
+
VirtualBox::Command.expects(:vboxmanage).never
|
166
|
+
assert !@hd.save
|
167
|
+
end
|
168
|
+
|
169
|
+
should "raise a ValidationFailedException if invalid and raise_errors is true" do
|
170
|
+
@hd.expects(:valid?).returns(false)
|
171
|
+
assert_raises(VirtualBox::Exceptions::ValidationFailedException) {
|
172
|
+
@hd.save(true)
|
173
|
+
}
|
174
|
+
end
|
139
175
|
end
|
140
176
|
|
141
177
|
context "finding a single hard drive" do
|
@@ -0,0 +1,231 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'test_helper')
|
2
|
+
|
3
|
+
class SharedFolderTest < Test::Unit::TestCase
|
4
|
+
setup do
|
5
|
+
@data = {
|
6
|
+
:sharedfoldernamemachinemapping1 => "foofolder",
|
7
|
+
:sharedfolderpathmachinemapping1 => "/foo",
|
8
|
+
:sharedfoldernamemachinemapping2 => "barfolder",
|
9
|
+
:sharedfolderpathmachinemapping2 => "/bar"
|
10
|
+
}
|
11
|
+
|
12
|
+
@caller = mock("caller")
|
13
|
+
@caller.stubs(:name).returns("foo")
|
14
|
+
|
15
|
+
VirtualBox::Command.stubs(:execute)
|
16
|
+
end
|
17
|
+
|
18
|
+
context "validations" do
|
19
|
+
setup do
|
20
|
+
@sf = VirtualBox::SharedFolder.new
|
21
|
+
@sf.name = "foo"
|
22
|
+
@sf.hostpath = "bar"
|
23
|
+
@sf.added_to_relationship(@caller)
|
24
|
+
end
|
25
|
+
|
26
|
+
should "be valid with all fields" do
|
27
|
+
assert @sf.valid?
|
28
|
+
end
|
29
|
+
|
30
|
+
should "be invalid with no name" do
|
31
|
+
@sf.name = nil
|
32
|
+
assert !@sf.valid?
|
33
|
+
end
|
34
|
+
|
35
|
+
should "be invalid with no hostpath" do
|
36
|
+
@sf.hostpath = nil
|
37
|
+
assert !@sf.valid?
|
38
|
+
end
|
39
|
+
|
40
|
+
should "be invalid if not in a relationship" do
|
41
|
+
@sf.write_attribute(:parent, nil)
|
42
|
+
assert !@sf.valid?
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context "saving an existing shared folder" do
|
47
|
+
setup do
|
48
|
+
@value = VirtualBox::SharedFolder.populate_relationship(@caller, @data)
|
49
|
+
@value = @value[0]
|
50
|
+
@value.name = "different"
|
51
|
+
assert @value.changed?
|
52
|
+
end
|
53
|
+
|
54
|
+
should "first destroy the shared folder then recreate it" do
|
55
|
+
seq = sequence("create_seq")
|
56
|
+
@value.expects(:destroy).in_sequence(seq)
|
57
|
+
VirtualBox::Command.expects(:vboxmanage).in_sequence(seq)
|
58
|
+
assert @value.save
|
59
|
+
end
|
60
|
+
|
61
|
+
should "call destroy with raise errors if set" do
|
62
|
+
@value.expects(:destroy).with(true).once
|
63
|
+
assert @value.save(true)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context "creating a new shared folder" do
|
68
|
+
setup do
|
69
|
+
@sf = VirtualBox::SharedFolder.new
|
70
|
+
@sf.name = "foo"
|
71
|
+
@sf.hostpath = "bar"
|
72
|
+
end
|
73
|
+
|
74
|
+
should "return false and not call vboxmanage if invalid" do
|
75
|
+
VirtualBox::Command.expects(:vboxmanage).never
|
76
|
+
@sf.expects(:valid?).returns(false)
|
77
|
+
assert !@sf.save
|
78
|
+
end
|
79
|
+
|
80
|
+
should "raise a ValidationFailedException if invalid and raise_errors is true" do
|
81
|
+
@sf.expects(:valid?).returns(false)
|
82
|
+
assert_raises(VirtualBox::Exceptions::ValidationFailedException) {
|
83
|
+
@sf.save(true)
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
context "has a parent" do
|
88
|
+
setup do
|
89
|
+
@sf.added_to_relationship(@caller)
|
90
|
+
VirtualBox::Command.stubs(:vboxmanage)
|
91
|
+
end
|
92
|
+
|
93
|
+
should "not call destroy since its a new record" do
|
94
|
+
@sf.expects(:destroy).never
|
95
|
+
assert @sf.save
|
96
|
+
end
|
97
|
+
|
98
|
+
should "call the proper vboxcommand" do
|
99
|
+
VirtualBox::Command.expects(:vboxmanage).with("sharedfolder add #{@caller.name} --name #{@sf.name} --hostpath #{@sf.hostpath}")
|
100
|
+
assert @sf.save
|
101
|
+
end
|
102
|
+
|
103
|
+
should "return false if the command failed" do
|
104
|
+
VirtualBox::Command.stubs(:vboxmanage).raises(VirtualBox::Exceptions::CommandFailedException)
|
105
|
+
assert !@sf.save
|
106
|
+
end
|
107
|
+
|
108
|
+
should "return true if the command was a success" do
|
109
|
+
assert @sf.save
|
110
|
+
end
|
111
|
+
|
112
|
+
should "raise an exception if true sent to save and error occured" do
|
113
|
+
VirtualBox::Command.stubs(:vboxmanage).raises(VirtualBox::Exceptions::CommandFailedException)
|
114
|
+
assert_raises(VirtualBox::Exceptions::CommandFailedException) {
|
115
|
+
@sf.save(true)
|
116
|
+
}
|
117
|
+
end
|
118
|
+
|
119
|
+
should "not be a new record after saving" do
|
120
|
+
assert @sf.new_record?
|
121
|
+
assert @sf.save
|
122
|
+
assert !@sf.new_record?
|
123
|
+
end
|
124
|
+
|
125
|
+
should "not be changed after saving" do
|
126
|
+
assert @sf.changed?
|
127
|
+
assert @sf.save
|
128
|
+
assert !@sf.changed?
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context "constructor" do
|
134
|
+
should "call initialize_for_relationship if 3 args are given" do
|
135
|
+
VirtualBox::SharedFolder.any_instance.expects(:initialize_for_relationship).with(1,2,3).once
|
136
|
+
VirtualBox::SharedFolder.new(1,2,3)
|
137
|
+
end
|
138
|
+
|
139
|
+
should "raise a NoMethodError if anything other than 0,1,or 3 arguments" do
|
140
|
+
2.upto(9) do |i|
|
141
|
+
next if i == 3
|
142
|
+
args = Array.new(i, "A")
|
143
|
+
|
144
|
+
assert_raises(NoMethodError) {
|
145
|
+
VirtualBox::SharedFolder.new(*args)
|
146
|
+
}
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
should "populate from a hash if one argument is given" do
|
151
|
+
VirtualBox::SharedFolder.any_instance.expects(:initialize_for_data).with("HI").once
|
152
|
+
VirtualBox::SharedFolder.new("HI")
|
153
|
+
end
|
154
|
+
|
155
|
+
context "initializing from data" do
|
156
|
+
setup do
|
157
|
+
@sf = VirtualBox::SharedFolder.new({:name => "foo", :hostpath => "bar"})
|
158
|
+
end
|
159
|
+
|
160
|
+
should "allow the use of :name and :hostpath in the hash" do
|
161
|
+
assert_equal "foo", @sf.name
|
162
|
+
assert_equal "bar", @sf.hostpath
|
163
|
+
end
|
164
|
+
|
165
|
+
should "keep the record new" do
|
166
|
+
assert @sf.new_record?
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
context "destroying" do
|
172
|
+
setup do
|
173
|
+
@value = VirtualBox::SharedFolder.populate_relationship(@caller, @data)
|
174
|
+
@value = @value[0]
|
175
|
+
end
|
176
|
+
|
177
|
+
should "call the proper command" do
|
178
|
+
VirtualBox::Command.expects(:vboxmanage).with("sharedfolder remove #{@caller.name} --name #{@value.name}").once
|
179
|
+
assert @value.destroy
|
180
|
+
end
|
181
|
+
|
182
|
+
should "shell escape VM name and storage controller name" do
|
183
|
+
shell_seq = sequence("shell_seq")
|
184
|
+
VirtualBox::Command.expects(:shell_escape).with(@caller.name).in_sequence(shell_seq)
|
185
|
+
VirtualBox::Command.expects(:shell_escape).with(@value.name).in_sequence(shell_seq)
|
186
|
+
VirtualBox::Command.expects(:vboxmanage).in_sequence(shell_seq)
|
187
|
+
assert @value.destroy
|
188
|
+
end
|
189
|
+
|
190
|
+
should "return false if destroy failed" do
|
191
|
+
VirtualBox::Command.stubs(:vboxmanage).raises(VirtualBox::Exceptions::CommandFailedException)
|
192
|
+
assert !@value.destroy
|
193
|
+
end
|
194
|
+
|
195
|
+
should "raise an exception if destroy failed and an error occured" do
|
196
|
+
VirtualBox::Command.stubs(:vboxmanage).raises(VirtualBox::Exceptions::CommandFailedException)
|
197
|
+
assert_raises(VirtualBox::Exceptions::CommandFailedException) {
|
198
|
+
@value.destroy(true)
|
199
|
+
}
|
200
|
+
end
|
201
|
+
|
202
|
+
should "use the old name if it was changed" do
|
203
|
+
@value.name = "DIFFERENT"
|
204
|
+
shell_seq = sequence("shell_seq")
|
205
|
+
VirtualBox::Command.expects(:shell_escape).with(@caller.name).in_sequence(shell_seq)
|
206
|
+
VirtualBox::Command.expects(:shell_escape).with(@value.name_was).in_sequence(shell_seq)
|
207
|
+
VirtualBox::Command.expects(:vboxmanage).in_sequence(shell_seq)
|
208
|
+
assert @value.destroy
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
context "populating relationships" do
|
213
|
+
setup do
|
214
|
+
@value = VirtualBox::SharedFolder.populate_relationship(@caller, @data)
|
215
|
+
end
|
216
|
+
|
217
|
+
should "create the correct amount of objects" do
|
218
|
+
assert_equal 2, @value.length
|
219
|
+
end
|
220
|
+
|
221
|
+
should "parse the proper data" do
|
222
|
+
value = @value[0]
|
223
|
+
assert_equal "foofolder", value.name
|
224
|
+
assert_equal "/foo", value.hostpath
|
225
|
+
|
226
|
+
value = @value[1]
|
227
|
+
assert_equal "barfolder", value.name
|
228
|
+
assert_equal "/bar", value.hostpath
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
data/test/virtualbox/vm_test.rb
CHANGED
@@ -71,7 +71,11 @@ uart2="off"
|
|
71
71
|
audio="none"
|
72
72
|
clipboard="bidirectional"
|
73
73
|
vrdp="off"
|
74
|
-
usb="off"
|
74
|
+
usb="off"
|
75
|
+
SharedFolderNameMachineMapping1="mysharedfolder"
|
76
|
+
SharedFolderPathMachineMapping1="/virtualbox"
|
77
|
+
SharedFolderNameMachineMapping2="otherfolder"
|
78
|
+
SharedFolderPathMachineMapping2="/virtualbox/lib"
|
75
79
|
showvminfo
|
76
80
|
|
77
81
|
@name = "foo"
|
@@ -354,6 +358,7 @@ raw
|
|
354
358
|
should "save the relationships as well" do
|
355
359
|
VirtualBox::Nic.expects(:save_relationship).once
|
356
360
|
VirtualBox::StorageController.expects(:save_relationship).once
|
361
|
+
VirtualBox::SharedFolder.expects(:save_relationship).once
|
357
362
|
assert @vm.save
|
358
363
|
end
|
359
364
|
end
|
@@ -391,6 +396,12 @@ raw
|
|
391
396
|
assert @vm.storage_controllers.is_a?(Array)
|
392
397
|
assert_equal 2, @vm.storage_controllers.length
|
393
398
|
end
|
399
|
+
|
400
|
+
should "properly load shared folder relationship" do
|
401
|
+
assert @vm.shared_folders
|
402
|
+
assert @vm.shared_folders.is_a?(Array)
|
403
|
+
assert_equal 2, @vm.shared_folders.length
|
404
|
+
end
|
394
405
|
end
|
395
406
|
|
396
407
|
context "parsing the showvminfo output" do
|
data/virtualbox.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{virtualbox}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.3.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Mitchell Hashimoto"]
|
12
|
-
s.date = %q{2010-01-
|
12
|
+
s.date = %q{2010-01-28}
|
13
13
|
s.description = %q{Create and modify virtual machines in VirtualBox using pure ruby.}
|
14
14
|
s.email = %q{mitchell.hashimoto@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -24,11 +24,13 @@ Gem::Specification.new do |s|
|
|
24
24
|
"TODO",
|
25
25
|
"VERSION",
|
26
26
|
"docs/GettingStarted.md",
|
27
|
+
"docs/WhatsNew.md",
|
27
28
|
"lib/virtualbox.rb",
|
28
29
|
"lib/virtualbox/abstract_model.rb",
|
29
30
|
"lib/virtualbox/abstract_model/attributable.rb",
|
30
31
|
"lib/virtualbox/abstract_model/dirty.rb",
|
31
32
|
"lib/virtualbox/abstract_model/relatable.rb",
|
33
|
+
"lib/virtualbox/abstract_model/validatable.rb",
|
32
34
|
"lib/virtualbox/attached_device.rb",
|
33
35
|
"lib/virtualbox/command.rb",
|
34
36
|
"lib/virtualbox/dvd.rb",
|
@@ -38,12 +40,14 @@ Gem::Specification.new do |s|
|
|
38
40
|
"lib/virtualbox/image.rb",
|
39
41
|
"lib/virtualbox/nic.rb",
|
40
42
|
"lib/virtualbox/proxies/collection.rb",
|
43
|
+
"lib/virtualbox/shared_folder.rb",
|
41
44
|
"lib/virtualbox/storage_controller.rb",
|
42
45
|
"lib/virtualbox/vm.rb",
|
43
46
|
"test/test_helper.rb",
|
44
47
|
"test/virtualbox/abstract_model/attributable_test.rb",
|
45
48
|
"test/virtualbox/abstract_model/dirty_test.rb",
|
46
49
|
"test/virtualbox/abstract_model/relatable_test.rb",
|
50
|
+
"test/virtualbox/abstract_model/validatable_test.rb",
|
47
51
|
"test/virtualbox/abstract_model_test.rb",
|
48
52
|
"test/virtualbox/attached_device_test.rb",
|
49
53
|
"test/virtualbox/command_test.rb",
|
@@ -53,6 +57,7 @@ Gem::Specification.new do |s|
|
|
53
57
|
"test/virtualbox/image_test.rb",
|
54
58
|
"test/virtualbox/nic_test.rb",
|
55
59
|
"test/virtualbox/proxies/collection_test.rb",
|
60
|
+
"test/virtualbox/shared_folder_test.rb",
|
56
61
|
"test/virtualbox/storage_controller_test.rb",
|
57
62
|
"test/virtualbox/vm_test.rb",
|
58
63
|
"virtualbox.gemspec"
|
@@ -67,6 +72,7 @@ Gem::Specification.new do |s|
|
|
67
72
|
"test/virtualbox/abstract_model/attributable_test.rb",
|
68
73
|
"test/virtualbox/abstract_model/dirty_test.rb",
|
69
74
|
"test/virtualbox/abstract_model/relatable_test.rb",
|
75
|
+
"test/virtualbox/abstract_model/validatable_test.rb",
|
70
76
|
"test/virtualbox/abstract_model_test.rb",
|
71
77
|
"test/virtualbox/attached_device_test.rb",
|
72
78
|
"test/virtualbox/command_test.rb",
|
@@ -76,6 +82,7 @@ Gem::Specification.new do |s|
|
|
76
82
|
"test/virtualbox/image_test.rb",
|
77
83
|
"test/virtualbox/nic_test.rb",
|
78
84
|
"test/virtualbox/proxies/collection_test.rb",
|
85
|
+
"test/virtualbox/shared_folder_test.rb",
|
79
86
|
"test/virtualbox/storage_controller_test.rb",
|
80
87
|
"test/virtualbox/vm_test.rb"
|
81
88
|
]
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: virtualbox
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mitchell Hashimoto
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-01-
|
12
|
+
date: 2010-01-28 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -30,11 +30,13 @@ files:
|
|
30
30
|
- TODO
|
31
31
|
- VERSION
|
32
32
|
- docs/GettingStarted.md
|
33
|
+
- docs/WhatsNew.md
|
33
34
|
- lib/virtualbox.rb
|
34
35
|
- lib/virtualbox/abstract_model.rb
|
35
36
|
- lib/virtualbox/abstract_model/attributable.rb
|
36
37
|
- lib/virtualbox/abstract_model/dirty.rb
|
37
38
|
- lib/virtualbox/abstract_model/relatable.rb
|
39
|
+
- lib/virtualbox/abstract_model/validatable.rb
|
38
40
|
- lib/virtualbox/attached_device.rb
|
39
41
|
- lib/virtualbox/command.rb
|
40
42
|
- lib/virtualbox/dvd.rb
|
@@ -44,12 +46,14 @@ files:
|
|
44
46
|
- lib/virtualbox/image.rb
|
45
47
|
- lib/virtualbox/nic.rb
|
46
48
|
- lib/virtualbox/proxies/collection.rb
|
49
|
+
- lib/virtualbox/shared_folder.rb
|
47
50
|
- lib/virtualbox/storage_controller.rb
|
48
51
|
- lib/virtualbox/vm.rb
|
49
52
|
- test/test_helper.rb
|
50
53
|
- test/virtualbox/abstract_model/attributable_test.rb
|
51
54
|
- test/virtualbox/abstract_model/dirty_test.rb
|
52
55
|
- test/virtualbox/abstract_model/relatable_test.rb
|
56
|
+
- test/virtualbox/abstract_model/validatable_test.rb
|
53
57
|
- test/virtualbox/abstract_model_test.rb
|
54
58
|
- test/virtualbox/attached_device_test.rb
|
55
59
|
- test/virtualbox/command_test.rb
|
@@ -59,6 +63,7 @@ files:
|
|
59
63
|
- test/virtualbox/image_test.rb
|
60
64
|
- test/virtualbox/nic_test.rb
|
61
65
|
- test/virtualbox/proxies/collection_test.rb
|
66
|
+
- test/virtualbox/shared_folder_test.rb
|
62
67
|
- test/virtualbox/storage_controller_test.rb
|
63
68
|
- test/virtualbox/vm_test.rb
|
64
69
|
- virtualbox.gemspec
|
@@ -95,6 +100,7 @@ test_files:
|
|
95
100
|
- test/virtualbox/abstract_model/attributable_test.rb
|
96
101
|
- test/virtualbox/abstract_model/dirty_test.rb
|
97
102
|
- test/virtualbox/abstract_model/relatable_test.rb
|
103
|
+
- test/virtualbox/abstract_model/validatable_test.rb
|
98
104
|
- test/virtualbox/abstract_model_test.rb
|
99
105
|
- test/virtualbox/attached_device_test.rb
|
100
106
|
- test/virtualbox/command_test.rb
|
@@ -104,5 +110,6 @@ test_files:
|
|
104
110
|
- test/virtualbox/image_test.rb
|
105
111
|
- test/virtualbox/nic_test.rb
|
106
112
|
- test/virtualbox/proxies/collection_test.rb
|
113
|
+
- test/virtualbox/shared_folder_test.rb
|
107
114
|
- test/virtualbox/storage_controller_test.rb
|
108
115
|
- test/virtualbox/vm_test.rb
|