adds parens

adds parens

# Batman.Model

For a general explanation of `Batman.Model` and how it works, see [the guide](/docs/models.html).

_Note_: This documentation uses the term _model_ to refer to the class `Model`
or a `Model` subclass, and the term _record_ to refer to one instance of a
model.

## @primaryKey : string

`primaryKey` is a class level configuration option to change which key batman.js uses as the primary key. Change the option using `set`, like so:

    test 'primary key can be set using @set', ->
      class Shop extends Batman.Model
        @set 'primaryKey', 'shop_id'

      equal Shop.get('primaryKey'), 'shop_id'

The `primaryKey` is what batman.js uses to compare instances to see if they represent the same domain-level object: if two records have the same value at the key specified by `primaryKey`, only one will be in the identity map. The key specified by `primaryKey` is also used by the associations system when determining if a record is related to another record, and by the remote storage adapters to generate URLs for records.

_Note_: The default primaryKey is 'id'.

## @storageKey : string

`storageKey` is a class level option which gives the storage adapters something to interpolate into their specific key generation schemes. In the case of `LocalStorage` or `SessionStorage` adapters, the `storageKey` defines what namespace to store this record under in the `localStorage` or `sessionStorage` host objects, and with the case of the `RestStorage` family of adapters, the `storageKey` assists in URL generation. See the documentation for the storage adapter of your choice for more information.

The default `storageKey` is `null`.

## @persist(mechanism : StorageAdapter) : StorageAdapter

`@persist` is how a `Model` subclass is told to persist itself by means of a `StorageAdapter`. `@persist` accepts either a `StorageAdapter` class or instance and will return either the instantiated class or the instance passed to it for further modification.

    test 'models can be told to persist via a storage adapter', ->
      class Shop extends Batman.Model
        @resourceName: 'shop'
        @persist TestStorageAdapter

      record = new Shop
      ok record.hasStorage()

    test '@persist returns the instantiated storage adapter', ->
      adapter = false
      class Shop extends Batman.Model
        @resourceName: 'shop'
        adapter = @persist TestStorageAdapter

      ok adapter instanceof Batman.StorageAdapter

    test '@persist accepts already instantiated storage adapters', ->
      adapter = new Batman.StorageAdapter
      adapter.someHandyConfigurationOption = true
      class Shop extends Batman.Model
        @resourceName: 'shop'
        @persist adapter

      record = new Shop
      ok record.hasStorage()

## @encode(keys...[, encoderObject : [Object|Function]])

`@encode` specifies a list of `keys` a model should expect from and send back to a storage adapter, and any transforms to apply to those attributes as they enter and exit the world of batman.js in the optional `encoderObject`.

The `encoderObject` should have an `encode` and/or a `decode` key which point to functions. The functions accept the "raw" data (the batman.js land value in the case of `encode`, and the backend land value in the case of `decode`), and should return the data suitable for the other side of the link. The functions should have the following signatures:

    encoderObject = {
      encode: (value, key, builtJSON, record) ->
      decode: (value, key, incomingJSON, outgoingObject, record) ->
    }

By default these functions are the identity functions. They apply no transformation. The arguments for `encode` functions are as follows:

 + `value` is the client side value of the `key` on the `record`
 + `key` is the key which the `value` is stored under on the `record`. This is useful when passing the same `encoderObject` which needs to pivot on what key is being encoded to different calls to `encode`.
 + `builtJSON` is the object which is modified by each encoder which will eventually be returned by `toJSON`. To send the server the encoded value under a different key than the `key`, modify this object by putting the value under the desired key, and return `undefined`.
 + `record` is the record on which `toJSON` has been called.

For `decode` functions:

 + `value` is the server side value of the `key` which will end up on the `record`.
 + `key` is the key which the `value` is stored under in the incoming JSON.
 + `incomingJSON` is the JSON which is being decoded into the `record`. This can be used to create compound key decoders.
 + `outgoingObject` is the object which is built up by the decoders and then `mixin`'d to the record.
 + `record` is the record on which `fromJSON` has been called.

The `encode` and `decode` keys can also be false to avoid using the default identity function encoder or decoder.

_Note_: `Batman.Model` subclasses have no encoders by default, except for one which automatically decodes the `primaryKey` of the model, which is usually `id`. To get any data into or out of your model, you must white-list the keys you expect from the server or storage attribute.

    test '@encode accepts a list of keys which are used during decoding', ->
      class Shop extends Batman.Model
        @resourceName: 'shop'
        @encode 'name', 'url', 'email', 'country'

      json = {name: "Snowdevil", url: "snowdevil.ca"}
      record = new Shop()
      record.fromJSON(json)
      equal record.get('name'), "Snowdevil"

    test '@encode accepts a list of keys which are used during encoding', ->
      class Shop extends Batman.Model
        @resourceName: 'shop'
        @encode 'name', 'url', 'email', 'country'

      record = new Shop(name: "Snowdevil", url: "snowdevil.ca")
      deepEqual record.toJSON(), {name: "Snowdevil", url: "snowdevil.ca"}

    test '@encode accepts custom encoders', ->
      class Shop extends Batman.Model
        @resourceName: 'shop'
        @encode 'name',
          encode: (name) -> name.toUpperCase()

      record = new Shop(name: "Snowdevil")
      deepEqual record.toJSON(), {name: "SNOWDEVIL"}

    test '@encode accepts custom decoders', ->
      class Shop extends Batman.Model
        @resourceName: 'shop'
        @encode 'name',
          decode: (name) -> name.replace('_', ' ')

      record = new Shop()
      record.fromJSON {name: "Snow_devil"}
      equal record.get('name'), "Snow devil"

    test '@encode can be passed an encoderObject with false to prevent the default encoder or decoder', ->
      class Shop extends Batman.Model
        @resourceName: 'shop'
        @encode 'name', {encode: false, decode: (x) -> x}
        @encode 'url'

      record = new Shop()
      record.fromJSON {name: "Snowdevil", url: "snowdevil.ca"}
      equal record.get('name'), 'Snowdevil'
      equal record.get('url'), "snowdevil.ca"
      deepEqual record.toJSON(), {url: "snowdevil.ca"}, 'The name key is absent because of encode: false'

Some more handy examples:

    test '@encode can be used to turn comma separated values into arrays', ->
      class Post extends Batman.Model
        @resourceName: 'post'
        @encode 'tags',
          decode: (string) -> string.split(', ')
          encode: (array) -> array.join(', ')

      record = new Post()
      record.fromJSON({tags: 'new, hot, cool'})
      deepEqual record.get('tags'), ['new', 'hot', 'cool']
      deepEqual record.toJSON(), {tags: 'new, hot, cool'}

    test '@encode can be used to turn arrays into sets', ->
      class Post extends Batman.Model
        @resourceName: 'post'
        @encode 'tags',
          decode: (array) -> new Batman.Set(array...)
          encode: (set) -> set.toArray()

      record = new Post()
      record.fromJSON({tags: ['new', 'hot', 'cool']})
      ok record.get('tags') instanceof Batman.Set
      deepEqual record.toJSON(), {tags: ['new', 'hot', 'cool']}

## @validate(keys...[, options : [Object|Function]])

Validations allow a model to be marked as `valid` or `invalid` based on a set of programmatic rules. By validating a model's data before it gets to the server we can provide immediate feedback to the user about what they have entered and forgo waiting on a round trip to the server. `validate` allows the attachment of validations to the model on particular keys, where the validation is either a built in one (invoked by use of options to pass to them) or a custom one (invoked by use of a custom function as the second argument).

_Note_: Validation in batman.js is always asynchronous, despite the fact that none of the validations may use an asynchronous operation to check for validity. This is so that the API is consistent regardless of the validations used.

Built in validators are attached by calling `@validate` with options designating how to calculate the validity of the key:

    test '@validate accepts options to check for validity', ->
      QUnit.expect(0)
      class Post extends Batman.Model
        @resourceName: 'post'
        @validate 'title', 'body', {presence: true}

The built in validation options are listed below:

 + `presence : boolean`: Assert that the string value is existent (not undefined or null) and has length greather than 0.
 + `numeric : true`: Assert that the value is or can be coerced into a number using `parseFloat`.
 + `greaterThan : number`: Assert that the value is greater than the given number.
 + `greaterThanOrEqualTo : number`: Assert that the value is greater than or equal to the given number.
 + `equalTo : number`: Assert that the value is equal to the given number.
 + `lessThan : number`: Assert that the value is less than the given number.
 + `lessThanOrEqualTo : number`: Assert that the value is less than or equal to the given number.
 + `minLength : number`: Assert that the value's `length` property is greater than the given number.
 + `maxLength : number`: Assert that the value's `length` property is less than the given number.
 + `length : number`: Assert that the value's `length` property is exactly the given number.
 + `lengthWithin : [number, number]` or `lengthIn : [number, number]`: Assert that the value's `length` property is within the ranger specified by the given array of two numbers, where the first number is the lower bound and the second number is the upper bound.
 + `inclusion : in : [list, of, acceptable, values]`: Assert that the value is equal to one of the values in an array.
 + `exclusion : in : [list, of, unacceptable, values]`: Assert that the value is not equal to any of the values in an array.

Custom validators should have the signature `(errors, record, key, callback)`. The arguments are as follows:

 + `errors`: an `ErrorsSet` instance which expects to have `add` called on it to add errors to the model
 + `record`: the record being validated
 + `key`: the key to which the validation has been attached
 + `callback`: a function to call once validation has been completed. Calling this function is __mandatory__.

See [`Model::validate`](/docs/api/batman.model.html#prototype_function_validate) for information on how to get a particular record's validity.

## @loaded : Set

The `loaded` set is available on every model class and holds every model instance seen by the system in order to function as an identity map. Successfully loading or saving individual records or batches of records will result in those records being added to the `loaded` set. Destroying instances will remove records from the identity set.

    test 'the loaded set stores all records seen', ->
      class Post extends Batman.Model
        @resourceName: 'post'
        @persist TestStorageAdapter
        @encode 'name'

      ok Post.get('loaded') instanceof Batman.Set
      equal Post.get('loaded.length'), 0
      post = new Post()
      post.save()
      equal Post.get('loaded.length'), 1

    test 'the loaded adds new records caused by loads and removes records caused by destroys', ->
      class Post extends Batman.Model
        @resourceName: 'post'
        @encode 'name'

      adapter = new TestStorageAdapter(Post)
      adapter.storage =
          'posts1': {name: "One", id:1}
          'posts2': {name: "Two", id:2}

      Post.persist(adapter)
      Post.load()
      equal Post.get('loaded.length'), 2
      post = false
      Post.find(1, (err, result) -> post = result)
      post.destroy()
      equal Post.get('loaded.length'), 1

## @all : Set

The `all` set is an alias to the `loaded` set but with an added implicit `load` on the model. `Model.get('all')` will synchronously return the `loaded` set and asynchronously call `Model.load()` without options to load a batch of records and populate the set originally returned (the `loaded` set) with the records returned by the server.

_Note_: The notion of "all the records" is relative only to the client. It completely depends on the storage adapter in use and any backends which they may contact to determine what comes back during a `Model.load`. This means that if for example your API paginates records, the set found in `all` may hold on the first 50 records instead of the entire backend set.

`all` is useful for listing every instance of a model in a view, and since the `loaded` set will change when the `load` returns, it can be safely bound to.

    asyncTest 'the all set asynchronously fetches records when gotten', ->
      class Post extends Batman.Model
        @resourceName: 'post'
        @encode 'name'

      adapter = new AsyncTestStorageAdapter(Post)
      adapter.storage =
          'posts1': {name: "One", id:1}
          'posts2': {name: "Two", id:2}

      Post.persist(adapter)
      equal Post.get('all.length'), 0, "The synchronously returned set is empty"
      delay ->
        equal Post.get('all.length'), 2, "After the async load the set is populated"

## @clear() : Set

`Model.clear()` empties that `Model`'s identity map. This is useful for tests and other unnatural situations where records new to the system are guaranteed to be as such.

    test 'clearing a model removes all records from the identity map', ->
      class Post extends Batman.Model
        @resourceName: 'post'
        @encode 'name'

      adapter = new TestStorageAdapter(Post)
      adapter.storage =
          'posts1': {name: "One", id:1}
          'posts2': {name: "Two", id:2}
      Post.persist(adapter)
      Post.load()
      equal Post.get('loaded.length'), 2
      Post.clear()
      equal Post.get('loaded.length'), 0, "After clear() the loaded set is empty"

## @find(id, callback : Function) : Model

`Model.find()` retrieves a record with the specified `id` from the storage adapter and calls back with an error if one occurred and the record if the operation was successful. `find` delegates to the storage adapter the `Model` has been `@persist`ed with, so it is up to the storage adapter's semantics to determine what type of errors may return and the timeline on which the callback may be called. The `callback` is a required function which should adopt the node style callback signature which accepts two arguments: an error, and the record asked for. `find` returns an "unloaded" record which, following the load completion, will be populated with the data from the storage adapter.

_Note_: `find` gives two results to calling code: one immediately, and one later. `find` returns a record synchronously as it is called and calls back with a record, and importantly these two records are __not__ guaranteed to be the same instance. This is because batman.js maps the identities of incoming and outgoing records such that there is only ever one canonical instance representing a record, which is useful so bindings are always bound to the same thing. In practice, this means that calling code should use the record `find` calls back with if anything is going to bind to that object, which is most of the time. The returned record however remains useful for state inspection and bookkeeping.

    asyncTest '@find calls back the requested model if no error occurs', ->
      class Post extends Batman.Model
        @resourceName: 'post'
        @encode 'name'
        @persist AsyncTestStorageAdapter,
          storage:
            'posts2': {name: "Two", id:2}

      post = Post.find 2, (err, result) ->
        throw err if err
        post = result
      equal post.get('name'), undefined
      delay ->
        equal post.get('name'), "Two"

_Note_: `find` must be passed a callback function. This is for two reasons: calling code must be aware that `find`'s return value is not necessarily the canonical instance, and calling code must be able to handle errors.

    asyncTest '@find calls back with the error if an error occurs', ->
      class Post extends Batman.Model
        @resourceName: 'post'
        @encode 'name'
        @persist AsyncTestStorageAdapter

      error = false
      post = Post.find 3, (err, result) ->
        error = err
      delay ->
        ok error instanceof Error

## @load(options = {}, callback : Function)

`Model.load()` retrieves an array of records according to the given `options` from the storage adapter and calls back with an error if one occurred and the set of records if the operation was successful. `load` delegates to the storage adapter the `Model` has been `@persist`ed with, so it is up to the storage adapter's semantics to determine what the options do, what kind of errors may arise, and the timeline on which the callback may be called. The `callback` is a required function which should adopt the node style callback signature which accepts two arguments, an error, and the array of records. `load` returns undefined.

For the two main `StorageAdapter`s batman.js provides, the `options` do different things:

- For `Batman.LocalStorage`, `options` act as a filter. The adapter will scan all the records in `localStorage` and return only those records which match all the key/value pairs given in the options.
- For `Batman.RestStorage`, `options` are serialized into query parameters on the `GET` request.

It accepts a callback with two arguments: any error that occurred, and an array of loaded records.

    asyncTest '@load calls back an array of records retrieved from the storage adapter', ->
      class Post extends Batman.Model
        @resourceName: 'post'
        @encode 'name'
        @persist TestStorageAdapter,
          storage:
            'posts1': {name: "One", id:1}
            'posts2': {name: "Two", id:2}

      posts = false
      Post.load (err, result) ->
        throw err if err
        posts = result

      delay ->
        equal posts.length, 2
        equal posts[0].get('name'), "One"

    asyncTest '@load calls back with an empty array if no records are found', ->
      class Post extends Batman.Model
        @resourceName: 'post'
        @encode 'name'
        @persist TestStorageAdapter, storage: []

      posts = false
      Post.load (err, result) ->
        throw err if err
        posts = result

      delay ->
        equal posts.length, 0

## @create(attributes = {}, callback) : Model

## @findOrCreate(attributes = {}, callback) : Model

## id : value

## dirtyKeys : Set

## ::%errors : Batman.ErrorsSet

`errors` is a `Batman.ErrorsSet`, which is simply a `Batman.Set` of [`Batman.ValidationError`](/docs/api/batman.validationerror.html)s present on the model instance.

- `user.get('errors')` returns the errors on the `user` record
- `user.get('errors.length')` returns the number of errors, total

You can also access the errors for a specific attribute of the record:

- `user.get('errors.email_address')` returns the errors on the `email_address` attribute
- `user.get('errors.email_address.length')` returns the number of errors on the `email_address` attribute

## constructor(idOrAttributes = {}) : Model

## ::isNew() : boolean
Returns whether or not the instance represents a record that hasn't yet been persisted to the server. The default implementation simply checks if `@get('id')` is undefined, but you could override this on your own models.

It is used to determine whether `record.save()` will perform a `create` action or a `save` action.

## updateAttributes(attributes) : Model

## toString() : string

## ::toJSON() : Object

Returns a JavaScript object containing the attributes of the record, using any specified encoders.

    test "toJSON returns a JavaScript object with the record's attributes", ->
      class Criminal extends Batman.Model
        @encode "name", "notorious"

      criminal = new Criminal(name: "Talia al Ghul", notorious: true)
      criminal_json = criminal.toJSON()
      equal criminal_json.name, "Talia al Ghul"
      equal criminal_json.notorious, true

## ::fromJSON() : Model

Reloads attributes of a record from a JavaScript object.

    test 'fromJSON overwrites existing attributes', ->
      class Criminal extends Batman.Model
        @encode "name", "notorious"

      criminal = new Criminal(name: "Dr. Jonathan Crane", notorious: false)
      new_params =
        name: "Scarecrow"
        notorious: true
      criminal.fromJSON(new_params)

      equal criminal.get("notorious"), true
      equal criminal.get("name"), "Scarecrow"

## toParam() : value

## state() : string

## hasStorage() : boolean

## ::load(options = {}, callback)
`Load` tries to read the record from its storage adapter. The options object will be passed to the storage adapter when it performs the `read` operation. The callback takes three parameters: error, the loaded record, and the environment. `Load`ing a record clears all errors on that record.

If the read operation fails or if the record is in a state which doesn't permit `load`, (for example, calling `load` on a deleted record) the callback will be invoked with an error.

## ::save(options = {}, callback)
`Save` [validates](http://localhost:4000/docs/api/batman.model.html#prototype_function_validate) the record, and if it passes, fires the corresponding storage operation (defined by the `Batman.StorageAdapter` passed to  [`@persist`](/docs/api/batman.model.html#class_function_persist)). When the storage operation is complete, the callback is invoked with two parameters: any JavaScript error and the record.

If the record [`isNew`](/docs/api/batman.model.html#prototype_function_isnew), `save` performs a `create` operation. Otherwise, it performs a `save` operation.

If the record is not valid, the [validation errors](/docs/api/batman.validationerror.html) will be passed to the first parameter of the callback function and the storage operation will not be performed.

## ::destroy(options = {}, callback)

`Destroy` fires the corresponding storage operation. The callback takes three arguments: JavaScript Error, the record, and the environment. If the operation is successful, the record is removed from its Model's [`loaded`](/docs/api/batman.model.html#class_function_loaded) set.

```coffeescript
criminal = new Criminal name: "The penguin"
criminal.destroy (err, record, env) ->
  if err
    console.log "Oh no! #{record.get('name')} is still on the loose!"
```

If the record's current lifecycle state doesn't allow the `destroy` action, the callback will be invoked with a `Batman.StateMachine.InvalidTransitionError`. For example, this could occur if `destroy` is called on an already-destroyed record.

## ::validate(callback)

`Model::validate` checks the model against the validations declared in the model definition (with [`Model@validate`](/docs/api/batman.model.html#class_function_validate)). This method accepts a callback with two arguments: any JS error that occured within the validator function, and the set of [`Batman.ValidationError`](/docs/api/batman.validationerror.html)s that the input generated.

For example:

    test "validate(callback) will call the callback only after all keys have been validated", ->
      class Product extends Batman.Model
        @validate 'name', 'price', presence: yes

      newProduct = new Product
      newProduct.validate (javascriptError, validationErrors) ->
        throw javascriptError if javascriptError
        equal validationErrors.length, 2
        equal newProduct.get('errors.length'), 2
        equal newProduct.get('errors.name.length'), 1
        equal newProduct.get('errors.price.length'), 1

# Batman.ValidationError

`Batman.ValidationError`s represent a failure for a model's field to to pass validation.
They are usually accessed by getting a model's [errors](/docs/api/batman.model.html#prototype_accessor_errors).

## ::%fullMessage

Returns the human-readable attribute name and the validation message:

    test "ValidationError should humanize attribute in the full message", ->
      error = new Batman.ValidationError("fooBarBaz", "isn't valid")
      equal error.get('fullMessage'), "Foo bar baz isn't valid"

# Batman.Model

For a general explanation of `Batman.Model` and how it works, see [the guide](/docs/models.html).

_Note_: This documentation uses the term _model_ to refer to the class `Model`
or a `Model` subclass, and the term _record_ to refer to one instance of a
model.

## @primaryKey : string

`primaryKey` is a class level configuration option to change which key batman.js uses as the primary key. Change the option using `set`, like so:

    test 'primary key can be set using @set', ->
      class Shop extends Batman.Model
        @set 'primaryKey', 'shop_id'

      equal Shop.get('primaryKey'), 'shop_id'

The `primaryKey` is what batman.js uses to compare instances to see if they represent the same domain-level object: if two records have the same value at the key specified by `primaryKey`, only one will be in the identity map. The key specified by `primaryKey` is also used by the associations system when determining if a record is related to another record, and by the remote storage adapters to generate URLs for records.

_Note_: The default primaryKey is 'id'.

## @storageKey : string

`storageKey` is a class level option which gives the storage adapters something to interpolate into their specific key generation schemes. In the case of `LocalStorage` or `SessionStorage` adapters, the `storageKey` defines what namespace to store this record under in the `localStorage` or `sessionStorage` host objects, and with the case of the `RestStorage` family of adapters, the `storageKey` assists in URL generation. See the documentation for the storage adapter of your choice for more information.

The default `storageKey` is `null`.

## @persist(mechanism : StorageAdapter) : StorageAdapter

`@persist` is how a `Model` subclass is told to persist itself by means of a `StorageAdapter`. `@persist` accepts either a `StorageAdapter` class or instance and will return either the instantiated class or the instance passed to it for further modification.

    test 'models can be told to persist via a storage adapter', ->
      class Shop extends Batman.Model
        @resourceName: 'shop'
        @persist TestStorageAdapter

      record = new Shop
      ok record.hasStorage()

    test '@persist returns the instantiated storage adapter', ->
      adapter = false
      class Shop extends Batman.Model
        @resourceName: 'shop'
        adapter = @persist TestStorageAdapter

      ok adapter instanceof Batman.StorageAdapter

    test '@persist accepts already instantiated storage adapters', ->
      adapter = new Batman.StorageAdapter
      adapter.someHandyConfigurationOption = true
      class Shop extends Batman.Model
        @resourceName: 'shop'
        @persist adapter

      record = new Shop
      ok record.hasStorage()

## @encode(keys...[, encoderObject : [Object|Function]])

`@encode` specifies a list of `keys` a model should expect from and send back to a storage adapter, and any transforms to apply to those attributes as they enter and exit the world of batman.js in the optional `encoderObject`.

The `encoderObject` should have an `encode` and/or a `decode` key which point to functions. The functions accept the "raw" data (the batman.js land value in the case of `encode`, and the backend land value in the case of `decode`), and should return the data suitable for the other side of the link. The functions should have the following signatures:

    encoderObject = {
      encode: (value, key, builtJSON, record) ->
      decode: (value, key, incomingJSON, outgoingObject, record) ->
    }

By default these functions are the identity functions. They apply no transformation. The arguments for `encode` functions are as follows:

 + `value` is the client side value of the `key` on the `record`
 + `key` is the key which the `value` is stored under on the `record`. This is useful when passing the same `encoderObject` which needs to pivot on what key is being encoded to different calls to `encode`.
 + `builtJSON` is the object which is modified by each encoder which will eventually be returned by `toJSON`. To send the server the encoded value under a different key than the `key`, modify this object by putting the value under the desired key, and return `undefined`.
 + `record` is the record on which `toJSON` has been called.

For `decode` functions:

 + `value` is the server side value of the `key` which will end up on the `record`.
 + `key` is the key which the `value` is stored under in the incoming JSON.
 + `incomingJSON` is the JSON which is being decoded into the `record`. This can be used to create compound key decoders.
 + `outgoingObject` is the object which is built up by the decoders and then `mixin`'d to the record.
 + `record` is the record on which `fromJSON` has been called.

The `encode` and `decode` keys can also be false to avoid using the default identity function encoder or decoder.

_Note_: `Batman.Model` subclasses have no encoders by default, except for one which automatically decodes the `primaryKey` of the model, which is usually `id`. To get any data into or out of your model, you must white-list the keys you expect from the server or storage attribute.

    test '@encode accepts a list of keys which are used during decoding', ->
      class Shop extends Batman.Model
        @resourceName: 'shop'
        @encode 'name', 'url', 'email', 'country'

      json = {name: "Snowdevil", url: "snowdevil.ca"}
      record = new Shop()
      record.fromJSON(json)
      equal record.get('name'), "Snowdevil"

    test '@encode accepts a list of keys which are used during encoding', ->
      class Shop extends Batman.Model
        @resourceName: 'shop'
        @encode 'name', 'url', 'email', 'country'

      record = new Shop(name: "Snowdevil", url: "snowdevil.ca")
      deepEqual record.toJSON(), {name: "Snowdevil", url: "snowdevil.ca"}

    test '@encode accepts custom encoders', ->
      class Shop extends Batman.Model
        @resourceName: 'shop'
        @encode 'name',
          encode: (name) -> name.toUpperCase()

      record = new Shop(name: "Snowdevil")
      deepEqual record.toJSON(), {name: "SNOWDEVIL"}

    test '@encode accepts custom decoders', ->
      class Shop extends Batman.Model
        @resourceName: 'shop'
        @encode 'name',
          decode: (name) -> name.replace('_', ' ')

      record = new Shop()
      record.fromJSON {name: "Snow_devil"}
      equal record.get('name'), "Snow devil"

    test '@encode can be passed an encoderObject with false to prevent the default encoder or decoder', ->
      class Shop extends Batman.Model
        @resourceName: 'shop'
        @encode 'name', {encode: false, decode: (x) -> x}
        @encode 'url'

      record = new Shop()
      record.fromJSON {name: "Snowdevil", url: "snowdevil.ca"}
      equal record.get('name'), 'Snowdevil'
      equal record.get('url'), "snowdevil.ca"
      deepEqual record.toJSON(), {url: "snowdevil.ca"}, 'The name key is absent because of encode: false'

Some more handy examples:

    test '@encode can be used to turn comma separated values into arrays', ->
      class Post extends Batman.Model
        @resourceName: 'post'
        @encode 'tags',
          decode: (string) -> string.split(', ')
          encode: (array) -> array.join(', ')

      record = new Post()
      record.fromJSON({tags: 'new, hot, cool'})
      deepEqual record.get('tags'), ['new', 'hot', 'cool']
      deepEqual record.toJSON(), {tags: 'new, hot, cool'}

    test '@encode can be used to turn arrays into sets', ->
      class Post extends Batman.Model
        @resourceName: 'post'
        @encode 'tags',
          decode: (array) -> new Batman.Set(array...)
          encode: (set) -> set.toArray()

      record = new Post()
      record.fromJSON({tags: ['new', 'hot', 'cool']})
      ok record.get('tags') instanceof Batman.Set
      deepEqual record.toJSON(), {tags: ['new', 'hot', 'cool']}

## @validate(keys...[, options : [Object|Function]])

Validations allow a model to be marked as `valid` or `invalid` based on a set of programmatic rules. By validating a model's data before it gets to the server we can provide immediate feedback to the user about what they have entered and forgo waiting on a round trip to the server. `validate` allows the attachment of validations to the model on particular keys, where the validation is either a built in one (invoked by use of options to pass to them) or a custom one (invoked by use of a custom function as the second argument).

_Note_: Validation in batman.js is always asynchronous, despite the fact that none of the validations may use an asynchronous operation to check for validity. This is so that the API is consistent regardless of the validations used.

Built in validators are attached by calling `@validate` with options designating how to calculate the validity of the key:

    test '@validate accepts options to check for validity', ->
      QUnit.expect(0)
      class Post extends Batman.Model
        @resourceName: 'post'
        @validate 'title', 'body', {presence: true}

The built in validation options are listed below:

 + `presence : boolean`: Assert that the string value is existent (not undefined or null) and has length greather than 0.
 + `numeric : true`: Assert that the value is or can be coerced into a number using `parseFloat`.
 + `greaterThan : number`: Assert that the value is greater than the given number.
 + `greaterThanOrEqualTo : number`: Assert that the value is greater than or equal to the given number.
 + `equalTo : number`: Assert that the value is equal to the given number.
 + `lessThan : number`: Assert that the value is less than the given number.
 + `lessThanOrEqualTo : number`: Assert that the value is less than or equal to the given number.
 + `minLength : number`: Assert that the value's `length` property is greater than the given number.
 + `maxLength : number`: Assert that the value's `length` property is less than the given number.
 + `length : number`: Assert that the value's `length` property is exactly the given number.
 + `lengthWithin : [number, number]` or `lengthIn : [number, number]`: Assert that the value's `length` property is within the ranger specified by the given array of two numbers, where the first number is the lower bound and the second number is the upper bound.
 + `inclusion : in : [list, of, acceptable, values]`: Assert that the value is equal to one of the values in an array.
 + `exclusion : in : [list, of, unacceptable, values]`: Assert that the value is not equal to any of the values in an array.

Custom validators should have the signature `(errors, record, key, callback)`. The arguments are as follows:

 + `errors`: an `ErrorsSet` instance which expects to have `add` called on it to add errors to the model
 + `record`: the record being validated
 + `key`: the key to which the validation has been attached
 + `callback`: a function to call once validation has been completed. Calling this function is __mandatory__.

See [`Model::validate`](/docs/api/batman.model.html#prototype_function_validate) for information on how to get a particular record's validity.

## @loaded : Set

The `loaded` set is available on every model class and holds every model instance seen by the system in order to function as an identity map. Successfully loading or saving individual records or batches of records will result in those records being added to the `loaded` set. Destroying instances will remove records from the identity set.

    test 'the loaded set stores all records seen', ->
      class Post extends Batman.Model
        @resourceName: 'post'
        @persist TestStorageAdapter
        @encode 'name'

      ok Post.get('loaded') instanceof Batman.Set
      equal Post.get('loaded.length'), 0
      post = new Post()
      post.save()
      equal Post.get('loaded.length'), 1

    test 'the loaded adds new records caused by loads and removes records caused by destroys', ->
      class Post extends Batman.Model
        @resourceName: 'post'
        @encode 'name'

      adapter = new TestStorageAdapter(Post)
      adapter.storage =
          'posts1': {name: "One", id:1}
          'posts2': {name: "Two", id:2}

      Post.persist(adapter)
      Post.load()
      equal Post.get('loaded.length'), 2
      post = false
      Post.find(1, (err, result) -> post = result)
      post.destroy()
      equal Post.get('loaded.length'), 1

## @all : Set

The `all` set is an alias to the `loaded` set but with an added implicit `load` on the model. `Model.get('all')` will synchronously return the `loaded` set and asynchronously call `Model.load()` without options to load a batch of records and populate the set originally returned (the `loaded` set) with the records returned by the server.

_Note_: The notion of "all the records" is relative only to the client. It completely depends on the storage adapter in use and any backends which they may contact to determine what comes back during a `Model.load`. This means that if for example your API paginates records, the set found in `all` may hold on the first 50 records instead of the entire backend set.

`all` is useful for listing every instance of a model in a view, and since the `loaded` set will change when the `load` returns, it can be safely bound to.

    asyncTest 'the all set asynchronously fetches records when gotten', ->
      class Post extends Batman.Model
        @resourceName: 'post'
        @encode 'name'

      adapter = new AsyncTestStorageAdapter(Post)
      adapter.storage =
          'posts1': {name: "One", id:1}
          'posts2': {name: "Two", id:2}

      Post.persist(adapter)
      equal Post.get('all.length'), 0, "The synchronously returned set is empty"
      delay ->
        equal Post.get('all.length'), 2, "After the async load the set is populated"

## @clear() : Set

`Model.clear()` empties that `Model`'s identity map. This is useful for tests and other unnatural situations where records new to the system are guaranteed to be as such.

    test 'clearing a model removes all records from the identity map', ->
      class Post extends Batman.Model
        @resourceName: 'post'
        @encode 'name'

      adapter = new TestStorageAdapter(Post)
      adapter.storage =
          'posts1': {name: "One", id:1}
          'posts2': {name: "Two", id:2}
      Post.persist(adapter)
      Post.load()
      equal Post.get('loaded.length'), 2
      Post.clear()
      equal Post.get('loaded.length'), 0, "After clear() the loaded set is empty"

## @find(id, callback : Function) : Model

`Model.find()` retrieves a record with the specified `id` from the storage adapter and calls back with an error if one occurred and the record if the operation was successful. `find` delegates to the storage adapter the `Model` has been `@persist`ed with, so it is up to the storage adapter's semantics to determine what type of errors may return and the timeline on which the callback may be called. The `callback` is a required function which should adopt the node style callback signature which accepts two arguments: an error, and the record asked for. `find` returns an "unloaded" record which, following the load completion, will be populated with the data from the storage adapter.

_Note_: `find` gives two results to calling code: one immediately, and one later. `find` returns a record synchronously as it is called and calls back with a record, and importantly these two records are __not__ guaranteed to be the same instance. This is because batman.js maps the identities of incoming and outgoing records such that there is only ever one canonical instance representing a record, which is useful so bindings are always bound to the same thing. In practice, this means that calling code should use the record `find` calls back with if anything is going to bind to that object, which is most of the time. The returned record however remains useful for state inspection and bookkeeping.

    asyncTest '@find calls back the requested model if no error occurs', ->
      class Post extends Batman.Model
        @resourceName: 'post'
        @encode 'name'
        @persist AsyncTestStorageAdapter,
          storage:
            'posts2': {name: "Two", id:2}

      post = Post.find 2, (err, result) ->
        throw err if err
        post = result
      equal post.get('name'), undefined
      delay ->
        equal post.get('name'), "Two"

_Note_: `find` must be passed a callback function. This is for two reasons: calling code must be aware that `find`'s return value is not necessarily the canonical instance, and calling code must be able to handle errors.

    asyncTest '@find calls back with the error if an error occurs', ->
      class Post extends Batman.Model
        @resourceName: 'post'
        @encode 'name'
        @persist AsyncTestStorageAdapter

      error = false
      post = Post.find 3, (err, result) ->
        error = err
      delay ->
        ok error instanceof Error

## @load(options = {}, callback : Function)

`Model.load()` retrieves an array of records according to the given `options` from the storage adapter and calls back with an error if one occurred and the set of records if the operation was successful. `load` delegates to the storage adapter the `Model` has been `@persist`ed with, so it is up to the storage adapter's semantics to determine what the options do, what kind of errors may arise, and the timeline on which the callback may be called. The `callback` is a required function which should adopt the node style callback signature which accepts two arguments, an error, and the array of records. `load` returns undefined.

For the two main `StorageAdapter`s batman.js provides, the `options` do different things:

- For `Batman.LocalStorage`, `options` act as a filter. The adapter will scan all the records in `localStorage` and return only those records which match all the key/value pairs given in the options.
- For `Batman.RestStorage`, `options` are serialized into query parameters on the `GET` request.

It accepts a callback with two arguments: any error that occurred, and an array of loaded records.

    asyncTest '@load calls back an array of records retrieved from the storage adapter', ->
      class Post extends Batman.Model
        @resourceName: 'post'
        @encode 'name'
        @persist TestStorageAdapter,
          storage:
            'posts1': {name: "One", id:1}
            'posts2': {name: "Two", id:2}

      posts = false
      Post.load (err, result) ->
        throw err if err
        posts = result

      delay ->
        equal posts.length, 2
        equal posts[0].get('name'), "One"

    asyncTest '@load calls back with an empty array if no records are found', ->
      class Post extends Batman.Model
        @resourceName: 'post'
        @encode 'name'
        @persist TestStorageAdapter, storage: []

      posts = false
      Post.load (err, result) ->
        throw err if err
        posts = result

      delay ->
        equal posts.length, 0

## @create(attributes = {}, callback) : Model

## @findOrCreate(attributes = {}, callback) : Model

## id : value

## dirtyKeys : Set

## ::%errors : Batman.ErrorsSet

`errors` is a `Batman.ErrorsSet`, which is simply a `Batman.Set` of [`Batman.ValidationError`](/docs/api/batman.validationerror.html)s present on the model instance.

- `user.get('errors')` returns the errors on the `user` record
- `user.get('errors.length')` returns the number of errors, total

You can also access the errors for a specific attribute of the record:

- `user.get('errors.email_address')` returns the errors on the `email_address` attribute
- `user.get('errors.email_address.length')` returns the number of errors on the `email_address` attribute

## constructor(idOrAttributes = {}) : Model

## ::isNew() : boolean
Returns whether or not the instance represents a record that hasn't yet been persisted to the server. The default implementation simply checks if `@get('id')` is undefined, but you could override this on your own models.

It is used to determine whether `record.save()` will perform a `create` action or a `save` action.

## updateAttributes(attributes) : Model

## toString() : string

## ::toJSON() : Object

Returns a JavaScript object containing the attributes of the record, using any specified encoders.

    test "toJSON returns a JavaScript object with the record's attributes", ->
      class Criminal extends Batman.Model
        @encode "name", "notorious"

      criminal = new Criminal name: "Talia al Ghul", notorious: true
      criminal_json = criminal.toJSON()
      equal criminal_json.name, "Talia al Ghul"
      equal criminal_json.notorious, true

## ::fromJSON() : Model

Reloads attributes of a record from a JavaScript object.

    test 'fromJSON overwrites existing attributes', ->
      class Criminal extends Batman.Model
        @encode "name", "notorious"

      criminal = new Criminal name: "Dr. Jonathan Crane", notorious: false
      new_params =
        name: "Scarecrow"
        notorious: true
      criminal.fromJSON(new_params)

      equal criminal.get("notorious"), true
      equal criminal.get("name"), "Scarecrow"

## toParam() : value

## state() : string

## hasStorage() : boolean

## ::load(options = {}, callback)
`Load` tries to read the record from its storage adapter. The options object will be passed to the storage adapter when it performs the `read` operation. The callback takes three parameters: error, the loaded record, and the environment. `Load`ing a record clears all errors on that record.

If the read operation fails or if the record is in a state which doesn't permit `load`, (for example, calling `load` on a deleted record) the callback will be invoked with an error.

## ::save(options = {}, callback)
`Save` [validates](http://localhost:4000/docs/api/batman.model.html#prototype_function_validate) the record, and if it passes, fires the corresponding storage operation (defined by the `Batman.StorageAdapter` passed to  [`@persist`](/docs/api/batman.model.html#class_function_persist)). When the storage operation is complete, the callback is invoked with two parameters: any JavaScript error and the record.

If the record [`isNew`](/docs/api/batman.model.html#prototype_function_isnew), `save` performs a `create` operation. Otherwise, it performs a `save` operation.

If the record is not valid, the [validation errors](/docs/api/batman.validationerror.html) will be passed to the first parameter of the callback function and the storage operation will not be performed.

## ::destroy(options = {}, callback)

`Destroy` fires the corresponding storage operation. The callback takes three arguments: JavaScript Error, the record, and the environment. If the operation is successful, the record is removed from its Model's [`loaded`](/docs/api/batman.model.html#class_function_loaded) set.

```coffeescript
criminal = new Criminal name: "The penguin"
criminal.destroy (err, record, env) ->
  if err
    console.log "Oh no! #{record.get('name')} is still on the loose!"
```

If the record's current lifecycle state doesn't allow the `destroy` action, the callback will be invoked with a `Batman.StateMachine.InvalidTransitionError`. For example, this could occur if `destroy` is called on an already-destroyed record.

## ::validate(callback)

`Model::validate` checks the model against the validations declared in the model definition (with [`Model@validate`](/docs/api/batman.model.html#class_function_validate)). This method accepts a callback with two arguments: any JS error that occured within the validator function, and the set of [`Batman.ValidationError`](/docs/api/batman.validationerror.html)s that the input generated.

For example:

    test "validate(callback) will call the callback only after all keys have been validated", ->
      class Product extends Batman.Model
        @validate 'name', 'price', presence: yes

      newProduct = new Product
      newProduct.validate (javascriptError, validationErrors) ->
        throw javascriptError if javascriptError
        equal validationErrors.length, 2
        equal newProduct.get('errors.length'), 2
        equal newProduct.get('errors.name.length'), 1
        equal newProduct.get('errors.price.length'), 1

# Batman.ValidationError

`Batman.ValidationError`s represent a failure for a model's field to to pass validation.
They are usually accessed by getting a model's [errors](/docs/api/batman.model.html#prototype_accessor_errors).

## ::%fullMessage

Returns the human-readable attribute name and the validation message:

    test "ValidationError should humanize attribute in the full message", ->
      error = new Batman.ValidationError("fooBarBaz", "isn't valid")
      equal error.get('fullMessage'), "Foo bar baz isn't valid"


Did a few things:

Did a few things:

1. Some variable renaming/whitespace cleanup
2. Reworked dependency hydration and method invocation

    q     = require 'q'
    fs    = require 'fs'
    async = require 'async'
    uuid  = require 'uuid-1345'

    microtime = require 'microtime'

    EventEmitter2 = require('eventemitter2').EventEmitter2
    emitter       = new EventEmitter2 wildcard: true

    available   = { }
    initialized = { }
    listeners   = { }

    code_root = process.env.SEARCH_ROOT || 'dist'

    underscore = (value) -> value.replace /\W+/g, '_'
    strip      = (name)  -> name.toLowerCase().replace /\W+/g, ''

    SEARCH_ROOTS  = { }
    WRAPPED       = { }
    _INITIALIZERS = { }

    INITIALIZERS = (cls, method) ->
      _INITIALIZERS[cls] = [ ] unless _INITIALIZERS[cls]?

      _INITIALIZERS[cls].push fn: method, called: false

    WRAP = (cls, method) ->
      unless WRAPPED[cls]?
        WRAPPED[cls] = [
          'constructor'
          'initialize'
          'details'
        ]

      WRAPPED[cls].indexOf(method) < 0

    class Madul

      details: =>
        name  = @.__proto__.constructor.name
        props = for own key, val of @.__proto__
          if key != 'constructor' &&
             key != 'details'     &&
             key != 'initialize'  && ((
               key[0]            == '_' &&
               @.__proto__[key]? == false
             ) || key[0] != '_')

            if typeof val == 'function'
              value = val.constructor.name
            else if Array.isArray val
              value = for v in val
                if typeof v == 'function'
                  v.constructor.name
                else if typeof v == 'object'
                  v.constructor.name
                else
                  v
              value = value.join ','
            else if typeof val == 'object'
              value = val.constructor.name
            else
              value = val

            "#{key} => #{value}"

        "\n  #{name}\n    #{props.filter((e) -> e?).join '\n    '}"

      @LISTEN: (event, callback) => emitter.on event, callback

      @FIRE: (event, args) => emitter.emit event, args

      @SEARCH_ROOT: (mod, path) =>
        Madul.FIRE '$.Madul.search_root.register', "#{mod}": path

        SEARCH_ROOTS[mod] = path.substring 0, path.lastIndexOf '/'

      listen: (event, callback) ->
        Madul.LISTEN "*.#{@constructor.name}.#{event}", callback

      fire: (event, args) ->
        Madul.FIRE "@.#{@constructor.name}.#{event}", args

      warn: (event, details) ->
        Madul.FIRE "!.#{@constructor.name}.#{event}", details

      _report: (name, method, state, id, args) =>
        Madul.FIRE "@.#{name}.#{method}.#{state}",
          uuid:      id
          args:      args
          timestamp: microtime.now()

      _respond: (proto, method, id, deferred) =>
        (state, data) =>
          process.nextTick =>
            deferred[state] data

            proto._report proto.constructor.name, method, state, id, data

      _prep_invocation: (proto, method, input, def) =>
        id  = uuid.v4fast()
        res = proto._respond proto, method, id, def

        if Array.isArray input
          input.push (out) => res 'resolve', out
          input.push (err) => res 'reject',  err
          input.push (out) => res 'notify',  out
        else if typeof input == 'object'
          input.done   = (out) => res 'resolve', out
          input.fail   = (err) => res 'reject',  err
          input.update = (out) => res 'notify',  out

          input = [ input ]
        else
          msg = "input to #{proto.constructor.name}.#{method} must be an Array of Object"
          @warn 'input.invalid', msg
          throw new Error msg

        proto._report proto.constructor.name, method, 'invoke', id, input

        input

      _do_wrap: (proto, method) =>
        if method != 'fire' && method != 'warn' && method != 'listen'
          Madul.FIRE "$.#{proto.constructor.name}.#{method}.wrap"

          if typeof proto[method] == 'function'
            proto["_#{method}"] = proto[method]

            proto[method] = =>
              def  = q.defer()
              args = if arguments[0]? then Array.prototype.slice.call arguments else [ ]

              proto["_#{method}"].apply @, proto._prep_invocation proto, method, args, def

              def.promise
          else if typeof proto[method] == 'object'

            if Array.isArray proto[method].validate
              args_format = 'ARRAY'
              decs        = for val in proto[method].validate
                @_parse_handle(val).ref

              proto[method].validate = decs
            else if typeof proto[method].validate == 'object'
              args_format = 'OBJECT'
              decs        = for own key, val of proto[method].validate
                { ref } = @_parse_handle val
                proto[method].validate[key] = ref
                ref
            else if typeof proto[method].validate == 'string'
              args_format = 'ARRAY'
              decs        = [ @_parse_handle(proto[method].validate).ref ]
            else if typeof proto[method].validate != 'undefined'
              @warn 'meta.invalid', validate: 'must be a String, Object, or Array'
              return
            else
              args_format = 'ARRAY'
              decs        = [ ]

            decs = decs.concat(proto[method].before.map (b, i) =>
              { ref } = @_parse_handle b
              proto[method].before[i] = ref
              ref
            ) if proto[method].before?

            decs = decs.concat(proto[method].after.map (a, i) =>
              { ref } = @_parse_handle a
              proto[method].after[i] = ref
              ref
            ) if proto[method].after?

            Madul.FIRE '$.Madul.decorators.hydrate', decs

            @_do_hydrate proto, decs, =>
              proto["_#{method}"] = proto[method]

              proto[method] = =>
                def = q.defer()
                me  = proto["_#{method}"]

                if args_format == 'ARRAY'
                  args = Array.prototype.slice.call arguments

                  if me.validate?
                    validators = for val, i in me.validate
                      { ref } = @_parse_handle val
                      Madul.FIRE '$.Madul.validator.execute', "#{ref}": args[i]
                      proto[ref].EXECUTE args[i]
                else if args_format == 'OBJECT'
                  args = arguments[0]

                  if me.validate?
                    validators = for own key, val of me.validate
                      { ref } = @_parse_handle val
                      Madul.FIRE '$.Madul.validator.execute', "#{ref}": args[key]
                      proto[ref].EXECUTE args[key]
                else
                  @warn 'cannot-continue', 'No arg format specified'

                  return def.reject 'No arg format specified'

                me.before = [ ] unless me.before?
                me.after  = [ ] unless me.after?

                q.all validators
                .then =>
                  q.all me.before.map (b) =>
                    Madul.FIRE '$.Madul.before_filter.execute', "#{b}": args
                    proto[b].before args
                .then =>
                  me.behavior.apply @, @_prep_invocation proto, method, args, def
                .then (result) =>
                  if me.after.length > 0
                    q.all me.after.map (b) =>
                      Madul.FIRE '$.Madul.after_filter.execute', "#{b}": result
                      b.after result
                  else
                    d = q.defer()

                    process.nextTick => d.resolve result

                    d.promise
                .then  (out) => def.resolve out
                .catch (err) => def.reject  err

                def.promise
          else
            @warn 'cannot-wrap', method

          WRAPPED[proto.constructor.name].push method

      _wrap_methods: =>
        proto = @.__proto__
        name  = proto.constructor.name

        while proto? && Object.keys(proto).length > 0 && WRAPPED[name]? == false
          for prop, body of proto
            if prop[0]                  != '_'         &&
               typeof proto["_#{prop}"] == 'undefined' &&
               WRAP(name, prop)         == true

              if typeof body == 'function' || typeof body.behavior == 'function'
                proto._do_wrap proto, prop

                INITIALIZERS name, prop if prop[0] == '$'

          proto = proto.__proto__

      _make_available: (name, mod) =>
        Madul.FIRE "$.#{name}.available"

        available[strip name] = mod

      _hydrate_deps: (done) =>
        proto  = @.__proto__
        protos = [ proto ]

        if proto.__proto__?
          while proto.__proto__? && Object.keys(proto.__proto__).length > 0
            protos.push proto = proto.__proto__

        async.each protos, (p, next) =>
          if p.deps?
            p._do_hydrate p, p.deps, next
          else
            next()
        , done

      _init_if_madul: (path, callback) =>
        mod      = require path
        proto    = mod
        is_madul = false

        while proto
          is_madul = true if proto.constructor.name == 'Madul'

          proto = proto.__super__

        if is_madul
          new mod()
            .initialize()
            .then callback
        else
          callback mod

      _load: (me, name, path, loaded) =>
        me._init_if_madul path, (mod) =>
          me._make_available name, mod
          loaded mod

      _check: (me, path, dep, ref, finished) =>
        fs.readdir path, (err, files) =>
          if err?
            me.warn 'file-not-found', err

            done type: 'ERROR', info: err
          else
            async.each files, (f, next) =>
              depth = "#{path}/#{f}"

              fs.stat depth, (e, s) =>
                if s.isDirectory() && f[0] != '.'
                  me._check me, depth, dep, ref, next
                else if s.isFile() && f.substring(0, f.length - 3) == dep
                  me._load me, ref, depth, (mod) => me._do_add me, mod, ref, next
                else
                  next()
            , finished

      _do_add: (me, mod, ref, next) =>
        Madul.FIRE "$.#{me.constructor.name}.dependency.register", ref

        me[underscore ref] = mod

        next()

      _add: (me, ref, path, next) =>
        me._init_if_madul path, (mod) =>
          me._make_available ref, mod
          me._do_add me, mod, ref, next

      _parse_handle: (value) =>
        tokens = value.split '='

        if tokens.length == 2
          initer = tokens[1].trim()
          t      = tokens[0].split '->'

          if t[0].indexOf('#') > -1
            r = t[0].split '#'

            root = SEARCH_ROOTS[r[0].trim()]
            t[0] = r[1]

          if t.length == 2
            name  = t[0].trim()
            alias = t[1].trim()
          else if t.length == 1
            name = alias = t[0].trim()
          else
            return next @warn 'alias.invalid', tokens[1]

          toks = initer.split ':'

          if toks.length == 2
            initer = toks[0].trim()
            pres   = toks[1].split(',').map (p) => p.trim()
          else if toks.length == 1
            initer = toks[0].trim()
          else
            return next @warn 'dependency-initializer.invalid', tokens[1]

        else if tokens.length == 1
          t = tokens[0].split '->'

          if t[0].indexOf('#') > -1
            r = t[0].split '#'

            root = SEARCH_ROOTS[r[0].trim()]
            t[0] = r[1]

          if t.length == 2
            name  = t[0].trim()
            alias = t[1].trim()
          else if t.length == 1
            name = alias = t[0].trim()
          else
            return next @warn 'alias.invalid', tokens[1]

        else
          return next @warn 'dependency.invalid', d

        { ref: alias || name, alias, name, root, initer, pres }

      _do_hydrate: (proto, deps, hydration_complete) =>
        initers = [ ]

        async.each deps, (d, next) =>
          { ref, alias, name, root, initer, pres } = @_parse_handle d

          if pres?
            insert_at = for p in pres
              index_for = (sibling) => sibling.alias == p.alias

              initers.find index_for

            insert_at.sort()

            insert_at = if insert_at.length == 0 then 0 else insert_at[insert_at.length - 1]

            initers.splice insert_at, 0, alias: alias, execute: initer

          if proto[underscore ref]? == false
            if available[strip ref]? == true
              proto._do_add proto, available[strip ref], ref, next
            else
              try
                proto._add proto, ref, name, next
              catch e
                if root?
                  Madul.FIRE '$.Madul.search_root.load', { name, alias, root }

                  proto._check proto, root, name, ref, next
                else
                  cwd = process.cwd()
                  pkg = "#{cwd}/node_modules/#{name}/package.json"

                  fs.stat pkg, (err, stat) =>
                    if stat?.isFile()
                      fs.readFile pkg, 'utf8', (err, data) =>
                        json = JSON.parse data
                        main = json.main || json._main || 'index.js'
                        path = "#{cwd}/node_modules/#{name}/#{main}"

                        proto._add proto, ref, path, next
                    else
                      proto._check proto, "#{cwd}/#{code_root}", name, ref, next
          else
            next()
        , =>
          async.eachSeries initers, (initer, next) =>
            @[initer.execute]().then next
          , hydration_complete

      _finish_up: (proto, args) =>
        name = proto.constructor.name

        @_call_initializers_for name, =>
          if listeners[name]?
            async.each listeners[name], (listener, next) =>
              listener @
              next()
            , => @_register_initialized name
          else
            @_register_initialized name

      _register_initialized: (name) =>
        @_make_available name, @

        initialized[name] = true

        Madul.FIRE "$.#{name}.initialized"

      _call_initializers_for: (name, callback) =>
        if _INITIALIZERS[name]?
          async.each _INITIALIZERS[name], (initializer, next) =>
            if initializer.called == false
              @[initializer.fn]()
                .then =>
                  initializer.called = true
                  next()
            else
              next()
          , callback
        else
          callback()

      _invoke_parent_initializers: (callback) =>
        proto = @.__proto__

        if proto.__proto__?
          parents = while proto.__proto__? && Object.keys(proto.__proto__).length > 0
            proto = proto.__proto__

          async.each parents, (parent, next) =>
            @_call_initializers_for parent.constructor.name, next
          , callback
        else
          callback()

      initialize: =>
        deferred = q.defer()
        proto    = @.__proto__
        name     = proto.constructor.name

        Madul.FIRE "$.#{name}.request_instance"

        if initialized[name] == undefined
          Madul.FIRE "$.#{name}.initialize"

          initialized[name] = false
          listeners[name]   = [ deferred.resolve ]

          @_wrap_methods()
          @_hydrate_deps => @_invoke_parent_initializers => @_finish_up proto
        else if initialized[name] == false
          listeners[name].push deferred.resolve
        else if initialized[name] == true
          process.nextTick => deferred.resolve available[strip name]

        deferred.promise

    module.exports = Madul

    q     = require 'q'
    fs    = require 'fs'
    async = require 'async'
    uuid  = require 'uuid-1345'

    microtime = require 'microtime'

    EventEmitter2 = require('eventemitter2').EventEmitter2
    emitter       = new EventEmitter2 wildcard: true

    available   = { }
    initialized = { }
    listeners   = { }

    code_root = process.env.MADUL_ROOT || 'dist'

    underscore = (value) -> value.replace /\W+/g, '_'
    strip      = (name)  -> name.toLowerCase().replace /\W+/g, ''

    WRAPPED = { }

    _INITIALIZERS = { }

    INITIALIZERS = (cls, method) ->
      _INITIALIZERS[cls] = [ ] unless _INITIALIZERS[cls]?

      _INITIALIZERS[cls].push fn: method, called: false

    WRAP = (cls, method) ->
      unless WRAPPED[cls]?
        WRAPPED[cls] = [
          'constructor'
          'initialize'
          'details'
        ]

      WRAPPED[cls].indexOf(method) < 0

    class Madul

      details: =>
        name  = @.__proto__.constructor.name
        props = for own key, val of @.__proto__
          if key != 'constructor' &&
             key != 'details'     &&
             key != 'initialize'  && ((
               key[0]            == '_' &&
               @.__proto__[key]? == false
             ) || key[0] != '_')

            if typeof val == 'function'
              value = val.constructor.name
            else if Array.isArray val
              value = for v in val
                if typeof v == 'function'
                  v.constructor.name
                else if typeof v == 'object'
                  v.constructor.name
                else
                  v
              value = value.join ','
            else if typeof val == 'object'
              value = val.constructor.name
            else
              value = val

            "#{key} => #{value}"

        "\n  #{name}\n    #{props.filter((e) -> e?).join '\n    '}"

      @LISTEN: (event, callback) => emitter.on event, callback

      @FIRE: (event, args) => emitter.emit event, args

      listen: (event, callback) ->
        Madul.LISTEN "*.#{@constructor.name}.#{event}", callback

      fire: (event, args) ->
        Madul.FIRE "@.#{@constructor.name}.#{event}", args

      warn: (event, details) ->
        Madul.FIRE "!.#{@constructor.name}.#{event}", details

      _report: (name, method, state, id, args) =>
        Madul.FIRE "@.#{name}.#{method}.#{state}",
          uuid:      id
          args:      args
          timestamp: microtime.now()

      _respond: (proto, method, id, deferred) =>
        (state, data) =>
          process.nextTick =>
            deferred[state] data

            proto._report proto.constructor.name, method, state, id, data

      _prep_invocation: (proto, method, input, def) =>
        id  = uuid.v4fast()
        res = proto._respond proto, method, id, def

        if Array.isArray input
          input.push (out) => res 'resolve', out
          input.push (err) => res 'reject',  err
          input.push (out) => res 'notify',  out
        else if typeof input == 'object'
          input.done   = (out) => res 'resolve', out
          input.fail   = (err) => res 'reject',  err
          input.update = (out) => res 'notify',  out

          input = [ input ]
        else
          msg = "input to #{proto.constructor.name}.#{method} must be an Array of Object"
          @warn 'input.invalid', msg
          throw new Error msg

        proto._report proto.constructor.name, method, 'invoke', id, input

        input

      _do_wrap: (proto, method) =>
        if method != 'fire' && method != 'warn' && method != 'listen'
          Madul.FIRE "$.#{proto.constructor.name}.#{method}.wrap"

          if typeof proto[method] == 'function'
            proto["_#{method}"] = proto[method]

            proto[method] = =>
              def  = q.defer()
              args = if arguments[0]? then Array.prototype.slice.call arguments else [ ]

              proto["_#{method}"].apply @, proto._prep_invocation proto, method, args, def

              def.promise
          else if typeof proto[method] == 'object'

            if Array.isArray proto[method].validate
              args_format = 'ARRAY'
              decs        = for val in proto[method].validate
                val
            else if typeof proto[method].validate == 'object'
              args_format = 'OBJECT'
              decs        = for own key, val of proto[method].validate
                val
            else if typeof proto[method].validate == 'string'
              args_format = 'ARRAY'
              decs        = [ proto[method].validate ]
            else if typeof proto[method].validate != 'undefined'
              @warn 'meta.invalid', validate: 'must be a String, Object, or Array'
              return
            else
              args_format = 'ARRAY'
              decs        = [ ]

            decs = decs.concat(proto[method].before) if proto[method].before?
            decs = decs.concat(proto[method].after)  if proto[method].after?

            @_do_hydrate proto, decs, =>
              proto["_#{method}"] = proto[method]

              out = undefined

              proto[method] = =>
                def = q.defer()
                me  = proto["_#{method}"]

                if args_format == 'ARRAY'
                  args = Array.prototype.slice.call arguments

                  if me.validate?
                    validators = for val, i in me.validate
                      proto[val].EXECUTE args[i]
                else if args_format == 'OBJECT'
                  args = arguments[0]

                  if me.validate?
                    validators = for own key, val of me.validate
                      proto[val].EXECUTE args[key]
                else
                  @warn 'cannot-continue', 'No arg format specified'

                  return def.reject 'No arg format specified'

                me.before = [ ] unless me.before?
                me.after  = [ ] unless me.after?

                q.all validators
                .then => q.all me.before.map (b) => proto[b].before args
                .then =>
                  me.behavior.apply @, @_prep_invocation proto, method, args, def
                .then (result) =>
                  out = result
                  q.all me.after.map (b) => b.after out
                .then          => def.resolve out
                .catch   (err) => def.reject  err

                def.promise
          else
            @warn 'cannot-wrap', method

          WRAPPED[proto.constructor.name].push method

      _wrap_methods: =>
        proto = @.__proto__
        name  = proto.constructor.name

        while proto? && Object.keys(proto).length > 0 && WRAPPED[name]? == false
          for method, body of proto
            if method[0]                  != '_'         &&
               (
                typeof body.behavior       == 'function' ||
                typeof body                == 'function'
               ) &&
               typeof proto["_#{method}"] == 'undefined' &&
               WRAP(name, method)         == true

              proto._do_wrap proto, method

              INITIALIZERS name, method if method[0] == '$'

          proto = proto.__proto__

      _make_available: (name, mod) =>
        Madul.FIRE "$.#{name}.available"

        available[strip name] = mod

      _hydrate_deps: (done) =>
        proto  = @.__proto__
        protos = [ proto ]

        if proto.__proto__?
          while proto.__proto__? && Object.keys(proto.__proto__).length > 0
            protos.push proto = proto.__proto__

        async.each protos, (p, next) =>
          if p.deps?
            p._do_hydrate p, p.deps, next
          else
            next()
        , done

      _init_if_madul: (path, initer, callback) =>
        mod      = require path
        proto    = mod
        is_madul = false

        while proto
          is_madul = true if proto.constructor.name == 'Madul'

          proto = proto.__super__

        if is_madul
          new mod()
            .initialize()
            .then (mod) =>
              if initer?
                mod[initer]()
                  .then => callback mod
              else
                callback mod
        else
          callback mod

      _load: (me, name, initer, path, loaded) =>
        me._init_if_madul path, initer, (mod) =>
          me._make_available name, mod
          loaded()

      _check: (me, path, dep, ref, initer, finished) =>
        fs.readdir path, (err, files) =>
          if err?
            me.warn 'file-not-found', err

            done type: 'ERROR', info: err
          else
            async.each files, (f, next) =>
              depth = "#{path}/#{f}"

              fs.stat depth, (e, s) =>
                if s.isDirectory() && f[0] != '.'
                  me._check me, depth, dep, ref, initer, next
                else if s.isFile() && f.substring(0, f.length - 3) == dep
                  me._load me, ref, initer, depth, => me._do_add me, dep, ref, next
                else
                  next()
            , finished

      _add_dependency: (me, name, mod) =>
        Madul.FIRE "$.#{me.constructor.name}.dependency.register", name

        me[underscore name] = mod

      _do_add: (me, name, ref, next) =>
        me._add_dependency me, ref, available[strip name]
        next()

      _add: (me, name, ref, initer, path, next) =>
        me._init_if_madul path, initer, (mod) =>
          me._make_available name, mod
          me._do_add me, name, ref, next

      _do_hydrate: (proto, deps, callback) =>
        async.each deps, (d, next) =>
          name, alias, initer = undefined
          tokens = d.split '->'

          if tokens.length == 2
            alias = tokens[0]
            t     = tokens[1].split '::'

            if t.length == 2
              name   = t[0]
              initer = t[1]
            else
              name = t[0]
          else
            name = alias = tokens[0]

          ref = alias || name

          if proto[underscore ref]? == false
            if available[strip name]? == true
              proto._do_add proto, name, ref, next
            else
              try
                proto._add proto, name, ref, initer, d, next
              catch e
                cwd = process.cwd()
                pkg = "#{cwd}/node_modules/#{d}/package.json"

                fs.stat pkg, (err, stat) =>
                  if stat?.isFile()
                    fs.readFile pkg, 'utf8', (err, data) =>
                      json = JSON.parse data
                      main = json.main || json._main || 'index.js'
                      path = "#{cwd}/node_modules/#{d}/#{main}"

                      proto._add proto, name, ref, initer, path, next
                  else
                    proto._check proto, "#{cwd}/#{code_root}", d, ref, initer next
          else
            next()
        , callback

      _finish_up: (proto, args) =>
        name = proto.constructor.name

        @_call_initializers_for name, =>
          if listeners[name]?
            async.each listeners[name], (listener, next) =>
              listener @
              next()
            , => @_register_initialized name
          else
            @_register_initialized name

      _register_initialized: (name) =>
        @_make_available name, @

        initialized[name] = true

        Madul.FIRE "$.#{name}.initialized"

      _call_initializers_for: (name, callback) =>
        if _INITIALIZERS[name]?
          async.each _INITIALIZERS[name], (initializer, next) =>
            if initializer.called == false
              @[initializer.fn]()
                .then =>
                  initializer.called = true
                  next()
            else
              next()
          , callback
        else
          callback()

      _invoke_parent_initializers: (callback) =>
        proto = @.__proto__

        if proto.__proto__?
          parents = while proto.__proto__? && Object.keys(proto.__proto__).length > 0
            proto = proto.__proto__

          async.each parents, (parent, next) =>
            @_call_initializers_for parent.constructor.name, next
          , callback
        else
          callback()

      initialize: =>
        deferred = q.defer()
        proto    = @.__proto__
        name     = proto.constructor.name

        Madul.FIRE "$.#{name}.request_instance"

        if initialized[name] == undefined
          Madul.FIRE "$.#{name}.initialize"

          initialized[name] = false
          listeners[name]   = [ deferred.resolve ]

          @_wrap_methods()
          @_hydrate_deps => @_invoke_parent_initializers => @_finish_up proto
        else if initialized[name] == false
          listeners[name].push deferred.resolve
        else if initialized[name] == true
          process.nextTick => deferred.resolve available[strip name]

        deferred.promise

    module.exports = Madul


Doc configalter invo

Doc configalter invo

# Configuration

*Gathers configuration and serves it as an Angular module for clients.*

    _ = require 'lodash'
    Promise = require 'bluebird'
    url = require 'url'

    config = require 'config'
    pkgman = require 'pkgman'

    {Config} = require 'client/modules/config'

    exports.pkgmanRegister = (registrar) ->

#### Implements hook `assetMiddleware`.

      registrar.registerHook 'assetMiddleware', ->

        label: 'Config'
        middleware: [

          (assets, next) ->

            assets.scripts.push '/js/config.js'

            next()

        ]

#### Implements hook `httpMiddleware`.

      registrar.registerHook 'httpMiddleware', (http) ->

        label: 'Serve package configuration'
        middleware: [

Serve the configuration module.

          (req, res, next) ->

Only if the path matches.

            return next() unless req.url is '/js/config.js'

            exports.renderPackageConfig(req).then((code) ->

Emit the configuration module.

              res.setHeader 'Content-Type', 'text/javascript'
              res.send code

            ).catch next

        ]

## shrub-config.renderPackageConfig

* (http.IncomingRequest) `req` - The request object.

USe a client's request object to render configuration.

    exports.renderPackageConfig = (req) ->

#### Invoke hook `config`.

Allows packages to specify configuration that will be sent to the client.
Implementations may return an object, or a promise that resolves to an object.

      subconfigs = pkgman.invoke 'config', req

      Promise.all(

        promise for path, promise of subconfigs

      ).then (fulfilledSubconfigs) ->

        config_ = new Config()

Package-independent...

        config_.set 'packageList', config.get 'packageList'

Merge in the subconfigs.

        index = 0
        for path of subconfigs

          subconfig = fulfilledSubconfigs[index++]
          for key, value of subconfig
            continue unless value?

###### TODO: Multiline

            config_.set "packageConfig:#{path.replace /\//g, ':'}:#{key.replace /\//g, ':'}", value

#### Invoke hook `configAlter`.

        pkgman.invoke 'configAlter', req, config_

Format the configuration to look nice.

        prettyPrintConfig = ->

          jsonArgs = [config_]

          if 'production' isnt config.get 'NODE_ENV'
            jsonArgs = jsonArgs.concat [null, '  ']

          stringified = JSON.stringify jsonArgs...
          [first, rest...] = stringified.split '\n'
          ([first].concat rest.map (line) -> '    ' + line).join '\n'

        """
    angular.module('shrub.config', ['shrub.require']).config([
      'shrub-requireProvider', function(requireProvider) {
        requireProvider.require('config').from(#{prettyPrintConfig()});
      }
    ]);
      """

# Configuration

*Gathers configuration and serves it as an Angular module for clients.*

    _ = require 'lodash'
    Promise = require 'bluebird'
    url = require 'url'

    config = require 'config'
    pkgman = require 'pkgman'

    {Config} = require 'client/modules/config'

    exports.pkgmanRegister = (registrar) ->

#### Implements hook `assetMiddleware`.

      registrar.registerHook 'assetMiddleware', ->

        label: 'Config'
        middleware: [

          (assets, next) ->

            assets.scripts.push '/js/config.js'

            next()

        ]

#### Implements hook `httpMiddleware`.

      registrar.registerHook 'httpMiddleware', (http) ->

        label: 'Serve package configuration'
        middleware: [

Serve the configuration module.

          (req, res, next) ->

Only if the path matches.

            return next() unless req.url is '/js/config.js'

            exports.renderPackageConfig(req).then((code) ->

Emit the configuration module.

              res.setHeader 'Content-Type', 'text/javascript'
              res.send code

            ).catch next

        ]

## shrub-config.renderPackageConfig

* (http.IncomingRequest) `req` - The request object.

USe a client's request object to render configuration.

    exports.renderPackageConfig = (req) ->

#### Invoke hook `config`.

Allows packages to specify configuration that will be sent to the client.
Implementations may return an object, or a promise that resolves to an object.

      subconfigs = pkgman.invoke 'config', req

      Promise.all(

        promise for path, promise of subconfigs

      ).then (fulfilledSubconfigs) ->

        config_ = new Config()

Package-independent...

        config_.set 'packageList', config.get 'packageList'

Merge in the subconfigs.

        index = 0
        for path of subconfigs

          subconfig = fulfilledSubconfigs[index++]
          for key, value of subconfig
            continue unless value?

###### TODO: Multiline

            config_.set "packageConfig:#{path.replace /\//g, ':'}:#{key.replace /\//g, ':'}", value

        pkgman.invoke 'configAlter', req, config_

Format the configuration to look nice.

        prettyPrintConfig = ->

          jsonArgs = [config_]

          if 'production' isnt config.get 'NODE_ENV'
            jsonArgs = jsonArgs.concat [null, '  ']

          stringified = JSON.stringify jsonArgs...
          [first, rest...] = stringified.split '\n'
          ([first].concat rest.map (line) -> '    ' + line).join '\n'

        """
    angular.module('shrub.config', ['shrub.require']).config([
      'shrub-requireProvider', function(requireProvider) {
        requireProvider.require('config').from(#{prettyPrintConfig()});
      }
    ]);
      """


Update callback documentation

Update callback documentation

    "use strict"

    redis    = require './db/redis'
    mongo    = require './db/mongo'
    ObjectID = require('mongodb').ObjectID

## getFilter()

Get data filter for object type.

### Params

* `String` type - object type to get filter for.
* `boolean` preventDefault - prevent default filter switch.

### Return

Returns a `String` key - `boolean` value `Object` where each key represent a
document field. The value determines if they field should be included or not.

Returns an empty `Object` if the `preventDefault` parameter is set to `true` and
no filter was found for the given `type`.

    getFilter = (type, preventDefault) ->

      dataFields =
        default:
          _id       : false
          tilbyder  : true
          endret    : true
          checksum  : true
          status    : true
          navn      : true
          bilder    : true
          grupper   : true

        bilder:
          _id       : false
          tilbyder  : true
          endret    : true
          checksum  : true
          status    : true
          navn      : true

      return dataFields[type] if dataFields[type]
      return {} if preventDefault
      return dataFields.default

## filterData()

Filter data for given type.

### Todo

* [ ] Handle undefined values (@starefossen)

### Params

* `String` type - object type to filter data for.
* `Object` data - data to filter on.

### Return

Returns an `Object` with a subset of the original object containing only the
accepted object properties for the given object type.

    filterData = (type, data) ->
      res = {}
      res[key] = data[key] for key,val of getFilter(type) when val is true and data[key]
      return res

## getDoc()

Get a document from MongoDB for given object type and object id.

This function will automaticly filter object keys according to type in order to
prevent fetching of uncesserary data.

### Params

* `String` type - object type to get document for.
* `String` id - object id for document.
* `function` cb - callback function (`Error` err, `Object` doc).

### Return

No return value or `undefined`.

    getDoc = (type, id, cb) ->
      mongo[type].findOne {_id: new ObjectID(id)}, getFilter(type), cb

## set()

Store data object in Redis for a given cache key.

### Params

* `String` key - cache key to store data for.
* `Object` data - data to store for cache key.
* `function` cb - callback function (`Error` err, `Object` data).

### Return

No return value or `undefined`.

    set = (key, data, cb) ->
      redis.hmset key, data, (err) -> cb(err, data)

## get()

Retrive data from cache for given a cache key.

### Params

* `String` key - cache key to get data for.
* `function` cb - callback function (`Error` err, `Object` data).

### Return

No return value or `undefined`.

    get = (key, cb) ->
      redis.hgetall key, cb

## setForType()

Store data in Redis for given object type and object id.

This function will automaticly remove object properties from input data in order
to match the object type cache preferences as defined in [#getFilter()](#getFilter).

### Params

* `String` type - object type to set cache data for.
* `String` id - object id to set cache data for.
* `Object` data - data store in cache for type and id.
* `function` cb - callback function (`Error` err, `Object` data).

### Return

No return value or `undefined`.

    setForType = (type, id, data, cb) ->
      set "#{type}:#{id}", filterData(type, data), cb

## getForType()

Get data from Redis for object type and object id.

`NB` This function will return `Arrays` if the document is retrived directly
from MongoDB. `Arrays` stored in Redis will be returned as comma seperated
strings.

### Params

* `String` type - object type to get cache data for.
* `String` id - object id to get cache data for.
* `function` cb - callback function (`Error` err, `Object` data).

### Return

No return value or `undefined`.

    getForType = (type, id, cb) ->
      get "#{type}:#{id}", (err, data) ->
        return cb null, data, true if data

        getDoc type, id, (err, data) ->
          return cb err, null, false if err

          data = status: 'Slettet' if not data

          # We don't need to use setForType() here since data is already formated
          # when using getDoc()

          set "#{type}:#{id}", data, (err, data) ->
            cb err, data, false

## Export

Expose the functions we want to be public by exporting them.

    module.exports =
      getFilter : getFilter
      filterData: filterData
      getDoc    : getDoc
      set       : set
      get       : get
      setForType: setForType
      getForType: getForType

    "use strict"

    redis    = require './db/redis'
    mongo    = require './db/mongo'
    ObjectID = require('mongodb').ObjectID

## getFilter()

Get data filter for object type.

### Params

* `String` type - object type to get filter for.
* `boolean` preventDefault - prevent default filter switch.

### Return

Returns a `String` key - `boolean` value `Object` where each key represent a
document field. The value determines if they field should be included or not.

Returns an empty `Object` if the `preventDefault` parameter is set to `true` and
no filter was found for the given `type`.

    getFilter = (type, preventDefault) ->

      dataFields =
        default:
          _id       : false
          tilbyder  : true
          endret    : true
          checksum  : true
          status    : true
          navn      : true
          bilder    : true
          grupper   : true

        bilder:
          _id       : false
          tilbyder  : true
          endret    : true
          checksum  : true
          status    : true
          navn      : true

      return dataFields[type] if dataFields[type]
      return {} if preventDefault
      return dataFields.default

## filterData()

Filter data for given type.

### Params

* `String` type - object type to filter data for.
* `Object` data - data to filter on.

### Return

Returns an `Object` with a subset of the original object containing only the
accepted object properties for the given object type.

### Todo

* [ ] Handle undefined values.

    filterData = (type, data) ->
      res = {}
      res[key] = data[key] for key,val of getFilter(type) when val is true and data[key]
      return res

## getDoc()

Get a document from MongoDB for given object type and object id.

This function will automaticly filter object keys according to type in order to
prevent fetching of uncesserary data.

### Params

* `String` type - object type to get document for.
* `String` id - object id for document.
* `function` cb - callback function.

### Callback

#### Params

* `Error` err - error object if lookup failed; otherwise `null`.
* `Object` doc - filtered document if found; otherwise `null`.

    getDoc = (type, id, cb) ->
      mongo[type].findOne {_id: new ObjectID(id)}, getFilter(type), cb

## set()

Store data object in Redis for a given cache key.

### Params

* `String` key - cache key to store data for.
* `Object` data - data to store for cache key.
* `function` cb - callback function.

### Callback

#### Params

* `Error` err - error object if Redis write failed; otherwise `null`.
* `Object` data - the original data as it was stored in Redis.

    set = (key, data, cb) ->
      redis.hmset key, data, (err) -> cb(err, data)

## get()

Retrive data from cache for given a cache key.

### Params

* `String` key - cache key to get data for.
* `function` cb - callback function

### Callback

#### Params

* `Error` err -  error object if Redis lookup failed, otherwise `null`.
* `Object` data - data retrieved from Redis if found; otherwise `null`.

    get = (key, cb) ->
      redis.hgetall key, cb

## setForType()

Store data in Redis for given object type and object id.

This function will automaticly remove object properties from input data in order
to match the object type cache preferences as defined in [#getFilter()](#getFilter).

### Params

* `String` type - object type to set cache data for.
* `String` id - object id to set cache data for.
* `Object` data - data store in cache for type and id.
* `function` cb - callback function.

### Callback

#### Params

* `Error` err - error object if Redis failed; otherwise `null`.
* `Object` data - the filtered data object as it was stored in Redis.

    setForType = (type, id, data, cb) ->
      set "#{type}:#{id}", filterData(type, data), cb

## getForType()

Get data from Redis for object type and object id.

`NB` This function will return `Arrays` if the document is retrived directly
from MongoDB. `Arrays` stored in Redis will be returned as comma seperated
strings.

### Params

* `String` type - object type to get cache data for.
* `String` id - object id to get cache data for.
* `function` cb - callback function.

### Callback

#### Params

* `Error` err - error object if Redis failed; otherwise `null`.
* `Object` data - the data object returned from Redis or Mongodb.

    getForType = (type, id, cb) ->
      get "#{type}:#{id}", (err, data) ->
        return cb null, data, true if data

        getDoc type, id, (err, data) ->
          return cb err, null, false if err

          data = status: 'Slettet' if not data

          # We don't need to use setForType() here since data is already formated
          # when using getDoc()

          set "#{type}:#{id}", data, (err, data) ->
            cb err, data, false

## Export

Expose the functions we want to be public by exporting them.

    module.exports =
      getFilter : getFilter
      filterData: filterData
      getDoc    : getDoc
      set       : set
      get       : get
      setForType: setForType
      getForType: getForType