virtualbox 0.4.1 → 0.4.2
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/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
|