@resin/pinejs
Version:
Pine.js is a sophisticated rules-driven API engine that enables you to define rules in a structured subset of English. Those rules are used in order for Pine.js to generate a database schema and the associated [OData](http://www.odata.org/) API. This make
148 lines (143 loc) • 4.15 kB
text/coffeescript
define ['bluebird', 'lodash'], (Promise, _) ->
window?.GLOBAL_PERMISSIONS = [
'resource.all'
]
app = do ->
enabled = {}
enabled.promise = new Promise (resolve) ->
enabled.resolve = resolve
appVars =
env: 'development'
handlers =
# USE is a list of middleware to run before any request.
USE: []
POST: []
PUT: []
DELETE: []
GET: []
PATCH: []
MERGE: []
OPTIONS: []
addHandler = (handlerName, match, middleware...) ->
#Strip wildcard
match = match.toLowerCase()
newMatch = match.replace(/[\/\*]*$/, '')
if newMatch != match
match = newMatch
paramName = '*'
else
paramMatch = /:(.*)$/.exec(match)
paramName = if !paramMatch? then null else paramMatch[1]
handlers[handlerName].push(
match: match
paramName: paramName
# Flatten middleware list to handle arrays of middleware in the arg list.
middleware: _.flattenDeep(middleware)
)
process = (method, uri, headers, body = '') ->
if !handlers[method]
return Promise.rejected(404)
req =
# Have a default user for in-browser with all permissions
user:
permissions: window.GLOBAL_PERMISSIONS
method: method
body: body
headers: headers
url: uri
params: {}
query: {}
login: (user, callback) -> callback()
console.log(method, uri, body)
if uri[-1..] == '/'
uri = uri[0...uri.length - 1]
uri = uri.toLowerCase()
new Promise (resolve, reject) ->
res =
statusCode: 200
status: (@statusCode) ->
return this
json: (obj) ->
# Stringify and parse to emulate passing over network.
obj = JSON.parse(JSON.stringify(obj))
if @statusCode >= 400
reject([@statusCode, obj, null])
else
resolve([@statusCode, obj, null])
send: (data) ->
data = _.cloneDeep(data)
if @statusCode >= 400
reject([@statusCode, data, null])
else
resolve([@statusCode, data, null])
sendStatus: (statusCode = @statusCode) ->
if statusCode >= 400
reject([statusCode, null, null])
else
resolve([statusCode, null, null])
redirect: ->
reject([307])
set: ->
type: ->
methodHandlers = handlers.USE.concat(handlers[method])
i = -1
j = -1
next = (route) ->
j++
if route == 'route' or j >= methodHandlers[i].middleware.length
checkMethodHandlers()
else
methodHandlers[i].middleware[j](req, res, next)
checkMethodHandlers = ->
i++
if i < methodHandlers.length
if uri[0...methodHandlers[i].match.length] == methodHandlers[i].match
j = -1
# Reset params that may have been added on previous routes that failed in middleware
req.params = {}
if methodHandlers[i].paramName?
req.params[methodHandlers[i].paramName] = uri[methodHandlers[i].match.length..]
next()
else if uri.length != methodHandlers[i].match.length
# Not an exact match and no parameter matching
checkMethodHandlers()
else
next()
else
checkMethodHandlers()
else
res.sendStatus(404)
checkMethodHandlers()
return {
use: _.partial(addHandler, 'USE', '/*')
get: (name, ..., callback) ->
if _.isFunction(callback)
addHandler('GET', arguments...)
else
return appVars[name]
post: _.partial(addHandler, 'POST')
put: _.partial(addHandler, 'PUT')
delete: _.partial(addHandler, 'DELETE')
patch: _.partial(addHandler, 'PATCH')
merge: _.partial(addHandler, 'MERGE')
options: _.partial(addHandler, 'OPTIONS')
all: (args...) ->
@post(args...)
@get(args...)
@put(args...)
@delete(args...)
process: (args...) ->
# The promise will run the real process function asynchronously once the app is enabled,
# which matches somewhat more closely to an AJAX call than doing it synchronously.
enabled.promise.then ->
process(args...)
listen: (..., callback) ->
enabled.resolve()
if _.isFunction(callback)
enabled.promise.then(callback)
set: (name, value) ->
appVars[name] = value
}
express = ->
return app
return express