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,98 @@
1
+ module VirtualBox
2
+ class AbstractModel
3
+ # Module which can be included which defines helper methods to DRY out the
4
+ # code which handles attributes with {VirtualBox::COM} interfaces. This
5
+ # module works _alongside_ the {Attributable} module, so **both are required**.
6
+ module InterfaceAttributes
7
+ # Loads the attributes which have an interface getter and writes
8
+ # their values.
9
+ #
10
+ # @param [VirtualBox::COM::Interface] interface
11
+ def load_interface_attributes(interface)
12
+ self.class.attributes.each do |key, options|
13
+ load_interface_attribute(key, interface)
14
+ end
15
+ end
16
+
17
+ # Loads a single interface attribute.
18
+ #
19
+ # @param [Symbol] key The attribute to load
20
+ # @param [VirtualBox::COM::Interface] interface The interface
21
+ def load_interface_attribute(key, interface)
22
+ # Return unless we have a valid interface attribute with a getter
23
+ return unless has_attribute?(key)
24
+ options = self.class.attributes[key.to_sym]
25
+ return if options.has_key?(:property) && !options[:property]
26
+ return if options.has_key?(:version) && !version_match?(options[:version], VirtualBox.version)
27
+ getter = options[:property] || options[:property_getter] || key.to_sym
28
+ return unless getter
29
+
30
+ # Convert the getter to a proc and call it
31
+ getter = spec_to_proc(getter)
32
+ write_attribute(key, getter.call(self, interface, key))
33
+ end
34
+
35
+ # Saves all the attributes which have an interface setter.
36
+ def save_interface_attributes(interface)
37
+ self.class.attributes.each do |key, options|
38
+ save_interface_attribute(key, interface)
39
+ end
40
+ end
41
+
42
+ # Saves a single interface attribute
43
+ #
44
+ # @param [Symbol] key The attribute to write
45
+ # @param [VirtualBox::COM::Interface] interface The interface
46
+ # @param [Object] value The value to write
47
+ def save_interface_attribute(key, interface)
48
+ # Return unless we have a valid interface attribute with a setter
49
+ return unless has_attribute?(key)
50
+ options = self.class.attributes[key.to_sym]
51
+ return if options[:readonly]
52
+ return if options.has_key?(:property) && !options[:property]
53
+ return if options.has_key?(:version) && !version_match?(options[:version], VirtualBox.version)
54
+
55
+ setter = options[:property] || options[:property_setter] || "#{key}=".to_sym
56
+ return unless setter
57
+
58
+ # Convert the setter to a proc and call it
59
+ setter = spec_to_proc(setter)
60
+ setter.call(self, interface, key, read_attribute(key))
61
+ end
62
+
63
+ # Converts a getter/setter specification to a Proc which can be called
64
+ # to obtain or set a value. There are multiple ways to specify the getter
65
+ # and/or setter of an interface attribute:
66
+ #
67
+ # ## Symbol
68
+ #
69
+ # A symbol represents a method to call on the interface. An example of the
70
+ # declaration and resulting method call are shown below:
71
+ #
72
+ # attribute :foo, :property_getter => :get_foo
73
+ #
74
+ # Converts to:
75
+ #
76
+ # interface.get_foo
77
+ #
78
+ # ## Proc
79
+ #
80
+ # A proc is called with the interface and it is expected to return the value
81
+ # for a getter. For a setter, the interface and the value is sent in as
82
+ # parameters to the Proc.
83
+ #
84
+ # attribute :foo, :property_getter => Proc.new { |i| i.get_foo }
85
+ #
86
+ def spec_to_proc(spec)
87
+ # Return the spec as-is if its a proc
88
+ return spec if spec.is_a?(Proc)
89
+
90
+ if spec.is_a?(Symbol)
91
+ # For symbols, wrap up a method send in a Proc and return
92
+ # that
93
+ return Proc.new { |this, m, key, *args| m.send(spec, *args) }
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,332 @@
1
+ require 'virtualbox/abstract_model/version_matcher'
2
+
3
+ module VirtualBox
4
+ class AbstractModel
5
+ # Provides simple relationship features to any class. These relationships
6
+ # can be anything, since this module makes no assumptions and doesn't
7
+ # differentiate between "has many" or "belongs to" or any of that.
8
+ #
9
+ # The way it works is simple:
10
+ #
11
+ # 1. Relationships are defined with a relationship name and a
12
+ # class of the relationship objects.
13
+ # 2. When {#populate_relationships} is called, `populate_relationship` is
14
+ # called on each relationship class (example: {StorageController.populate_relationship}).
15
+ # This is expected to return the relationship, which can be any object.
16
+ # 3. When {#save_relationships} is called, `save_relationship` is
17
+ # called on each relationship class, which manages saving its own
18
+ # relationship.
19
+ # 4. When {#destroy_relationships} is called, `destroy_relationship` is
20
+ # called on each relationship class, which manages destroying
21
+ # its own relationship.
22
+ #
23
+ # Be sure to read {ClassMethods} for complete documentation of methods.
24
+ #
25
+ # # Defining Relationships
26
+ #
27
+ # Every relationship has two mandatory parameters: the name and the class.
28
+ #
29
+ # relationship :bacons, Bacon
30
+ #
31
+ # In this case, there is a relationship `bacons` which refers to the `Bacon`
32
+ # class.
33
+ #
34
+ # # Accessing Relationships
35
+ #
36
+ # Relatable offers up dynamically generated accessors for every relationship
37
+ # which simply returns the relationship data.
38
+ #
39
+ # relationship :bacons, Bacon
40
+ #
41
+ # # Accessing through an instance "instance"
42
+ # instance.bacons # => whatever Bacon.populate_relationship created
43
+ #
44
+ # # Settable Relationships
45
+ #
46
+ # It is often convenient that relationships become "settable." That is,
47
+ # for a relationship `foos`, there would exist a `foos=` method. This is
48
+ # possible by implementing the `set_relationship` method on the relationship
49
+ # class. Consider the following relationship:
50
+ #
51
+ # relationship :foos, Foo
52
+ #
53
+ # If `Foo` has the `set_relationship` method, then it will be called by
54
+ # `foos=`. It is expected to return the new value for the relationship. To
55
+ # facilitate this need, the `set_relationship` method is given three
56
+ # parameters: caller, old value, and new value. An example implementation,
57
+ # albeit a silly one, is below:
58
+ #
59
+ # class Foo
60
+ # def self.set_relationship(caller, old_value, new_value)
61
+ # return "Changed to: #{new_value}"
62
+ # end
63
+ # end
64
+ #
65
+ # In this case, the following behavior would occur:
66
+ #
67
+ # instance.foos # => assume "foo"
68
+ # instance.foos = "bar"
69
+ # instance.foos # => "Changed to: bar"
70
+ #
71
+ # If the relationship class _does not implement_ the `set_relationship`
72
+ # method, then a {Exceptions::NonSettableRelationshipException} will be raised if
73
+ # a user attempts to set that relationship.
74
+ #
75
+ # # Dependent Relationships
76
+ #
77
+ # By setting `:dependent => :destroy` on relationships, {AbstractModel}
78
+ # will automatically call {#destroy_relationships} when {AbstractModel#destroy}
79
+ # is called.
80
+ #
81
+ # This is not a feature built-in to Relatable but figured it should be
82
+ # mentioned here.
83
+ #
84
+ # # Lazy Relationships
85
+ #
86
+ # Often, relationships are pretty heavy things to load. Data may have to be
87
+ # retrieved, classes instantiated, etc. If a class has many relationships, or
88
+ # many relationships within many relationships, the time and memory required
89
+ # for relationships really begins to add up. To address this issue, _lazy relationships_
90
+ # are available. Lazy relationships defer loading their content until the
91
+ # last possible moment, or rather, when a user requests the data. By specifing
92
+ # the `:lazy => true` option to relationships, relationships will not be loaded
93
+ # immediately. Instead, when they're first requested, `load_relationship` will
94
+ # be called on the model, with the name of the relationship given as a
95
+ # parameter. It is up to this method to call {#populate_relationship} at some
96
+ # point with the data to setup the relationship. An example follows:
97
+ #
98
+ # class SomeModel
99
+ # include VirtualBox::AbstractModel::Relatable
100
+ #
101
+ # relationship :foos, Foo, :lazy => true
102
+ #
103
+ # def load_relationship(name)
104
+ # if name == :foos
105
+ # populate_relationship(name, get_data_for_a_long_time)
106
+ # end
107
+ # end
108
+ # end
109
+ #
110
+ # Using the above class, we can use it like so:
111
+ #
112
+ # model = SomeModel.new
113
+ #
114
+ # # This initial load takes awhile as it loads...
115
+ # model.foos
116
+ #
117
+ # # Instant! (Just a hash lookup. No load necessary)
118
+ # model.foos
119
+ #
120
+ # One catch: If a model attempts to {#destroy_relationship destroy} a lazy
121
+ # relationship, it will first load the relationship, since destroy typically
122
+ # depends on some data of the relationship.
123
+ module Relatable
124
+ include VersionMatcher
125
+
126
+ def self.included(base)
127
+ base.extend ClassMethods
128
+ end
129
+
130
+ module ClassMethods
131
+ # Define a relationship. The name and class must be specified. This
132
+ # class will be used to call the `populate_relationship,
133
+ # `save_relationship`, etc. methods.
134
+ #
135
+ # @param [Symbol] name Relationship name. This will also be used for
136
+ # the dynamically generated accessor.
137
+ # @param [Class] klass Class of the relationship.
138
+ # @option options [Symbol] :dependent (nil) - If set to `:destroy`
139
+ # {AbstractModel#destroy} will propagate through to relationships.
140
+ def relationship(name, klass, options = {})
141
+ name = name.to_sym
142
+
143
+ relationships << [name, { :klass => klass }.merge(options)]
144
+
145
+ # Define the method to read the relationship
146
+ define_method(name) { read_relationship(name) }
147
+
148
+ # Define the method to set the relationship
149
+ define_method("#{name}=") { |*args| set_relationship(name, *args) }
150
+ end
151
+
152
+ # Returns a hash of all the relationships.
153
+ #
154
+ # @return [Hash]
155
+ def relationships_hash
156
+ Hash[*relationships.flatten]
157
+ end
158
+
159
+ # Returns an array of the relationships in order of being added.
160
+ #
161
+ # @return [Array]
162
+ def relationships
163
+ @relationships ||= []
164
+ end
165
+
166
+ # Returns a boolean of whether a relationship exists.
167
+ #
168
+ # @return [Boolean]
169
+ def has_relationship?(name)
170
+ !!relationships.detect { |r| r[0] == name }
171
+ end
172
+
173
+ # Used to propagate relationships to subclasses. This method makes sure that
174
+ # subclasses of a class with {Relatable} included will inherit the
175
+ # relationships as well, which would be the expected behaviour.
176
+ def inherited(subclass)
177
+ super rescue NoMethodError
178
+
179
+ relationships.each do |name, options|
180
+ subclass.relationship(name, nil, options)
181
+ end
182
+ end
183
+ end
184
+
185
+ # Reads a relationship. This is equivalent to {Attributable#read_attribute},
186
+ # but for relationships.
187
+ def read_relationship(name)
188
+ options = self.class.relationships_hash[name.to_sym]
189
+ assert_version_match(options[:version], VirtualBox.version) if options[:version]
190
+
191
+ if lazy_relationship?(name) && !loaded_relationship?(name)
192
+ load_relationship(name)
193
+ end
194
+
195
+ relationship_data[name.to_sym]
196
+ end
197
+
198
+ # Saves the model, calls save_relationship on all relations. It is up to
199
+ # the relation to determine whether anything changed, etc. Simply
200
+ # calls `save_relationship` on each relationship class passing in the
201
+ # following parameters:
202
+ #
203
+ # * **caller** - The class which is calling save
204
+ # * **data** - The data associated with the relationship
205
+ #
206
+ # In addition to those two args, any arbitrary args may be tacked on to the
207
+ # end and they'll be pushed through to the `save_relationship` method.
208
+ def save_relationships(*args)
209
+ # Can't use `all?` here since it short circuits
210
+ results = self.class.relationships.collect do |data|
211
+ name, options = data
212
+ !!save_relationship(name, *args)
213
+ end
214
+
215
+ !results.include?(false)
216
+ end
217
+
218
+ # Saves a single relationship. It is up to the relationship class to
219
+ # determine whether anything changed and how saving is implemented. Simply
220
+ # calls `save_relationship` on the relationship class.
221
+ def save_relationship(name, *args)
222
+ options = self.class.relationships_hash[name]
223
+ return if lazy_relationship?(name) && !loaded_relationship?(name)
224
+ return if options[:version] && !version_match?(options[:version], VirtualBox.version)
225
+ return unless relationship_class(name).respond_to?(:save_relationship)
226
+ relationship_class(name).save_relationship(self, relationship_data[name], *args)
227
+ end
228
+
229
+ # The equivalent to {Attributable#populate_attributes}, but with
230
+ # relationships.
231
+ def populate_relationships(data)
232
+ self.class.relationships.each do |name, options|
233
+ populate_relationship(name, data) unless lazy_relationship?(name)
234
+ end
235
+ end
236
+
237
+ # Populate a single relationship.
238
+ def populate_relationship(name, data)
239
+ options = self.class.relationships_hash[name]
240
+ return unless relationship_class(name).respond_to?(:populate_relationship)
241
+ return if options[:version] && !version_match?(options[:version], VirtualBox.version)
242
+ relationship_data[name] = relationship_class(name).populate_relationship(self, data)
243
+ end
244
+
245
+ # Calls `destroy_relationship` on each of the relationships. Any
246
+ # arbitrary args may be added and they will be forarded to the
247
+ # relationship's `destroy_relationship` method.
248
+ def destroy_relationships(*args)
249
+ self.class.relationships.each do |name, options|
250
+ destroy_relationship(name, *args)
251
+ end
252
+ end
253
+
254
+ # Destroys only a single relationship. Any arbitrary args
255
+ # may be added to the end and they will be pushed through to
256
+ # the class's `destroy_relationship` method.
257
+ #
258
+ # @param [Symbol] name The name of the relationship
259
+ def destroy_relationship(name, *args)
260
+ options = self.class.relationships_hash[name]
261
+ return unless options && relationship_class(name).respond_to?(:destroy_relationship)
262
+
263
+ # Read relationship, which forces lazy relationships to load, which is
264
+ # probably necessary for destroying
265
+ read_relationship(name)
266
+
267
+ relationship_class(name).destroy_relationship(self, relationship_data[name], *args)
268
+ end
269
+
270
+ # Hash to data associated with relationships. You should instead
271
+ # use the accessors created by Relatable.
272
+ #
273
+ # @return [Hash]
274
+ def relationship_data
275
+ @relationship_data ||= {}
276
+ end
277
+
278
+ # Returns boolean denoting if a relationship exists.
279
+ #
280
+ # @return [Boolean]
281
+ def has_relationship?(key)
282
+ self.class.has_relationship?(key.to_sym)
283
+ end
284
+
285
+ # Returns boolean denoting if a relationship is to be lazy loaded.
286
+ #
287
+ # @return [Boolean]
288
+ def lazy_relationship?(key)
289
+ options = self.class.relationships_hash[key.to_sym]
290
+ !options.nil? && options[:lazy]
291
+ end
292
+
293
+ # Returns boolean denoting if a relationship has been loaded.
294
+ def loaded_relationship?(key)
295
+ relationship_data.has_key?(key)
296
+ end
297
+
298
+ # Returns the class for a given relationship. This method handles converting
299
+ # a string/symbol into the proper class.
300
+ #
301
+ # @return [Class]
302
+ def relationship_class(key)
303
+ options = self.class.relationships_hash[key.to_sym]
304
+ klass = options[:klass]
305
+ klass = Object.module_eval("#{klass}") unless klass.is_a?(Class)
306
+ klass
307
+ end
308
+
309
+ # Sets a relationship to the given value. This is not guaranteed to
310
+ # do anything, since "set_relationship" will be called on the class
311
+ # that the relationship is associated with and its expected to return
312
+ # the resulting relationship to set.
313
+ #
314
+ # If the relationship class doesn't respond to the set_relationship
315
+ # method, then an exception {Exceptions::NonSettableRelationshipException} will
316
+ # be raised.
317
+ #
318
+ # This method is called by the "magic" method of `relationship=`.
319
+ #
320
+ # @param [Symbol] key Relationship key.
321
+ # @param [Object] value The new value of the relationship.
322
+ def set_relationship(key, value)
323
+ key = key.to_sym
324
+ relationship = self.class.relationships_hash[key]
325
+ return unless relationship
326
+
327
+ raise Exceptions::NonSettableRelationshipException.new unless relationship_class(key).respond_to?(:set_relationship)
328
+ relationship_data[key] = relationship_class(key).set_relationship(self, relationship_data[key], value)
329
+ end
330
+ end
331
+ end
332
+ end
@@ -0,0 +1,167 @@
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
+ # Returns the errors on a model. The structure of this is a hash, keyed
7
+ # by the field name. The value of each member in the hash is an array of
8
+ # error messages.
9
+ #
10
+ # @return [Hash]
11
+ def errors
12
+ @errors ||= {}
13
+ end
14
+
15
+ # Returns the errors on a specific field. This returns nil if there are
16
+ # no errors, otherwise it returns an array of error messages.
17
+ def errors_on(field)
18
+ @errors[field.to_sym]
19
+ end
20
+
21
+ # Adds an error to a field. The error is a message.
22
+ def add_error(field, error)
23
+ errors[field] ||= []
24
+ errors[field].push(error)
25
+ end
26
+
27
+ # Clears all the errors from a model.
28
+ def clear_errors
29
+ @errors = {}
30
+ end
31
+
32
+ def full_error_messages
33
+ full_error_messages = Array.new
34
+ errors.each do |field_name, messages|
35
+ messages.each do |message|
36
+ human_field_name = field_name.to_s.gsub('_', ' ').capitalize
37
+ full_error_messages << "#{human_field_name} #{message}"
38
+ end
39
+ end
40
+ full_error_messages
41
+ end
42
+
43
+ # This method calls the validate method on the model (which any subclass
44
+ # is expected to implement), and then checks that the validations didn't
45
+ # add any errors.
46
+ #
47
+ # @return [Boolean]
48
+ def valid?
49
+ validate
50
+ errors.empty?
51
+ end
52
+
53
+ # Subclasses should override this method. Validation can be done any
54
+ # way an implementer feels. Helper methods such as {#validates_presence_of},
55
+ # {#validates_inclusion_of}, etc. exist, but they're use isn't required.
56
+ # {#add_error} can be used to add an error to any field. By convention
57
+ # this method should return `true` or `false` to signal any errors.
58
+ #
59
+ # @return [Boolean]
60
+ def validate
61
+ true
62
+ end
63
+
64
+ # Validates the presence (non-emptiness) of a field or fields. This
65
+ # validation fails if the specified fields are either blank ("") or
66
+ # nil.
67
+ #
68
+ # Additionally, a custom error message can be specified:
69
+ #
70
+ # validates_presence_of :foo, :bar
71
+ # validates_presence_of :baz, :message => "must not be blank!"
72
+ #
73
+ # @return [Boolean]
74
+ def validates_presence_of(*fields)
75
+ options = __validates_extract_options(fields, {
76
+ :message => "can't be blank."
77
+ })
78
+
79
+ fields.collect { |field|
80
+ value = send(field)
81
+ if value.nil? || value.to_s.empty?
82
+ add_error(field, options[:message])
83
+ false
84
+ else
85
+ true
86
+ end
87
+ }.compact.all? { |v| v == true }
88
+ end
89
+
90
+ # Validates the format of a field with a given regular expression.
91
+ #
92
+ # validates_format_of :foo, :with => /\d+/
93
+ #
94
+ def validates_format_of(*fields)
95
+ options = __validates_extract_options(fields, {
96
+ :with => nil,
97
+ :message => "is not properly formatted."
98
+ })
99
+
100
+ fields.collect { |field|
101
+ value = send(field)
102
+ # Use validates_presence_of if you need it to be set
103
+ next if value.nil? || value.to_s.empty?
104
+ if options[:with] && value =~ options[:with]
105
+ true
106
+ else
107
+ add_error(field, options[:message])
108
+ false
109
+ end
110
+ }.compact.all? { |v| v == true }
111
+ end
112
+
113
+ # Validates the numericality of a specific field.
114
+ #
115
+ # validates_numericality_of :field
116
+ #
117
+ def validates_numericality_of(*fields)
118
+ options = __validates_extract_options(fields, {
119
+ :message => "is not a number."
120
+ })
121
+
122
+ fields.collect { |field|
123
+ value = send(field)
124
+ # Use validates_presence_of if you need it to be set
125
+ next if value.nil? || value.to_s.empty?
126
+ if value.is_a?(Numeric) && value.to_s =~ /^\d$/
127
+ true
128
+ else
129
+ add_error(field, options[:message])
130
+ false
131
+ end
132
+ }.compact.all? { |v| v == true }
133
+ end
134
+
135
+ # Validates that a field's value is within a specific range,
136
+ # array, etc.
137
+ #
138
+ # validates_inclusion_of :foo, :in => [1,2,3]
139
+ # validates_inclusion_of :bar, :in => (1..6)
140
+ #
141
+ def validates_inclusion_of(*fields)
142
+ options = __validates_extract_options(fields, {
143
+ :in => nil,
144
+ :message => "value %s is not included in the list."
145
+ })
146
+
147
+ fields.collect { |field|
148
+ value = send(field)
149
+ # Use validates_presence_of if you need it to be set
150
+ return if value.nil? || value.to_s.empty?
151
+ if options[:in] && options[:in].include?(value)
152
+ true
153
+ else
154
+ message = options[:message] % value
155
+ add_error(field, message)
156
+ false
157
+ end
158
+ }.compact.all? { |v| v == true }
159
+ end
160
+
161
+ # Internal method. Should never be called.
162
+ def __validates_extract_options(fields, defaults)
163
+ defaults.merge(fields.last.is_a?(Hash) ? fields.pop : {})
164
+ end
165
+ end
166
+ end
167
+ end