App.Route support for Document.scope -> root added

Route @class
============

    'use strict'

    utils = require 'src/utils'
    assert = require 'src/assert'
    log = require 'src/log'
    Schema = require 'src/schema'
    Networking = require 'src/networking'
    Document = require 'src/document'
    Renderer = require 'src/renderer'
    Dict = require 'src/dict'

    log = log.scope 'App', 'Route'

    module.exports = (app) -> class Route

        if utils.isNode
            usedTemplates = []
        else
            templates = Object.create null

        lastClientHTMLRoute = null

*Document* Route.getTemplateView(*String* viewName)
---------------------------------------------------

        @getTemplateView = do ->
            if utils.isNode
                (name) ->
                    scope = app: app, routes: new Dict
                    tmpl = app.views[name].render null, scope
                    usedTemplates.push tmpl
                    tmpl
            else
                (name) ->
                    scope = app: app, routes: new Dict
                    templates[name] ?= app.views[name].render null, scope

*Route* Route(*Object* options)
-------------------------------

Access it with:
```javascript
module.exports = function(app){
  var Route = app.Route;
};
```

Acceptable syntaxes:
```javascript
*Route* app.Route(*String* method, *String* uri, *Object* options)
*Route* app.Route(*String* methodWithUri, *Function* getData)
*Route* app.Route(*String* methodWithUri, *Object* options)
*Route* app.Route(*String* uri, *Function* getData)
*Route* app.Route(*String* uri, *Object* options)
*Route* app.Route(*String* method, *String* uri)
*Route* app.Route(*String* uri)
*Route* app.Route(*String* methodWithUri)
```

        constructor: (method, uri, opts) ->
            if utils.isObject(method)
                # opts
                opts = method
            else if utils.isObject(uri)
                opts = uri
            else unless utils.isObject(opts)
                opts = {}

            if typeof method is 'string' and typeof uri isnt 'string'
                opts.uri = method
            else if typeof method is 'string' and typeof uri is 'string'
                opts.method ?= method
                opts.uri ?= uri
            if typeof uri is 'function'
                opts.getData ?= uri

            assert.isObject opts
            opts = utils.clone opts

            if typeof opts.uri is 'string'
                # support methodWithUri e.g. 'get /home'
                spaceIndex = opts.uri.indexOf ' '
                if spaceIndex isnt -1
                    opts.method ?= opts.uri.slice 0, spaceIndex
                    opts.uri = opts.uri.slice spaceIndex+1
                opts.uri = new Networking.Uri opts.uri
            assert.instanceOf opts.uri, Networking.Uri

            opts.method ?= 'get'
            assert.isString opts.method
            opts.method = opts.method.toLowerCase()
            assert.ok utils.has(Networking.Request.METHODS, opts.method)
            , "Networking doesn't provide a `#{opts.method}` method"

            if opts.schema?
                if utils.isPlainObject(opts.schema)
                    opts.schema = new Schema opts.schema
                assert.instanceOf opts.schema, Schema

            if opts.redirect?
                if typeof opts.redirect is 'string'
                    opts.redirect = new Networking.Uri opts.redirect
                else
                    assert.isFunction opts.redirect

            if utils.isObject(opts.toHTML)
                opts.toHTML = createToHTMLFromObject opts.toHTML

            for key, val of opts
                @[key] = val
            @__id__ = utils.uid()
            @app = app
            @name ||= getRouteName(@)

            app.networking.createHandler
                method: @method
                uri: @uri
                schema: @schema
                callback: utils.bindFunctionContext(handleRequest, @)

        getRouteName = (route) ->
            assert.instanceOf route, Route

            uri = route.uri._uri
            uri = uri.replace Networking.Uri.NAMES_RE, ''
            uri = uri.replace /\*/g, ''
            while uri.indexOf('//') isnt -1
                uri = uri.replace /\/\//g, '/'
            uri = uri.replace /^\//, ''
            uri = uri.replace /\/$/, ''
            uri

        routesCache = Object.create null
        pendingRoutes = Object.create null

        factoryRoute = do ->
            createInstance = (route) ->
                r = Object.create route
                r.__hash__ = utils.uid()
                r.factory?()
                r

            (route) ->
                assert.instanceOf route, Route

                id = route.__id__
                routesCache[id] ?= []
                r = routesCache[id].pop() or createInstance(route)
                r = Object.create r
                r.request = r.response = null
                r.route = r
                r._dataPrepared = false
                r._destroyViewOnEnd = false
                r

        destroyRoute = (route) ->
            assert.instanceOf route, Route

            if lastClientHTMLRoute is route
                lastClientHTMLRoute = null

            route.response.onSend.disconnect onResponseSent, route
            pendingRoutes[route.__hash__] = false
            route.destroy?()
            if route._dataPrepared
                switch route.request.type
                    when 'text'
                        route.destroyText?()
                    when 'json'
                        route.destroyJSON?()
                    when 'html'
                        route.destroyHTML?()
            if route._destroyViewOnEnd
                route.response.data.destroy()
            routesCache[route.__id__].push Object.getPrototypeOf(route)

        resolveSyncGetDataFunc = (route) ->
            assert.instanceOf route, Route

            try
                data = route.getData()
                if data?
                    route.data = data
            catch err
                if route.response.status is 200
                    route.response.status = 500
                route.error = err

        resolveAsyncGetDataFuncCallback = (route, err, data) ->
            assert.instanceOf route, Route

            if err?
                if route.response.status is 200
                    route.response.status = 500
                if route._dataPrepared and route.error is err
                    return false
                route.error = err
            else
                if route._dataPrepared and route.data is data
                    return false
                route.data = data
            true

        prepareRouteData = (route) ->
            assert.instanceOf route, Route
            {response} = route

            if route.error
                log.error "Error in route '#{route.uri}':\n#{route.error}"

            respData = response.data
            switch route.request.type
                when 'text'
                    data = route.toText()
                when 'json'
                    data = route.toJSON()
                when 'html'
                    data = route.toHTML()
                    if respData instanceof Document and route._destroyViewOnEnd
                        respData.destroy()
                        response.data = null
                    if not (data instanceof Document) and response.data is respData
                        data = renderViewFromConfig.call route, data
            route._dataPrepared = true
            if data?
                response.data = data
            else if response.data is respData
                response.data = ''

        onResponseSent = ->
            if utils.isNode or @request.type isnt 'html'
                destroyRoute @

                if utils.isNode and utils.has(usedTemplates, @response.data)
                    @response.data.destroy()
                    utils.remove usedTemplates, @response.data
            return

        finishRequest = (route) ->
            assert.instanceOf route, Route
            if route.response.data instanceof Document
                Renderer.window.document.node = route.response.data.node
            if route.response.pending
                route.response.send()
            return

        callNextIfNeeded = (route, next) ->
            unless pendingRoutes[route.__hash__]
                if route.response.pending
                    next()
                return true
            false

        handleRequest = (req, res, next) ->
            assert.instanceOf req, Networking.Request
            assert.instanceOf res, Networking.Response
            assert.isFunction next

            route = factoryRoute @
            hash = route.__hash__
            assert.notOk pendingRoutes[hash]

            if utils.isClient
                if lastClientHTMLRoute
                    destroyRoute lastClientHTMLRoute
                lastClientHTMLRoute = route

            route.request = req
            route.response = res
            pendingRoutes[hash] = true

            res.onSend onResponseSent, route

            # init
            route.init?()

            unless pendingRoutes[hash]
                return next()

            # redirect
            {redirect} = route
            if typeof redirect is 'function'
                redirect = route.redirect()
                unless pendingRoutes[hash]
                    return next()
            if typeof redirect is 'string'
                redirect = new Networking.Uri redirect
            if redirect instanceof Networking.Uri
                res.redirect redirect.toString(req.params)
                return

            # getData
            {getData} = route
            fakeAsync = false
            if typeof getData is 'function'
                if getData.length is 1
                    route.getData (err, data) ->
                        fakeAsync = true
                        if callNextIfNeeded(route, next)
                            return
                        unless resolveAsyncGetDataFuncCallback(route, err, data)
                            return
                        prepareRouteData route
                        if callNextIfNeeded(route, next)
                            return
                        finishRequest route
                else
                    resolveSyncGetDataFunc route
                    if callNextIfNeeded(route, next)
                        return
                    prepareRouteData route
                    if callNextIfNeeded(route, next)
                        return
                    finishRequest route
            else
                prepareRouteData route
                if callNextIfNeeded(route, next)
                    return
                finishRequest route

            if not fakeAsync and callNextIfNeeded(route, next)
                return

*String* Route::method = 'get'
------------------------------

*Networking.Uri* Route::uri
---------------------------

*App* Route::app
----------------

*Route* Route::route
--------------------

*String* Route::name
--------------------

*Schema* Route::schema
----------------------

*Any* Route::data
-----------------

*Any* Route::error
------------------

*Function* Route::factory()
---------------------------

*Function* Route::init()
------------------------

*Function* Route::getData([*Function* callback])
------------------------------------------------

*Function* Route::destroy()
---------------------------

*Function* Route::destroyJSON()
-------------------------------

*Function* Route::destroyText()
-------------------------------

*Function* Route::destroyHTML()
-------------------------------

*Function|Networking.Uri* Route::redirect
-----------------------------------------

*Networking.Request* Route::request
-----------------------------------

*Networking.Response* Route::response
-------------------------------------

*Function* Route::next()
------------------------

        next: ->
            assert.ok pendingRoutes[@__hash__]
            destroyRoute @

*Any* Route::toJSON()
---------------------

        toJSON: ->
            if @response.status < 400
                @data?.toJSON?() or @data
            else
                @error.toJSON?() or @error

*String* Route::toText()
------------------------

        toText: ->
            if @response.status < 400
                @data+''
            else
                @error+''

*Document* Route::toHTML()
--------------------------

        routeToString = ->
            "#{@method} #{@uri}"

        getDefaultRouteViewName = ->
            path = "views/#{@name}.html"
            if app.views[path]
                return path

        renderViewFromConfig = (opts) ->
            viewName = opts?.view or getDefaultRouteViewName.call(@) or 'views/index.html'
            tmplName = opts?.template or 'views/template.html'
            useName = opts?.use or 'body'

            logtime = log.time 'Render'
            if viewName isnt tmplName
                if tmpl = app.views[tmplName]
                    tmplView = Route.getTemplateView tmplName
                    tmplView.use useName, null
                else
                    log.warn "Template view '#{tmplName}' can't be found for route '#{routeToString.call(@)}'"
            if view = app.views[viewName]
                r = view.render null, @
            else
                log.warn "View '#{tmplName}' can't be found for route '#{routeToString.call(@)}'"
            if tmplView
                if r?
                    r = tmplView.use(useName, r)
                else
                    r = tmplView
                if tmplView.root.routes.has(useName)
                    tmplView.root.routes.pop useName
                tmplView.root.routes.set useName, @
                @_destroyViewOnEnd = false
            else
                @_destroyViewOnEnd = true
            log.end logtime
            r

        createToHTMLFromObject = (opts) ->
            -> renderViewFromConfig.call @, opts

        toHTML: createToHTMLFromObject
            view: ''
            template: ''
            use: ''

Route @class
============

    'use strict'

    utils = require 'src/utils'
    assert = require 'src/assert'
    log = require 'src/log'
    Schema = require 'src/schema'
    Networking = require 'src/networking'
    Document = require 'src/document'
    Renderer = require 'src/renderer'
    Dict = require 'src/dict'

    log = log.scope 'App', 'Route'

    module.exports = (app) -> class Route

        if utils.isNode
            usedTemplates = []
        else
            templates = Object.create null

        lastClientHTMLRoute = null

*Document* Route.getTemplateView(*String* viewName)
---------------------------------------------------

        @getTemplateView = do ->
            if utils.isNode
                (name) ->
                    scope = app: app, routes: new Dict
                    tmpl = app.views[name].render null, scope
                    usedTemplates.push tmpl
                    tmpl
            else
                (name) ->
                    scope = app: app, routes: new Dict
                    templates[name] ?= app.views[name].render null, scope

*Route* Route(*Object* options)
-------------------------------

Access it with:
```javascript
module.exports = function(app){
  var Route = app.Route;
};
```

Acceptable syntaxes:
```javascript
*Route* app.Route(*String* method, *String* uri, *Object* options)
*Route* app.Route(*String* methodWithUri, *Function* getData)
*Route* app.Route(*String* methodWithUri, *Object* options)
*Route* app.Route(*String* uri, *Function* getData)
*Route* app.Route(*String* uri, *Object* options)
*Route* app.Route(*String* method, *String* uri)
*Route* app.Route(*String* uri)
*Route* app.Route(*String* methodWithUri)
```

        constructor: (method, uri, opts) ->
            if utils.isObject(method)
                # opts
                opts = method
            else if utils.isObject(uri)
                opts = uri
            else unless utils.isObject(opts)
                opts = {}

            if typeof method is 'string' and typeof uri isnt 'string'
                opts.uri = method
            else if typeof method is 'string' and typeof uri is 'string'
                opts.method ?= method
                opts.uri ?= uri
            if typeof uri is 'function'
                opts.getData ?= uri

            assert.isObject opts
            opts = utils.clone opts

            if typeof opts.uri is 'string'
                # support methodWithUri e.g. 'get /home'
                spaceIndex = opts.uri.indexOf ' '
                if spaceIndex isnt -1
                    opts.method ?= opts.uri.slice 0, spaceIndex
                    opts.uri = opts.uri.slice spaceIndex+1
                opts.uri = new Networking.Uri opts.uri
            assert.instanceOf opts.uri, Networking.Uri

            opts.method ?= 'get'
            assert.isString opts.method
            opts.method = opts.method.toLowerCase()
            assert.ok utils.has(Networking.Request.METHODS, opts.method)
            , "Networking doesn't provide a `#{opts.method}` method"

            if opts.schema?
                if utils.isPlainObject(opts.schema)
                    opts.schema = new Schema opts.schema
                assert.instanceOf opts.schema, Schema

            if opts.redirect?
                if typeof opts.redirect is 'string'
                    opts.redirect = new Networking.Uri opts.redirect
                else
                    assert.isFunction opts.redirect

            if utils.isObject(opts.toHTML)
                opts.toHTML = createToHTMLFromObject opts.toHTML

            for key, val of opts
                @[key] = val
            @__id__ = utils.uid()
            @app = app
            @name ||= getRouteName(@)

            app.networking.createHandler
                method: @method
                uri: @uri
                schema: @schema
                callback: utils.bindFunctionContext(handleRequest, @)

        getRouteName = (route) ->
            assert.instanceOf route, Route

            uri = route.uri._uri
            uri = uri.replace Networking.Uri.NAMES_RE, ''
            uri = uri.replace /\*/g, ''
            while uri.indexOf('//') isnt -1
                uri = uri.replace /\/\//g, '/'
            uri = uri.replace /^\//, ''
            uri = uri.replace /\/$/, ''
            uri

        routesCache = Object.create null
        pendingRoutes = Object.create null

        factoryRoute = do ->
            createInstance = (route) ->
                r = Object.create route
                r.__hash__ = utils.uid()
                r.factory?()
                r

            (route) ->
                assert.instanceOf route, Route

                id = route.__id__
                routesCache[id] ?= []
                r = routesCache[id].pop() or createInstance(route)
                r = Object.create r
                r.request = r.response = null
                r.route = r
                r._dataPrepared = false
                r._destroyViewOnEnd = false
                r

        destroyRoute = (route) ->
            assert.instanceOf route, Route

            if lastClientHTMLRoute is route
                lastClientHTMLRoute = null

            route.response.onSend.disconnect onResponseSent, route
            pendingRoutes[route.__hash__] = false
            route.destroy?()
            if route._dataPrepared
                switch route.request.type
                    when 'text'
                        route.destroyText?()
                    when 'json'
                        route.destroyJSON?()
                    when 'html'
                        route.destroyHTML?()
            if route._destroyViewOnEnd
                route.response.data.destroy()
            routesCache[route.__id__].push Object.getPrototypeOf(route)

        resolveSyncGetDataFunc = (route) ->
            assert.instanceOf route, Route

            try
                data = route.getData()
                if data?
                    route.data = data
            catch err
                if route.response.status is 200
                    route.response.status = 500
                route.error = err

        resolveAsyncGetDataFuncCallback = (route, err, data) ->
            assert.instanceOf route, Route

            if err?
                if route.response.status is 200
                    route.response.status = 500
                if route._dataPrepared and route.error is err
                    return false
                route.error = err
            else
                if route._dataPrepared and route.data is data
                    return false
                route.data = data
            true

        prepareRouteData = (route) ->
            assert.instanceOf route, Route
            {response} = route

            if route.error
                log.error "Error in route '#{route.uri}':\n#{route.error}"

            respData = response.data
            switch route.request.type
                when 'text'
                    data = route.toText()
                when 'json'
                    data = route.toJSON()
                when 'html'
                    data = route.toHTML()
                    if respData instanceof Document and route._destroyViewOnEnd
                        respData.destroy()
                        response.data = null
                    if not (data instanceof Document) and response.data is respData
                        data = renderViewFromConfig.call route, data
            route._dataPrepared = true
            if data?
                response.data = data
            else if response.data is respData
                response.data = ''

        onResponseSent = ->
            if utils.isNode or @request.type isnt 'html'
                destroyRoute @

                if utils.isNode and utils.has(usedTemplates, @response.data)
                    @response.data.destroy()
                    utils.remove usedTemplates, @response.data
            return

        finishRequest = (route) ->
            assert.instanceOf route, Route
            if route.response.data instanceof Document
                Renderer.window.document.node = route.response.data.node
            if route.response.pending
                route.response.send()
            return

        callNextIfNeeded = (route, next) ->
            unless pendingRoutes[route.__hash__]
                if route.response.pending
                    next()
                return true
            false

        handleRequest = (req, res, next) ->
            assert.instanceOf req, Networking.Request
            assert.instanceOf res, Networking.Response
            assert.isFunction next

            route = factoryRoute @
            hash = route.__hash__
            assert.notOk pendingRoutes[hash]

            if utils.isClient
                if lastClientHTMLRoute
                    destroyRoute lastClientHTMLRoute
                lastClientHTMLRoute = route

            route.request = req
            route.response = res
            pendingRoutes[hash] = true

            res.onSend onResponseSent, route

            # init
            route.init?()

            unless pendingRoutes[hash]
                return next()

            # redirect
            {redirect} = route
            if typeof redirect is 'function'
                redirect = route.redirect()
                unless pendingRoutes[hash]
                    return next()
            if typeof redirect is 'string'
                redirect = new Networking.Uri redirect
            if redirect instanceof Networking.Uri
                res.redirect redirect.toString(req.params)
                return

            # getData
            {getData} = route
            fakeAsync = false
            if typeof getData is 'function'
                if getData.length is 1
                    route.getData (err, data) ->
                        fakeAsync = true
                        if callNextIfNeeded(route, next)
                            return
                        unless resolveAsyncGetDataFuncCallback(route, err, data)
                            return
                        prepareRouteData route
                        if callNextIfNeeded(route, next)
                            return
                        finishRequest route
                else
                    resolveSyncGetDataFunc route
                    if callNextIfNeeded(route, next)
                        return
                    prepareRouteData route
                    if callNextIfNeeded(route, next)
                        return
                    finishRequest route
            else
                prepareRouteData route
                if callNextIfNeeded(route, next)
                    return
                finishRequest route

            if not fakeAsync and callNextIfNeeded(route, next)
                return

*String* Route::method = 'get'
------------------------------

*Networking.Uri* Route::uri
---------------------------

*App* Route::app
----------------

*Route* Route::route
--------------------

*String* Route::name
--------------------

*Schema* Route::schema
----------------------

*Any* Route::data
-----------------

*Any* Route::error
------------------

*Function* Route::factory()
---------------------------

*Function* Route::init()
------------------------

*Function* Route::getData([*Function* callback])
------------------------------------------------

*Function* Route::destroy()
---------------------------

*Function* Route::destroyJSON()
-------------------------------

*Function* Route::destroyText()
-------------------------------

*Function* Route::destroyHTML()
-------------------------------

*Function|Networking.Uri* Route::redirect
-----------------------------------------

*Networking.Request* Route::request
-----------------------------------

*Networking.Response* Route::response
-------------------------------------

*Function* Route::next()
------------------------

        next: ->
            assert.ok pendingRoutes[@__hash__]
            destroyRoute @

*Any* Route::toJSON()
---------------------

        toJSON: ->
            if @response.status < 400
                @data?.toJSON?() or @data
            else
                @error.toJSON?() or @error

*String* Route::toText()
------------------------

        toText: ->
            if @response.status < 400
                @data+''
            else
                @error+''

*Document* Route::toHTML()
--------------------------

        routeToString = ->
            "#{@method} #{@uri}"

        getDefaultRouteViewName = ->
            path = "views/#{@name}.html"
            if app.views[path]
                return path

        renderViewFromConfig = (opts) ->
            viewName = opts?.view or getDefaultRouteViewName.call(@) or 'views/index.html'
            tmplName = opts?.template or 'views/template.html'
            useName = opts?.use or 'body'

            logtime = log.time 'Render'
            if viewName isnt tmplName
                if tmpl = app.views[tmplName]
                    tmplView = Route.getTemplateView tmplName
                    tmplView.use useName, null
                else
                    log.warn "Template view '#{tmplName}' can't be found for route '#{routeToString.call(@)}'"
            if view = app.views[viewName]
                r = view.render null, @
            else
                log.warn "View '#{tmplName}' can't be found for route '#{routeToString.call(@)}'"
            if tmplView
                if r?
                    r = tmplView.use(useName, r)
                else
                    r = tmplView
                if tmplView.scope.routes.has(useName)
                    tmplView.scope.routes.pop useName
                tmplView.scope.routes.set useName, @
                @_destroyViewOnEnd = false
            else
                @_destroyViewOnEnd = true
            log.end logtime
            r

        createToHTMLFromObject = (opts) ->
            -> renderViewFromConfig.call @, opts

        toHTML: createToHTMLFromObject
            view: ''
            template: ''
            use: ''


Fix it again.

Fix it again.

Libraries

    _ = require 'lodash'
    Promise = require "bluebird"
    policies = require '../config/policies'

    module.exports = (routeActions, req, res) ->
        new Promise (resolve, reject) ->
            require('./getPolicies')()
            .then (actualPolicies) ->
                policyResults = _.flatten _.map routeActions, (action) ->
                        # What I'm doing here is looking for the policies file.
                        # If there is a policy defined for that action, use that, else use the * catch all.
                        tempPolicies = _.flatten [policies[action.controllerName][action.actionName] || policies[action.controllerName]['*'] ]

                        _.map tempPolicies, (policy) ->
                            # This is pretty straightforward; we'll loop up the policy in the hash
                            # table and then return back their result (which can just be a true or false
                            #
                            # We want to make sure this is properly wrapped in promisey stuff, so we can init
                            # an empty promise then run our stuff in the "then". Since our empty promise only
                            # resolves, it won't hit the catch.
                            tempPromise = new Promise (resolve, reject) -> do resolve
                            tempPromise.then -> actualPolicies[policy] req

                Promise.all policyResults
            .then (results) ->
                # Not entirely sure I like this logic, but it's pretty straightforward:
                # I want to allow synchronous or asynchronous policies.  Subsequently, we can
                # either allow the policy to reject if not permitted or to simply return false.
                unless false in results
                  resolve true
                else
                  reject "Not Authorized"


using global instead of window

using global instead of window

heterarchy
==========

> This file is part of [Heterarchy](http://sinusoid.es/heterarchy).
> - **View me [on a static web](http://sinusoid.es/heterarchy/heterarchy.html)**
> - **View me [on GitHub](https://github.com/arximboldi/heterarchy/blob/master/heterarchy.litcoffee)**

Adds multiple inheritance support to CoffeeScript (and JavaScript).
It uses the C3 linearization algorithm as described in the [famous
Dylan paper](http://192.220.96.201/dylan/linearization-oopsla96.html).

    {head, tail, map, find, some, without, isEmpty, every, memoize, reject,
     partial, isEqual} =
        require 'underscore'

    assert = (value, error) ->
        if not value
           throw new Error(if error? then error else "Assertion failed")

Multiple inheritance
--------------------

The **multi** function takes a list of classes and returns a *special*
class object that merges the class hierarchies, as linearized by the
C3 algorithm. Limitations of the approach are:

- `instanceof` does not always work as expected. For example:

  > ```coffee
  > class A
  > class B extends A
  > class C extends A
  > class D extends multi B, C
  > assert new D not instanceof B
  > ```

  Instead, one should use the provided `isinstance` function.

- Some of the bases of a multi-inherited hierarchy are *frozen* when
  the sub-class is defined -- i.e. later modifications to the
  superclass are not visible to the subclass or its instances.  For
  example, in the previous heterarchy:

  > ```coffee
  > B::newProperty = 42
  > assert D::newProperty == undefined
  > ```

The `multi` function memoizes its results such that identity is
maintained, implying `multi X, Y is multi X, Y`.

    exports.multi = (bases...) ->
        generate merge map(bases, mro).concat [bases]

This takes a list of classes representing a hierarchy (from most to
least derived) and generates a single-inheritance hierarchy that
behaves like a class that would be have such a hierarchy.

    generate = memoize (linearization) ->
        next = head linearization
        if isEqual linearization, hierarchy next
            next
        else
            class Result extends generate tail linearization
                __mro__: linearization
                constructor: reparent next, @, next::constructor
                copyOwn next, @
                copyOwn next::, @::, partial reparent, next, @

This utility lets us copy own properties of a *from* object that are
not own properties of a *to* object into the *to* object, optionally
transformed via a projection function.

    copyOwn = (from, to, project = (x) -> x) ->
        for own key, value of from
            if not to.hasOwnProperty key
                to[key] = project value
        to

Methods in CoffeeScript call super directly, so we have to change the
`__super__` attribute of the original class during the scope of the
method so it calls the right super of the linearization.  Also,
programmers don't call super in constructor of root classes --indeed
doing so would rise an error-- so we have to inject such a call when
there are classes after these in the linearization.  The **reparent**
function takes care of all these and given an original class, and the
new class that is replacing it a linearized heterarchy, returns a
wrapped copy of a value of the former that is suitable for replacing
it in the later.

    reparent = (oldklass, newklass, value) ->
        if value not instanceof Function
            value
        else if value is oldklass::constructor and inherited(oldklass) is Object
            superctor = inherited(newklass)::constructor
            ->
                superctor.apply @, arguments
                value.apply @, arguments
        else
            newsuper = inherited(newklass)::
            oldsuper = oldklass.__super__
            ->
                oldklass.__super__ = newsuper
                try
                    value.apply @, arguments
                finally
                    oldklass.__super__ = oldsuper

This is the C3 linearization algorithm, as translated from the
original paper.

    merge = (inputs) ->
        while not isEmpty inputs
            next = find (map inputs, head), (candidate) ->
                every inputs, (input) -> candidate not in tail input
            assert next?, "Inconsistent multiple inheritance"
            inputs = reject map(inputs, (lst) -> without lst, next), isEmpty
            next

Introspection
-------------

The **mro** function returns the method resolution order
(linearization) of a given class:

> ```coffee
> class A
> class B extends A
> class C extends B
> assert mro(C).equals [C, B, A]
> ```

It returns the original classes that were mixed in when used with
mult-inherited classes:

> ```coffee
> class A
> class B extends A
> class C extends A
> class D extends multi B, C
> assert mro(D).equals [D, B, C, A, Object]
> ```

    isJavaScriptClass = (cls) ->
        standardClasses = [
            Array
            Boolean
            Date
            Error
            Function
            Number
            RegExp
            String
            Object
            EvalError
            RangeError
            ReferenceError
            SyntaxError
            TypeError
            URIError
        ]

        if cls in standardClasses
            true
        else
            # according to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects
            nonStandardClasses = [
                "Symbol"
                # typed arrays
                "Int8Array"
                "Uint8Array"
                "Uint8ClampedArray"
                "Int16Array"
                "Uint16Array"
                "Int32Array"
                "Uint32Array"
                "Float32Array"
                "Float64Array"
                # keyed Keyed collections
                "Map"
                "Set"
                "WeakMap"
                "WeakSet"
                # Structured data
                "ArrayBuffer"
                "DataView"
                # Control abstraction objects
                "Promise"
                "Generator"
                "GeneratorFunction"
                # Reflection
                "Reflect"
                "Proxy"
            ]
            for nonStandardClass in nonStandardClasses when cls is global[nonStandardClass]
                return true
            false

    exports.mro = mro = (cls) ->
        if not cls? or not cls::?
            []
        else if not cls::hasOwnProperty '__mro__'
            result = [cls].concat mro inherited(cls)
            cls::__mro__ = result unless isJavaScriptClass cls
            result
        else
            cls::__mro__

The **inherited** function returns the CoffeeScript superclass of an
object, for example:

> ```coffee
> class A
> class B extends A
> assert inherited(B) == A
> ```

Note that for multiple inherited classes, this returns the mixed
object, not the next class in the MRO, as in:

> ```coffee
> class C extends multi A, B
> assert inherited(C) == multi(A, B)
> ```

    exports.inherited = inherited = (cls) ->
        Object.getPrototypeOf(cls.prototype)?.constructor

The **hierarchy** returns the CoffeeScript hierarchy of classes of a
given class, including the class itself.  For multiple inherited
classes, it may return speciall classes that were generated to produce
the flattening, as in:

> ```coffee
> class A
> class B extends A
> class C extends A
> class D extends multi B, C
> assert not mro(D).equals hierarchy(D)
> assert hierarchy(D).equals
>     [ D, multi(B, C), inherited(multi B, C), A, Object ]
> ```

    exports.hierarchy = hierarchy = (cls) ->
        if not cls?
            []
        else
            [cls].concat hierarchy inherited cls

The **isinstance** function takes an object and a class or classes and
returns whether the object is an instance of any of those classes. It
is compatible with multi-inherited classes.

    exports.isinstance = (obj, classes...) ->
        exports.issubclass obj?.constructor, classes...

The **issubclass** tells whether a class is a subtype of another type.

    exports.issubclass = (klass, classes...) ->
        linearization = mro klass
        some classes, (cls) -> cls in linearization

License
-------

> Copyright (c) 2013, 2015 Juan Pedro Bolivar Puente <raskolnikov@gnu.org>
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in
> all copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> THE SOFTWARE.

heterarchy
==========

> This file is part of [Heterarchy](http://sinusoid.es/heterarchy).
> - **View me [on a static web](http://sinusoid.es/heterarchy/heterarchy.html)**
> - **View me [on GitHub](https://github.com/arximboldi/heterarchy/blob/master/heterarchy.litcoffee)**

Adds multiple inheritance support to CoffeeScript (and JavaScript).
It uses the C3 linearization algorithm as described in the [famous
Dylan paper](http://192.220.96.201/dylan/linearization-oopsla96.html).

    {head, tail, map, find, some, without, isEmpty, every, memoize, reject,
     partial, isEqual} =
        require 'underscore'

    assert = (value, error) ->
        if not value
           throw new Error(if error? then error else "Assertion failed")

Multiple inheritance
--------------------

The **multi** function takes a list of classes and returns a *special*
class object that merges the class hierarchies, as linearized by the
C3 algorithm. Limitations of the approach are:

- `instanceof` does not always work as expected. For example:

  > ```coffee
  > class A
  > class B extends A
  > class C extends A
  > class D extends multi B, C
  > assert new D not instanceof B
  > ```

  Instead, one should use the provided `isinstance` function.

- Some of the bases of a multi-inherited hierarchy are *frozen* when
  the sub-class is defined -- i.e. later modifications to the
  superclass are not visible to the subclass or its instances.  For
  example, in the previous heterarchy:

  > ```coffee
  > B::newProperty = 42
  > assert D::newProperty == undefined
  > ```

The `multi` function memoizes its results such that identity is
maintained, implying `multi X, Y is multi X, Y`.

    exports.multi = (bases...) ->
        generate merge map(bases, mro).concat [bases]

This takes a list of classes representing a hierarchy (from most to
least derived) and generates a single-inheritance hierarchy that
behaves like a class that would be have such a hierarchy.

    generate = memoize (linearization) ->
        next = head linearization
        if isEqual linearization, hierarchy next
            next
        else
            class Result extends generate tail linearization
                __mro__: linearization
                constructor: reparent next, @, next::constructor
                copyOwn next, @
                copyOwn next::, @::, partial reparent, next, @

This utility lets us copy own properties of a *from* object that are
not own properties of a *to* object into the *to* object, optionally
transformed via a projection function.

    copyOwn = (from, to, project = (x) -> x) ->
        for own key, value of from
            if not to.hasOwnProperty key
                to[key] = project value
        to

Methods in CoffeeScript call super directly, so we have to change the
`__super__` attribute of the original class during the scope of the
method so it calls the right super of the linearization.  Also,
programmers don't call super in constructor of root classes --indeed
doing so would rise an error-- so we have to inject such a call when
there are classes after these in the linearization.  The **reparent**
function takes care of all these and given an original class, and the
new class that is replacing it a linearized heterarchy, returns a
wrapped copy of a value of the former that is suitable for replacing
it in the later.

    reparent = (oldklass, newklass, value) ->
        if value not instanceof Function
            value
        else if value is oldklass::constructor and inherited(oldklass) is Object
            superctor = inherited(newklass)::constructor
            ->
                superctor.apply @, arguments
                value.apply @, arguments
        else
            newsuper = inherited(newklass)::
            oldsuper = oldklass.__super__
            ->
                oldklass.__super__ = newsuper
                try
                    value.apply @, arguments
                finally
                    oldklass.__super__ = oldsuper

This is the C3 linearization algorithm, as translated from the
original paper.

    merge = (inputs) ->
        while not isEmpty inputs
            next = find (map inputs, head), (candidate) ->
                every inputs, (input) -> candidate not in tail input
            assert next?, "Inconsistent multiple inheritance"
            inputs = reject map(inputs, (lst) -> without lst, next), isEmpty
            next

Introspection
-------------

The **mro** function returns the method resolution order
(linearization) of a given class:

> ```coffee
> class A
> class B extends A
> class C extends B
> assert mro(C).equals [C, B, A]
> ```

It returns the original classes that were mixed in when used with
mult-inherited classes:

> ```coffee
> class A
> class B extends A
> class C extends A
> class D extends multi B, C
> assert mro(D).equals [D, B, C, A, Object]
> ```

    isJavaScriptClass = (cls) ->
        standardClasses = [
            Array
            Boolean
            Date
            Error
            Function
            Number
            RegExp
            String
            Object
            EvalError
            RangeError
            ReferenceError
            SyntaxError
            TypeError
            URIError
        ]

        if cls in standardClasses
            true
        else
            # according to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects
            nonStandardClasses = [
                "Symbol"
                # typed arrays
                "Int8Array"
                "Uint8Array"
                "Uint8ClampedArray"
                "Int16Array"
                "Uint16Array"
                "Int32Array"
                "Uint32Array"
                "Float32Array"
                "Float64Array"
                # keyed Keyed collections
                "Map"
                "Set"
                "WeakMap"
                "WeakSet"
                # Structured data
                "ArrayBuffer"
                "DataView"
                # Control abstraction objects
                "Promise"
                "Generator"
                "GeneratorFunction"
                # Reflection
                "Reflect"
                "Proxy"
            ]
            for nonStandardClass in nonStandardClasses when cls is window[nonStandardClass]
                return true
            false

    exports.mro = mro = (cls) ->
        if not cls? or not cls::?
            []
        else if not cls::hasOwnProperty '__mro__'
            result = [cls].concat mro inherited(cls)
            cls::__mro__ = result unless isJavaScriptClass cls
            result
        else
            cls::__mro__

The **inherited** function returns the CoffeeScript superclass of an
object, for example:

> ```coffee
> class A
> class B extends A
> assert inherited(B) == A
> ```

Note that for multiple inherited classes, this returns the mixed
object, not the next class in the MRO, as in:

> ```coffee
> class C extends multi A, B
> assert inherited(C) == multi(A, B)
> ```

    exports.inherited = inherited = (cls) ->
        Object.getPrototypeOf(cls.prototype)?.constructor

The **hierarchy** returns the CoffeeScript hierarchy of classes of a
given class, including the class itself.  For multiple inherited
classes, it may return speciall classes that were generated to produce
the flattening, as in:

> ```coffee
> class A
> class B extends A
> class C extends A
> class D extends multi B, C
> assert not mro(D).equals hierarchy(D)
> assert hierarchy(D).equals
>     [ D, multi(B, C), inherited(multi B, C), A, Object ]
> ```

    exports.hierarchy = hierarchy = (cls) ->
        if not cls?
            []
        else
            [cls].concat hierarchy inherited cls

The **isinstance** function takes an object and a class or classes and
returns whether the object is an instance of any of those classes. It
is compatible with multi-inherited classes.

    exports.isinstance = (obj, classes...) ->
        exports.issubclass obj?.constructor, classes...

The **issubclass** tells whether a class is a subtype of another type.

    exports.issubclass = (klass, classes...) ->
        linearization = mro klass
        some classes, (cls) -> cls in linearization

License
-------

> Copyright (c) 2013, 2015 Juan Pedro Bolivar Puente <raskolnikov@gnu.org>
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in
> all copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> THE SOFTWARE.