bbrowning-virtualbox 0.7.6.dev

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.
Files changed (281) hide show
  1. data/.gitignore +8 -0
  2. data/.yardopts +3 -0
  3. data/Gemfile +16 -0
  4. data/LICENSE +19 -0
  5. data/Rakefile +33 -0
  6. data/Readme.md +70 -0
  7. data/docs/GettingStarted.md +196 -0
  8. data/docs/WhatsNew.md +12 -0
  9. data/features/README.md +33 -0
  10. data/features/global.feature +19 -0
  11. data/features/global_extra_data.feature +27 -0
  12. data/features/step_definitions/abstract_model_steps.rb +39 -0
  13. data/features/step_definitions/extra_data_steps.rb +36 -0
  14. data/features/step_definitions/global_steps.rb +29 -0
  15. data/features/step_definitions/nat_engine_steps.rb +76 -0
  16. data/features/step_definitions/network_adapter_steps.rb +38 -0
  17. data/features/step_definitions/shared_folder_steps.rb +76 -0
  18. data/features/step_definitions/snapshot_steps.rb +74 -0
  19. data/features/step_definitions/storage_controller_steps.rb +16 -0
  20. data/features/step_definitions/virtualbox_steps.rb +17 -0
  21. data/features/step_definitions/vm_steps.rb +50 -0
  22. data/features/support/env.rb +61 -0
  23. data/features/support/helpers.rb +38 -0
  24. data/features/support/hooks.rb +30 -0
  25. data/features/support/ordered_hash.rb +49 -0
  26. data/features/support/vboxmanage.rb +191 -0
  27. data/features/version.feature +16 -0
  28. data/features/vm.feature +13 -0
  29. data/features/vm_bios.feature +29 -0
  30. data/features/vm_cpu.feature +29 -0
  31. data/features/vm_extra_data.feature +35 -0
  32. data/features/vm_hw_virt.feature +29 -0
  33. data/features/vm_nat_engine.feature +57 -0
  34. data/features/vm_network_adapters.feature +27 -0
  35. data/features/vm_shared_folders.feature +42 -0
  36. data/features/vm_snapshots.feature +29 -0
  37. data/features/vm_storage_controllers.feature +11 -0
  38. data/lib/virtualbox.rb +11 -0
  39. data/lib/virtualbox/abstract_model.rb +281 -0
  40. data/lib/virtualbox/abstract_model/attributable.rb +290 -0
  41. data/lib/virtualbox/abstract_model/dirty.rb +177 -0
  42. data/lib/virtualbox/abstract_model/interface_attributes.rb +98 -0
  43. data/lib/virtualbox/abstract_model/relatable.rb +332 -0
  44. data/lib/virtualbox/abstract_model/validatable.rb +167 -0
  45. data/lib/virtualbox/abstract_model/version_matcher.rb +35 -0
  46. data/lib/virtualbox/appliance.rb +62 -0
  47. data/lib/virtualbox/audio_adapter.rb +52 -0
  48. data/lib/virtualbox/bios.rb +50 -0
  49. data/lib/virtualbox/com.rb +23 -0
  50. data/lib/virtualbox/com/abstract_enum.rb +43 -0
  51. data/lib/virtualbox/com/abstract_implementer.rb +45 -0
  52. data/lib/virtualbox/com/abstract_interface.rb +167 -0
  53. data/lib/virtualbox/com/base_interface.rb +38 -0
  54. data/lib/virtualbox/com/ffi/interface.rb +150 -0
  55. data/lib/virtualbox/com/ffi/interfaces.rb +54 -0
  56. data/lib/virtualbox/com/ffi/util.rb +119 -0
  57. data/lib/virtualbox/com/ffi/vboxxpcomc.rb +31 -0
  58. data/lib/virtualbox/com/ffi_interface.rb +96 -0
  59. data/lib/virtualbox/com/implementer/base.rb +59 -0
  60. data/lib/virtualbox/com/implementer/ffi.rb +361 -0
  61. data/lib/virtualbox/com/implementer/mscom.rb +175 -0
  62. data/lib/virtualbox/com/implementer/nil.rb +10 -0
  63. data/lib/virtualbox/com/interface/3.1.x/access_mode.rb +11 -0
  64. data/lib/virtualbox/com/interface/3.1.x/appliance.rb +22 -0
  65. data/lib/virtualbox/com/interface/3.1.x/audio_adapter.rb +15 -0
  66. data/lib/virtualbox/com/interface/3.1.x/audio_controller_type.rb +11 -0
  67. data/lib/virtualbox/com/interface/3.1.x/audio_driver_type.rb +11 -0
  68. data/lib/virtualbox/com/interface/3.1.x/bios_boot_menu_mode.rb +11 -0
  69. data/lib/virtualbox/com/interface/3.1.x/bios_settings.rb +21 -0
  70. data/lib/virtualbox/com/interface/3.1.x/clipboard_mode.rb +11 -0
  71. data/lib/virtualbox/com/interface/3.1.x/console.rb +50 -0
  72. data/lib/virtualbox/com/interface/3.1.x/cpu_property_type.rb +11 -0
  73. data/lib/virtualbox/com/interface/3.1.x/device_type.rb +11 -0
  74. data/lib/virtualbox/com/interface/3.1.x/dhcp_server.rb +22 -0
  75. data/lib/virtualbox/com/interface/3.1.x/firmware_type.rb +11 -0
  76. data/lib/virtualbox/com/interface/3.1.x/guest_os_type.rb +23 -0
  77. data/lib/virtualbox/com/interface/3.1.x/host.rb +42 -0
  78. data/lib/virtualbox/com/interface/3.1.x/host_network_interface.rb +30 -0
  79. data/lib/virtualbox/com/interface/3.1.x/host_network_interface_medium_type.rb +11 -0
  80. data/lib/virtualbox/com/interface/3.1.x/host_network_interface_status.rb +11 -0
  81. data/lib/virtualbox/com/interface/3.1.x/host_network_interface_type.rb +11 -0
  82. data/lib/virtualbox/com/interface/3.1.x/host_usb_device.rb +13 -0
  83. data/lib/virtualbox/com/interface/3.1.x/host_usb_device_filter.rb +13 -0
  84. data/lib/virtualbox/com/interface/3.1.x/hw_virt_ex_property_type.rb +11 -0
  85. data/lib/virtualbox/com/interface/3.1.x/machine.rb +105 -0
  86. data/lib/virtualbox/com/interface/3.1.x/machine_state.rb +14 -0
  87. data/lib/virtualbox/com/interface/3.1.x/medium.rb +50 -0
  88. data/lib/virtualbox/com/interface/3.1.x/medium_attachment.rb +18 -0
  89. data/lib/virtualbox/com/interface/3.1.x/medium_format.rb +18 -0
  90. data/lib/virtualbox/com/interface/3.1.x/medium_state.rb +11 -0
  91. data/lib/virtualbox/com/interface/3.1.x/medium_type.rb +11 -0
  92. data/lib/virtualbox/com/interface/3.1.x/medium_variant.rb +11 -0
  93. data/lib/virtualbox/com/interface/3.1.x/network_adapter.rb +30 -0
  94. data/lib/virtualbox/com/interface/3.1.x/network_adapter_type.rb +11 -0
  95. data/lib/virtualbox/com/interface/3.1.x/network_attachment_type.rb +11 -0
  96. data/lib/virtualbox/com/interface/3.1.x/nsiexception.rb +23 -0
  97. data/lib/virtualbox/com/interface/3.1.x/nsisupports.rb +15 -0
  98. data/lib/virtualbox/com/interface/3.1.x/parallel_port.rb +17 -0
  99. data/lib/virtualbox/com/interface/3.1.x/port_mode.rb +11 -0
  100. data/lib/virtualbox/com/interface/3.1.x/progress.rb +63 -0
  101. data/lib/virtualbox/com/interface/3.1.x/serial_port.rb +19 -0
  102. data/lib/virtualbox/com/interface/3.1.x/session.rb +18 -0
  103. data/lib/virtualbox/com/interface/3.1.x/session_state.rb +11 -0
  104. data/lib/virtualbox/com/interface/3.1.x/session_type.rb +11 -0
  105. data/lib/virtualbox/com/interface/3.1.x/shared_folder.rb +17 -0
  106. data/lib/virtualbox/com/interface/3.1.x/snapshot.rb +20 -0
  107. data/lib/virtualbox/com/interface/3.1.x/storage_bus.rb +11 -0
  108. data/lib/virtualbox/com/interface/3.1.x/storage_controller.rb +23 -0
  109. data/lib/virtualbox/com/interface/3.1.x/storage_controller_type.rb +11 -0
  110. data/lib/virtualbox/com/interface/3.1.x/system_properties.rb +37 -0
  111. data/lib/virtualbox/com/interface/3.1.x/usb_controller.rb +20 -0
  112. data/lib/virtualbox/com/interface/3.1.x/usb_device.rb +24 -0
  113. data/lib/virtualbox/com/interface/3.1.x/usb_device_filter.rb +23 -0
  114. data/lib/virtualbox/com/interface/3.1.x/usb_device_filter_action.rb +11 -0
  115. data/lib/virtualbox/com/interface/3.1.x/usb_device_state.rb +11 -0
  116. data/lib/virtualbox/com/interface/3.1.x/virtual_box_error_info.rb +17 -0
  117. data/lib/virtualbox/com/interface/3.1.x/virtual_system_description.rb +19 -0
  118. data/lib/virtualbox/com/interface/3.1.x/virtual_system_description_type.rb +14 -0
  119. data/lib/virtualbox/com/interface/3.1.x/virtual_system_description_value_type.rb +11 -0
  120. data/lib/virtualbox/com/interface/3.1.x/virtualbox.rb +67 -0
  121. data/lib/virtualbox/com/interface/3.1.x/vrdp_auth_type.rb +11 -0
  122. data/lib/virtualbox/com/interface/3.1.x/vrdp_server.rb +19 -0
  123. data/lib/virtualbox/com/interface/3.2.x/access_mode.rb +11 -0
  124. data/lib/virtualbox/com/interface/3.2.x/appliance.rb +22 -0
  125. data/lib/virtualbox/com/interface/3.2.x/audio_adapter.rb +15 -0
  126. data/lib/virtualbox/com/interface/3.2.x/audio_controller_type.rb +11 -0
  127. data/lib/virtualbox/com/interface/3.2.x/audio_driver_type.rb +11 -0
  128. data/lib/virtualbox/com/interface/3.2.x/bios_boot_menu_mode.rb +11 -0
  129. data/lib/virtualbox/com/interface/3.2.x/bios_settings.rb +21 -0
  130. data/lib/virtualbox/com/interface/3.2.x/clipboard_mode.rb +11 -0
  131. data/lib/virtualbox/com/interface/3.2.x/console.rb +50 -0
  132. data/lib/virtualbox/com/interface/3.2.x/cpu_property_type.rb +11 -0
  133. data/lib/virtualbox/com/interface/3.2.x/device_type.rb +11 -0
  134. data/lib/virtualbox/com/interface/3.2.x/dhcp_server.rb +22 -0
  135. data/lib/virtualbox/com/interface/3.2.x/firmware_type.rb +11 -0
  136. data/lib/virtualbox/com/interface/3.2.x/guest.rb +13 -0
  137. data/lib/virtualbox/com/interface/3.2.x/guest_os_type.rb +33 -0
  138. data/lib/virtualbox/com/interface/3.2.x/host.rb +43 -0
  139. data/lib/virtualbox/com/interface/3.2.x/host_network_interface.rb +30 -0
  140. data/lib/virtualbox/com/interface/3.2.x/host_network_interface_medium_type.rb +11 -0
  141. data/lib/virtualbox/com/interface/3.2.x/host_network_interface_status.rb +11 -0
  142. data/lib/virtualbox/com/interface/3.2.x/host_network_interface_type.rb +11 -0
  143. data/lib/virtualbox/com/interface/3.2.x/host_usb_device.rb +13 -0
  144. data/lib/virtualbox/com/interface/3.2.x/host_usb_device_filter.rb +13 -0
  145. data/lib/virtualbox/com/interface/3.2.x/hw_virt_ex_property_type.rb +11 -0
  146. data/lib/virtualbox/com/interface/3.2.x/keyboard_hid_type.rb +11 -0
  147. data/lib/virtualbox/com/interface/3.2.x/machine.rb +118 -0
  148. data/lib/virtualbox/com/interface/3.2.x/machine_state.rb +14 -0
  149. data/lib/virtualbox/com/interface/3.2.x/medium.rb +51 -0
  150. data/lib/virtualbox/com/interface/3.2.x/medium_attachment.rb +18 -0
  151. data/lib/virtualbox/com/interface/3.2.x/medium_format.rb +18 -0
  152. data/lib/virtualbox/com/interface/3.2.x/medium_state.rb +11 -0
  153. data/lib/virtualbox/com/interface/3.2.x/medium_type.rb +11 -0
  154. data/lib/virtualbox/com/interface/3.2.x/medium_variant.rb +11 -0
  155. data/lib/virtualbox/com/interface/3.2.x/nat_alias_mode.rb +11 -0
  156. data/lib/virtualbox/com/interface/3.2.x/nat_engine.rb +27 -0
  157. data/lib/virtualbox/com/interface/3.2.x/nat_protocol.rb +11 -0
  158. data/lib/virtualbox/com/interface/3.2.x/network_adapter.rb +34 -0
  159. data/lib/virtualbox/com/interface/3.2.x/network_adapter_type.rb +11 -0
  160. data/lib/virtualbox/com/interface/3.2.x/network_attachment_type.rb +11 -0
  161. data/lib/virtualbox/com/interface/3.2.x/nsiexception.rb +23 -0
  162. data/lib/virtualbox/com/interface/3.2.x/nsisupports.rb +15 -0
  163. data/lib/virtualbox/com/interface/3.2.x/parallel_port.rb +17 -0
  164. data/lib/virtualbox/com/interface/3.2.x/pointing_hid_type.rb +11 -0
  165. data/lib/virtualbox/com/interface/3.2.x/port_mode.rb +11 -0
  166. data/lib/virtualbox/com/interface/3.2.x/progress.rb +63 -0
  167. data/lib/virtualbox/com/interface/3.2.x/serial_port.rb +19 -0
  168. data/lib/virtualbox/com/interface/3.2.x/session.rb +18 -0
  169. data/lib/virtualbox/com/interface/3.2.x/session_state.rb +11 -0
  170. data/lib/virtualbox/com/interface/3.2.x/session_type.rb +11 -0
  171. data/lib/virtualbox/com/interface/3.2.x/shared_folder.rb +17 -0
  172. data/lib/virtualbox/com/interface/3.2.x/snapshot.rb +20 -0
  173. data/lib/virtualbox/com/interface/3.2.x/storage_bus.rb +11 -0
  174. data/lib/virtualbox/com/interface/3.2.x/storage_controller.rb +24 -0
  175. data/lib/virtualbox/com/interface/3.2.x/storage_controller_type.rb +11 -0
  176. data/lib/virtualbox/com/interface/3.2.x/system_properties.rb +42 -0
  177. data/lib/virtualbox/com/interface/3.2.x/usb_controller.rb +21 -0
  178. data/lib/virtualbox/com/interface/3.2.x/usb_device.rb +24 -0
  179. data/lib/virtualbox/com/interface/3.2.x/usb_device_filter.rb +23 -0
  180. data/lib/virtualbox/com/interface/3.2.x/usb_device_filter_action.rb +11 -0
  181. data/lib/virtualbox/com/interface/3.2.x/usb_device_state.rb +11 -0
  182. data/lib/virtualbox/com/interface/3.2.x/virtual_box_error_info.rb +17 -0
  183. data/lib/virtualbox/com/interface/3.2.x/virtual_system_description.rb +19 -0
  184. data/lib/virtualbox/com/interface/3.2.x/virtual_system_description_type.rb +14 -0
  185. data/lib/virtualbox/com/interface/3.2.x/virtual_system_description_value_type.rb +11 -0
  186. data/lib/virtualbox/com/interface/3.2.x/virtualbox.rb +67 -0
  187. data/lib/virtualbox/com/interface/3.2.x/vrdp_auth_type.rb +11 -0
  188. data/lib/virtualbox/com/interface/3.2.x/vrdp_server.rb +21 -0
  189. data/lib/virtualbox/com/mscom_interface.rb +44 -0
  190. data/lib/virtualbox/com/nil_interface.rb +7 -0
  191. data/lib/virtualbox/com/util.rb +32 -0
  192. data/lib/virtualbox/cpu.rb +61 -0
  193. data/lib/virtualbox/dhcp_server.rb +89 -0
  194. data/lib/virtualbox/dvd.rb +27 -0
  195. data/lib/virtualbox/exceptions.rb +39 -0
  196. data/lib/virtualbox/ext/byte_normalizer.rb +17 -0
  197. data/lib/virtualbox/ext/glob_loader.rb +22 -0
  198. data/lib/virtualbox/ext/logger.rb +38 -0
  199. data/lib/virtualbox/ext/platform.rb +27 -0
  200. data/lib/virtualbox/ext/subclass_listing.rb +24 -0
  201. data/lib/virtualbox/extra_data.rb +127 -0
  202. data/lib/virtualbox/forwarded_port.rb +222 -0
  203. data/lib/virtualbox/global.rb +102 -0
  204. data/lib/virtualbox/guest_property.rb +116 -0
  205. data/lib/virtualbox/hard_drive.rb +246 -0
  206. data/lib/virtualbox/host.rb +71 -0
  207. data/lib/virtualbox/host_network_interface.rb +137 -0
  208. data/lib/virtualbox/hw_virtualization.rb +63 -0
  209. data/lib/virtualbox/lib.rb +84 -0
  210. data/lib/virtualbox/media.rb +20 -0
  211. data/lib/virtualbox/medium.rb +145 -0
  212. data/lib/virtualbox/medium_attachment.rb +61 -0
  213. data/lib/virtualbox/nat_engine.rb +71 -0
  214. data/lib/virtualbox/nat_forwarded_port.rb +171 -0
  215. data/lib/virtualbox/network_adapter.rb +166 -0
  216. data/lib/virtualbox/proxies/collection.rb +57 -0
  217. data/lib/virtualbox/shared_folder.rb +220 -0
  218. data/lib/virtualbox/snapshot.rb +185 -0
  219. data/lib/virtualbox/storage_controller.rb +160 -0
  220. data/lib/virtualbox/system_properties.rb +74 -0
  221. data/lib/virtualbox/usb_controller.rb +59 -0
  222. data/lib/virtualbox/usb_device_filter.rb +74 -0
  223. data/lib/virtualbox/version.rb +36 -0
  224. data/lib/virtualbox/virtual_system_description.rb +47 -0
  225. data/lib/virtualbox/vm.rb +684 -0
  226. data/lib/virtualbox/vrdp_server.rb +59 -0
  227. data/test/test_helper.rb +18 -0
  228. data/test/virtualbox/abstract_model/attributable_test.rb +269 -0
  229. data/test/virtualbox/abstract_model/dirty_test.rb +83 -0
  230. data/test/virtualbox/abstract_model/interface_attributes_test.rb +194 -0
  231. data/test/virtualbox/abstract_model/relatable_test.rb +348 -0
  232. data/test/virtualbox/abstract_model/validatable_test.rb +308 -0
  233. data/test/virtualbox/abstract_model/version_matcher_test.rb +41 -0
  234. data/test/virtualbox/abstract_model_test.rb +462 -0
  235. data/test/virtualbox/appliance_test.rb +159 -0
  236. data/test/virtualbox/audio_adapter_test.rb +83 -0
  237. data/test/virtualbox/bios_test.rb +83 -0
  238. data/test/virtualbox/com/abstract_enum_test.rb +49 -0
  239. data/test/virtualbox/com/abstract_implementer_test.rb +40 -0
  240. data/test/virtualbox/com/abstract_interface_test.rb +140 -0
  241. data/test/virtualbox/com/ffi/interface_test.rb +249 -0
  242. data/test/virtualbox/com/ffi/util_test.rb +108 -0
  243. data/test/virtualbox/com/ffi_interface_test.rb +42 -0
  244. data/test/virtualbox/com/implementer/base_test.rb +38 -0
  245. data/test/virtualbox/com/implementer/ffi_test.rb +527 -0
  246. data/test/virtualbox/com/implementer/mscom_test.rb +247 -0
  247. data/test/virtualbox/com/mscom_interface_test.rb +17 -0
  248. data/test/virtualbox/com/util_test.rb +17 -0
  249. data/test/virtualbox/cpu_test.rb +103 -0
  250. data/test/virtualbox/dhcp_server_test.rb +165 -0
  251. data/test/virtualbox/dvd_test.rb +28 -0
  252. data/test/virtualbox/ext/byte_normalizer_test.rb +34 -0
  253. data/test/virtualbox/ext/platform_test.rb +50 -0
  254. data/test/virtualbox/ext/subclass_listing_test.rb +25 -0
  255. data/test/virtualbox/extra_data_test.rb +155 -0
  256. data/test/virtualbox/forwarded_port_test.rb +286 -0
  257. data/test/virtualbox/global_test.rb +46 -0
  258. data/test/virtualbox/hard_drive_test.rb +141 -0
  259. data/test/virtualbox/host_network_interface_test.rb +254 -0
  260. data/test/virtualbox/host_test.rb +94 -0
  261. data/test/virtualbox/hw_virtualization_test.rb +103 -0
  262. data/test/virtualbox/lib_test.rb +93 -0
  263. data/test/virtualbox/medium_attachment_test.rb +147 -0
  264. data/test/virtualbox/medium_test.rb +192 -0
  265. data/test/virtualbox/nat_engine_test.rb +106 -0
  266. data/test/virtualbox/nat_forwarded_port_test.rb +222 -0
  267. data/test/virtualbox/network_adapter_test.rb +191 -0
  268. data/test/virtualbox/proxies/collection_test.rb +102 -0
  269. data/test/virtualbox/shared_folder_test.rb +219 -0
  270. data/test/virtualbox/snapshot_test.rb +231 -0
  271. data/test/virtualbox/storage_controller_test.rb +197 -0
  272. data/test/virtualbox/system_properties_test.rb +87 -0
  273. data/test/virtualbox/usb_controller_test.rb +112 -0
  274. data/test/virtualbox/usb_device_filter_test.rb +93 -0
  275. data/test/virtualbox/version_test.rb +59 -0
  276. data/test/virtualbox/virtual_system_description_test.rb +61 -0
  277. data/test/virtualbox/vm_test.rb +637 -0
  278. data/test/virtualbox/vrdp_server_test.rb +83 -0
  279. data/test/virtualbox_test.rb +11 -0
  280. data/virtualbox.gemspec +25 -0
  281. metadata +397 -0
@@ -0,0 +1,290 @@
1
+ require 'virtualbox/abstract_model/version_matcher'
2
+
3
+ module VirtualBox
4
+ class AbstractModel
5
+ # Module which can be included into any other class which allows
6
+ # that class to have attributes using the "attribute" class method.
7
+ # This creates the reader/writer for the attribute but also provides
8
+ # other useful options such as readonly attributes, default values,
9
+ # and more.
10
+ #
11
+ # Make sure to also see the {ClassMethods}.
12
+ #
13
+ # ## Defining a Basic Attribute
14
+ #
15
+ # attribute :name
16
+ #
17
+ # The example above would put the "name" attribute on the class. This
18
+ # would give the class the following abilities
19
+ #
20
+ # instance.name = "Harry!"
21
+ # puts instance.name # => "Harry!"
22
+ #
23
+ # Basic attributes alone are not different than ruby's built-in
24
+ # `attr_*` methods.
25
+ #
26
+ # ## Defining a Readonly Attribute
27
+ #
28
+ # attribute :age, :readonly => true
29
+ #
30
+ # The example above allows age to be read, but not written to via the
31
+ # `age=` method. The attribute is still able to written using
32
+ # {#write_attribute} but this is generally only for
33
+ # inter-class use, and not for users of it.
34
+ #
35
+ # ## Defining Default Values
36
+ #
37
+ # attribute :format, :default => "VDI"
38
+ #
39
+ # The example above applies a default value to format. So if no value
40
+ # is ever given to it, `format` would return `VDI`.
41
+ #
42
+ # ## Attributes for a Specific VirtualBox Version
43
+ #
44
+ # Attributes change with different VirtualBox versions. One way to
45
+ # provide version-specific behavior is to specify the version
46
+ # which the attribute applies to.
47
+ #
48
+ # attribute :name, :version => "3.2"
49
+ # attribute :age, :version => "3.1"
50
+ #
51
+ # These versions only apply to major and minor releases of
52
+ # VirtualBox. Patch releases can't be specified (such as "3.2.4")
53
+ #
54
+ # ## Populating Multiple Attributes
55
+ #
56
+ # Attributes can be mass populated using {#populate_attributes}. Below
57
+ # is an example of the use.
58
+ #
59
+ # class Person
60
+ # include Attributable
61
+ #
62
+ # attribute :name
63
+ # attribute :age, :readonly => true
64
+ #
65
+ # def initialize
66
+ # populate_attributes({
67
+ # :name => "Steven",
68
+ # :age => 27
69
+ # })
70
+ # end
71
+ # end
72
+ #
73
+ # **Note:** Populating attributes is not the same as mass-updating attributes.
74
+ # {#populate_attributes} is meant to do initial population only. There is
75
+ # currently no method for mass assignment for updating.
76
+ #
77
+ # ## Custom Populate Keys
78
+ #
79
+ # Sometimes the attribute names don't match the keys of the hash that will be
80
+ # used to populate it. For this purpose, you can define a custom
81
+ # `populate_key`. Example:
82
+ #
83
+ # attribute :path, :populate_key => :location
84
+ #
85
+ # def initialize
86
+ # populate_attributes(:location => "Home")
87
+ # puts path # => "Home"
88
+ # end
89
+ #
90
+ # ## Lazy Loading Attributes
91
+ #
92
+ # While most attributes are fairly trivial to calculate and populate, sometimes
93
+ # attributes may have an expensive cost to populate, and are generally not worth
94
+ # populating unless a user of the class requests that attribute. This is known as
95
+ # _lazy loading_ the attributes. This is possibly by specifying the `:lazy` option
96
+ # on the attribute. In this case, the first time (and _only_ the first time) the
97
+ # attribute is requested, `load_attribute` will be called with the name of the
98
+ # attribute as the parameter. This method is then expected to call `write_attribute`
99
+ # on that attribute to give it a value.
100
+ #
101
+ # class ExpensiveAttributeModel
102
+ # include VirtualBox::AbstractModel::Attributable
103
+ # attribute :expensive_attribute, :lazy => true
104
+ #
105
+ # def load_attribute(name)
106
+ # if name == :expensive_attribute
107
+ # write_attribute(name, perform_expensive_calculation)
108
+ # end
109
+ # end
110
+ # end
111
+ #
112
+ # Using the above definition, we could use the class like so:
113
+ #
114
+ # # Initializing is fast, since no attribute population is done
115
+ # model = ExpensiveAttributeModel.new
116
+ #
117
+ # # But this is slow, since it has to calculate.
118
+ # puts model.expensive_attribute
119
+ #
120
+ # # But ONLY THE FIRST TIME. This time is FAST!
121
+ # puts model.expensive_attribute
122
+ #
123
+ # In addition to calling `load_attribute` on initial read, `write_attribute`
124
+ # when performed on a lazy loaded attribute will mark it as "loaded" so there
125
+ # will be no load called on the first request. Example, using the above class
126
+ # once again:
127
+ #
128
+ # model = ExpensiveAttributeModel.new
129
+ # model.write_attribute(:expensive_attribute, 42)
130
+ #
131
+ # # This is FAST, since "load_attribute" is not called
132
+ # puts model.expensive_attribute # => 42
133
+ #
134
+ module Attributable
135
+ include VersionMatcher
136
+
137
+ def self.included(base)
138
+ base.extend ClassMethods
139
+ end
140
+
141
+ # Defines the class methods for the {Attributable} module. For
142
+ # detailed overview documentation, see {Attributable}.
143
+ module ClassMethods
144
+ # Defines an attribute on the model.
145
+ #
146
+ # @param [Symbol] name The name of the attribute, which will also be
147
+ # used to set the accessor methods.
148
+ # @option options [Boolean] :readonly (false) If true, attribute will be readonly.
149
+ # More specifically, the `attribute=` method won't be defined for it.
150
+ # @option options [Object] :default (nil) Specifies a default value for the
151
+ # attribute.
152
+ # @option options [Symbol] :populate_key (attribute name) Specifies
153
+ # a custom populate key to use for {Attributable#populate_attributes}
154
+ def attribute(name, options = {})
155
+ name = name.to_sym
156
+ attributes[name] = defined?(@__attribute_scope) ? @__attribute_scope : {}
157
+ attributes[name].merge!(options)
158
+
159
+ # Create the method for reading this attribute
160
+ define_method("#{name}?") { read_attribute(name) } if options[:boolean]
161
+ define_method(name) { read_attribute(name) }
162
+
163
+ # Create the writer method for it unless the attribute is readonly,
164
+ # then remove the method if it exists
165
+ if !options[:readonly]
166
+ define_method("#{name}=") do |value|
167
+ write_attribute(name, value)
168
+ end
169
+ elsif method_defined?("#{name}=")
170
+ undef_method("#{name}=")
171
+ end
172
+ end
173
+
174
+ # Defines the specified scope for all attributes within the block.
175
+ # The scope is reset to the previous value once the block ends. Multiple
176
+ # scopes can be nested and they'll inherit from each other.
177
+ def attribute_scope(options, &block)
178
+ @__attribute_scope ||= {}
179
+ old_value = @__attribute_scope
180
+ @__attribute_scope = old_value.merge(options)
181
+
182
+ instance_eval(&block)
183
+
184
+ @__attribute_scope = old_value
185
+ end
186
+
187
+ # Returns the hash of attributes and their associated options.
188
+ def attributes
189
+ @attributes ||= {}
190
+ end
191
+
192
+ # Used to propagate attributes to subclasses. This method makes sure that
193
+ # subclasses of a class with {Attributable} included will inherit the
194
+ # attributes as well, which would be the expected behaviour.
195
+ def inherited(subclass)
196
+ super rescue NoMethodError
197
+
198
+ attributes.each do |name, option|
199
+ subclass.attribute(name, option)
200
+ end
201
+ end
202
+ end
203
+
204
+ # Does the initial population of the various attributes. It will
205
+ # ignore attributes which are not defined or have no value in the
206
+ # hash.
207
+ #
208
+ # Population uses the attributes `populate_key` if present to
209
+ # determine which value to take. Example:
210
+ #
211
+ # attribute :name, :populate_key => :namae
212
+ # attribute :age
213
+ #
214
+ # def initialize
215
+ # populate_attributes(:namae => "Henry", :age => 27)
216
+ # end
217
+ #
218
+ # The above example would set `name` to `Henry` since that is
219
+ # the `populate_key`. If a `populate_key` is not present, the
220
+ # attribute name is used.
221
+ def populate_attributes(attribs)
222
+ self.class.attributes.each do |key, options|
223
+ value_key = options[:populate_key] || key
224
+ write_attribute(key, attribs[value_key]) if attribs.has_key?(value_key)
225
+ end
226
+ end
227
+
228
+ # Writes an attribute. This method ignores the `readonly` option
229
+ # on attribute definitions. This method is mostly meant for
230
+ # internal use on setting attributes (including readonly
231
+ # attributes), whereas users of a class which includes this
232
+ # module should use the accessor methods, such as `name=`.
233
+ def write_attribute(name, value)
234
+ attributes[name] = value
235
+ end
236
+
237
+ # Reads an attribute. This method will return `nil` if the
238
+ # attribute doesn't exist. If the attribute does exist but
239
+ # doesn't have a value set, it'll use the `default` value
240
+ # if specified.
241
+ def read_attribute(name)
242
+ if has_attribute?(name)
243
+ options = self.class.attributes[name]
244
+
245
+ assert_version_match(options[:version], VirtualBox.version) if options[:version]
246
+ if lazy_attribute?(name) && !loaded_attribute?(name)
247
+ # Load the lazy attribute
248
+ load_attribute(name.to_sym)
249
+ end
250
+
251
+ if attributes[name].nil?
252
+ options[:default]
253
+ else
254
+ attributes[name]
255
+ end
256
+ end
257
+ end
258
+
259
+ # Returns a hash of all attributes and their options.
260
+ def attributes
261
+ @attribute_values ||= {}
262
+ end
263
+
264
+ # Returns boolean value denoting if an attribute exists.
265
+ def has_attribute?(name)
266
+ self.class.attributes.has_key?(name.to_sym)
267
+ end
268
+
269
+ # Returns boolean value denoting if an attribute is "lazy loaded"
270
+ def lazy_attribute?(name)
271
+ has_attribute?(name) && self.class.attributes[name.to_sym][:lazy]
272
+ end
273
+
274
+ # Returns boolean value denoting if an attribute has been loaded
275
+ # yet.
276
+ def loaded_attribute?(name)
277
+ attributes.has_key?(name)
278
+ end
279
+
280
+ # Returns a boolean value denoting if an attribute is readonly.
281
+ # This method also returns false for **nonexistent attributes**
282
+ # so it should be used in conjunction with {#has_attribute?} if
283
+ # existence is important.
284
+ def readonly_attribute?(name)
285
+ name = name.to_sym
286
+ has_attribute?(name) && self.class.attributes[name][:readonly]
287
+ end
288
+ end
289
+ end
290
+ end
@@ -0,0 +1,177 @@
1
+ module VirtualBox
2
+ class AbstractModel
3
+ # Tracks "dirtiness" of values for a class. Its not tied to AbstractModel
4
+ # in any way other than the namespace.
5
+ #
6
+ # # Checking if a Value was Changed
7
+ #
8
+ # Dynamic methods allow functionality for checking if values changed:
9
+ #
10
+ # obj.foo_changed?
11
+ #
12
+ # # Previous Value
13
+ #
14
+ # Can also view the previous value of an attribute:
15
+ #
16
+ # obj.foo # => "foo" initially
17
+ # obj.foo = "bar"
18
+ # obj.foo_was # => "foo"
19
+ #
20
+ # # Previous and Current Value
21
+ #
22
+ # Using the `_change` dynamic method, can view the changes of a field.
23
+ #
24
+ # obj.foo # => "foo" initially
25
+ # obj.foo = "bar"
26
+ # obj.foo_change # => ["foo", "bar"]
27
+ #
28
+ # # All Changes
29
+ #
30
+ # Can also view all changes for a class with the `changes` method.
31
+ #
32
+ # obj.foo # => "foo" initially
33
+ # obj.bar # => "bar" initially
34
+ # obj.foo = "far"
35
+ # obj.bar = "baz"
36
+ # obj.changes # => { :foo => ["foo", "far"], :bar => ["bar", "baz"]}
37
+ #
38
+ # # Setting Dirty
39
+ #
40
+ # Dirtiness tracking only occurs for values which the implementor
41
+ # explicitly sets as dirty. This is done with the {#set_dirty!}
42
+ # method. Example implementation below:
43
+ #
44
+ # class Person
45
+ # include VirtualBox::AbstractModel::Dirty
46
+ #
47
+ # attr_reader :name
48
+ #
49
+ # def name=(value)
50
+ # set_dirty!(:name, @name, value)
51
+ # @name = value
52
+ # end
53
+ # end
54
+ #
55
+ # The above example has all the changes necessary to track changes
56
+ # on an attribute.
57
+ #
58
+ # # Ignoring Dirtiness Tracking
59
+ #
60
+ # Sometimes, for features such as mass assignment, dirtiness tracking
61
+ # should be disabled. This can be done with the `ignore_dirty` method.
62
+ #
63
+ # ignore_dirty do |obj|
64
+ # obj.name = "Foo"
65
+ # end
66
+ #
67
+ # obj.changed? # => false
68
+ #
69
+ # # Clearing Dirty State
70
+ #
71
+ # Sometimes, such as after saving a model, dirty states should be cleared.
72
+ # This can be done with the `clear_dirty!` method.
73
+ #
74
+ # obj.clear_dirty!(:name)
75
+ # obj.name_changed? # => false
76
+ #
77
+ # If no specific field is speciied, `clear_dirty!` will clear the dirty
78
+ # status on the entire model.
79
+ #
80
+ # obj.changed? # => assume true
81
+ # obj.clear_dirty!
82
+ # obj.changed? # => false
83
+ #
84
+ module Dirty
85
+ # Manages dirty state for an attribute. This method will handle
86
+ # setting the dirty state of an attribute (or even clearing it
87
+ # if the old value is reset). Any implementors of this mixin should
88
+ # call this for any fields they want tracked.
89
+ #
90
+ # @param [Symbol] name Name of field
91
+ # @param [Object] current Current value (not necessarilly the
92
+ # original value, but the **current** value)
93
+ # @param [Object] value The new value being set
94
+ def set_dirty!(name, current, value)
95
+ if current != value
96
+ name = name.to_sym
97
+
98
+ # If its the first time this attribute has changed, store the
99
+ # original value in the first field
100
+ changes[name] ||= [current, nil]
101
+
102
+ # Then store the changed value
103
+ changes[name][1] = value
104
+
105
+ # If the value changed back to the original value, remove from the
106
+ # dirty hash
107
+ if changes[name][0] == changes[name][1]
108
+ changes.delete(name)
109
+ end
110
+ end
111
+ end
112
+
113
+ # Clears dirty state for a field.
114
+ #
115
+ # @param [Symbol] key The field to clear dirty state.
116
+ def clear_dirty!(key=nil)
117
+ if key.nil?
118
+ @changed_attributes = {}
119
+ else
120
+ changes.delete(key)
121
+ end
122
+ end
123
+
124
+ # Ignores any dirty changes during the duration of the block.
125
+ # Guarantees the dirty state will be the same before and after
126
+ # the method call, but not within the block itself.
127
+ def ignore_dirty(&block)
128
+ current_changes = changes.dup
129
+ yield self
130
+ @changed_attributes = current_changes
131
+ end
132
+
133
+ # Returns boolean denoting if field changed or not. If no attribute
134
+ # is specified, returns true of false showing whether the model
135
+ # changed at all.
136
+ #
137
+ # @param [Symbol] attribute The attribute to check, or if nil,
138
+ # all fields checked.
139
+ def changed?(attribute = nil)
140
+ if attribute.nil?
141
+ !changes.empty?
142
+ else
143
+ changes.has_key?(attribute)
144
+ end
145
+ end
146
+
147
+ # Returns hash of changes. Keys are fields, values are an
148
+ # array of the original value and the current value.
149
+ #
150
+ # @return [Hash]
151
+ def changes
152
+ @changed_attributes ||= {}
153
+ end
154
+
155
+ # Method missing is used to implement the "magic" methods of
156
+ # `field_changed`, `field_change`, and `field_was`.
157
+ def method_missing(meth, *args)
158
+ meth_string = meth.to_s
159
+
160
+ if meth_string =~ /^(.+?)_changed\?$/
161
+ changed?($1.to_sym)
162
+ elsif meth_string =~ /^(.+?)_change$/
163
+ changes[$1.to_sym]
164
+ elsif meth_string =~ /^(.+?)_was$/
165
+ change = changes[$1.to_sym]
166
+ if change.nil?
167
+ nil
168
+ else
169
+ change[0]
170
+ end
171
+ else
172
+ super
173
+ end
174
+ end
175
+ end
176
+ end
177
+ end