###
Bongo.js
Unfancy models for MongoDB

@class: JsPath
@description: a tool for setting, getting and memoizing with
  Mongo-style "dot-notation" against JS objects. Also, it can
  be used to build paths, or add paths to another structure.
@author: Christopher Thorn
###
module.exports = class JsPath
  ###
  @constructor.
  @signature: new JsPath(path, val)
  @param: path - a dot-notation style "path" to identify a
    nested JS object.
  @description: Initialize a new js object with the provided
    path.  I've never actually used this constructor for any-
    thing, and it is here for the sake of "comprehensiveness"
    at this time, although I am incredulous as to it's overall
    usefulness.
  ###
  constructor:(path, val)->
    @constructor.setTo @, path, val or {}
  ###
  @method. property of the constuctor.
  @signature: JsPath.getFrom(ref, path)
  @param: ref - the object to traverse.
  @param: path - a dot-notation style "path" to identify a
    nested JS object.
  @return: the object that can be found inside ref at the path
    described by the second parameter or undefined if the path
    is not valid.
  ###
  @getFrom =(ref, path)->
    if 'function' is typeof path.split # ^1
      path = path.split '.'
    else
      path = path.slice()
    while ref? and prop = path.shift()
     ref = ref[prop]
    ref
  ###
  @method. property of the constuctor.
  @signature: JsPath.getFrom(ref, path)
  @param: obj - the object to extend.
  @param: path - a dot-notation style "path" to identify a
    nested JS object.
  @param: val - the value to assign to the path of the obj.
  @return: the object that was extended.
  @description: set a property to the path provided by the
    second parameter with the value provided by the third
    parameter.
  ###
  primTypes = /^(string|number|boolean)$/
  @setTo =(obj, path, val)->
    if 'function' is typeof path.split # ^1
      path = path.split '.'
    last = path.pop()
    prev = []
    ref = obj
    while component = path.shift()
      if primTypes.test typeof ref[component]
        throw new Error \
          """
          #{prev.concat(component).join '.'} is 
          primitive, and cannot be extended.
          """
      ref = ref[component] or= {}
      prev.push component
    ref[last] = val
    obj

###
Footnotes:
  1 - if there's no .split() method, assume it's already an array