# Nugrant [![Build Status](https://travis-ci.org/maoueh/nugrant.png)](https://travis-ci.org/maoueh/nugrant)

Nugrant is a library to easily handle parameters that need to be
injected into an application via different sources (system, user,
current, defaults).

But Nugrant is foremost a Vagrant plugin that will enhance
Vagrantfile to allow user specific configuration values. The plugin
will let users define a `.vagrantuser` file at different locations. This
file will contain parameters that will be injected into the Vagrantfile.

## Installation

### Library

If you would like to use Nugrant as a library, simply reference
it as a dependency of your application. Probably by adding it to
your `Gemfile` or your `.gemspec` file.

    nugrant ~> 1.0.1

### Vagrant

If you would like to use Nugrant as a Vagrant plugin, the
detailed installation steps are provided below. Without a
doubt, you need Vagrant installed for those steps to work ;)

First of all, Vagrant's plugin system is very well done and
Nugrant supports version `v1` (1.0.z branch, like 1.0.7) and
`v2` (1.y.z branch, like 1.1.3). However, the installation
procedure between the two versions is different.

To know which version you currently have installed, type
`vagrant -v` in a terminal.

#### Version 1.0.z (latest version tested 1.0.7)

In this version, there is two different ways to install Nugrant.
You can install it via Vagrant or directly via the system gem
container.

When you install via Vagrant, the main benefit is that
it's decoupled from other system gems. There is less
chance for this gem's dependencies, even if they are minimal,
to clash with gems already installed on your system. This is the
recommended installation method. To install, simply run in
a terminal:

    > vagrant gem install nugrant

If you prefer to install the gem in via the system gem
container, please use this command instead:

    > gem install nugrant

#### Version 1.y.z (latest version tested 1.1.3)

In those versions, probably until 2.y.z is out, there
is a new way to install and register plugin with the Vagrant
environment.

To install when using one of this versions, simply run in a
terminal:

    > vagrant plugin install nugrant

Since the plugin system has been completely rewritten in those
versions, it is not possible anymore to make the plugin available
within Vagrant when installing Nugrant in the system
gem container.

## Usage

Whether used as a library or a Vagrant plugin, Nugrant has some
common concepts that apply to both usages. The most important
one is the parameters hierarchy.

Nugrant can read parameters from various locations and will merge
them all together in a single set. Merging is done in a fairly
standard fashion.

Here the precedence rules that apply when merging parameters
from various location. List index indicate the priority of the
entry. Entry with lower number has lower priority (values at this
priority will be overridden by values defined on higher priorities).

 1. Defaults
 2. System
 3. User
 4. Current

In text, this means that current parameters overrides user
parameters, user parameters overrides system parameters and
finally system parameters overrides defaults parameters.

### Library

Using Nugrant as a library to handle parameters from various
location is really easy. Two main classes need to be handled.

First, you need to create a `Nugrant::Config` object. This
configuration holds the values that needs to be customized
by your own application. This includes the different parameters paths
and the format of the parameters file.

### Vagrant

All examples shown here are for Vagrant 1.1+. They have
been tested with Vagrant 1.2.2. Keep this in mind when
copying examples.

Let start with a small use case. Say the git repository you want
to share with your guest VM is not located under the root folder of
your `Vagrantfile`. That means you will need to specify
an absolute host path to share the folder on the guest VM.

Your `Vagrantfile` would look like this:

    Vagrant.configure("2") do |config|
      config.vm.box = "base"
      config.vm.synced_folder "/home/user/work/git", "/git"
    end

However, what happens when multiple developers
need to share the same `Vagrantfile`? This is the main
use case this plugin try to address.

When Vagrant starts, it loads all vagrant plugins it knows
about. If you installed the plugin with one of the two
methods we listed above, Vagrant will know about Nugrant
and will load it correctly.

To use the plugin, first create a YAML file named
`.vagrantuser` in the same folder where your `Vagrantfile` is
located. The file must be a valid YAML file:

    repository:
      project: "/home/user/work/git"

The configuration hierarchy you define in the `.vagrantuser` file
is imported into the `config` object of the `Vagrantfile`
under the key `user`. So, with the `.vagrantuser` file above, you
could have this `Vagrantfile` that abstract absolute paths.

    Vagrant.configure("2") do |config|
      config.vm.box = "base"
      config.vm.synced_folder config.user.repository.project, "/git"
    end

This way, paths can be customized by every developer. They just
have to add a `.vagrantuser` file where user specific configuration
values can be specified. The `.vagrantuser` should be ignored by you
version control system so it is to committed with the project.

Additionally, you can also have a `.vagrantuser` under your user home
directory. This way, you can set parameters that will be
available to all your `Vagrantfile'. The `.vagrantuser` located
within the same folder as the `Vagrantfile` file will overrides
parameters defined in the `.vagrantuser` file defined in the user
home directory.

For example, you have `.vagrantuser` file located at `~/.vagrantuser`
that has the following content:

    ssh_port: 2223
    repository:
      project: "/home/user/work/git"

And another `.vagrantuser` within the same folder as your `Vagrantfile`:

    ssh_port: 3332
    repository:
      personal: "/home/user/personal/git"

Then, the `Vagrantfile` could be defined like this:

    Vagrant.configure("2") do |config|
      config.ssh.port config.user.ssh_port

      config.vm.synced_folder config.user.repository.project, "/git"
      config.vm.synced_folder config.user.repository.personal, "/personal"
    end

That would be equivalent to:

    Vagrant.configure("2") do |config|
      config.ssh.port 3332

      config.vm.synced_folder "/home/user/work/git", "/git"
      config.vm.synced_folder "/home/user/personal/git", "/personal"
    end

As you can see, the parameters defined in the second `.vagrantuser` file
(the current one) overrides settings defined in the `.vagrantuser` found
in the home directory (the user one).

Here the list of locations where Nugrant looks for parameters:

 1. Defaults (via `config.user.defaults` in `Vagrantfile`)
 2. System (`/etc/.vagrantuser` on Unix, `%PROGRAMDATA%/.vagrantuser` or `%ALLUSERSPROFILE%/.vagrantuser` on Windows)
 3. Home (`~/.vagrantuser`)
 4. current (`.vagrantuser` within the same folder as the `Vagrantfile`)

### Paths

When you want to specify paths on, specially on Windows, it's probably
better to only use foward slash (`/`). The main reason for this is because
Ruby, which will be used at the end by Vagrant is able to deal with forward
slash even on Windows. This is great because with this, you can avoid
values escaping in YAML file. If you need to use backward slash (`\`), don't
forget to properly escape it!

    value: "C:/Users/user/work/git"
    value: "C:\\Users\\user\\work\\git"

### Parameters access

Parameters in the `Vagrantfile` can be retrieved via method call
of array access.

    config.user['repository']['project'] # Array access
    config.user.repository.project       # Method access

You can even mix the two if you want, but we do not recommend
it since its always better to be consistent:

    config.user['repository'].project # Mixed access
    config.user.repository['project'] # Mixed access

Only the root key, i.e. `config.user`, cannot be access with
both syntax, only the method syntax can be used since this
is not provided by this plugin but by Vagrant itself.

### Default values

When using parameters, it is often needed so set default values
for certain parameters so if the user does not define one, the
default value will be picked up.

For example, say you want a parameter that will hold the ssh
port of the vm. This parameter will be accessible via the
parameter `config.user.vm.ssh_port`.

You can use the following snippet directly within your Vagrantfile
to set a default value for this parameter:

    Vagrant.configure("2") do |config|
      config.user.defaults = {
        "vm" => {
          "ssh_port" => "3335"
        }
      }

      config.ssh.port config.user.vm.ssh_port
    end

With this Vagrantfile, the parameter `config.user.vm.ssh_port`
will default to `3335` in cases where it is not defined by the
user.

If the user decides to change it, he just has to set it in his
own `.vagrantuser` and it will override the default value defined
in the Vagrantfile.

### Vagrant commands

In this section, we describe the various vagrant commands defined
by this plugin that can be used to interact with it.

#### Parameters

This command will print the currently defined parameters at the
given location. All rules are respected when using this command.
It is usefull to see what parameters are available and what are
the current values of those parameters.

Usage:

    > vagrant user parameters
    ---
    config:
      user:
        chef:
          cookbooks_path: /Users/Chef/kitchen/cookbooks
          nodes_path: /Users/Chef/kitchen/nodes
          roles_path: /Users/Chef/kitchen/roles

## Contributing

You can contribute by filling issues when something goes
wrong or was not what you expected. I will do my best
to fix the issue either in the code or in the documentation,
where applicable.

You can also send pull requests for any feature or improvement
you think should be included in this plugin. I will evaluate
each of them and merge them as fast as possible.