in. jokes

in. jokes

#! /usr/bin/env coffee

The expander supports multiple asyncronous functions that `appear = synchronous()`.

To achieve that it needed to do some peculiar things.

__Very peculiar!__

Things that [some](https://www.google.co.za/search?q=uptight+people&espv=2&biw=1063&bih=1058&source=lnms&tbm=isch&sa=X&ei=q6CJVYTyIpPB7Ab95YLQBA&ved=0CAYQ_AUoAQ#tbm=isch&q=uptight) may disaprove of.

Like wearing [spandex in public](https://www.google.co.za/search?q=spandex+in+public+nuts&espv=2&biw=1063&bih=1058&tbm=isch&source=lnms&sa=X&ei=5Z-JVez6KeWY7gbl_oCYAg&ved=0CAYQ_AUoAQ#tbm=isch&q=spandex+in+public).

It broke the [expan](https://github.com/nomilous/in./blob/master/lib/expander.js)sion (to be [eval](https://github.com/nomilous/in./blob/master/lib/compiler.js#L57)'d) [into pieces and substituted](https://www.google.co.za/search?q=into+pieces+and+substituted+an+array&es_sm=91&source=lnms&tbm=isch&sa=X&ei=laOJVbKFOqKM7Abs4Y_ACw&ved=0CAkQ_AUoAw&biw=1063&bih=1058#tbm=isch&q=into+pieces+and+substituted) an array ($e[n]) for each function's results.

The results of the division are loaded into a variable called `spandex`...

Which happens to be in scope at injection eval.

    require('../')

    $$in (arg) ->

        ### in(arg).as {{1}}###

        console.log arg

#### Return and finish later - few bits still to wire together

#! /usr/bin/env coffee

The expander supports multiple asyncronous functions that `appear = syncronous()`.

To achieve that it needed to do some peculiar things.

__Very peculiar!__

Things that [some](https://www.google.co.za/search?q=uptight+people&espv=2&biw=1063&bih=1058&source=lnms&tbm=isch&sa=X&ei=q6CJVYTyIpPB7Ab95YLQBA&ved=0CAYQ_AUoAQ#tbm=isch&q=uptight) may disaprove of.

Like wearing [spandex in public](https://www.google.co.za/search?q=spandex+in+public+nuts&espv=2&biw=1063&bih=1058&tbm=isch&source=lnms&sa=X&ei=5Z-JVez6KeWY7gbl_oCYAg&ved=0CAYQ_AUoAQ#tbm=isch&q=spandex+in+public).

It broke the [expan](https://github.com/nomilous/in./blob/master/lib/expander.js)sion (to be [eval](https://github.com/nomilous/in./blob/master/lib/compiler.js#L57)'d) [into pieces and substituted](https://www.google.co.za/search?q=into+pieces+and+substituted+an+array&es_sm=91&source=lnms&tbm=isch&sa=X&ei=laOJVbKFOqKM7Abs4Y_ACw&ved=0CAkQ_AUoAw&biw=1063&bih=1058#tbm=isch&q=into+pieces+and+substituted) an array ($e[n]) for each function's results.

The results of the division are loaded into a variable called `spandex`...

Which happens to be in scope at injection eval.

    require('../')

    $$in (arg) ->

        ### in(arg).as {{1}}###

        console.log arg

#### Return and finish later - few bits still to wire together


Temporarily accept "Privat" as a valid lisence

    joi = require 'joi'

## All

### Required

These fields are required for all objects.

    exports.required =
      _id: joi.string().regex(/^[a-f0-9]{24}$/)
      tilbyder: joi.string()
      endret: joi.string()
      checksum: joi.string()
      lisens: joi.string().valid([
        'Privat' # @TODO This is just temporarily
        'CC BY 3.0 NO'
        'CC BY-SA 3.0 NO'
        'CC BY-ND 3.0 NO'
        'CC BY-NC 3.0 NO'
        'CC BY-NC-SA 3.0 NO'
        'CC BY-NC-ND 3.0 NO'
        'CC BY 4.0'
        'CC BY-SA 4.0'
        'CC BY-ND 4.0'
        'CC BY-NC 4.0'
        'CC BY-NC-SA 4.0'
        'CC BY-NC-ND 4.0'
      ])
      navngiving: joi.string()
      status: joi.string().valid([
        'Offentlig'
        'Privat'
        'Kladd'
        'Slettet'
      ])

### Optional

There optional are optional for all objects.

    exports.optional =
      navn: joi.string()
      kommuner: joi.array().includes(joi.string())
      fylker: joi.array().includes(joi.string())
      områder: joi.array().includes(joi.string().regex(/^[a-f0-9]{24}$/))
      beskrivelse: joi.string()
      lenker: joi.array().includes(joi.object(
        tittel: joi.string().optional()
        type: joi.string().optional()
        url: joi.string().regex(/^https?:\/\//)
      ))
      tags: joi.array().includes(joi.string())
      privat: joi.object()
      grupper: joi.array().includes(joi.string().regex(/^[a-f0-9]{24}$/))
      steder: joi.array().includes(joi.string().regex(/^[a-f0-9]{24}$/))
      bilder: joi.array().includes(joi.string().regex(/^[a-f0-9]{24}$/))
      url: joi.string().regex(/^https?:\/\//)

## Data types

    exports.type =

### Bilder

      bilder:
        geojson: joi.object(
          type: joi.string().valid('Point').required()
          properties: joi.object(
            altitude: joi.number().integer()
          )
          coordinates: joi.array().includes(joi.number()).length(2).required()
        )
        fotograf: joi.object(
          navn: joi.string()
          telefon: joi.string()
          epost: joi.string().email()
        )
        eier: joi.object(
          navn: joi.string()
          telefon: joi.string()
          epost: joi.string().email()
          url: joi.string().regex(/^https?:\/\//)
        )
        img: joi.array().includes(joi.object(
          url: joi.string().regex(/^https?:\/\//).required()
          width: joi.number().integer()
          height: joi.number().integer()
          size: joi.number()
        ))

### Grupper

      grupper:
        geojson: joi.object(
          type: joi.string().valid('Polygon').required()
          coordinates: joi.array().includes(
            joi.array().includes(
              joi.array().includes(joi.number()).length(2)
            )
          ).required()
        )
        organisasjonsnr: joi.string()
        logo: joi.string().regex(/^https?:\/\//)
        ansatte: joi.number().integer()
        kontaktinfo: joi.array().includes(joi.object(
          tittel: joi.string()
          adresse1: joi.string()
          adresse2: joi.string()
          postnummer: joi.number().integer()
          poststed: joi.string()
          land: joi.string()
          telefon: joi.string()
          fax: joi.string()
          epost: joi.string().email()
          url: joi.string().regex(/^https?:\/\//)
        ))
        foreldregruppe: joi.string().regex(/^[a-f0-9]{24}$/)

### Turer

      turer:
        geojson: joi.object(
          type: joi.string().valid('LineString').required()
          coordinates: joi.array().includes(
            joi.array().includes(joi.number()).length(2)
          ).required()
        )
        distanse: joi.number()
        retning: joi.string().valid([
          "AB"
          "BA"
          "ABA"
          "BAB"
        ])
        adkomst: joi.string()
        gradering: joi.string().valid([
          "Enkel"
          "Middels"
          "Krevende"
          "Ekspert"
        ])
        passer_for: joi.array().includes(joi.string())
        tilrettelagt_for: joi.array().includes(joi.string())
        sesong: joi.array().includes(joi.number().integer().min(1).max(12))
        tidsbruk: joi.object(
          normal: joi.object(
            dager: joi.number().integer()
            timer: joi.number().integer()
            minutter: joi.number().integer()
          )
          min: joi.object(
            dager: joi.number().integer()
            timer: joi.number().integer()
            minutter: joi.number().integer()
          )
          max: joi.object(
            dager: joi.number().integer()
            timer: joi.number().integer()
            minutter: joi.number().integer()
          )
        )

### Områder

      områder:
        geojson: joi.object(
          type: joi.string().valid('Polygon').required()
          coordinates: joi.array().includes(
            joi.array().includes(
              joi.array().includes(joi.number()).length(2)
            )
          ).required()
        )
        foreldreområder: joi.array().includes(joi.string().regex(/^[a-f0-9]{24}$/))

### Steder

      steder:
        navn_alt: joi.array().includes(joi.string())
        ssr_id: joi.number().integer()
        geojson: joi.object(
          type: joi.string().valid('Point').required()
          properties: joi.object(
            altitude: joi.number().integer()
          )
          coordinates: joi.array().includes(joi.number()).length(2).required()
        )
        kommune: joi.string()
        fylke: joi.string()
        adkomst: joi.object(
          sommer: joi.string()
          vinter: joi.string()
        )
        tilrettelagt_for: joi.array().includes(joi.string())
        fasiliteter: joi.object()
        byggeår: joi.number().integer()
        besøksstatisikk: joi.object()
        betjeningsgrad: joi.string()
        kart: joi.string()
        turkart: joi.array().includes(joi.string())

    joi = require 'joi'

## All

### Required

These fields are required for all objects.

    exports.required =
      _id: joi.string().regex(/^[a-f0-9]{24}$/)
      tilbyder: joi.string()
      endret: joi.string()
      checksum: joi.string()
      lisens: joi.string().valid([
        'CC BY 3.0 NO'
        'CC BY-SA 3.0 NO'
        'CC BY-ND 3.0 NO'
        'CC BY-NC 3.0 NO'
        'CC BY-NC-SA 3.0 NO'
        'CC BY-NC-ND 3.0 NO'
        'CC BY 4.0'
        'CC BY-SA 4.0'
        'CC BY-ND 4.0'
        'CC BY-NC 4.0'
        'CC BY-NC-SA 4.0'
        'CC BY-NC-ND 4.0'
      ])
      navngiving: joi.string()
      status: joi.string().valid([
        'Offentlig'
        'Privat'
        'Kladd'
        'Slettet'
      ])

### Optional

There optional are optional for all objects.

    exports.optional =
      navn: joi.string()
      kommuner: joi.array().includes(joi.string())
      fylker: joi.array().includes(joi.string())
      områder: joi.array().includes(joi.string().regex(/^[a-f0-9]{24}$/))
      beskrivelse: joi.string()
      lenker: joi.array().includes(joi.object(
        tittel: joi.string().optional()
        type: joi.string().optional()
        url: joi.string().regex(/^https?:\/\//)
      ))
      tags: joi.array().includes(joi.string())
      privat: joi.object()
      grupper: joi.array().includes(joi.string().regex(/^[a-f0-9]{24}$/))
      steder: joi.array().includes(joi.string().regex(/^[a-f0-9]{24}$/))
      bilder: joi.array().includes(joi.string().regex(/^[a-f0-9]{24}$/))
      url: joi.string().regex(/^https?:\/\//)

## Data types

    exports.type =

### Bilder

      bilder:
        geojson: joi.object(
          type: joi.string().valid('Point').required()
          properties: joi.object(
            altitude: joi.number().integer()
          )
          coordinates: joi.array().includes(joi.number()).length(2).required()
        )
        fotograf: joi.object(
          navn: joi.string()
          telefon: joi.string()
          epost: joi.string().email()
        )
        eier: joi.object(
          navn: joi.string()
          telefon: joi.string()
          epost: joi.string().email()
          url: joi.string().regex(/^https?:\/\//)
        )
        img: joi.array().includes(joi.object(
          url: joi.string().regex(/^https?:\/\//).required()
          width: joi.number().integer()
          height: joi.number().integer()
          size: joi.number()
        ))

### Grupper

      grupper:
        geojson: joi.object(
          type: joi.string().valid('Polygon').required()
          coordinates: joi.array().includes(
            joi.array().includes(
              joi.array().includes(joi.number()).length(2)
            )
          ).required()
        )
        organisasjonsnr: joi.string()
        logo: joi.string().regex(/^https?:\/\//)
        ansatte: joi.number().integer()
        kontaktinfo: joi.array().includes(joi.object(
          tittel: joi.string()
          adresse1: joi.string()
          adresse2: joi.string()
          postnummer: joi.number().integer()
          poststed: joi.string()
          land: joi.string()
          telefon: joi.string()
          fax: joi.string()
          epost: joi.string().email()
          url: joi.string().regex(/^https?:\/\//)
        ))
        foreldregruppe: joi.string().regex(/^[a-f0-9]{24}$/)

### Turer

      turer:
        geojson: joi.object(
          type: joi.string().valid('LineString').required()
          coordinates: joi.array().includes(
            joi.array().includes(joi.number()).length(2)
          ).required()
        )
        distanse: joi.number()
        retning: joi.string().valid([
          "AB"
          "BA"
          "ABA"
          "BAB"
        ])
        adkomst: joi.string()
        gradering: joi.string().valid([
          "Enkel"
          "Middels"
          "Krevende"
          "Ekspert"
        ])
        passer_for: joi.array().includes(joi.string())
        tilrettelagt_for: joi.array().includes(joi.string())
        sesong: joi.array().includes(joi.number().integer().min(1).max(12))
        tidsbruk: joi.object(
          normal: joi.object(
            dager: joi.number().integer()
            timer: joi.number().integer()
            minutter: joi.number().integer()
          )
          min: joi.object(
            dager: joi.number().integer()
            timer: joi.number().integer()
            minutter: joi.number().integer()
          )
          max: joi.object(
            dager: joi.number().integer()
            timer: joi.number().integer()
            minutter: joi.number().integer()
          )
        )

### Områder

      områder:
        geojson: joi.object(
          type: joi.string().valid('Polygon').required()
          coordinates: joi.array().includes(
            joi.array().includes(
              joi.array().includes(joi.number()).length(2)
            )
          ).required()
        )
        foreldreområder: joi.array().includes(joi.string().regex(/^[a-f0-9]{24}$/))

### Steder

      steder:
        navn_alt: joi.array().includes(joi.string())
        ssr_id: joi.number().integer()
        geojson: joi.object(
          type: joi.string().valid('Point').required()
          properties: joi.object(
            altitude: joi.number().integer()
          )
          coordinates: joi.array().includes(joi.number()).length(2).required()
        )
        kommune: joi.string()
        fylke: joi.string()
        adkomst: joi.object(
          sommer: joi.string()
          vinter: joi.string()
        )
        tilrettelagt_for: joi.array().includes(joi.string())
        fasiliteter: joi.object()
        byggeår: joi.number().integer()
        besøksstatisikk: joi.object()
        betjeningsgrad: joi.string()
        kart: joi.string()
        turkart: joi.array().includes(joi.string())


provide proper return during Property.merge to only include newly merged element(s). see #40

# Property - controller of Object properties

The `Property` class is the *secretive shadowy* element that governs
`Object` behavior and are bound to the `Object` via
`Object.defineProperty`. It acts like a shadow `Proxy/Reflector` to
the `Object` instance and provides tight control via the
`Getter/Setter` interfaces.

The `Property` instances attach themselves to the `Object.__` property
and are rarely accessed directly - but they are the **fundamental**
actors that *actualize* YANG schema compliance into ordinary JS
objects.

Below are list of properties available to every instance of `Property`:

property | type | mapping | description
--- | --- | --- | ---
name   | string | direct | name of the property
schema | object | direct | a schema instance (usually [Yang](./src/yang.listcoffee))
state  | object | direct | *private* object holding internal state
container | object | access(state) | reference to object containing this property
configurable | boolean | getter(state) | defines whether this property can be redefined
enumerable   | boolean | getter(state) | defines whether this property is enumerable
content | any | computed | getter/setter for `state.value`
context | object | computed | dynamically generated using [context](./src/context.coffee)
root  | [Property](./src/property.litcoffee) | computed | dynamically returns the root Property instance
props | array(Property) | computed | returns children Property instances
key   | string/number | computed | conditionally returns unique key for Property if a list item
path  | [XPath](./src/xpath.coffee) | computed | dynamically generate XPath for this Property from root

## Class Property

    debug    = require('debug')('yang:property') if process.env.DEBUG?
    co       = require 'co'
    delegate = require 'delegates'
    Emitter  = require('events').EventEmitter
    context  = require './context'
    XPath    = require './xpath'

    class Property

      @property: (prop, desc) ->
        Object.defineProperty @prototype, prop, desc

      constructor: (@name, @schema={}) ->
        unless this instanceof Property then return new Property arguments...

        @state =
          value: null
          container: null
          configurable: true
          enumerable: @binding?
          mutable: @schema.config?.valueOf() isnt false
        Object.setPrototypeOf @state, Emitter.prototype

        @schema.kind ?= 'anydata'

        # Bind the get/set functions to call with 'this' bound to this
        # Property instance.  This is needed since native Object
        # Getter/Setter uses the Object itself as 'this'
        @set = @set.bind this
        @get = @get.bind this

        # soft freeze this instance
        Object.preventExtensions this

      delegate @prototype, 'state'
        .method 'once'
        .method 'on'
        .access 'container'
        .getter 'configurable'
        .getter 'enumerable'
        .getter 'mutable'

      delegate @prototype, 'schema'
        .getter 'kind'
        .getter 'type'
        .getter 'binding'

### Computed Properties

      @property 'content',
        get: -> @state.value
        set: (value) -> @set value, force: true

      @property 'context',
        get: ->
          ctx = Object.create(context)
          ctx.property = this
          Object.defineProperty ctx, 'action',
            get: -> @content if @content instanceof Function
          Object.preventExtensions ctx
          return ctx

      @property 'parent', get: -> @container?.__

      @property 'root',
        get: ->
          return this if @kind is 'module'
          if @parent instanceof Property then @parent.root
          else this

      @property 'props',
        get: -> prop for k, prop of @content?.__props__

      @property 'key',
        get: ->
          return unless @schema is @parent?.schema
          switch
            when @content not instanceof Object  then @name + 1
            when @content.hasOwnProperty('@key') then @content['@key']
            when Array.isArray @container
              for idx, item of @container when item is @content
                idx = Number(idx) unless (Number.isNaN (Number idx))
                return idx+1
              return undefined

      # TODO: cache this in @state so that it's not reconstructed every time
      @property 'path',
        get: ->
          return XPath.parse '/', @schema if this is @root
          key = @key
          #debug? "[#{@name}:path] #{@kind}(#{@name}) has #{key} #{typeof key}"
          entity = switch typeof key
            when 'number' then ".[#{key}]"
            when 'string' then ".[key() = '#{key}']"
            else switch
              when @kind is 'list' then @schema.datakey
              else @name
          #debug? "[#{@name}:path] #{@parent.name} + #{entity}"
          @parent.path.append entity

## Instance-level methods

      emit: (event) ->
        @state.emit arguments...
        unless this is @root
          debug? "[emit] '#{event}' from '#{@name}' to '#{@root.name}'"
          @root.emit arguments...

### join (obj)

This call is the primary mechanism via which the `Property` instance
attaches itself to the provided target `obj`. It registers itself into
`obj.__props__` as well as defined in the target `obj` via
`Object.defineProperty`.

      join: (obj, opts={ replace: false, suppress: false }) ->
        return obj unless obj instanceof Object

        # when joining for the first time, apply the data found in the
        # 'obj' into the property instance
        unless @container?
          debug? "[join] #{@kind}(#{@name}) assigning container"
          debug? opts
          @container = obj
          unless opts.replace is true
            opts.suppress = true
            exists = obj[@name]
            unless exists?.__ instanceof Property
              @set obj[@name], opts
            return obj

        debug? "[join] #{@kind}(#{@name}) into #{obj.constructor.name} container"
        if Array.isArray(obj) and Array.isArray(@content)
          debug? @content
          throw @error "cannot join array property into list container"
        if @kind is 'list' and not Array.isArray(obj) and @content? and not Array.isArray(@content)
          throw @error "cannot join non-list array property into containing object"

        unless obj.hasOwnProperty '__props__'
          Object.defineProperty obj, '__props__', value: {}
        obj.__props__[@name] = this
        try Object.defineProperty obj, @name, this
        @emit 'update', this if this is @root or not opts.suppress
        return obj

### get (pattern)

This is the main `Getter` for the target object's property value. When
called with optional `pattern` it will perform an internal
[find](#find-xpath) operation to traverse/locate that value being
requested instead of returning its own `@content`.

It also provides special handling based on different types of
`@content` currently held.

When `@content` is a function, it will call it with the current
`@context` instance as the bound context for the function being
called.

      get: (pattern) -> switch
        when pattern?
          match = @find pattern
          debug? match
          switch
            when match.length is 1 then match[0].get()
            when match.length > 1  then match.map (x) -> x.get()
            else undefined
        when @kind in [ 'rpc', 'action' ] then @invoke.bind this
        else
          @binding.call @context if @binding?
          # TODO: should utilize yield to resolve promises
          @content

### set (value)

This is the main `Setter` for the target object's property value.  It
utilizes internal `@schema` attribute if available to enforce schema
validations.

      set: (value, opts={ force: false, suppress: false }) ->
        debug? "[set] #{@kind}(#{@name}) enter with:"
        debug? value
        debug? opts
        unless @mutable or not value? or opts.force
          throw @error "cannot set data on read-only element"

        try Object.defineProperty value, '__', configurable: true, value: this
        value = switch
          when @schema.apply?
            @schema.apply value, @context.with(opts)
          else value
        try Object.defineProperty value, '__', value: this

        @state.prev = @state.value
        @state.enumerable = value? or @binding?
        @state.value = value

        try @join @container, opts
        catch e then @state.value = @state.prev; throw e
        debug? "[set] #{@kind}(#{@name}) completed"
        return this

### merge (value)

Performs a granular merge of `value` into existing `@content` if
available, otherwise performs [set](#set-value) operation.

      merge: (value, opts={ replace: true, suppress: false }) ->
        unless typeof @content is 'object' then return @set value

        value = value[@name] if value? and value.hasOwnProperty @name
        return this unless typeof value is 'object'

        if Array.isArray @content
          length = @content.length
          debug? "[merge] merging into existing Array(#{length}) for #{@name}"
          value = [ value ] unless Array.isArray value
          value = @schema.apply value
          # apply schema on a combined array
          combine = @content.concat value
          attr.apply combine for attr in @schema.attrs
          value.forEach (item) =>
            item.__.name += length
            item.__.join @content, opts
          prop = new Property @name, @schema
          prop.state.container = @container
          prop.state.value = value
          return prop
        else
          # TODO: protect this as a transaction?
          @content[k] = v for k, v of value when @content.hasOwnProperty k
          # TODO: need to reapply schema to self
          return this

### create (value)

A simple convenience wrap around the above [merge](#merge-value) operation.

      create: (value) ->
        if not @content? and @kind is 'list' and not Array.isArray value
          value = [ value ]
        res = @merge value, replace: false
        @emit 'create', this
        return res

### remove

The reverse of [join](#join-obj), it will detach itself from the
`@parent` containing object.

      remove: ->
        if @key?
          @container.splice @name, 1
          #delete @parent[@name]
        else
          @state.enumerable = false
          @state.value = undefined unless @kind is 'list'
          @join @container
        @emit 'delete', this
        return this

### find (pattern)

This helper routine can be used to allow traversal to other elements
in the data tree from the relative location of the current `Property`
instance. It returns matching `Property` instances based on the
provided `pattern` in the form of XPATH or YPATH.

It is internally used via [get](#get) and generally used inside
controller logic bound inside the [Yang expression](./yang.litcoffee)
as well as event handler listening on [Model](./model.litcoffee)
events.

It *always* returns an array (empty to denote no match) unless it
encounters an error, in which case it will throw an Error.

      find: (pattern='.', opts={}) ->
        xpath = switch
          when pattern instanceof XPath then pattern
          else XPath.parse pattern, @schema
        debug? "[#{@path}] finding #{pattern} starting with #{xpath.tag}"
        if opts.root or not @container? or xpath.tag not in [ '/', '..' ]
          debug? "[#{@path}] #{@name} applying '#{xpath}'"
          debug? @content
          xpath.apply(@content).props ? []
        else switch
          when xpath.tag is '/'  and @parent? then @parent.find xpath, opts
          when xpath.tag is '..' and @parent? then @parent.find xpath.xpath, opts
          else []

### invoke

A convenience wrap to a Property instance that holds a function to
perform a Promise-based execution.

      invoke: (args...) ->
        try
          ctx = @context
          unless ctx.action?
            throw @error "cannot invoke on a property without function"
          debug? "[invoke] calling #{@name} method"
          # TODO: need to ensure unique instance of 'input' and 'output' for concurrency
          ctx.input = args[0] ? {}
          ctx.action.apply ctx, args
          return co -> yield Promise.resolve ctx.output
        catch e
          return Promise.reject e

### error (msg)

Provides more contextual error message pertaining to the Property instance.

      error: (msg, ctx=this) ->
        at = "#{@path}"
        at += @name if at is '/'
        res = new Error "[#{at}] #{msg}"
        res.name = 'PropertyError'
        res.context = ctx
        return res

### inspect

      inspect: ->
        return {
          name:   @schema.tag ? @name
          kind:   @schema.kind
          key:    @key
          xpath:  @path.toString()
          schema: @schema.toJSON tag: false, extended: true
          active: @enumerable
          readonly: not @mutable
        }

### toJSON

This call creates a new copy of the current `Property.content`
completely detached/unbound to the underlying data schema. It's main
utility is to represent the current data state for subsequent
serialization/transmission. It accepts optional argument `tag` which
when called with `false` will not tag the produced object with the
property's `@name`.

      toJSON: (tag=true) ->
        copy = (src) ->
          return unless src? and typeof src isnt 'function'
          if typeof src is 'object'
            try res = new src.constructor
            catch then res = {}
            res[k] = copy v for own k, v of src
            return res
          src.constructor.call src, src
        value = copy @get()
        value ?= [] if @kind is 'list'
        if tag
          name = switch
            when @kind is 'list' then @schema.datakey
            else @name
          "#{name}": value
        else value

## Export Property Class

    module.exports = Property

# Property - controller of Object properties

The `Property` class is the *secretive shadowy* element that governs
`Object` behavior and are bound to the `Object` via
`Object.defineProperty`. It acts like a shadow `Proxy/Reflector` to
the `Object` instance and provides tight control via the
`Getter/Setter` interfaces.

The `Property` instances attach themselves to the `Object.__` property
and are rarely accessed directly - but they are the **fundamental**
actors that *actualize* YANG schema compliance into ordinary JS
objects.

Below are list of properties available to every instance of `Property`:

property | type | mapping | description
--- | --- | --- | ---
name   | string | direct | name of the property
schema | object | direct | a schema instance (usually [Yang](./src/yang.listcoffee))
state  | object | direct | *private* object holding internal state
container | object | access(state) | reference to object containing this property
configurable | boolean | getter(state) | defines whether this property can be redefined
enumerable   | boolean | getter(state) | defines whether this property is enumerable
content | any | computed | getter/setter for `state.value`
context | object | computed | dynamically generated using [context](./src/context.coffee)
root  | [Property](./src/property.litcoffee) | computed | dynamically returns the root Property instance
props | array(Property) | computed | returns children Property instances
key   | string/number | computed | conditionally returns unique key for Property if a list item
path  | [XPath](./src/xpath.coffee) | computed | dynamically generate XPath for this Property from root

## Class Property

    debug    = require('debug')('yang:property') if process.env.DEBUG?
    co       = require 'co'
    delegate = require 'delegates'
    Emitter  = require('events').EventEmitter
    context  = require './context'
    XPath    = require './xpath'

    class Property

      @property: (prop, desc) ->
        Object.defineProperty @prototype, prop, desc

      constructor: (@name, @schema={}) ->
        unless this instanceof Property then return new Property arguments...

        @state =
          value: null
          container: null
          configurable: true
          enumerable: @binding?
          mutable: @schema.config?.valueOf() isnt false
        Object.setPrototypeOf @state, Emitter.prototype

        @schema.kind ?= 'anydata'

        # Bind the get/set functions to call with 'this' bound to this
        # Property instance.  This is needed since native Object
        # Getter/Setter uses the Object itself as 'this'
        @set = @set.bind this
        @get = @get.bind this

        # soft freeze this instance
        Object.preventExtensions this

      delegate @prototype, 'state'
        .method 'once'
        .method 'on'
        .access 'container'
        .getter 'configurable'
        .getter 'enumerable'
        .getter 'mutable'

      delegate @prototype, 'schema'
        .getter 'kind'
        .getter 'type'
        .getter 'binding'

### Computed Properties

      @property 'content',
        get: -> @state.value
        set: (value) -> @set value, force: true

      @property 'context',
        get: ->
          ctx = Object.create(context)
          ctx.property = this
          Object.defineProperty ctx, 'action',
            get: -> @content if @content instanceof Function
          Object.preventExtensions ctx
          return ctx

      @property 'parent', get: -> @container?.__

      @property 'root',
        get: ->
          return this if @kind is 'module'
          if @parent instanceof Property then @parent.root
          else this

      @property 'props',
        get: -> prop for k, prop of @content?.__props__

      @property 'key',
        get: ->
          return unless @schema is @parent?.schema
          switch
            when @content not instanceof Object  then @name + 1
            when @content.hasOwnProperty('@key') then @content['@key']
            when Array.isArray @container
              for idx, item of @container when item is @content
                idx = Number(idx) unless (Number.isNaN (Number idx))
                return idx+1
              return undefined

      # TODO: cache this in @state so that it's not reconstructed every time
      @property 'path',
        get: ->
          return XPath.parse '/', @schema if this is @root
          key = @key
          #debug? "[#{@name}:path] #{@kind}(#{@name}) has #{key} #{typeof key}"
          entity = switch typeof key
            when 'number' then ".[#{key}]"
            when 'string' then ".[key() = '#{key}']"
            else switch
              when @kind is 'list' then @schema.datakey
              else @name
          #debug? "[#{@name}:path] #{@parent.name} + #{entity}"
          @parent.path.append entity

## Instance-level methods

      emit: (event) ->
        @state.emit arguments...
        unless this is @root
          debug? "[emit] '#{event}' from '#{@name}' to '#{@root.name}'"
          @root.emit arguments...

### join (obj)

This call is the primary mechanism via which the `Property` instance
attaches itself to the provided target `obj`. It registers itself into
`obj.__props__` as well as defined in the target `obj` via
`Object.defineProperty`.

      join: (obj, opts={ replace: false, suppress: false }) ->
        return obj unless obj instanceof Object

        # when joining for the first time, apply the data found in the
        # 'obj' into the property instance
        unless @container?
          debug? "[join] #{@kind}(#{@name}) assigning container"
          debug? opts
          @container = obj
          unless opts.replace is true
            opts.suppress = true
            exists = obj[@name]
            unless exists?.__ instanceof Property
              @set obj[@name], opts
            return obj

        debug? "[join] #{@kind}(#{@name}) into #{obj.constructor.name} container"
        if Array.isArray(obj) and Array.isArray(@content)
          debug? @content
          throw @error "cannot join array property into list container"
        if @kind is 'list' and not Array.isArray(obj) and @content? and not Array.isArray(@content)
          throw @error "cannot join non-list array property into containing object"

        unless obj.hasOwnProperty '__props__'
          Object.defineProperty obj, '__props__', value: {}
        obj.__props__[@name] = this
        try Object.defineProperty obj, @name, this
        @emit 'update', this if this is @root or not opts.suppress
        return obj

### get (pattern)

This is the main `Getter` for the target object's property value. When
called with optional `pattern` it will perform an internal
[find](#find-xpath) operation to traverse/locate that value being
requested instead of returning its own `@content`.

It also provides special handling based on different types of
`@content` currently held.

When `@content` is a function, it will call it with the current
`@context` instance as the bound context for the function being
called.

      get: (pattern) -> switch
        when pattern?
          match = @find pattern
          debug? match
          switch
            when match.length is 1 then match[0].get()
            when match.length > 1  then match.map (x) -> x.get()
            else undefined
        when @kind in [ 'rpc', 'action' ] then @invoke.bind this
        else
          @binding.call @context if @binding?
          # TODO: should utilize yield to resolve promises
          @content

### set (value)

This is the main `Setter` for the target object's property value.  It
utilizes internal `@schema` attribute if available to enforce schema
validations.

      set: (value, opts={ force: false, suppress: false }) ->
        debug? "[set] #{@kind}(#{@name}) enter with:"
        debug? value
        debug? opts
        unless @mutable or not value? or opts.force
          throw @error "cannot set data on read-only element"

        try Object.defineProperty value, '__', configurable: true, value: this
        value = switch
          when @schema.apply?
            @schema.apply value, @context.with(opts)
          else value
        try Object.defineProperty value, '__', value: this

        @state.prev = @state.value
        @state.enumerable = value? or @binding?
        @state.value = value

        try @join @container, opts
        catch e then @state.value = @state.prev; throw e
        debug? "[set] #{@kind}(#{@name}) completed"
        return this

### merge (value)

Performs a granular merge of `value` into existing `@content` if
available, otherwise performs [set](#set-value) operation.

      merge: (value, opts={ replace: true, suppress: false }) ->
        unless typeof @content is 'object' then return @set value

        value = value[@name] if value? and value.hasOwnProperty @name
        return unless typeof value is 'object'

        if Array.isArray @content
          length = @content.length
          debug? "[merge] merging into existing Array(#{length}) for #{@name}"
          value = [ value ] unless Array.isArray value
          try Object.defineProperty value, '__', configurable: true, value: this
          value = @schema.apply value
          # apply schema on a combined array
          combine = @content.concat value
          attr.apply combine for attr in @schema.attrs
          value.forEach (item) =>
            item.__.name += length
            item.__.join @content, opts
          return value[0].__ if value.length is 1
        else
          # TODO: protect this as a transaction?
          @content[k] = v for k, v of value when @content.hasOwnProperty k
          # TODO: need to reapply schema to self
        return this

### create (value)

A simple convenience wrap around the above [merge](#merge-value) operation.

      create: (value) ->
        if not @content? and @kind is 'list' and not Array.isArray value
          value = [ value ]
        res = @merge value, replace: false
        @emit 'create', this
        return res

### remove

The reverse of [join](#join-obj), it will detach itself from the
`@parent` containing object.

      remove: ->
        if @key?
          @container.splice @name, 1
          #delete @parent[@name]
        else
          @state.enumerable = false
          @state.value = undefined unless @kind is 'list'
          @join @container
        @emit 'delete', this
        return this

### find (pattern)

This helper routine can be used to allow traversal to other elements
in the data tree from the relative location of the current `Property`
instance. It returns matching `Property` instances based on the
provided `pattern` in the form of XPATH or YPATH.

It is internally used via [get](#get) and generally used inside
controller logic bound inside the [Yang expression](./yang.litcoffee)
as well as event handler listening on [Model](./model.litcoffee)
events.

It *always* returns an array (empty to denote no match) unless it
encounters an error, in which case it will throw an Error.

      find: (pattern='.', opts={}) ->
        xpath = switch
          when pattern instanceof XPath then pattern
          else XPath.parse pattern, @schema
        debug? "[#{@path}] finding #{pattern} starting with #{xpath.tag}"
        if opts.root or not @container? or xpath.tag not in [ '/', '..' ]
          debug? "[#{@path}] #{@name} applying '#{xpath}'"
          debug? @content
          xpath.apply(@content).props ? []
        else switch
          when xpath.tag is '/'  and @parent? then @parent.find xpath, opts
          when xpath.tag is '..' and @parent? then @parent.find xpath.xpath, opts
          else []

### invoke

A convenience wrap to a Property instance that holds a function to
perform a Promise-based execution.

      invoke: (args...) ->
        try
          ctx = @context
          unless ctx.action?
            throw @error "cannot invoke on a property without function"
          debug? "[invoke] calling #{@name} method"
          # TODO: need to ensure unique instance of 'input' and 'output' for concurrency
          ctx.input = args[0] ? {}
          ctx.action.apply ctx, args
          return co -> yield Promise.resolve ctx.output
        catch e
          return Promise.reject e

### error (msg)

Provides more contextual error message pertaining to the Property instance.

      error: (msg, ctx=this) ->
        at = "#{@path}"
        at += @name if at is '/'
        res = new Error "[#{at}] #{msg}"
        res.name = 'PropertyError'
        res.context = ctx
        return res

### inspect

      inspect: ->
        return {
          name:   @schema.tag ? @name
          kind:   @schema.kind
          key:    @key
          xpath:  @path.toString()
          schema: @schema.toJSON tag: false, extended: true
          active: @enumerable
          readonly: not @mutable
        }

### toJSON

This call creates a new copy of the current `Property.content`
completely detached/unbound to the underlying data schema. It's main
utility is to represent the current data state for subsequent
serialization/transmission. It accepts optional argument `tag` which
when called with `false` will not tag the produced object with the
property's `@name`.

      toJSON: (tag=true) ->
        copy = (src) ->
          return unless src? and typeof src isnt 'function'
          if typeof src is 'object'
            try res = new src.constructor
            catch then res = {}
            res[k] = copy v for own k, v of src
            return res
          src.constructor.call src, src
        value = copy @get()
        value ?= [] if @kind is 'list'
        if tag
          name = switch
            when @kind is 'list' then @schema.datakey
            else @name
          "#{name}": value
        else value

## Export Property Class

    module.exports = Property


fix detection for invalid array joining another array

# Property - controller of Object properties

The `Property` class is the *secretive shadowy* element that governs
`Object` behavior and are bound to the `Object` via
`Object.defineProperty`. It acts like a shadow `Proxy/Reflector` to
the `Object` instance and provides tight control via the
`Getter/Setter` interfaces.

The `Property` instances attach themselves to the `Object.__` property
and are rarely accessed directly - but they are the **fundamental**
actors that *actualize* YANG schema compliance into ordinary JS
objects.

## Class Property

    debug    = require('debug')('yang:property')
    co       = require 'co'
    delegate = require 'delegates'
    context  = require './context'
    XPath    = require './xpath'

    class Property

      @property: (prop, desc) ->
        Object.defineProperty @prototype, prop, desc

      constructor: (@name, @schema={}) ->
        unless this instanceof Property then return new Property arguments...

        @state  =
          value: null
          parent: null
          configurable: true
          enumerable: false

        @schema.kind   ?= 'anydata'
        @schema.config ?= true

        # Bind the get/set functions to call with 'this' bound to this
        # Property instance.  This is needed since native Object
        # Getter/Setter uses the Object itself as 'this'
        @set = @set.bind this
        @get = @get.bind this

        # soft freeze this instance
        Object.preventExtensions this

      delegate @prototype, 'state'
        .access 'parent'
        .getter 'configurable'
        .getter 'enumerable'

      delegate @prototype, 'schema'
        .getter 'kind'
        .getter 'type'
        .getter 'config'
        .getter 'binding'

### Computed Properties

      @property 'content',
        get: -> @state.value
        set: (value) -> @set value, force: true

      @property 'context',
        get: ->
          ctx = Object.create(context)
          ctx.property = this
          ctx.state = {}
          Object.defineProperty ctx, 'action',
            get: -> @content if @content instanceof Function
          return ctx

      @property 'root',
        get: ->
          #debug "looking for root from #{@name} has parent: #{@parent?}"
          return this if @kind is 'module'
          if @parent?.__ instanceof Property then @parent.__.root
          else this

      @property 'props',
        get: -> prop for k, prop of @content?.__props__

      @property 'key',
        get: -> switch
          when @content not instanceof Object  then undefined
          when @content.hasOwnProperty('@key') then @content['@key']
          when Array.isArray @parent
            for idx, item of @parent when item is @content
              idx = Number(idx) unless (Number.isNaN (Number idx))
              return idx+1
            return undefined

      @property 'path',
        get: ->
          return XPath.parse '/', @schema if this is @root
          entity = switch typeof @key
            when 'number' then ".[#{@key}]"
            when 'string' then ".[key() = '#{@key}']"
            else switch
              when @kind is 'list' then @schema.datakey
              else @name
          debug "[#{@name}] path: #{@parent.__.name} + #{entity}"
          @parent.__.path.append entity

## Instance-level methods

      emit: (event) ->
        return if this is @root
        debug "[emit] '#{event}' from '#{@name}' to '#{@root.name}'"
        @root.emit arguments...

### join (obj)

This call is the primary mechanism via which the `Property` instance
attaches itself to the provided target `obj`. It registers itself into
`obj.__props__` as well as defined in the target `obj` via
`Object.defineProperty`.

      join: (obj, opts={ replace: false, suppress: false }) ->
        return obj unless obj instanceof Object

        # when joining for the first time, apply the data found in the
        # 'obj' into the property instance
        unless @parent?
          debug "[join] assigning parent to new property: #{@name}"
          debug obj
          @parent = obj
          @set obj[@name] unless opts.replace is true
          return obj

        debug "[join] object with new property: #{@name}"

        if Array.isArray(obj) and Array.isArray(@content)
          throw @error "cannot join array property into containing list"
        if @kind is 'list' and not Array.isArray(obj) and @content? and not Array.isArray(@content)
          throw @error "cannot join non-list array property into containing object"

        unless obj.hasOwnProperty '__props__'
          Object.defineProperty obj, '__props__', value: {}
        prev = obj.__props__[@name]
        obj.__props__[@name] = this
        Object.defineProperty obj, @name, this
        @emit 'update', this, prev unless opts.suppress
        return obj

### get (pattern)

This is the main `Getter` for the target object's property value. When
called with optional `pattern` it will perform an internal
[find](#find-xpath) operation to traverse/locate that value being
requested instead of returning its own `@content`.

It also provides special handling based on different types of
`@content` currently held.

When `@content` is a function, it will call it with the current
`@context` instance as the bound context for the function being
called.

      get: (pattern) -> switch
        when pattern?
          match = @find pattern
          switch
            when match.length is 1 then match[0].get()
            when match.length > 1  then match.map (x) -> x.get()
            else undefined
        when @kind in [ 'rpc', 'action' ] then @invoke.bind this
        else
          @binding.call @context if @binding?
          # TODO: should utilize yield to resolve promises
          @content

### set (value)

This is the main `Setter` for the target object's property value.  It
utilizes internal `@schema` attribute if available to enforce schema
validations.

      set: (value, opts={ force: false, suppress: false }) ->
        debug "[set] setting '#{@name}' with:"
        debug value

        debug "[set] #{@kind}(#{@name}) attaching '__' property"
        try Object.defineProperty value, '__', configurable: true, value: this

        unless not value? or opts.force or @config?.valueOf() isnt false
          throw @error "cannot set data on read-only element"

        value = switch
          when @schema.apply? then @schema.apply value, @context
          else value

        try
          Object.defineProperty value, '__', value: this
          delete value[k] for own k of value when k not of value.__props__

        @state.prev = @state.value
        @state.enumerable = value? or @binding?
        @state.value = value

        try @join @parent, opts
        catch e then @state.value = @state.prev; throw e
        return this

### merge (value)

Performs a granular merge of `value` into existing `@content` if
available, otherwise performs [set](#set-value) operation.

      merge: (value, opts={ replace: true, suppress: false }) ->
        unless typeof @content is 'object' then return @set value

        value = value[@name] if value? and value.hasOwnProperty @name
        return unless typeof value is 'object'

        if Array.isArray @content
          length = @content.length
          debug "[merge] merging into existing Array(#{length}) for #{@name}"
          value = [ value ] unless Array.isArray value
          value = @schema.apply value
          value.forEach (item) =>
            item.__.name += length
            item.__.join @content, opts
          # TODO: need to re-apply schema on the 'list'
        else
          # TODO: protect this as a transaction?
          @content[k] = v for k, v of value when @content.hasOwnProperty k
          # TODO: need to reapply schema to self
        return this

### create (value)

A simple convenience wrap around the above [merge](#merge-value) operation.

      create: (value) ->
        @merge value, replace: false
        @emit 'create', this

### remove

The reverse of [join](#join-obj), it will detach itself from the
`@parent` containing object.

      remove: ->
        if @key?
          @parent.splice @name, 1
          #delete @parent[@name]
        else
          @state.enumerable = false
          @state.value = undefined unless @kind is 'list'
          @join @parent
        @emit 'delete', this
        return this

### find (pattern)

This helper routine can be used to allow traversal to other elements
in the data tree from the relative location of the current `Property`
instance. It returns matching `Property` instances based on the
provided `pattern` in the form of XPATH or YPATH.

It is internally used via [get](#get) and generally used inside
controller logic bound inside the [Yang expression](./yang.litcoffee)
as well as event handler listening on [Model](./model.litcoffee)
events.

      find: (pattern='.', opts={}) ->
        xpath = switch
          when pattern instanceof XPath then pattern
          else XPath.parse pattern, @schema
        debug "[#{@path}] finding #{pattern} starting with #{xpath.tag}"
        if opts.root or not @parent? or xpath.tag not in [ '/', '..' ]
          debug "[#{@path}] #{@name} applying '#{xpath}'"
          debug @content
          xpath.apply(@content).props
        else switch
          when xpath.tag is '/'  and @parent.__? then @parent.__.find xpath, opts
          when xpath.tag is '..' and @parent.__? then @parent.__.find xpath.xpath, opts
          else []

### invoke

A convenience wrap to a Property instance that holds a function to
perform a Promise-based execution.

      invoke: (args...) ->
        try
          ctx = @context
          unless ctx.action?
            throw @error "cannot invoke on a property without function"
          debug "[invoke] calling #{@name} method"
          # TODO: need to ensure unique instance of 'input' and 'output' for concurrency
          ctx.input = args[0] ? {}
          ctx.action.apply ctx, args
          return co -> yield Promise.resolve ctx.output
        catch e
          return Promise.reject e

### error (msg)

Provides more contextual error message pertaining to the Property instance.

      error: (msg, ctx=this) ->
        at = "#{@path}"
        at += @name if at is '/'
        res = new Error "[#{at}] #{msg}"
        res.name = 'PropertyError'
        res.context = ctx
        return res

### valueOf (tag)

This call creates a new copy of the current `Property.content`
completely detached/unbound to the underlying data schema. It's main
utility is to represent the current data state for subsequent
serialization/transmission. It accepts optional argument `tag` which
when called with `false` will not tag the produced object with the
property's `@name`.

      valueOf: (tag=true) ->
        copy = (src) ->
          return unless src? and typeof src isnt 'function'
          if typeof src is 'object'
            try res = new src.constructor
            catch then res = {}
            res[k] = copy v for own k, v of src
            return res
          src.constructor.call src, src
        value = copy @get()
        value ?= [] if @kind is 'list'
        if tag
          name = switch
            when @kind is 'list' then @schema.datakey
            else @name
          "#{name}": value
        else value

## Export Property Class

    module.exports = Property

# Property - controller of Object properties

The `Property` class is the *secretive shadowy* element that governs
`Object` behavior and are bound to the `Object` via
`Object.defineProperty`. It acts like a shadow `Proxy/Reflector` to
the `Object` instance and provides tight control via the
`Getter/Setter` interfaces.

The `Property` instances attach themselves to the `Object.__` property
and are rarely accessed directly - but they are the **fundamental**
actors that *actualize* YANG schema compliance into ordinary JS
objects.

## Class Property

    debug    = require('debug')('yang:property')
    co       = require 'co'
    delegate = require 'delegates'
    context  = require './context'
    XPath    = require './xpath'

    class Property

      @property: (prop, desc) ->
        Object.defineProperty @prototype, prop, desc

      constructor: (@name, @schema={}) ->
        unless this instanceof Property then return new Property arguments...

        @state  =
          value: null
          parent: null
          configurable: true
          enumerable: false

        @schema.kind   ?= 'anydata'
        @schema.config ?= true

        # Bind the get/set functions to call with 'this' bound to this
        # Property instance.  This is needed since native Object
        # Getter/Setter uses the Object itself as 'this'
        @set = @set.bind this
        @get = @get.bind this

        # soft freeze this instance
        Object.preventExtensions this

      delegate @prototype, 'state'
        .access 'parent'
        .getter 'configurable'
        .getter 'enumerable'

      delegate @prototype, 'schema'
        .getter 'kind'
        .getter 'type'
        .getter 'config'
        .getter 'binding'

### Computed Properties

      @property 'content',
        get: -> @state.value
        set: (value) -> @set value, force: true

      @property 'context',
        get: ->
          ctx = Object.create(context)
          ctx.property = this
          ctx.state = {}
          Object.defineProperty ctx, 'action',
            get: -> @content if @content instanceof Function
          return ctx

      @property 'root',
        get: ->
          #debug "looking for root from #{@name} has parent: #{@parent?}"
          return this if @kind is 'module'
          if @parent?.__ instanceof Property then @parent.__.root
          else this

      @property 'props',
        get: -> prop for k, prop of @content?.__props__

      @property 'key',
        get: -> switch
          when @content not instanceof Object  then undefined
          when @content.hasOwnProperty('@key') then @content['@key']
          when Array.isArray @parent
            for idx, item of @parent
              return idx if item is @content
            return undefined

      @property 'path',
        get: ->
          return XPath.parse '/', @schema if this is @root
          entity = switch typeof @key
            when 'number' then ".[#{@key}]"
            when 'string' then ".[key() = '#{@key}']"
            else switch
              when @kind is 'list' then @schema.datakey
              else @name
          debug "[#{@name}] path: #{@parent.__.name} + #{entity}"
          @parent.__.path.append entity

## Instance-level methods

      emit: (event) ->
        return if this is @root
        debug "[emit] '#{event}' from '#{@name}' to '#{@root.name}'"
        @root.emit arguments...

### join (obj)

This call is the primary mechanism via which the `Property` instance
attaches itself to the provided target `obj`. It registers itself into
`obj.__props__` as well as defined in the target `obj` via
`Object.defineProperty`.

      join: (obj, opts={ replace: false, suppress: false }) ->
        return obj unless obj instanceof Object

        # when joining for the first time, apply the data found in the
        # 'obj' into the property instance
        unless @parent?
          debug "[join] assigning parent to new property: #{@name}"
          debug obj
          @parent = obj
          @set obj[@name] unless opts.replace is true
          return obj

        debug "[join] object with new property: #{@name}"

        if Array.isArray obj and Array.isArray @content
          throw @error "cannot join array property into containing list"
        if @kind is 'list' and not Array.isArray(obj) and @content? and not Array.isArray(@content)
          throw @error "cannot join non-list array property into containing object"

        unless obj.hasOwnProperty '__props__'
          Object.defineProperty obj, '__props__', value: {}
        prev = obj.__props__[@name]
        obj.__props__[@name] = this
        Object.defineProperty obj, @name, this
        @emit 'update', this, prev unless opts.suppress
        return obj

### get (pattern)

This is the main `Getter` for the target object's property value. When
called with optional `pattern` it will perform an internal
[find](#find-xpath) operation to traverse/locate that value being
requested instead of returning its own `@content`.

It also provides special handling based on different types of
`@content` currently held.

When `@content` is a function, it will call it with the current
`@context` instance as the bound context for the function being
called.

      get: (pattern) -> switch
        when pattern?
          match = @find pattern
          switch
            when match.length is 1 then match[0].get()
            when match.length > 1  then match.map (x) -> x.get()
            else undefined
        when @kind in [ 'rpc', 'action' ] then @invoke.bind this
        else
          @binding.call @context if @binding?
          # TODO: should utilize yield to resolve promises
          @content

### set (value)

This is the main `Setter` for the target object's property value.  It
utilizes internal `@schema` attribute if available to enforce schema
validations.

      set: (value, opts={ force: false, suppress: false }) ->
        debug "[set] setting '#{@name}' with:"
        debug value

        debug "[set] #{@kind}(#{@name}) attaching '__' property"
        try Object.defineProperty value, '__', configurable: true, value: this

        unless not value? or opts.force or @config?.valueOf() isnt false
          throw @error "cannot set data on read-only element"

        value = switch
          when @schema.apply? then @schema.apply value, @context
          else value

        try
          Object.defineProperty value, '__', value: this
          delete value[k] for own k of value when k not of value.__props__

        @state.prev = @state.value
        @state.enumerable = value? or @binding?
        @state.value = value

        try @join @parent, opts
        catch e then @state.value = @state.prev; throw e
        return this

### merge (value)

Performs a granular merge of `value` into existing `@content` if
available, otherwise performs [set](#set-value) operation.

      merge: (value, opts={ replace: true, suppress: false }) ->
        unless typeof @content is 'object' then return @set value

        value = value[@name] if value? and value.hasOwnProperty @name
        return unless typeof value is 'object'

        if Array.isArray @content
          length = @content.length
          debug "[merge] merging into existing Array(#{length}) for #{@name}"
          value = [ value ] unless Array.isArray value
          value = @schema.apply value
          value.forEach (item) =>
            item.__.name += length
            item.__.join @content, opts
          # TODO: need to re-apply schema on the 'list'
        else
          # TODO: protect this as a transaction?
          @content[k] = v for k, v of value when @content.hasOwnProperty k
          # TODO: need to reapply schema to self
        return this

### create (value)

A simple convenience wrap around the above [merge](#merge-value) operation.

      create: (value) ->
        @merge value, replace: false
        @emit 'create', this

### remove

The reverse of [join](#join-obj), it will detach itself from the
`@parent` containing object.

      remove: ->
        if @key?
          @parent.splice @name, 1
          #delete @parent[@name]
        else
          @state.enumerable = false
          @state.value = undefined unless @kind is 'list'
          @join @parent
        @emit 'delete', this
        return this

### find (pattern)

This helper routine can be used to allow traversal to other elements
in the data tree from the relative location of the current `Property`
instance. It returns matching `Property` instances based on the
provided `pattern` in the form of XPATH or YPATH.

It is internally used via [get](#get) and generally used inside
controller logic bound inside the [Yang expression](./yang.litcoffee)
as well as event handler listening on [Model](./model.litcoffee)
events.

      find: (pattern='.', opts={}) ->
        xpath = switch
          when pattern instanceof XPath then pattern
          else XPath.parse pattern, @schema
        debug "[#{@path}] finding #{pattern} starting with #{xpath.tag}"
        if opts.root or not @parent? or xpath.tag not in [ '/', '..' ]
          debug "[#{@path}] #{@name} applying '#{xpath}'"
          debug @content
          xpath.apply(@content).props
        else switch
          when xpath.tag is '/'  and @parent.__? then @parent.__.find xpath, opts
          when xpath.tag is '..' and @parent.__? then @parent.__.find xpath.xpath, opts
          else []

### invoke

A convenience wrap to a Property instance that holds a function to
perform a Promise-based execution.

      invoke: (args...) ->
        try
          ctx = @context
          unless ctx.action?
            throw @error "cannot invoke on a property without function"
          debug "[invoke] calling #{@name} method"
          # TODO: need to ensure unique instance of 'input' and 'output' for concurrency
          ctx.input = args[0] ? {}
          ctx.action.apply ctx, args
          return co -> yield Promise.resolve ctx.output
        catch e
          return Promise.reject e

### error (msg)

Provides more contextual error message pertaining to the Property instance.

      error: (msg, ctx=this) ->
        at = "#{@path}"
        at += @name if at is '/'
        res = new Error "[#{at}] #{msg}"
        res.name = 'PropertyError'
        res.context = ctx
        return res

### valueOf (tag)

This call creates a new copy of the current `Property.content`
completely detached/unbound to the underlying data schema. It's main
utility is to represent the current data state for subsequent
serialization/transmission. It accepts optional argument `tag` which
when called with `false` will not tag the produced object with the
property's `@name`.

      valueOf: (tag=true) ->
        copy = (src) ->
          return unless src? and typeof src isnt 'function'
          if typeof src is 'object'
            try res = new src.constructor
            catch then res = {}
            res[k] = copy v for own k, v of src
            return res
          src.constructor.call src, src
        value = copy @get()
        value ?= [] if @kind is 'list'
        if tag
          name = switch
            when @kind is 'list' then @schema.datakey
            else @name
          "#{name}": value
        else value

## Export Property Class

    module.exports = Property


spec SetIndex and UniqueSetIndex

spec SetIndex and UniqueSetIndex

# Batman.Set

`Set` is an observable, `Batman.Object` wrapper around `SimpleSet`. `Set` also extends [`Batman.Enumerable`](/docs/api/batman.enumerable.html), which provides [many useful methods](/docs/api/batman.enumerable.html).

### SimpleSet vs Set

`SimpleSet` and `Set` are two distinct classes in Batman. `SimpleSet` implements the basic set semantics, but it is *not* a `Batman.Object`, so properties on it (like its `length` or `toArray`) cannot be bound. `Set` is a `Batman.Object`, so it can be observed, and thus plays nicely with the accessor system. Use a `SimpleSet` only when you know nothing will need to be observed on the set you are creating, which usually isn't a valid assumption. If it is in fact valid, consider using a native array as well, as iteration and membership checks will be faster.

## constructor(items...)

When creating a `Set`, items forming the initial set can be passed as separate arguments to the constructor

    test 'new Set constructor can be called without arguments', ->
      set = new Batman.Set
      deepEqual set.toArray(), []

    test 'new Set constructor can be passed items to add to the set.', ->
      set = new Batman.Set('a', 'b', 'c')
      deepEqual set.toArray().sort(), ['a', 'b', 'c']

## length : number

A count of the items in a `Set` can be found at its `length` property.

## isEmpty : boolean

Observable property for `isEmpty()`

## toArray : Array

Observable property for `toArray()`. Whenever items are added or removed on the set, the `toArray` property will change. This is the mechanism by which Batman's view bindings get notified of collection updates.

    test 'observers on the toArray property fire when the set changes', ->
      results = null
      set = new Batman.Set('a', 'b', 'c')
      set.observe('toArray', (newArray) -> results = newArray.sort())
      deepEqual set.add('d'), ['d']
      deepEqual results, ['a', 'b', 'c', 'd']
      deepEqual set.remove('b'), ['b']
      deepEqual results, ['a', 'c', 'd']

## has(item) : Boolean

`has` returns a boolean describing if the given `item` is a member of the set.

_Note_: Using `has(item)` in an accessor body will register the set `has` is called upon as a source of the property being calculated. This so that whenever the set changes, the property will be recalculated, because the set may now have or not have the item in question.

    test 'Set::has indicates if an item is a member of the set or not.', ->
      set = new Batman.Set('a', 'b', 'c')
      ok set.has('a')
      equal set.has('d'), false

    test 'Set::has registers the set as a source of an accessor', ->
      class Team extends Batman.Object
        constructor: ->
          @awards = new Batman.Set()

        @accessor 'bestEver?', -> @get('awards').has('Stanley Cup')

      result = null
      team = new Team
      team.observeAndFire 'bestEver?', (status) -> result = status
      team.get('awards').add 'Eastern Conference Champs'
      equal result, false
      team.get('awards').add 'Stanley Cup'
      equal result, true

## add(items...)

`add` adds 0 or more new items to the set. `add` returns an array of the items which have been newly added to the set, which is to say the intersection of the argument items and the set's complement before addition.

`add` fires the `itemsWereAdded` event with the list of items newly added to the set if that list has length greater than 0. This is to say the event will not be fired if the items passed to add were all already members of the set.

    test 'Set::add adds an item to the set', ->
      set = new Batman.Set()
      equal set.has('a'), false
      deepEqual set.add('a'), ['a']
      equal set.has('a'), true

    test 'Set::add returns only the new items that weren\'t previously in the set', ->
      set = new Batman.Set('a', 'b')
      deepEqual set.add('b','c','d').sort(), ['c', 'd']
      deepEqual set.toArray().sort(), ['a', 'b', 'c', 'd']

    test 'Set::add fires the itemsWereAdded event with the items newly added to the set', ->
      results = null
      set = new Batman.Set('a', 'b')
      set.on('itemsWereAdded', (item) -> results = item)
      set.add('b','c','d')
      deepEqual results.sort(), ['c','d']

    test 'Set::add does not fire the itemsWereAdded event if the added items were already in the set.', ->
      results = undefined
      set = new Batman.Set('a', 'b')
      set.on('itemsWereAdded', (items) -> results = items)
      set.add('a', 'b')
      equal typeof results, 'undefined'

## remove(items...)

`remove` removes 0 or more items from the set. `remove` returns an array of the items which were successfully removed from the set, which is to say the intersection of the argument items and the set itself before removal.

`remove` fires the `itemsWereRemoved` event with the list of removed items if that list has length greater than 0. This is to say the event will not be fired if none of the passed items were members of the set.

    test 'Set::remove removes an item from the set', ->
      set = new Batman.Set('a')
      equal set.has('a'), true
      deepEqual set.remove('a'), ['a']
      equal set.has('a'), false

    test 'Set::remove returns only the new items that were previously in the set', ->
      set = new Batman.Set('a', 'b')
      deepEqual set.remove('b','c','d').sort(), ['b']
      deepEqual set.toArray(), ['a']

    test 'Set::remove fires the itemsWereRemoved event with the items removed to the set', ->
      results = null
      set = new Batman.Set('a', 'b', 'c')
      set.on('itemsWereRemoved', (items) -> results = items)
      set.remove('b','c')
      deepEqual results.sort(), ['b','c']

    test 'Set::remove does not fire the itemsWereRemoved event if the removed items were not already members of the set.', ->
      results = undefined
      set = new Batman.Set('a', 'b')
      set.on('itemsWereRemoved', (items) -> results = items)
      set.remove('c', 'd')
      equal typeof results, 'undefined'

## find(testFunction : function) : [Object]

`find` returns the first item within the set for which the `testFunction` called with the item returns `true`, or `undefined` if no item passes the test.

_Note_: `find` returns the first item the test passes for, but since set iteration has no specified order, no guarantee can be made about which item will be returned if more than one item in the set passes the test. However, set iteration order is stable, so `find` called on the same set twice should return the same item.

    test 'Set::find returns the first item for which the test function passes', ->
      set = new Batman.Set(1, 2, 3)
      equal set.find((x) -> x % 2 == 0), 2

    test 'Set::find returns undefined if no items pass the test function', ->
      set = new Batman.Set(1, 2, 3)
      equal typeof set.find((x) -> x > 5), 'undefined'

## forEach(iteratorFunction : function[, context: Object])

`forEach` calls the `iteratorFunction` with each item in the set, optionally executing the `iteratorFunction` in the passed context. Returns `undefined`.

_Note_: Set iteration order is not defined, thus Set client code cannot rely on one item being iterated over before another, regardless of when the Set's items were added. If you need an ordered set, Batman provides `SetSort` for exactly this, while including the added benefit of observability. If you need a one time ordering of a set, you can get the array representation with `toArray` and then use vanilla JavaScript `sort` on that array.

_Note_: Using `forEach()` in an accessor body will register the set iterated over as a source of the property being calculated. This so that whenever the set changes, the property will be recalculated. This can become an issue if you iterate over a set and modify the set's items, as when the property recalculates that modification will potentially happen on items it's happened on previously.

    test 'Set::forEach iterates over each item in the set', ->
      sum = 0
      set = new Batman.Set(1,2,3)
      set.forEach (x) -> sum += x
      equal sum, 6

    test 'Set::forEach iterates over each item in the set optionally in the provided context', ->
      context = {sum: 0}
      set = new Batman.Set(1,2,3)
      set.forEach((x) ->
        @sum += x
      , context)
      equal context.sum, 6

    test 'Set::forEach registers the set as a source if called in an accessor body', ->
      class Team extends Batman.Object
        constructor: ->
          @players = new Batman.Set()
        @accessor 'willWinTheCup?', ->
          sedinCount = 0
          @players.forEach (player) ->
            sedinCount++ if player.split(' ')[1] == 'Sedin'
          sedinCount >= 2

      result = null
      team = new Team()
      team.observeAndFire 'willWinTheCup?', (status) -> result = status
      equal team.get('willWinTheCup?'), false
      team.get('players').add 'Henrik Sedin'
      equal result, false
      team.get('players').add 'Daniel Sedin'
      equal result, true

## isEmpty() : boolean

`isEmpty` returns a boolean: `true` if the set has no items, and `false` if it has any items.

_Note_: Using `isEmpty()` in an accessor body will register the set `isEmpty` is called on as a source of the property being calculated, so that whenever the set changes the property will be recalculated.

    test 'Set::isEmpty returns true if the set has no items', ->
      set = new Batman.Set()
      ok set.isEmpty()
      set.add('a')
      equal set.isEmpty(), false

    test 'Set::isEmpty registers the set as a source of an accessor', ->
      class Team extends Batman.Object
        constructor: ->
          @games = new Batman.Set()
        @accessor 'seasonStarted?', -> !@games.isEmpty()

      team = new Team
      equal team.get('seasonStarted?'), false
      team.get('games').add({win: true})
      equal team.get('seasonStarted?'), true

## clear() : Array

`clear` removes all items from a set. Returns an array of all the items in the set.

`clear` will fire the `itemsWereRemoved` event once with all the items in the set.

_Note_: Set iteration order is not defined, so the order of the array of items returned by `clear` is undefined.

    test 'Set::clear empties the set', ->
      set = new Batman.Set('a', 'b', 'c')
      equal set.isEmpty(), false
      deepEqual set.clear().sort(), ['a', 'b', 'c']
      ok set.isEmpty()

    test 'Set::clear fires the itemsWereRemoved event with all the items in the set', ->
      result = null
      set = new Batman.Set('a', 'b', 'c')
      set.on('itemsWereRemoved', (items) -> result = items)
      set.clear()
      deepEqual result.sort(), ['a', 'b', 'c']

## replace(collection : Enumerable) : Array

`replace` removes all the items in a set and then adds all the items found in another `collection`. The other collection must have a `toArray` function which returns an array representation of the collection. Returns the array of items added.

`replace` will fire the `itemsWereRemoved` event once with all the items in the set, and then the `itemsWereAdded` event once with the items from the incoming collection.

    test 'Set::replace empties the set and then adds items from a different collection', ->
      set = new Batman.Set('a', 'b', 'c')
      secondSet = new Batman.Set('d', 'e', 'f')
      set.replace(secondSet)
      deepEqual set.toArray().sort(), ['d', 'e', 'f']

    test 'Set::replace fires the itemsWereRemoved event with all the items in the set', ->
      result = null
      set = new Batman.Set('a', 'b', 'c')
      set.on('itemsWereRemoved', (items) -> result = items)
      set.replace(new Batman.SimpleSet())
      deepEqual result.sort(), ['a', 'b', 'c']

    test 'Set::replace fires the itemsWereAdded event with all the items in the incoming set', ->
      result = null
      set = new Batman.Set()
      set.on('itemsWereAdded', (items) -> result = items)
      set.replace(new Batman.SimpleSet('a', 'b', 'c'))
      deepEqual result.sort(), ['a', 'b', 'c']

## toArray() : Array

`toArray` returns an array representation of the set.

_Note_: Set iteration order is not defined, so the order in which the set's items appear in the array is not defined. It is however stable, so the order of the items in two successive `toArray` calls where the set was not modified in between should be the same.

_Note_: `toArray` is also an observable property.

_Note_: Using `toArray()` in an accessor body will register the set `toArray` is called on as a source of the property being calculated, so that whenever the set changes the property will be recalculated.

    test 'Set::toArray returns an array representation of the set', ->
      set = new Batman.Set()
      deepEqual set.toArray(), []
      set.add('a', 'b', 'c')
      deepEqual set.toArray().sort(), ['a', 'b', 'c']

## merge(collections... : Enumerable) : Set

`merge` adds all the items in a set and all the items in the passed `collections` to a new set and returns it. A `collection` is an object which has a `forEach` function. `merge` is a non-destructive collection union, so the set `merge` is called on and each `collection` passed to `merge` are unaffected by the call.

_Note_: Be careful about using `merge` within accessors. Calling `merge` in an accessor function body will register the set `merge` is called upon as a source of the property being calculated, which means when the set changes, that accessor will be recalculated. This means the O(n * m) merge will occur again each time, and return an entirely new `Set` instance. If the previously returned `Set` instance is retained after recalculation, this is a big memory leak. Instead of merging in accessors, try to use a `SetUnion` or a `SetIntersection`.

    test 'Set::merge returns a new set with the items of the original set and the passed set', ->
      abc = new Batman.Set('a', 'b', 'c')
      def = new Batman.Set('d', 'e', 'f')
      equal Batman.typeOf(set = abc.merge(def)), 'Object'
      deepEqual set.toArray().sort(), ['a', 'b', 'c', 'd', 'e', 'f']

## indexedBy(key : String) : SetIndex

`indexedBy` returns a hash of sets which buckets all the items in the callee set by the value of a particular `key`. The value of the passed `key` is `get`ted from each object in the set, and then a hash of each value to a set of the items with that value at the `key` is built. This hash of sets is a smart object called a `SetIndex` which will continue to observe the set and the value of the `key` on each item in the set to ensure the set index remains up to date. `SetIndex` also has a friend named `UniqueSetIndex` which will give you a hash of items instead of a hash of sets with items for easy access if you know each item's value at the `key` is unique.

    test 'Set::indexedBy returns a new SetIndex with the items bucketed by the value of the key', ->
      set = new Batman.Set(Batman(colour: 'blue'), Batman(colour: 'green'), Batman(colour: 'blue'))
      index = set.indexedBy('colour')
      ok index.get('blue') instanceof Batman.Set
      equal index.get('blue').get('length'), 2
      equal index.get('green').get('length'), 1

    test 'Set::indexedBy returns a new SetIndex which observes the set for new additions and stays up to date', ->
      set = new Batman.Set(Batman(colour: 'blue'), Batman(colour: 'green'))
      index = set.indexedBy('colour')
      equal index.get('blue').get('length'), 1
      newItem = Batman(colour: 'blue')
      set.add(newItem)
      equal index.get('blue').get('length'), 2
      ok index.get('blue').has(newItem)
      set.remove(newItem)
      equal index.get('blue').get('length'), 1

    test 'Set::indexedBy returns a new SetIndex which observes the items in the set for changes to the observed key', ->
      itemA = Batman(colour: 'blue')
      itemB = Batman(colour: 'green')
      set = new Batman.Set(itemA, itemB)
      index = set.indexedBy('colour')
      equal index.get('blue').get('length'), 1
      equal index.get('green').get('length'), 1
      itemA.set('colour', 'green')
      equal index.get('blue').get('length'), 0
      equal index.get('green').get('length'), 2

`SetIndex`es can be created by calling the `indexedBy` function on the `Set`, as well as by `get`ting a `key` on the `indexedBy` property.

    test "Set::get('indexedBy.someKey') returns a new SetIndex for 'someKey'", ->
      set = new Batman.Set(Batman(colour: 'blue'), Batman(colour: 'green'))
      index = set.get('indexedBy.colour')
      equal index.get('blue').get('length'), 1

## indexedByUnique(key : String) : UniqueSetIndex

`indexedByUnique` returns a hash of items keyed by the value of the given `key` on each item from the callee set. The value of the passed `key` is `get`ted from each object in the set, and then a hash of each value to an item with that value at the `key` is built. This hash of items is a smart object called a `UniqueSetIndex` which will continue to observe the set and the value of the `key` on each item in the set to ensure the index remains up to date. Note that the semantics for which item ends up in the hash if two items have the same value for the `key` are undefined, so it is wise to only use `UniqueSetIndex`es on keys who's values are going to be unique in the set. If the values are not going to be unique, you may be interested in `SetIndex` and `Set::indexedBy`.

    test 'Set::indexedByUnique returns a new UniqueSetIndex with the items hashed by the value of the key', ->
      greenItem = Batman(colour: 'green')
      blueItem = Batman(colour: 'blue')
      set = new Batman.Set(greenItem, blueItem)
      index = set.indexedByUnique('colour')
      ok blueItem == index.get('blue')
      ok greenItem == index.get('green')
      equal undefined, index.get('red')

    test 'Set::indexedByUnique returns a new UniqueSetIndex which observes the set for new additions and stays up to date', ->
      set = new Batman.Set(Batman(colour: 'blue'), Batman(colour: 'green'))
      index = set.indexedByUnique('colour')
      newItem = Batman(colour: 'red')
      set.add(newItem)
      ok newItem == index.get('red')
      set.remove(newItem)
      equal undefined, index.get('red')

    test 'Set::indexedByUnique returns a new UniqueSetIndex which observes the items in the set for changes to the observed key', ->
      itemA = Batman(colour: 'blue')
      itemB = Batman(colour: 'green')
      set = new Batman.Set(itemA, itemB)
      index = set.indexedByUnique('colour')
      equal index.get('blue')?, true
      equal index.get('green')?, true
      equal index.get('red')?, false
      itemA.set('colour', 'red')
      equal index.get('blue')?, false
      equal index.get('green')?, true
      equal index.get('red')?, true

`UniqueSetIndex`es can be created by calling the `indexedByUnique` function on the `Set`, as well as by `get`ting a `key` on the `indexedByUnique` property.

    test "Set::get('indexedByUnique.someKey') returns a new UniqueSetIndex for 'someKey'", ->
      set = new Batman.Set(Batman(colour: 'blue'), Batman(colour: 'green'))
      index = set.get('indexedByUnique.colour')
      equal 'blue', index.get('blue').get('colour')

## sortedBy(key: String [, order: String]) : SetSort

`sortedBy` returns a `Set` like object containing all the items of the callee set but with a defined iteration order (unlike `Set`). The iteration order is defined as the alpha numeric sorting of the values of the passed `key` gotten from each item. The direction of the sort can be controlled with the `order` argument, which defaults to `asc` (short for ascending) or can be passed as `desc` (short for descending). This `Set` like object is a `SetSort` which encapsulates the logic to get the values from each item at the passed `key` and traverse the `Set` in the values sorted order.

`SetSort`s are useful for getting a transform of a `Set` which sorted, but also because the sort stays up to date as items are added or removed to the sorted set, or the value at the `key` changes on any of the items in the set.

    test 'Set::sortedBy returns a new SetSort who can be iterated in the sorted order of the value of the key on each item', ->
      set = new Batman.Set(Batman(place: 3, name: 'Harry'), Batman(place: 1, name: 'Tom'), Batman(place: 2, name: 'Camilo'))
      sort = set.sortedBy('place')
      deepEqual sort.toArray().map((item) -> item.get('name')), ['Tom', 'Camilo', 'Harry']

    test 'Set::sortedBy returns a new SetSort which observes the callee set for additions or removals and puts new items in the sorted order', ->
      set = new Batman.Set(Batman(place: 3, name: 'Harry'), Batman(place: 1, name: 'Tom'), Batman(place: 2, name: 'Camilo'))
      sort = set.sortedBy('place')
      deepEqual sort.toArray().map((item) -> item.get('name')), ['Tom', 'Camilo', 'Harry']
      burke = Batman(place: 1.5, name: 'Burke')
      set.add(burke)
      deepEqual sort.toArray().map((item) -> item.get('name')), ['Tom', 'Burke', 'Camilo', 'Harry']

    test 'Set::sortedBy returns a new SetSort which observes each item in the callee set for changes to the sort key', ->
      harry = Batman(place: 2, name: 'Harry')
      tom = Batman(place: 1, name: 'Tom')
      set = new Batman.Set(harry, tom)
      sort = set.sortedBy('place')
      deepEqual sort.toArray().map((item) -> item.get('name')), ['Tom', 'Harry']
      tom.set('place', 3)
      deepEqual sort.toArray().map((item) -> item.get('name')), ['Harry', 'Tom']

`SetSort`s can be created by calling the `sortedBy` function on the `Set`, as well as by `get`ting a `key` on the `sortedBy` property. Note that with this instantiation form you can't pass an order to the `SetSort`.

    test "Set::get('sortedBy.someKey') returns a new SetSort onn 'someKey'", ->
      set = new Batman.Set(Batman(place: 3, name: 'Harry'), Batman(place: 1, name: 'Tom'), Batman(place: 2, name: 'Camilo'))
      sort = set.get('sortedBy.place')
      equal 'Harry', sort.get('toArray')[2].get('name')

# Batman.SetIndex

`Batman.SetIndex` is a grouped collection of items derived from a [`Batman.Set`]() filled with [`Batman.Object`]()s. It extends `Batman.Object` and [`Batman.Enumerable`](), so it inherits methods from them, too. In short, a `SetIndex` tracks its base `Set` and contains "buckets" of items from that `Set`, grouped by the provided key.

```coffeescript
batarang = new Batman.Object(name: "Batarang", type: "ranged")
grappleGun = new Batman.Object(name: "Grapple Gun", type: "ranged")
fists = new Batman.Object(name: "Fists", type: "melee")

weapons = new Batman.Set(batarang, grappleGun, fists)
weaponsByType = new Batman.SetIndex(set, 'type')

weaponsByType.get('ranged.length') # => 2
weaponsByType.get('melee.length')  # => 1
weaponsByType.get('ranged').map (weapon) -> weapon.get('name')  # => ["Batarang", "GrappleGun"]
weaponsByType.toArray()                                         # => ["ranged", "melee"]
```

## ::constructor(base : Set, key : String ) : SetIndex

A `SetIndex` is made with a `base` and a `key`. Items in the `base` set will be grouped according to their value for `key`. The resulting `SetIndex` observes its `base`, so any items added to the `base` are also added (and indexed) in the `SetIndex`

## ::get(value : String) : Set

Returns a `Batman.Set` of items whose indexed `key` matches `value`. It returns an empty set if no items match `value`, but if any matching items are added to the `base` set, they will also be added to this set.

## ::toArray() : Array

Returns an array with the distinct values of `key` provided to the constructor.

## ::forEach(func)

Calls `func(key, group)` for each group in the SetIndex.

# Batman.UniqueSetIndex

`Batman.UniqueSetIndex` extends `SetIndex` but adds a new consideration: its index contains _first matching item_ for each value of the `key` (rather than all matching items).

```coffeescript
batarang = new Batman.Object(name: "Batarang", type: "ranged")
grappleGun = new Batman.Object(name: "Grapple Gun", type: "ranged")
fists = new Batman.Object(name: "Fists", type: "melee")

weapons = new Batman.Set(batarang, grappleGun, fists)
weaponsByUniqueType = new Batman.UniqueSetIndex(set, 'type')
weaponsByUniqueType.get('ranged.name') # => "Batarang"
```

## ::get(value : String) : Object

Returns the first matching member whose indexed `key` is equal to `value`.

# Batman.SetSort
# Batman.SetUnion
# Batman.SetIntersection
# Batman.SetComplement