virtualbox 0.4.1 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Readme.md +9 -9
- data/VERSION +1 -1
- data/docs/GettingStarted.md +11 -11
- data/docs/WhatsNew.md +1 -1
- data/lib/virtualbox.rb +10 -1
- data/lib/virtualbox/abstract_model.rb +47 -29
- data/lib/virtualbox/abstract_model/attributable.rb +16 -16
- data/lib/virtualbox/abstract_model/dirty.rb +10 -10
- data/lib/virtualbox/abstract_model/relatable.rb +22 -22
- data/lib/virtualbox/abstract_model/validatable.rb +4 -4
- data/lib/virtualbox/attached_device.rb +23 -23
- data/lib/virtualbox/command.rb +9 -9
- data/lib/virtualbox/dvd.rb +7 -7
- data/lib/virtualbox/ext/subclass_listing.rb +1 -1
- data/lib/virtualbox/extra_data.rb +17 -17
- data/lib/virtualbox/forwarded_port.rb +23 -23
- data/lib/virtualbox/hard_drive.rb +27 -27
- data/lib/virtualbox/image.rb +25 -18
- data/lib/virtualbox/nic.rb +22 -22
- data/lib/virtualbox/proxies/collection.rb +4 -4
- data/lib/virtualbox/shared_folder.rb +25 -25
- data/lib/virtualbox/storage_controller.rb +16 -16
- data/lib/virtualbox/vm.rb +95 -42
- data/test/virtualbox/abstract_model/attributable_test.rb +28 -28
- data/test/virtualbox/abstract_model/dirty_test.rb +13 -13
- data/test/virtualbox/abstract_model/relatable_test.rb +36 -36
- data/test/virtualbox/abstract_model/validatable_test.rb +22 -22
- data/test/virtualbox/abstract_model_test.rb +46 -36
- data/test/virtualbox/attached_device_test.rb +47 -47
- data/test/virtualbox/command_test.rb +12 -12
- data/test/virtualbox/dvd_test.rb +15 -19
- data/test/virtualbox/ext/subclass_listing_test.rb +2 -2
- data/test/virtualbox/extra_data_test.rb +37 -37
- data/test/virtualbox/forwarded_port_test.rb +31 -31
- data/test/virtualbox/hard_drive_test.rb +40 -48
- data/test/virtualbox/image_test.rb +36 -33
- data/test/virtualbox/nic_test.rb +22 -22
- data/test/virtualbox/proxies/collection_test.rb +6 -6
- data/test/virtualbox/shared_folder_test.rb +36 -36
- data/test/virtualbox/storage_controller_test.rb +14 -14
- data/test/virtualbox/vm_test.rb +121 -70
- data/test/virtualbox_test.rb +19 -0
- data/virtualbox.gemspec +5 -3
- metadata +4 -2
data/lib/virtualbox/vm.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module VirtualBox
|
2
2
|
# Represents a single VirtualBox virtual machine. All attributes which are
|
3
|
-
# not read-only can be modified and saved.
|
3
|
+
# not read-only can be modified and saved.
|
4
4
|
#
|
5
5
|
# # Finding Virtual Machines
|
6
6
|
#
|
@@ -21,10 +21,10 @@ module VirtualBox
|
|
21
21
|
# vm.save
|
22
22
|
#
|
23
23
|
# # Attributes and Relationships
|
24
|
-
#
|
24
|
+
#
|
25
25
|
# Properties of the virtual machine are exposed using standard ruby instance
|
26
26
|
# methods which are generated on the fly. Because of this, they are not listed
|
27
|
-
# below as available instance methods.
|
27
|
+
# below as available instance methods.
|
28
28
|
#
|
29
29
|
# These attributes can be accessed and modified via standard ruby-style
|
30
30
|
# `instance.attribute` and `instance.attribute=` methods. The attributes are
|
@@ -33,7 +33,7 @@ module VirtualBox
|
|
33
33
|
# Relationships are also accessed like attributes but can't be set. Instead,
|
34
34
|
# they are typically references to other objects such as a {HardDrive} which
|
35
35
|
# in turn have their own attributes which can be modified.
|
36
|
-
#
|
36
|
+
#
|
37
37
|
# ## Attributes
|
38
38
|
#
|
39
39
|
# This is copied directly from the class header, but lists all available
|
@@ -54,6 +54,7 @@ module VirtualBox
|
|
54
54
|
# attribute :nestedpaging
|
55
55
|
# attribute :vtxvpid
|
56
56
|
# attribute :accelerate3d
|
57
|
+
# attribute :accelerate2dvideo
|
57
58
|
# attribute :biosbootmenu, :populate_key => :bootmenu
|
58
59
|
# attribute :boot1
|
59
60
|
# attribute :boot2
|
@@ -64,6 +65,7 @@ module VirtualBox
|
|
64
65
|
# attribute :usb
|
65
66
|
# attribute :audio
|
66
67
|
# attribute :vrdp
|
68
|
+
# attribute :vrdpports
|
67
69
|
# attribute :state, :populate_key => :vmstate, :readonly => true
|
68
70
|
#
|
69
71
|
# ## Relationships
|
@@ -94,6 +96,7 @@ module VirtualBox
|
|
94
96
|
attribute :nestedpaging
|
95
97
|
attribute :vtxvpid
|
96
98
|
attribute :accelerate3d
|
99
|
+
attribute :accelerate2dvideo
|
97
100
|
attribute :biosbootmenu, :populate_key => :bootmenu
|
98
101
|
attribute :boot1
|
99
102
|
attribute :boot2
|
@@ -104,13 +107,14 @@ module VirtualBox
|
|
104
107
|
attribute :usb
|
105
108
|
attribute :audio
|
106
109
|
attribute :vrdp
|
110
|
+
attribute :vrdpports
|
107
111
|
attribute :state, :populate_key => :vmstate, :readonly => true
|
108
112
|
relationship :nics, Nic
|
109
113
|
relationship :storage_controllers, StorageController, :dependent => :destroy
|
110
114
|
relationship :shared_folders, SharedFolder
|
111
115
|
relationship :extra_data, ExtraData
|
112
116
|
relationship :forwarded_ports, ForwardedPort
|
113
|
-
|
117
|
+
|
114
118
|
class <<self
|
115
119
|
# Returns an array of all available VMs.
|
116
120
|
#
|
@@ -119,16 +123,18 @@ module VirtualBox
|
|
119
123
|
raw = Command.vboxmanage("list vms")
|
120
124
|
parse_vm_list(raw)
|
121
125
|
end
|
122
|
-
|
126
|
+
|
123
127
|
# Finds a VM by UUID or registered name and returns a
|
124
128
|
# new VM object. If the VM doesn't exist, will return `nil`.
|
125
129
|
#
|
126
130
|
# @return [VM]
|
127
131
|
def find(name)
|
128
132
|
new(raw_info(name))
|
133
|
+
rescue Exceptions::CommandFailedException
|
134
|
+
nil
|
129
135
|
end
|
130
|
-
|
131
|
-
# Imports a VM, blocking the entire thread during this time.
|
136
|
+
|
137
|
+
# Imports a VM, blocking the entire thread during this time.
|
132
138
|
# When finished, on success, will return the VM object. This
|
133
139
|
# VM object can be used to make any modifications necessary
|
134
140
|
# (RAM, cpus, etc.).
|
@@ -137,10 +143,10 @@ module VirtualBox
|
|
137
143
|
def import(source_path)
|
138
144
|
raw = Command.vboxmanage("import #{Command.shell_escape(source_path)}")
|
139
145
|
return nil unless raw
|
140
|
-
|
146
|
+
|
141
147
|
find(parse_vm_name(raw))
|
142
148
|
end
|
143
|
-
|
149
|
+
|
144
150
|
# Gets the non-machine-readable info for a given VM and returns
|
145
151
|
# it as a raw string.
|
146
152
|
#
|
@@ -150,16 +156,16 @@ module VirtualBox
|
|
150
156
|
def human_info(name)
|
151
157
|
Command.vboxmanage("showvminfo #{Command.shell_escape(name)}")
|
152
158
|
end
|
153
|
-
|
159
|
+
|
154
160
|
# Gets the VM info (machine readable) for a given VM and returns it
|
155
|
-
# as a hash.
|
161
|
+
# as a hash.
|
156
162
|
#
|
157
163
|
# @return [Hash] Parsed VM info.
|
158
164
|
def raw_info(name)
|
159
165
|
raw = Command.vboxmanage("showvminfo #{Command.shell_escape(name)} --machinereadable")
|
160
166
|
parse_vm_info(raw)
|
161
167
|
end
|
162
|
-
|
168
|
+
|
163
169
|
# Parses the machine-readable format outputted by VBoxManage showvminfo
|
164
170
|
# into a hash. Ignores lines which don't match the format.
|
165
171
|
def parse_vm_info(raw)
|
@@ -172,7 +178,7 @@ module VirtualBox
|
|
172
178
|
|
173
179
|
parsed
|
174
180
|
end
|
175
|
-
|
181
|
+
|
176
182
|
# Parses the list of VMs returned by the "list vms" command used
|
177
183
|
# in {VM.all}.
|
178
184
|
#
|
@@ -185,10 +191,10 @@ module VirtualBox
|
|
185
191
|
next unless line =~ /^"(.+?)"\s+\{(.+?)\}$/
|
186
192
|
results.push(find($1.to_s))
|
187
193
|
end
|
188
|
-
|
194
|
+
|
189
195
|
results
|
190
196
|
end
|
191
|
-
|
197
|
+
|
192
198
|
# Parses the vm name from the import results.
|
193
199
|
#
|
194
200
|
# **This method typically won't be used except internally.**
|
@@ -199,7 +205,7 @@ module VirtualBox
|
|
199
205
|
$1.to_s
|
200
206
|
end
|
201
207
|
end
|
202
|
-
|
208
|
+
|
203
209
|
# Creates a new instance of a virtual machine.
|
204
210
|
#
|
205
211
|
# **Currently can NOT be used to create a NEW virtual machine**.
|
@@ -208,11 +214,11 @@ module VirtualBox
|
|
208
214
|
# initialize the VMs.
|
209
215
|
def initialize(data)
|
210
216
|
super()
|
211
|
-
|
217
|
+
|
212
218
|
populate_attributes(data)
|
213
219
|
@original_name = data[:name]
|
214
220
|
end
|
215
|
-
|
221
|
+
|
216
222
|
# State of the virtual machine. Returns the state of the virtual
|
217
223
|
# machine. This state will represent the state that was assigned
|
218
224
|
# when the VM was found unless `reload` is set to `true`.
|
@@ -225,10 +231,10 @@ module VirtualBox
|
|
225
231
|
info = self.class.raw_info(@original_name)
|
226
232
|
write_attribute(:state, info[:vmstate])
|
227
233
|
end
|
228
|
-
|
234
|
+
|
229
235
|
read_attribute(:state)
|
230
236
|
end
|
231
|
-
|
237
|
+
|
232
238
|
# Saves the virtual machine if modified. This method saves any modified
|
233
239
|
# attributes of the virtual machine. If any related attributes were saved
|
234
240
|
# as well (such as storage controllers), those will be saved, too.
|
@@ -239,24 +245,24 @@ module VirtualBox
|
|
239
245
|
save_attribute(:name, name)
|
240
246
|
@original_name = name
|
241
247
|
end
|
242
|
-
|
248
|
+
|
243
249
|
super()
|
244
|
-
|
250
|
+
|
245
251
|
true
|
246
252
|
rescue Exceptions::CommandFailedException
|
247
253
|
raise if raise_errors
|
248
254
|
return false
|
249
255
|
end
|
250
|
-
|
256
|
+
|
251
257
|
# Saves a single attribute of the virtual machine. This should **not**
|
252
258
|
# be called except interally. Instead, you're probably looking for {#save}.
|
253
259
|
#
|
254
260
|
# **This method typically won't be used except internally.**
|
255
261
|
def save_attribute(key, value)
|
256
|
-
Command.vboxmanage("modifyvm #{@original_name} --#{key} #{Command.shell_escape(value.to_s)}")
|
262
|
+
Command.vboxmanage("modifyvm #{Command.shell_escape(@original_name)} --#{key} #{Command.shell_escape(value.to_s)}")
|
257
263
|
super
|
258
264
|
end
|
259
|
-
|
265
|
+
|
260
266
|
# Exports a virtual machine. The virtual machine will be exported
|
261
267
|
# to the specified OVF file name. This directory will also have the
|
262
268
|
# `mf` file which contains the file checksums and also the virtual
|
@@ -283,21 +289,21 @@ module VirtualBox
|
|
283
289
|
options = options.inject([]) do |acc, kv|
|
284
290
|
acc.push("--#{kv[0]} #{Command.shell_escape(kv[1])}")
|
285
291
|
end
|
286
|
-
|
292
|
+
|
287
293
|
options.unshift("--vsys 0") unless options.empty?
|
288
|
-
|
289
|
-
raw = Command.vboxmanage("export #{@original_name} -o #{Command.shell_escape(filename)} #{options.join(" ")}".strip)
|
294
|
+
|
295
|
+
raw = Command.vboxmanage("export #{Command.shell_escape(@original_name)} -o #{Command.shell_escape(filename)} #{options.join(" ")}".strip)
|
290
296
|
true
|
291
297
|
rescue Exceptions::CommandFailedException
|
292
298
|
raise if raise_error
|
293
299
|
false
|
294
300
|
end
|
295
|
-
|
301
|
+
|
296
302
|
# Starts the virtual machine. The virtual machine can be started in a
|
297
303
|
# variety of modes:
|
298
304
|
#
|
299
305
|
# * **gui** -- The VirtualBox GUI will open with the screen of the VM.
|
300
|
-
# * **headless** -- The VM will run in the background. No GUI will be
|
306
|
+
# * **headless** -- The VM will run in the background. No GUI will be
|
301
307
|
# present at all.
|
302
308
|
#
|
303
309
|
# All modes will start their processes and return almost immediately.
|
@@ -308,16 +314,28 @@ module VirtualBox
|
|
308
314
|
# will be raised if the command failed.
|
309
315
|
# @return [Boolean] True if command was successful, false otherwise.
|
310
316
|
def start(mode=:gui, raise_errors=false)
|
311
|
-
Command.vboxmanage("startvm #{@original_name} --type #{mode}")
|
317
|
+
Command.vboxmanage("startvm #{Command.shell_escape(@original_name)} --type #{mode}")
|
312
318
|
true
|
313
319
|
rescue Exceptions::CommandFailedException
|
314
320
|
raise if raise_errors
|
315
321
|
false
|
316
322
|
end
|
317
|
-
|
323
|
+
|
324
|
+
# Shuts down the VM by directly calling "acpipowerbutton". Depending on the
|
325
|
+
# settings of the Virtual Machine, this may not work. For example, some linux
|
326
|
+
# installations don't respond to the ACPI power button at all. In such cases,
|
327
|
+
# {#stop} or {#save_state} may be used instead.
|
328
|
+
#
|
329
|
+
# @param [Boolean] raise_errors If true, {Exceptions::CommandFailedException}
|
330
|
+
# will be raised if the command failed.
|
331
|
+
# @return [Boolean] True if command was successful, false otherwise.
|
332
|
+
def shutdown(raise_errors=false)
|
333
|
+
control(:acpipowerbutton, raise_errors)
|
334
|
+
end
|
335
|
+
|
318
336
|
# Stops the VM by directly calling "poweroff." Immediately halts the
|
319
337
|
# virtual machine without saving state. This could result in a loss
|
320
|
-
# of data.
|
338
|
+
# of data. To prevent data loss, see {#shutdown}
|
321
339
|
#
|
322
340
|
# @param [Boolean] raise_errors If true, {Exceptions::CommandFailedException}
|
323
341
|
# will be raised if the command failed.
|
@@ -325,7 +343,7 @@ module VirtualBox
|
|
325
343
|
def stop(raise_errors=false)
|
326
344
|
control(:poweroff, raise_errors)
|
327
345
|
end
|
328
|
-
|
346
|
+
|
329
347
|
# Pauses the VM, putting it on hold temporarily. The VM can be resumed
|
330
348
|
# again by calling {#resume}
|
331
349
|
#
|
@@ -335,7 +353,7 @@ module VirtualBox
|
|
335
353
|
def pause(raise_errors=false)
|
336
354
|
control(:pause, raise_errors)
|
337
355
|
end
|
338
|
-
|
356
|
+
|
339
357
|
# Resume a paused VM.
|
340
358
|
#
|
341
359
|
# @param [Boolean] raise_errors If true, {Exceptions::CommandFailedException}
|
@@ -344,7 +362,7 @@ module VirtualBox
|
|
344
362
|
def resume(raise_errors=false)
|
345
363
|
control(:resume, raise_errors)
|
346
364
|
end
|
347
|
-
|
365
|
+
|
348
366
|
# Saves the state of a VM and stops it. The VM can be resumed
|
349
367
|
# again by calling "start" again.
|
350
368
|
#
|
@@ -354,7 +372,7 @@ module VirtualBox
|
|
354
372
|
def save_state(raise_errors=false)
|
355
373
|
control(:savestate, raise_errors)
|
356
374
|
end
|
357
|
-
|
375
|
+
|
358
376
|
# Controls the virtual machine. This method is used by {#stop},
|
359
377
|
# {#pause}, {#resume}, and {#save_state} to control the virtual machine.
|
360
378
|
# Typically, you won't ever have to call this method and should
|
@@ -365,13 +383,13 @@ module VirtualBox
|
|
365
383
|
# will be raised if the command failed.
|
366
384
|
# @return [Boolean] True if command was successful, false otherwise.
|
367
385
|
def control(command, raise_errors=false)
|
368
|
-
Command.vboxmanage("controlvm #{@original_name} #{command}")
|
386
|
+
Command.vboxmanage("controlvm #{Command.shell_escape(@original_name)} #{command}")
|
369
387
|
true
|
370
388
|
rescue Exceptions::CommandFailedException
|
371
389
|
raise if raise_errors
|
372
390
|
false
|
373
391
|
end
|
374
|
-
|
392
|
+
|
375
393
|
# Destroys the virtual machine. This method also removes all attached
|
376
394
|
# media (required by VirtualBox to destroy a VM). By default,
|
377
395
|
# this **will not** destroy attached hard drives, but will if given
|
@@ -386,8 +404,43 @@ module VirtualBox
|
|
386
404
|
# Call super first to destroy relationships, necessary before
|
387
405
|
# unregistering a VM
|
388
406
|
super
|
389
|
-
|
390
|
-
Command.vboxmanage("unregistervm #{@original_name} --delete")
|
407
|
+
|
408
|
+
Command.vboxmanage("unregistervm #{Command.shell_escape(@original_name)} --delete")
|
409
|
+
end
|
410
|
+
|
411
|
+
# Returns true if the virtual machine state is running
|
412
|
+
#
|
413
|
+
# @return [Boolean] True if virtual machine state is running
|
414
|
+
def running?
|
415
|
+
state == 'running'
|
416
|
+
end
|
417
|
+
|
418
|
+
# Returns true if the virtual machine state is powered off
|
419
|
+
#
|
420
|
+
# @return [Boolean] True if virtual machine state is powered off
|
421
|
+
def powered_off?
|
422
|
+
state == 'poweroff'
|
423
|
+
end
|
424
|
+
|
425
|
+
# Returns true if the virtual machine state is paused
|
426
|
+
#
|
427
|
+
# @return [Boolean] True if virtual machine state is paused
|
428
|
+
def paused?
|
429
|
+
state == 'paused'
|
430
|
+
end
|
431
|
+
|
432
|
+
# Returns true if the virtual machine state is saved
|
433
|
+
#
|
434
|
+
# @return [Boolean] True if virtual machine state is saved
|
435
|
+
def saved?
|
436
|
+
state == 'saved'
|
437
|
+
end
|
438
|
+
|
439
|
+
# Returns true if the virtual machine state is aborted
|
440
|
+
#
|
441
|
+
# @return [Boolean] True if virtual machine state is aborted
|
442
|
+
def aborted?
|
443
|
+
state == 'aborted'
|
391
444
|
end
|
392
445
|
end
|
393
446
|
end
|
@@ -2,28 +2,28 @@ require File.join(File.dirname(__FILE__), '..', '..', 'test_helper')
|
|
2
2
|
|
3
3
|
class AttributableTest < Test::Unit::TestCase
|
4
4
|
class EmptyAttributeModel
|
5
|
-
include VirtualBox::AbstractModel::Attributable
|
5
|
+
include VirtualBox::AbstractModel::Attributable
|
6
6
|
end
|
7
|
-
|
7
|
+
|
8
8
|
class AttributeModel < EmptyAttributeModel
|
9
9
|
attribute :foo
|
10
10
|
attribute :bar
|
11
|
-
|
11
|
+
|
12
12
|
def initialize
|
13
13
|
super
|
14
|
-
|
14
|
+
|
15
15
|
populate_attributes({
|
16
16
|
:foo => "foo",
|
17
17
|
:bar => "bar"
|
18
18
|
})
|
19
19
|
end
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
context "subclasses" do
|
23
23
|
class SubModel < AttributeModel
|
24
24
|
attribute :baz
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
should "have foo bar and baz" do
|
28
28
|
attributes = SubModel.attributes
|
29
29
|
assert attributes.has_key?(:foo)
|
@@ -31,118 +31,118 @@ class AttributableTest < Test::Unit::TestCase
|
|
31
31
|
assert attributes.has_key?(:baz)
|
32
32
|
end
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
context "attribute options" do
|
36
36
|
context "custom populate keys" do
|
37
37
|
class CustomPopulateModel < AttributeModel
|
38
38
|
attribute :foo, :populate_key => :foo_key
|
39
39
|
end
|
40
|
-
|
40
|
+
|
41
41
|
setup do
|
42
42
|
@model = CustomPopulateModel.new
|
43
43
|
end
|
44
|
-
|
44
|
+
|
45
45
|
should "use the populate key instead of the attribute name" do
|
46
46
|
@model.populate_attributes({
|
47
47
|
:foo => "not me!",
|
48
48
|
:foo_key => "bar"
|
49
49
|
})
|
50
|
-
|
50
|
+
|
51
51
|
assert_equal "bar", @model.foo
|
52
52
|
end
|
53
53
|
end
|
54
|
-
|
54
|
+
|
55
55
|
context "readonly attributes" do
|
56
56
|
class ReadonlyModel < AttributeModel
|
57
57
|
attribute :foo, :readonly => :readonly
|
58
|
-
|
58
|
+
|
59
59
|
def initialize
|
60
60
|
super
|
61
61
|
populate_attributes({ :foo => "foo" })
|
62
62
|
end
|
63
63
|
end
|
64
|
-
|
64
|
+
|
65
65
|
setup do
|
66
66
|
@model = ReadonlyModel.new
|
67
67
|
end
|
68
|
-
|
68
|
+
|
69
69
|
should "be readonly" do
|
70
70
|
assert @model.readonly_attribute?(:foo)
|
71
71
|
end
|
72
|
-
|
72
|
+
|
73
73
|
should "allow reading" do
|
74
74
|
assert_equal "foo", @model.foo
|
75
75
|
end
|
76
|
-
|
76
|
+
|
77
77
|
should "not allow writing" do
|
78
78
|
assert_raises(NoMethodError) { @model.foo = "YO" }
|
79
79
|
end
|
80
80
|
end
|
81
|
-
|
81
|
+
|
82
82
|
context "default values" do
|
83
83
|
class DefaultModel < EmptyAttributeModel
|
84
84
|
attribute :foo, :default => "FOO!"
|
85
85
|
attribute :bar
|
86
86
|
end
|
87
|
-
|
87
|
+
|
88
88
|
setup do
|
89
89
|
@model = DefaultModel.new
|
90
90
|
end
|
91
|
-
|
91
|
+
|
92
92
|
should "read default values" do
|
93
93
|
assert_equal "FOO!", @model.foo
|
94
94
|
assert_nil @model.bar
|
95
95
|
end
|
96
96
|
end
|
97
97
|
end
|
98
|
-
|
98
|
+
|
99
99
|
context "populating attributes" do
|
100
100
|
setup do
|
101
101
|
@model = AttributeModel.new
|
102
102
|
end
|
103
|
-
|
103
|
+
|
104
104
|
should "write all valid attributes" do
|
105
105
|
new_attributes = {
|
106
106
|
:foo => "zxcv",
|
107
107
|
:bar => "qwerty"
|
108
108
|
}
|
109
|
-
|
109
|
+
|
110
110
|
@model.populate_attributes(new_attributes)
|
111
111
|
new_attributes.each do |k,v|
|
112
112
|
assert_equal v, @model.send(k)
|
113
113
|
end
|
114
114
|
end
|
115
115
|
end
|
116
|
-
|
116
|
+
|
117
117
|
context "reading and writing attributes" do
|
118
118
|
setup do
|
119
119
|
@model = AttributeModel.new
|
120
120
|
@checkstring = "HEY"
|
121
121
|
end
|
122
|
-
|
122
|
+
|
123
123
|
should "be able to read an entire hash of attributes" do
|
124
124
|
atts = @model.attributes
|
125
125
|
assert atts.is_a?(Hash)
|
126
126
|
assert atts.has_key?(:foo)
|
127
127
|
assert atts.has_key?(:bar)
|
128
128
|
end
|
129
|
-
|
129
|
+
|
130
130
|
should "be able to write defined attributes" do
|
131
131
|
assert_nothing_raised {
|
132
132
|
@model.foo = @check_string
|
133
133
|
}
|
134
134
|
end
|
135
|
-
|
135
|
+
|
136
136
|
should "be able to read defined attributes" do
|
137
137
|
assert_nothing_raised {
|
138
138
|
assert_equal "foo", @model.foo
|
139
139
|
}
|
140
140
|
end
|
141
|
-
|
141
|
+
|
142
142
|
should "raise an error if attempting to write an undefined attribute" do
|
143
143
|
assert_raises(NoMethodError) { @model.baz = @check_string }
|
144
144
|
end
|
145
|
-
|
145
|
+
|
146
146
|
should "raise an error if attempting to read an undefined attribute" do
|
147
147
|
assert_raises(NoMethodError) { @model.baz }
|
148
148
|
end
|