# Getting Started with the VirtualBox Gem
* [Basic Conventions](#basic-conventions)
* [Finding Models](#bc-finding-models)
* [Accessing Models](#bc-accessing-models)
* [Modifying Models](#bc-modifying-models)
* [Saving Models](#bc-saving-models)
# Basic Conventions
The entire virtualbox library follows a few conventions to make sure
things work uniformly across the entire codebase, and so that nothing
should surprise any developers once they understand these conventions.
When browsing the documentation, you'll probably notice that a lot of the
classes inherit from {VirtualBox::AbstractModel}. This just means that all
these classes act the same way! Every {VirtualBox::AbstractModel AbstractModel}
shares the following behaviors:
* Finding
* Accessing
* Modifying
* Saving
These behaviors should be similar if not the exact same across all
virtualbox models. Each of these behaviors is covered below.
## Finding Models
All data models have a `find` or `all` method (or sometimes both!) These
methods do what you expect them to: `all` will return an array of all instances
of that model which is typically unordered. `find` will allow you to find a
specific instance of that model, typically by name or UUID. Below are a couple
examples of this.
### All
This example uses {VirtualBox::HardDrive}. As you can see, its just an
unmodified ruby `Array` which is returned by `all`. This can be used find,
sort, enumerate, etc.
drives = VirtualBox::HardDrive.all
puts "You have #{drives.length} hard drives!"
drives.each do |drive|
puts "Drive: #{drive.uuid}"
end
In the case that `all` returns an empty array, this simply means that none
of that model exist.
### Find
This example uses {VirtualBox::VM}, which will probably be the most common
model you search for.
vm = VirtualBox::VM.find("MyVM")
puts "This VM has #{vm.memory} MB of RAM allocated to it."
Find can also be used with UUIDs:
vm = VirtualBox::VM.find("3d0f87b4-50f7-4fc5-ad89-93375b1b32a3")
puts "This VM's name is: #{vm.name}"
When a find fails, it will return `nil`.
## Accessing Models
Every model has an _attribute list_ associated with it. These attributes are
what can be accessed on the model via the typical ruby attribute accessing
syntax with the `.` (dot) operator. Because these methods are generated
dynamically, they don't show up as methods in the documentation. Because of this,
attributes are listed for every model in their overviews. For examples, see the
overviews of {VirtualBox::VM}, {VirtualBox::HardDrive}, etc.
In addition to an attribute list, many models also have _relationships_.
Relationships are, for our purposes, similar enough to attributes that they
can be treated the same. Relationship accessing methods are also dynamically
generated, so they are listed within the overviews of the models as well (if they
have any). Relationships allow two models to show that they are connected in some
way, and can therefore be accessed through each other.
### Attributes
Reading attributes is simple. Let's use a {VirtualBox::VM} as an example:
vm = VirtualBox::VM.find("FooVM")
# Accessing attributes:
vm.memory
vm.name
vm.boot1
vm.ioapic
### Relationships
Relationships are read the exact same way as attributes. Again using a
{VirtualBox::VM} as an example:
vm = VirtualBox::VM.find("FooVM")
# storage_controllers is a relationship containing an array of all the
# storage controllers on this VM
vm.storage_controllers.each do |sc|
puts "Storage Controller: #{sc.uuid}"
end
The difference from an attribute is that while attributes are typically ruby
primitives such as `String` or `Boolean`, relationship objects are always other
virtualbox models such as {VirtualBox::StorageController}.
## Modifying Models
In addition to simply reading attributes and relationships, most can be modified
as well. I say "most" because some attributes are `readonly` and some relationships
simply don't support being directly modified (though their objects may, I'll get to
this in a moment). By looking at the attribute list it is easy to spot a readonly
attribute, which will have the `:readonly` option set to `true`. Below is an example
of what you might see in the overview of some model:
attribute :uuid, :readonly => true
In the above case, you could read the `uuid` attribute as normal, but it wouldn't support
modification (and you'll simply get a `NoMethodError` if you try to set it).
Relationships are a little bit trickier, since when discussing modifying a relationship,
it could either be taken to mean the items _in_ the relationship, or the relationship
itself. A good rule of thumb, assuming there exists a relationship `foos`,is if you ever
want to do `object.foos =` something, then you're _modifying the relationship_ and _not_
the objects. But if you ever do `object.foos[0].destroy`, then you're _modifying the
relationship objects_ and _not_ the relationship itself.
### Attributes
Attributes which support modification are modified like standard ruby attributes. The
following example uses {VirtualBox::HardDrive}:
hd = VirtualBox::HardDrive.new
hd.size = 2000 # megabytes
hd.format = "VMDK"
As you can see, there is nothing sneaky going on here, and does what you expect.
### Relationships
Modifying relationships, on the other hand, is a little different. If the model supports
modifying the relationship (which it'll note in its respective documentation), then
you can set it just like an attribute. Below, we use {VirtualBox::AttachedDevice} as
an example:
ad = VirtualBox::AttachedDevice.new
# Attached devices have an image relationship
ad.image = VirtualBox::DVD.empty_drive
If a relationship doesn't support setting it, it will raise a {VirtualBox::Exceptions::NonSettableRelationshipException}.
**Note**: Below is an example of modifying a relationship object, rather than a
relationship itself. The example below uses {VirtualBox::VM}.
vm = VirtualBox::VM.find("FooVM")
vm.storage_controllers[0].name = "Foo Controller"
## Saving Models
Saving models is _really_ easy: you simply call `save`. That's all! Well, there are
some subtleties, but that's the basic idea. `save` will typically **also save relationships**
so if you modify a relationship object or relationship itself, calling `save` on the
parent object will typically save the relationships as well. `save` always returns
`true` or `false` depending on whether the operation was a success or not. If you'd like
instead to know why a `save` failed, you can call the method with a `true` parameter
which sets `raise_errors` to `true` and will raise a {VirtualBox::Exceptions::CommandFailedException}
if there is a failure. The message on this object contains the reason.
Below is an example of saving a simple {VirtualBox::VM} object:
vm = VirtualBox::VM.find("FooVM")
# Double the memory
vm.memory = vm.memory.to_i * 2
# This will return true/false depending on success
vm.save
Below is an example where an exception will be raised if an error occurs:
vm = VirtualBox::VM.find("FooVM")
vm.memory = "INVALID"
# This will raise an exception, since the memory is invalid
vm.save(true)