Comparing version 0.1.0-beta-6 to 0.1.0-beta-60
@@ -10,3 +10,2 @@ "use strict"; | ||
ENV_END_PATTERN = new RegExp('.*\\.json$'), | ||
DEFAULT_SERVER_PORT = 8080, | ||
Bootstrap; | ||
@@ -16,3 +15,8 @@ | ||
Bootstrap = Type.create({ | ||
initalized: Type.BOOLEAN | ||
initalized: Type.BOOLEAN, | ||
listenPort: Type.NUMBER, | ||
listenHost: Type.STRING, | ||
controllersPath: Type.STRING, | ||
modulesPath: Type.STRING, | ||
modelsPath: Type.STRING | ||
}, { | ||
@@ -31,2 +35,7 @@ /** | ||
this.initalized = false; | ||
this.listenPort = 8080; | ||
this.listenHost = null; | ||
this.controllersPath = '@{appPath}/controllers/'; | ||
this.modulesPath = '@{appPath}/modules/'; | ||
this.modelsPath = '@{appPath}/models/'; | ||
}, | ||
@@ -82,2 +91,9 @@ /** | ||
} | ||
if (Type.isNumber(env.port)) { | ||
this.setListenPort(env.port); | ||
} | ||
if (Type.isString(env.host)) { | ||
this.setListenHost(env.host); | ||
} | ||
// set aliases | ||
@@ -93,8 +109,20 @@ if (Type.isArray(env.aliases)) { | ||
if (!di.hasAlias('controllersPath')) { | ||
di.setAlias('controllersPath', '@{appPath}/controllers/'); | ||
di.setAlias('controllersPath', this.controllersPath); | ||
} else { | ||
this.controllersPath = di.getAlias('controllersPath'); | ||
} | ||
// if there is no modules path | ||
if (!di.hasAlias('modulesPath')) { | ||
di.setAlias('modulesPath', this.modulesPath); | ||
} else { | ||
this.modulesPath = di.getAlias('modulesPath'); | ||
} | ||
// if there is no models path | ||
if (!di.hasAlias('modelsPath')) { | ||
di.setAlias('modelsPath', '@{appPath}/models/'); | ||
di.setAlias('modelsPath', this.modelsPath); | ||
} else { | ||
this.modelsPath = di.getAlias('modelsPath'); | ||
} | ||
// assets path | ||
@@ -108,2 +136,12 @@ if (Type.isString(env.assetsPath)) { | ||
} | ||
// load config | ||
if (Type.isString(env.config)) { | ||
try { | ||
di.load(envPath + '/' + env.config)(component, di, this); | ||
} catch (e) { | ||
throw new error.Exception('Initialize config: ' + envPath + '/' + env.config, e); | ||
} | ||
} else { | ||
throw new error.DataError(env.config, 'Config file is not defined'); | ||
} | ||
// if there is no logger init logger | ||
@@ -115,6 +153,5 @@ if (!component.has('core/logger')) { | ||
} | ||
// add memory cache | ||
if (!component.has('cache/memory')) { | ||
component.set('cache/memory', {}); | ||
if (!component.has('storage/memory')) { | ||
component.set('storage/memory', {}); | ||
} | ||
@@ -129,6 +166,2 @@ // register router | ||
} | ||
// set favicon path | ||
if (!component.has('core/favicon')) { | ||
component.set('core/favicon', {}); | ||
} | ||
// set view component | ||
@@ -138,12 +171,2 @@ if (!component.has('core/view')) { | ||
} | ||
// load config | ||
if (Type.isString(env.config)) { | ||
try { | ||
di.load(envPath + '/' + env.config)(component, di); | ||
} catch (e) { | ||
throw new error.Exception('Initialize config: ' + envPath + '/' + env.config, e); | ||
} | ||
} else { | ||
throw new error.DataError(env.config, 'Config file is not defined'); | ||
} | ||
// http | ||
@@ -161,6 +184,13 @@ if (!component.has('core/http')) { | ||
logger.print('Create new request', request.url); | ||
// set paths on each request | ||
di.setAlias('controllersPath', this.controllersPath); | ||
di.setAlias('modulesPath', this.modulesPath); | ||
di.setAlias('modelsPath', this.modelsPath); | ||
// new request | ||
var nRequest = new Request({ | ||
request: request, | ||
response: response | ||
response: response, | ||
encoding: server.getEncoding() | ||
}, request.url); | ||
@@ -170,6 +200,5 @@ /// parse request | ||
// on end destory | ||
request.on('end', function () { | ||
nRequest.destroy(); | ||
}); | ||
}); | ||
nRequest.onEnd(nRequest.destroy.bind(nRequest)); | ||
}.bind(this)); | ||
// server close event | ||
@@ -180,7 +209,8 @@ server.on('close', function () { | ||
}); | ||
// this must be last !! in config order | ||
if (Type.isNumber(env.port)) { | ||
server.listen(env.port); | ||
// this must be last ! | ||
if (!!this.listenHost) { | ||
server.listen(this.listenPort, this.listenHost); | ||
} else { | ||
server.listen(DEFAULT_SERVER_PORT); | ||
server.listen(this.listenPort); | ||
} | ||
@@ -196,2 +226,30 @@ | ||
* @author Igor Ivanovic | ||
* @method Bootstrap#setListenHost | ||
* | ||
* @description | ||
* Set listen host | ||
*/ | ||
setListenHost: function Bootstrap_setListenHost(host) { | ||
if (Type.isString(host)) { | ||
this.listenHost = host; | ||
} | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Bootstrap#setListenPort | ||
* | ||
* @description | ||
* Set listen port | ||
*/ | ||
setListenPort: function Bootstrap_setListenPort(port) { | ||
if (Type.isNumber(port)) { | ||
this.listenPort = port; | ||
} else { | ||
this.listenPort = parseInt(port); | ||
} | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Bootstrap#setBasePath | ||
@@ -198,0 +256,0 @@ * |
@@ -132,3 +132,64 @@ "use strict"; | ||
} | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @function compare | ||
* | ||
* @description | ||
* Compare object a with b | ||
*/ | ||
function compare(a, b) { | ||
if (Type.isString(a)) { | ||
return a === b; | ||
} else if (Type.isNumber(a)) { | ||
if (isNaN(a) || isNaN(b)) { | ||
return isNaN(a) === isNaN(b); | ||
} | ||
return a === b; | ||
} else if (Type.isBoolean(a)) { | ||
return a === b; | ||
} else if (Type.isDate(a) && Type.isDate(b)) { | ||
return a.getTime() === b.getTime(); | ||
} else if (Type.isRegExp(a) && Type.isRegExp(b)) { | ||
return a.source === b.source; | ||
} else if (Type.isArray(a) && Type.isArray(b)) { | ||
// check references first | ||
if (a === b) { | ||
return true; | ||
} | ||
return a.every(function (item, index) { | ||
try { | ||
return compare(item, b[index]); | ||
} catch (e) { | ||
throw e; | ||
} | ||
}); | ||
} else if (Type.isObject(a) && Type.isObject(b)) { | ||
var equal = []; | ||
// check references first | ||
if (a === b) { | ||
return true; | ||
} | ||
try { | ||
for (var key in a) { | ||
equal.push(compare(a[key], b[key])); | ||
} | ||
} catch (e) { | ||
throw e; | ||
} | ||
return equal.every(function (item) { | ||
return item === true; | ||
}); | ||
/// compare undefined and nulls and nans | ||
} else if (a === b) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
/** | ||
* Export functions | ||
@@ -144,3 +205,4 @@ * @type {{isBoolean: isBoolean, isUndefined: isUndefined, isDefined: isDefined, isObject: isObject, isString: isString, isNumber: isNumber, isDate: isDate, isArray: isArray, isFunction: isFunction, isRegExp: isRegExp, isConstructor: isConstructor, copy: copy, trim: trim, throwError: throwError}} | ||
createRegex: createRegex, | ||
toObject: toObject | ||
toObject: toObject, | ||
compare: compare | ||
}; |
@@ -8,2 +8,4 @@ "use strict"; | ||
mime = di.load('mime-types'), | ||
fs = di.load('fs'), | ||
Promise = di.load('promise'), | ||
component = di.load('core/component'), | ||
@@ -27,3 +29,4 @@ logger = component.get('core/logger'), | ||
config: Type.OBJECT, | ||
cache: Type.OBJECT | ||
cache: Type.OBJECT, | ||
regex: Type.REGEX | ||
}, { | ||
@@ -33,2 +36,3 @@ _construct: function Favicon_construct(config) { | ||
path: '@{basePath}/storage/', | ||
skip: false, | ||
hook: '^\\/assets' | ||
@@ -38,3 +42,4 @@ }, config); | ||
logger.print('Assets.construct', config); | ||
hook.set(new RegExp(this.config.hook), this.onRequest.bind(this)); | ||
this.regex = new RegExp(this.config.hook); | ||
hook.set(this.regex, this.onRequest.bind(this)); | ||
}, | ||
@@ -52,8 +57,13 @@ /** | ||
var maxAge = 60 * 60 * 24 * 30 * 12, // one year | ||
filePath = this.config.path + api.parsedUrl.pathname, | ||
mimeType, | ||
file; | ||
var url = api.parsedUrl.pathname, | ||
maxAge = 60 * 60 * 24 * 30 * 12, // one year | ||
filePath, | ||
mimeType; | ||
if (this.config.skip) { | ||
url = url.replace(this.regex, ''); | ||
} | ||
filePath = di.normalizePath(this.config.path + url); | ||
mimeType = this.mimeType(filePath); | ||
@@ -63,23 +73,39 @@ | ||
logger.print('MimeType', mimeType, filePath); | ||
return false; | ||
return this.handleError(function() { | ||
new error.HttpError(500, {path: filePath}, 'Invalid mime type'); | ||
}); | ||
} | ||
file = this.readFile(filePath); | ||
if (api.getMethod() !== 'GET') { | ||
return this.handleError(function() { | ||
new error.HttpError(500, {path: filePath}, 'Assets are accessible only via GET request'); | ||
}); | ||
} | ||
return new Promise(function (resolve, reject) { | ||
api.addHeader('Content-Type', mimeType); | ||
api.addHeader('Cache-Control', 'public, max-age=' + ~~(maxAge)); | ||
api.addHeader('ETag', etag(file)); | ||
this.readFile(filePath, function (err, file) { | ||
if (err) { | ||
try { | ||
new error.HttpError(500, {path: filePath}, 'No file found', err); | ||
} catch (e) { | ||
return reject(e); | ||
} | ||
} | ||
if (api.getMethod() !== 'GET') { | ||
throw new error.HttpError(500, {path: filePath}, 'Assets are accessible only via GET request'); | ||
} else if (api.isHeaderCacheUnModified()) { | ||
api.sendNoChange(); | ||
} | ||
api.addHeader('Content-Type', mimeType); | ||
api.addHeader('Cache-Control', 'public, max-age=' + ~~(maxAge)); | ||
api.addHeader('ETag', etag(file)); | ||
logger.print('MimeType', mimeType, filePath); | ||
if (api.isHeaderCacheUnModified()) { | ||
return resolve(api.sendNoChange()); | ||
} | ||
return file; | ||
logger.print('MimeType', mimeType, filePath); | ||
resolve(file); | ||
}); | ||
}.bind(this)); | ||
}, | ||
@@ -89,2 +115,19 @@ /** | ||
* @author Igor Ivanovic | ||
* @method Favicon#handleError | ||
* | ||
* @description | ||
* Handle error response | ||
*/ | ||
handleError: function Assets_handleError(callback) { | ||
return new Promise(function (resolve, reject) { | ||
try { | ||
callback(); | ||
} catch (e) { | ||
reject(e); | ||
} | ||
}); | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Favicon#mimeType | ||
@@ -106,4 +149,4 @@ * | ||
*/ | ||
readFile: function Assets_readFile(filePath) { | ||
return di.readFileSync(filePath); | ||
readFile: function Assets_readFile() { | ||
return fs.readFile.apply(fs, arguments); | ||
} | ||
@@ -110,0 +153,0 @@ }); |
@@ -66,2 +66,34 @@ "use strict"; | ||
* @author Igor Ivanovic | ||
* @method Initialize#initOne | ||
* | ||
* @description | ||
* Initialize | ||
*/ | ||
checkDependency: function(component, components) { | ||
var deps, depsName, depsConfig; | ||
if (Type.assert(Type.STRING, component.name)) { | ||
deps = this.getDependency(component.name); | ||
if (Type.isArray(deps)) { | ||
while (true) { | ||
depsName = deps.shift(); | ||
if (!depsName) { | ||
break; | ||
} | ||
if (!this.has(depsName)) { | ||
if (!!components) { | ||
depsConfig = this.find(depsName, components); | ||
if (depsConfig) { | ||
this.set(depsName, depsConfig); | ||
break; | ||
} | ||
} | ||
this.set(depsName, {}); | ||
} | ||
} | ||
} | ||
} | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Initialize#components | ||
@@ -73,3 +105,3 @@ * | ||
init: function Component_init(components) { | ||
var component, data, deps, depsName, depsConfig; | ||
var component, data; | ||
if (!Type.isArray(components)) { | ||
@@ -79,3 +111,2 @@ throw new error.Exception('Component.init: components argument must be array type'); | ||
data = core.copy(components); // copy | ||
while (true) { | ||
@@ -86,23 +117,5 @@ component = data.shift(); | ||
} | ||
if (Type.assert(Type.STRING, component.name)) { | ||
deps = this.getDependency(component.name); | ||
if (Type.isArray(deps)) { | ||
while (true) { | ||
depsName = deps.shift(); | ||
if (!depsName) { | ||
break; | ||
} | ||
if (!this.has(depsName)) { | ||
depsConfig = this.find(depsName, components); | ||
if (depsConfig) { | ||
this.set(depsName, depsConfig); | ||
} else { | ||
this.set(depsName, {}); | ||
} | ||
} | ||
} | ||
} | ||
if (!this.has(component.name)) { | ||
this.set(component.name, component); | ||
} | ||
this.checkDependency(component, components); | ||
if (!this.has(component.name)) { | ||
this.set(component.name, component); | ||
} | ||
@@ -122,3 +135,5 @@ } | ||
if (!this.has(name)) { | ||
config.name = name; | ||
try { | ||
this.checkDependency(config); | ||
if (!Type.isFunction(Func)) { | ||
@@ -125,0 +140,0 @@ if (config.filePath) { |
@@ -12,3 +12,3 @@ { | ||
"core/logger", | ||
"cache/memory", | ||
"storage/memory", | ||
"hooks/request" | ||
@@ -18,3 +18,3 @@ ], | ||
"core/logger", | ||
"cache/memory", | ||
"storage/memory", | ||
"hooks/request" | ||
@@ -27,3 +27,6 @@ ], | ||
"core/logger" | ||
], | ||
"storage/session": [ | ||
"storage/memory" | ||
] | ||
} |
@@ -5,4 +5,6 @@ "use strict"; | ||
Type = di.load('typejs'), | ||
error = di.load('error'), | ||
component = di.load('core/component'), | ||
ControllerInterface = di.load('interface/controller'), | ||
BodyParser = di.load('core/bodyParser'), | ||
view = component.get('core/view'), | ||
@@ -20,13 +22,137 @@ Controller; | ||
*/ | ||
Controller = ControllerInterface.inherit({}, { | ||
Controller = ControllerInterface.inherit({ | ||
__cookies__: Type.OBJECT | ||
}, { | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Controller#onEnd | ||
* @method Controller#setStatusCode | ||
* @param code {number} | ||
* @description | ||
* Set status code | ||
*/ | ||
setStatusCode: function Controller_setStatusCode(code) { | ||
this.__requestApi__.setStatusCode(code); | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Controller#stopChain | ||
* | ||
* @description | ||
* On end | ||
* Stop promise chain | ||
*/ | ||
stopChain: function Controller_stopChain() { | ||
return this.__requestApi__.stopPromiseChain(); | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Controller#hasHeader | ||
* @param key {string} | ||
* @description | ||
* has response header | ||
*/ | ||
hasHeader: function Controller_hasHeader(key) { | ||
return this.__requestApi__.hasHeader(key); | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Controller#getRequestBody | ||
* | ||
* @description | ||
* Get request body | ||
*/ | ||
getRequestBody: function Controller_getRequestBody() { | ||
return this.__requestApi__.getRequestBody(); | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Controller#getRequestHeader | ||
* @param key {string} | ||
* @description | ||
* Get request header | ||
*/ | ||
getRequestHeader: function Controller_getRequestHeader(key) { | ||
return this.__requestApi__.getRequestHeader(key); | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Controller#getHeaders | ||
* | ||
* @description | ||
* Return response headers | ||
*/ | ||
getHeaders: function Controller_getHeaders() { | ||
return this.__requestApi__.getHeaders(); | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Controller#getMethod | ||
* | ||
* @description | ||
* Return request method | ||
*/ | ||
getMethod: function Controller_getMethod() { | ||
return this.__requestApi__.getMethod(); | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Controller#getRequestHeaders | ||
* | ||
* @description | ||
* Return request headers | ||
*/ | ||
getRequestHeaders: function Controller_getRequestHeaders() { | ||
return this.__requestApi__.getRequestHeaders(); | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Controller#isHeaderCacheUnModified | ||
* | ||
* @description | ||
* Check if cache is unmodified | ||
*/ | ||
isHeaderCacheUnModified: function Controller_isHeaderCacheUnModified() { | ||
return this.__requestApi__.isHeaderCacheUnModified(); | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Controller#sendNoChange | ||
* | ||
* @description | ||
* Send no change 304 response | ||
*/ | ||
sendNoChange: function Controller_sendNoChange() { | ||
return this.__requestApi__.sendNoChange(); | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Controller#getParsedUrl | ||
* | ||
* @description | ||
* Return parsed url | ||
*/ | ||
getParsedUrl: function Controller_getParsedUrl() { | ||
return this.__requestApi__.parsedUrl; | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Controller#onEnd | ||
* @param route {string} | ||
* @param params {object} | ||
* @description | ||
* Create an url depends on route an parameters to router service | ||
*/ | ||
createUrl: function Controller_createUrl(route, params) { | ||
return this._request.createUrl(route, params); | ||
return this.__requestApi__.createUrl(route, params); | ||
}, | ||
@@ -37,8 +163,8 @@ /** | ||
* @method Controller#onEnd | ||
* | ||
* @param callback {function} | ||
* @description | ||
* On end | ||
* On end exec callback | ||
*/ | ||
onEnd: function Controller_onEnd(callback) { | ||
return this._request.onEnd(callback); | ||
return this.__requestApi__.onEnd(callback); | ||
}, | ||
@@ -49,3 +175,4 @@ /** | ||
* @method Controller#addHeader | ||
* | ||
* @param key {string} | ||
* @param value {string} | ||
* @description | ||
@@ -55,3 +182,3 @@ * Add header to request | ||
addHeader: function Controller_addHeader(key, value) { | ||
return this._request.addHeader(key, value); | ||
return this.__requestApi__.addHeader(key, value); | ||
}, | ||
@@ -62,3 +189,4 @@ /** | ||
* @method Controller#forward | ||
* | ||
* @param route {string} | ||
* @param params {object} | ||
* @description | ||
@@ -68,3 +196,3 @@ * Redirect to some url | ||
forward: function Controller_forward(route, params) { | ||
return this._request.forward(route, params); | ||
return this.__requestApi__.forward(route, params); | ||
}, | ||
@@ -74,4 +202,16 @@ /** | ||
* @author Igor Ivanovic | ||
* @method Controller#forwardUrl | ||
* @param url {string} | ||
* @description | ||
* Redirect to some url | ||
*/ | ||
forwardUrl: function Controller_forwardUrl(url) { | ||
return this.__requestApi__.forwardUrl(url); | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Controller#redirect | ||
* | ||
* @param url {string} | ||
* @param isTemp {boolean} | ||
* @description | ||
@@ -81,5 +221,4 @@ * Redirect to some url | ||
redirect: function Controller_redirect(url, isTemp) { | ||
return this._request.redirect(url, isTemp); | ||
return this.__requestApi__.redirect(url, isTemp); | ||
}, | ||
/** | ||
@@ -89,3 +228,4 @@ * @since 0.0.1 | ||
* @method Controller#renderFile | ||
* | ||
* @param pathName {string} | ||
* @param locals {object} | ||
* @description | ||
@@ -95,3 +235,3 @@ * Render file | ||
renderFile: function Controller_renderFile(pathName, locals) { | ||
return view.renderFile(pathName, locals); | ||
return view.renderFile(pathName, locals, this.__config__.viewsPath); | ||
}, | ||
@@ -102,3 +242,5 @@ /** | ||
* @method Controller#render | ||
* | ||
* @param source {string} | ||
* @param locals {object} | ||
* @param escape {boolean} | ||
* @description | ||
@@ -113,5 +255,210 @@ * Render view | ||
* @author Igor Ivanovic | ||
* @method Controller#hasAction | ||
* @method Controller#getActionName | ||
* | ||
* @description | ||
* Get action name | ||
* @return {string} | ||
*/ | ||
getActionName: function Controller_getActionName() { | ||
return this.__config__.action; | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Controller#getControllerName | ||
* | ||
* @description | ||
* Get controller name | ||
* @return {string} | ||
*/ | ||
getControllerName: function Controller_getControllerName() { | ||
return this.__config__.controller; | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Controller#getModuleName | ||
* | ||
* @description | ||
* Get module name | ||
* @return {string} | ||
*/ | ||
getModuleName: function Controller_getModuleName() { | ||
return this.__config__.module; | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Controller#getSession | ||
* @param key {string} | ||
* @description | ||
* Get session key | ||
* @return {string} | ||
*/ | ||
getSession: function Controller_getSession(key) { | ||
var session = component.get('storage/session'), | ||
session_id = this.getCookie(session.getCookieKey()); | ||
if (Type.isString(key)) { | ||
return session.get(session_id + key); | ||
} | ||
throw new error.HttpError(500, {key: key, session_id: session_id}, 'Controller.getSession: key must be string type'); | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Controller#setSession key value | ||
* @param key {string} | ||
* @param value {object|mixed} | ||
* @description | ||
* Set session | ||
* @return {string} | ||
*/ | ||
setSession: function Controller_setSession(key, value) { | ||
var session = component.get('storage/session'), | ||
session_id = this.getCookie(session.getCookieKey()); | ||
if (!Type.isString(key)) { | ||
throw new error.HttpError(500, {key: key, session_id: session_id}, 'Controller.getSession: key must be string type'); | ||
} else if (!session_id) { | ||
session_id = this.__requestApi__.uuid() + '_' + (new Date).getTime(); | ||
this.setCookie(session.getCookieKey(), session_id, session.getExpiredTime()); | ||
} | ||
session.set(session_id + key, value); | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Controller#removeSession | ||
* @param key {string} | ||
* @description | ||
* Remove session key | ||
* @return {string} | ||
*/ | ||
removeSession: function Controller_removeSession(key) { | ||
var session = component.get('storage/session'), | ||
session_id = this.getCookie(session.getCookieKey()); | ||
if (Type.isString(key)) { | ||
return session.remove(session_id + key); | ||
} | ||
throw new error.HttpError(500, {key: key, session_id: session_id}, 'Controller.removeSession: key must be string type'); | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Controller#setCookie | ||
* @param key {string} | ||
* @param value {string} | ||
* @param expires {string|object|number} | ||
* @param path {string} | ||
* @param domain {string} | ||
* @param isHttpOnly {boolean} | ||
* @description | ||
* Set cookie header | ||
*/ | ||
setCookie: function Controller_setCookie(key, value, expires, path, domain, isHttpOnly) { | ||
var cookie, date; | ||
if (Type.isUndefined(key) || Type.isUndefined(value)) { | ||
throw new error.HttpError(500, { | ||
key: key, | ||
value: value, | ||
expires: expires, | ||
path: path, | ||
domain: domain, | ||
isHttpOnly: isHttpOnly | ||
}, 'Controller.setCookie: Key and Value must be provided!'); | ||
} | ||
cookie = key + "=" + value; | ||
if (!!expires) { | ||
if (Type.isNumber(expires)) { | ||
date = new Date(); | ||
date.setTime(date.getTime() + expires); | ||
cookie += "; Expires=" + date.toGMTString(); | ||
} else if (Type.isString(expires)) { | ||
cookie += "; Expires=" + expires; | ||
} else if (Type.isDate(expires)) { | ||
cookie += "; Expires=" + expires.toGMTString(); | ||
} | ||
} | ||
if (!!path) { | ||
cookie += "; Path=" + path; | ||
} | ||
if (!!domain) { | ||
cookie += "; Domain=" + domain; | ||
} | ||
if (!!isHttpOnly) { | ||
cookie += "; HttpOnly"; | ||
} | ||
this.addHeader('Set-cookie', cookie); | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Controller#getCookies | ||
* | ||
* @description | ||
* Parse cookies | ||
* @return {object} | ||
*/ | ||
getCookies: function Controller_getCookies() { | ||
var data; | ||
if (!!this.__cookies__) { | ||
return this.__cookies__; | ||
} | ||
this.__cookies__ = {}; | ||
data = this.getRequestHeader('Cookie'); | ||
if (!!data) { | ||
data.replace(/(\w+[^=]+)=([^;]+)/g, function (cookie, key, value) { | ||
this.__cookies__[key] = value; | ||
}.bind(this)); | ||
} | ||
return this.__cookies__; | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Controller#getCookie | ||
* @param key {string} | ||
* @description | ||
* Get all cookies | ||
* @return {null|string} | ||
*/ | ||
getCookie: function Controller_getCookie(key) { | ||
var cookies = this.getCookies(); | ||
if (cookies.hasOwnProperty(key)) { | ||
return cookies[key]; | ||
} | ||
return null; | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Controller#getParsedBody | ||
* | ||
* @description | ||
* Parse body and return parsed object | ||
* @return {object} | ||
*/ | ||
getParsedBody: function Controller_getParsedBody() { | ||
var parser = new BodyParser( | ||
this.getRequestHeader('content-type'), | ||
this.getRequestBody() | ||
); | ||
parser.parse(); | ||
return parser.getBody(); | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Controller#hasAction | ||
* @param name {string} | ||
* @description | ||
* Check if controller have action | ||
@@ -127,3 +474,3 @@ * @return {boolean} | ||
* @method Controller#getAction | ||
* | ||
* @param name {string} | ||
* @description | ||
@@ -130,0 +477,0 @@ * Get controller action |
@@ -5,2 +5,3 @@ "use strict"; | ||
HttpServiceInterface = di.load('interface/http'), | ||
core = di.load('core'), | ||
http = di.load('http'), | ||
@@ -20,10 +21,27 @@ HttpService; | ||
HttpService = HttpServiceInterface.inherit({ | ||
server: Type.OBJECT | ||
server: Type.OBJECT, | ||
config: Type.OBJECT | ||
}, { | ||
_construct: function HttpService() { | ||
_construct: function HttpService(config) { | ||
this.config = { | ||
encoding: 'utf8' | ||
}; | ||
core.extend(this.config, config); | ||
this.server = http.createServer(); | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method HttpService#getEncoding | ||
* | ||
* @description | ||
* Return encoding | ||
*/ | ||
getEncoding: function HttpService_getEncoding() { | ||
return this.config.encoding; | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method HttpService#on | ||
@@ -30,0 +48,0 @@ * |
@@ -30,2 +30,3 @@ "use strict"; | ||
config: Type.OBJECT, | ||
length: Type.NUMBER, | ||
hooks: Type.ARRAY | ||
@@ -36,4 +37,4 @@ }, | ||
var file; | ||
this.stream = null; | ||
this.server = null; | ||
this.length = 0; | ||
this.stream = this.server = null; | ||
this.hooks = []; | ||
@@ -45,2 +46,3 @@ this.config = core.extend({ | ||
console: false, | ||
readLength: 20000, | ||
port: 9001, | ||
@@ -55,8 +57,36 @@ file: "server.log", | ||
if (this.config.publish) { | ||
this.server = http.createServer(); | ||
this.server.on('request', function (request, response) { | ||
var read = fs.readFileSync(file); | ||
response.writeHead(200, {'Content-type': 'text/plain', 'Content-Length': read.length}); | ||
response.end(read); | ||
}); | ||
var len = this.length, | ||
start = len - this.config.readLength, | ||
blen = 0, | ||
buffer, | ||
blenMessage; | ||
if (start < 0) { | ||
start = 0; | ||
blen = 1000; | ||
} else { | ||
blen = len - start; | ||
} | ||
blenMessage = 'LAST '+ blen + ' BYTES:\n\n'; | ||
buffer = new Buffer(blen + blenMessage.length, 'utf8'); | ||
fs.open(file, 'r', 755, function(status, fd) { | ||
fs.read(fd, buffer, 0, blen, start, function(err) { | ||
if (err) { | ||
var errorMessage = 'Error reading logger buffer'; | ||
response.writeHead(200, {'Content-type': 'text/plain', 'Content-Length': errorMessage.length}); | ||
response.end(errorMessage); | ||
} else { | ||
response.writeHead(200, {'Content-type': 'text/plain', 'Content-Length': buffer.length}); | ||
response.write(blenMessage); | ||
response.end(buffer); | ||
} | ||
}); | ||
}); | ||
}.bind(this)); | ||
this.server.listen(this.config.port); | ||
@@ -117,3 +147,3 @@ this.print('Publishing log write stream on port: ' + this.config.port); | ||
date = new Date().toISOString(); | ||
logs += date + '\n'; | ||
logs += date + "\n"; | ||
try { | ||
@@ -123,3 +153,3 @@ throw new Error(); | ||
url = core.trim(e.stack.split('\n').slice(3, 4).shift()); | ||
logs += url + '\n'; | ||
logs += url + "\n"; | ||
} | ||
@@ -139,3 +169,3 @@ args.forEach(function (item) { | ||
logs = logs.replace(/\\'/g, "'"); | ||
logs = logs.replace(/\\n/g, '\n'); | ||
logs = logs.replace(/\\n/g, "\n"); | ||
logs = logs.replace(/\\u001b/g, '\u001b'); | ||
@@ -149,2 +179,3 @@ | ||
try { | ||
this.length += logs.length; | ||
this.stream.write(logs); | ||
@@ -151,0 +182,0 @@ } catch (e) { |
@@ -5,2 +5,3 @@ "use strict"; | ||
ControllerInterface = di.load('interface/controller'), | ||
ModuleInterface = di.load('interface/module'), | ||
component = di.load('core/component'), | ||
@@ -40,6 +41,16 @@ router = component.get('core/router'), | ||
isRendered: Type.BOOLEAN, | ||
isERROR: Type.BOOLEAN | ||
isERROR: Type.BOOLEAN, | ||
isPromiseChainStopped: Type.BOOLEAN, | ||
isForwarded: Type.BOOLEAN, | ||
encoding: Type.STRING, | ||
body: Type.STRING, | ||
id: Type.STRING | ||
}, { | ||
_construct: function Request(config, url) { | ||
this.isForwarded = false; | ||
this.body = ''; | ||
this.isERROR = false; | ||
// body and isForwarded can be overriden | ||
core.extend(this, config); | ||
this.statusCode = 200; | ||
@@ -49,2 +60,5 @@ this.headers = {}; | ||
this.parsedUrl = URLParser.parse(this.url, true); | ||
this.isPromiseChainStopped = false; | ||
this.isRendered = false; | ||
this.id = this._uuid(); | ||
}, | ||
@@ -60,3 +74,3 @@ /** | ||
onEnd: function Request_onEnd(callback) { | ||
this.request.on('end', callback); | ||
this.request.on('destory', callback); | ||
}, | ||
@@ -108,5 +122,31 @@ /** | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Request#setStatusCode | ||
* | ||
* @description | ||
* HTTP status code | ||
*/ | ||
setStatusCode: function Request_setStatusCode(code) { | ||
if (!Type.isNumber(code)) { | ||
throw new error.HttpError(500, {code: code}, "Status code must be number type"); | ||
} | ||
this.statusCode = code; | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Request#stopPromiseChain | ||
* | ||
* @description | ||
* Stop promise chain | ||
*/ | ||
stopPromiseChain: function Request_stopPromiseChain() { | ||
this.isPromiseChainStopped = true; | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Request#getHeaders | ||
@@ -123,2 +163,13 @@ * | ||
* @author Igor Ivanovic | ||
* @method Request#getRequestBody | ||
* | ||
* @description | ||
* Return body data | ||
*/ | ||
getRequestBody: function Request_getRequestBody() { | ||
return this.body; | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Request#getRequestHeaders | ||
@@ -186,4 +237,17 @@ * | ||
addHeader: function Request_addHeader(key, value) { | ||
var item; | ||
if (Type.isString(key)) { | ||
this.headers[key.toLowerCase()] = Type.isString(value) ? value : value.toString(); | ||
key = key.toLowerCase(); | ||
value = Type.isString(value) ? value : value.toString(); | ||
if (this.hasHeader(key) && !Type.isArray(this.headers[key])) { | ||
item = this.getHeader(key); | ||
this.headers[key] = []; | ||
this.headers[key].push(item); | ||
this.headers[key].push(value); | ||
} else if (this.hasHeader(key) && Type.isArray(this.headers[key])) { | ||
this.headers[key].push(value); | ||
} else { | ||
this.headers[key] = value; | ||
} | ||
} else { | ||
@@ -199,2 +263,30 @@ throw new error.HttpError(500, { | ||
* @author Igor Ivanovic | ||
* @method Request#forwardUrl | ||
* | ||
* @description | ||
* Forward to route | ||
*/ | ||
forwardUrl: function Request_forwardUrl(url) { | ||
var request; | ||
if (this.url === url) { | ||
throw new error.HttpError(500, { | ||
url: url | ||
}, 'Cannot forward to same url'); | ||
} else { | ||
this.stopPromiseChain(); | ||
request = new Request({ | ||
request: this.request, | ||
response: this.response, | ||
isForwarded: true, | ||
body: this.body | ||
}, url); | ||
logger.print('Request.forward.url', url); | ||
return request.parse(); | ||
} | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Request#forward | ||
@@ -216,8 +308,12 @@ * | ||
this.stopPromiseChain(); | ||
request = new Request({ | ||
request: this.request, | ||
response: this.response | ||
response: this.response, | ||
isForwarded: true, | ||
body: this.body | ||
}, router.createUrl(route, params)); | ||
logger.print('Request.forward', route, params); | ||
logger.print('Request.forward.route', route, params); | ||
@@ -238,2 +334,4 @@ return request.parse(); | ||
this.addHeader('Location', url); | ||
this.stopPromiseChain(); | ||
this.isRendered = true; | ||
if (Type.isBoolean(isTemp) && !!isTemp) { | ||
@@ -255,4 +353,5 @@ this.response.writeHead(302, this.headers); | ||
sendNoChange: function () { | ||
this.response.writeHead(304, this.headers); | ||
this.response.end(); | ||
this.stopPromiseChain(); | ||
this.setStatusCode(304); | ||
this._render(''); | ||
}, | ||
@@ -280,2 +379,44 @@ /** | ||
if (this.isForwarded) { | ||
return this._process().then( | ||
this.request.emit.bind(this.request, 'destory'), | ||
this.request.emit.bind(this.request, 'destory') | ||
); // emit destroy on error and resolve | ||
} | ||
this.request.setEncoding(this.encoding); | ||
this.request.on('data', function (body) { | ||
this.body += body; | ||
}.bind(this)); | ||
return new Promise(this.request.on.bind(this.request, 'end')) | ||
.then(this._process.bind(this)) | ||
.then( | ||
this.request.emit.bind(this.request, 'destory'), | ||
this.request.emit.bind(this.request, 'destory') | ||
); // emit destroy on error and resolve | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Request#_uuid | ||
* | ||
* @description | ||
* Generate uuid | ||
*/ | ||
_uuid: function () { | ||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { | ||
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); | ||
return v.toString(16); | ||
}); | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Request#_process | ||
* | ||
* @description | ||
* Process request | ||
*/ | ||
_process: function () { | ||
return hooks.process(this._getApi()) | ||
@@ -285,10 +426,15 @@ .then(function handleHooks(data) { | ||
return data; | ||
} else if (this.isPromiseChainStopped) { | ||
return false; | ||
} | ||
return router | ||
.process(this.request.method, this.parsedUrl) // find route | ||
.then(this._resolveRoute.bind(this), this._handleError.bind(this)); // resolve route chain | ||
.then(this._resolveRoute.bind(this)); // resolve route chain | ||
}.bind(this), this._handleError.bind(this)) | ||
.then(this._render.bind(this), this._handleError.bind(this)) // render chain | ||
.then(this._render.bind(this), this._handleError.bind(this)); // render error thrown in render function | ||
}.bind(this)) // handle hook chain | ||
.then(this._render.bind(this)) // resolve route chain | ||
.catch(this._handleError.bind(this)) // catch hook error | ||
.then(this._render.bind(this)) // render hook error | ||
.catch(this._handleError.bind(this)) // catch render error | ||
.then(this._render.bind(this)); // resolve render error | ||
}, | ||
@@ -316,24 +462,58 @@ /** | ||
* Handle error | ||
* @return boolean | ||
*/ | ||
_handleError: function Request_handleError(response) { | ||
var request; | ||
if (this.isRendered) { | ||
// we have multiple recursion in parse for catching | ||
return false; | ||
} | ||
// log error request | ||
logger.print('Request.error', { | ||
url: this.url, | ||
status: this.statusCode, | ||
id: this.id, | ||
isRendered: this.isRendered, | ||
content_type: this.getHeader('content-type') | ||
}, response); | ||
// set status codes | ||
if (response.code) { | ||
this.setStatusCode(response.code); | ||
} else { | ||
this.setStatusCode(500); | ||
} | ||
// stop current chain!!! | ||
this.stopPromiseChain(); | ||
if (response instanceof Error && !this.isERROR && !!router.getErrorRoute()) { | ||
// return new request | ||
request = new Request({ | ||
request: this.request, | ||
response: this.response, | ||
isForwarded: true, | ||
body: this.body, | ||
isERROR: true | ||
}, router.createUrl(router.getErrorRoute())); | ||
// pass exception response over parsed url query as query parameter | ||
request.parsedUrl.query.exception = response; | ||
// set status codes for new request | ||
if (response.code) { | ||
this.statusCode = response.code; | ||
request.setStatusCode(response.code); | ||
} else { | ||
this.statusCode = 500; | ||
request.setStatusCode(500); | ||
} | ||
this.isERROR = true; | ||
return this._resolveRoute([router.getErrorRoute(), response]).then(null, this._handleError); | ||
// return parsed request | ||
return request.parse(); | ||
} else if (response.trace) { | ||
this.addHeader('Content-Type', 'text/plain'); | ||
this._render(response.trace); | ||
return this._render(response.trace); | ||
} else if (response.stack) { | ||
this.addHeader('Content-Type', 'text/plain'); | ||
this._render(response.stack); | ||
return this._render(response.stack); | ||
} else if (this.isERROR) { | ||
this.addHeader('Content-Type', 'text/plain'); | ||
this._render(util.inspect(response)); | ||
return this._render(util.inspect(response)); | ||
} else { | ||
this._render(response); | ||
return this._render(response); | ||
} | ||
@@ -348,26 +528,37 @@ }, | ||
* End request | ||
* @return boolean | ||
*/ | ||
_render: function Request__render(response) { | ||
if (!this.isRendered) { | ||
if (this.isRendered) { | ||
return false; | ||
} | ||
logger.print('Request.render', response); | ||
this._checkContentType('text/html'); | ||
this._checkContentType('text/html'); | ||
this.response.writeHead(this.statusCode, this.headers); | ||
this.response.writeHead(this.statusCode, this.headers); | ||
if (Type.isString(response)) { | ||
this.addHeader('Content-Length', response.length); | ||
this.response.end(response); | ||
} else if (response instanceof Buffer) { | ||
this.addHeader('Content-Length', response.length); | ||
this.response.end(response); | ||
} else if (!response) { | ||
throw new error.HttpError(500, {}, 'No data to render'); | ||
} else { | ||
throw new error.HttpError(500, {}, 'Invalid response type, string or buffer is required!'); | ||
} | ||
if (Type.isString(response)) { | ||
this.addHeader('Content-Length', response.length); | ||
this.response.end(response); | ||
} else if (response instanceof Buffer) { | ||
this.addHeader('Content-Length', response.length); | ||
this.response.end(response); | ||
} else if (!response) { | ||
throw new error.HttpError(500, {}, 'No data to render'); | ||
} else { | ||
throw new error.HttpError(500, {}, 'Invalid response type, string or buffer is required!'); | ||
} | ||
} | ||
logger.print('Request.render', { | ||
url: this.url, | ||
status: this.statusCode, | ||
id: this.id, | ||
isRendered: this.isRendered, | ||
content_type: this.getHeader('content-type') | ||
}); | ||
this.isRendered = true; | ||
return true; | ||
}, | ||
@@ -391,2 +582,3 @@ | ||
getMethod: this.getMethod.bind(this), | ||
getRequestBody: this.getRequestBody.bind(this), | ||
getRequestHeaders: this.getRequestHeaders.bind(this), | ||
@@ -397,4 +589,8 @@ getRequestHeader: this.getRequestHeader.bind(this), | ||
sendNoChange: this.sendNoChange.bind(this), | ||
stopPromiseChain: this.stopPromiseChain.bind(this), | ||
setStatusCode: this.setStatusCode.bind(this), | ||
createUrl: router.createUrl.bind(router), | ||
parsedUrl: core.copy(this.parsedUrl) | ||
parsedUrl: core.copy(this.parsedUrl), | ||
forwardUrl: this.forward.bind(this), | ||
uuid: this._uuid.bind(this) | ||
}; | ||
@@ -412,7 +608,11 @@ }, | ||
if (!promise) { | ||
return new Promise(function(resolve, reject) { | ||
return new Promise(function (resolve, reject) { | ||
try { | ||
resolve(next.apply(next, arguments)); | ||
} catch (e) { | ||
reject(new error.HttpError(500, arguments, "Error on executing action", e)); | ||
if (e instanceof error.HttpError) { | ||
reject(e); | ||
} else { | ||
reject(new error.HttpError(500, {}, "Error on executing action", e)); | ||
} | ||
} | ||
@@ -423,4 +623,7 @@ }); | ||
return promise.then(function (data) { | ||
return Promise.resolve(_handler(data)); | ||
}, this._handleError.bind(this)); | ||
if (!!this.isPromiseChainStopped) { | ||
return promise; | ||
} | ||
return _handler(data); | ||
}.bind(this), this._handleError.bind(this)); | ||
@@ -431,3 +634,7 @@ function _handler() { | ||
} catch (e) { | ||
throw new error.HttpError(500, arguments, "Error on executing action", e); | ||
if (e instanceof error.HttpError) { | ||
throw e; | ||
} else { | ||
throw new error.HttpError(500, {}, "Error on executing action", e); | ||
} | ||
} | ||
@@ -439,3 +646,3 @@ } | ||
* @author Igor Ivanovic | ||
* @method Request#_handleRoute | ||
* @method Request#_handleController | ||
* | ||
@@ -445,10 +652,8 @@ * @description | ||
*/ | ||
_handleRoute: function Request_handleRoute() { | ||
var controllerToLoad = '@{controllersPath}/' + this.controller, | ||
_handleController: function Request_handleController(controllersPath, viewsPath) { | ||
var controllerToLoad = controllersPath + this.controller, | ||
LoadedController, | ||
controller, | ||
action, | ||
promise, | ||
afterActionPromise = false, | ||
afterEachPromise = false; | ||
promise; | ||
@@ -461,4 +666,13 @@ try { | ||
controller = new LoadedController(this._getApi()); | ||
if (!Type.assert(Type.FUNCTION, LoadedController)) { | ||
throw new error.HttpError(500, {path: controllerToLoad}, 'Controller must be function type'); | ||
} | ||
controller = new LoadedController(this._getApi(), { | ||
controller: this.controller, | ||
action: this.action, | ||
module: this.module, | ||
viewsPath: viewsPath | ||
}); | ||
if (!(controller instanceof ControllerInterface)) { | ||
@@ -504,7 +718,7 @@ throw new error.HttpError(500, controller, 'Controller must be instance of ControllerInterface "core/controller"'); | ||
if (controller.has('after_' + this.action)) { | ||
afterActionPromise = this._chain(promise, controller.get('after_' + this.action).bind(controller, this.params)); | ||
promise = this._chain(promise, controller.get('after_' + this.action).bind(controller, this.params)); | ||
} | ||
if (controller.has("afterEach")) { | ||
afterEachPromise = this._chain(promise, controller.afterEach.bind(controller, this.action, this.params)); | ||
promise = this._chain(promise, controller.afterEach.bind(controller, this.action, this.params)); | ||
} | ||
@@ -514,8 +728,37 @@ | ||
return Promise.all([afterActionPromise, afterEachPromise]).then(function (data) { | ||
logger.print('afterActionPromise, afterEachPromise', data); | ||
return promise; | ||
}); | ||
return promise; | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Request#_handleModule | ||
* | ||
* @description | ||
* Handle module | ||
* @return {object} Promise | ||
*/ | ||
_handleModule: function Request__handleModule(path) { | ||
var moduleToLoad = path + this.module, | ||
LoadedModule, | ||
module; | ||
try { | ||
LoadedModule = di.load(moduleToLoad); | ||
} catch (e) { | ||
throw new error.HttpError(500, {path: moduleToLoad}, 'Missing module', e); | ||
} | ||
if (!Type.assert(Type.FUNCTION, LoadedModule)) { | ||
throw new error.HttpError(500, {path: moduleToLoad}, 'Module must be function type'); | ||
} | ||
module = new LoadedModule(this.module); | ||
if (!(module instanceof ModuleInterface)) { | ||
throw new error.HttpError(500, module, 'Module must be instance of ModuleInterface "core/module"'); | ||
} | ||
return this._handleController(module.getControllersPath(), module.getViewsPath()); | ||
}, | ||
/** | ||
@@ -529,7 +772,5 @@ * @since 0.0.1 | ||
* @return {object} Promise | ||
* @todo implement modules | ||
*/ | ||
_resolveRoute: function Request__resolveRoute(routeRule) { | ||
var route; | ||
this.statusCode = 200; | ||
this.route = routeRule.shift(); | ||
@@ -544,3 +785,7 @@ this.params = routeRule.shift(); | ||
return this._handleRoute(); | ||
if (!!this.module) { | ||
return this._handleModule(di.getAlias('modulesPath')); | ||
} | ||
return this._handleController(di.getAlias('controllersPath'), di.getAlias('viewsPath')); | ||
} | ||
@@ -547,0 +792,0 @@ |
@@ -25,2 +25,3 @@ "use strict"; | ||
routes: Type.ARRAY, | ||
createUrlRgx: Type.REGEX, | ||
config: Type.OBJECT | ||
@@ -30,5 +31,13 @@ }, { | ||
this.routes = []; | ||
this.createUrlRgx = /\/\//g; | ||
this.config = core.extend({ | ||
errorRoute: false | ||
errorRoute: false, | ||
errorUrl: '/error' | ||
}, config); | ||
// add error route so we can resolve it | ||
this.add({ | ||
pattern: this.config.errorUrl, | ||
route: this.config.errorRoute ? this.config.errorRoute : 'core/error', | ||
method: ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'TRACE', 'OPTIONS', 'CONNECT', 'PATCH'] | ||
}); | ||
}, | ||
@@ -72,3 +81,3 @@ /** | ||
if (!(rule instanceof RouteRuleInterface)) { | ||
throw new error.HttpError(500, {rule: rule}, 'Router.add: rule must be instance of RouteRuleInterface'); | ||
throw new error.HttpError(500, {rule: rule, route: route}, 'Router.add: rule must be instance of RouteRuleInterface'); | ||
} | ||
@@ -78,8 +87,18 @@ | ||
this.routes.push(rule); | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Router#normalizeUrl | ||
* | ||
* @description | ||
* Create url | ||
* @return {string} | ||
*/ | ||
normalizeUrl: function Router_normalizeUrl(url) { | ||
return url.replace(this.createUrlRgx, '/').replace(this.createUrlRgx, '/'); | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method Router#createUrl | ||
@@ -92,3 +111,3 @@ * | ||
createUrl: function Router_createUrl(route, params) { | ||
var i, len = this.routes.length, routeRule, url, anchor = ''; | ||
var routeRule, url, anchor = '', routes; | ||
@@ -112,8 +131,9 @@ if (!Type.isString(route)) { | ||
params = core.copy(params); | ||
routes = this.routes.slice(); | ||
for (i = len - 1; i > -1; --i) { | ||
routeRule = this.routes[i]; | ||
while (routes.length) { | ||
routeRule = routes.shift(); | ||
url = routeRule.createUrl(route, params); | ||
if (url) { | ||
return '/' + url + anchor; | ||
return this.normalizeUrl('/' + url + anchor); | ||
} | ||
@@ -127,3 +147,4 @@ } | ||
} | ||
return url + anchor; | ||
return this.normalizeUrl(url + anchor); | ||
}, | ||
@@ -139,17 +160,25 @@ /** | ||
parseRequest: function Router_parseRequest(method, parsedUrl) { | ||
var i, len = this.routes.length, routeRule, route = []; | ||
var all = []; | ||
for (i = len - 1; i > -1; --i) { | ||
routeRule = this.routes[i]; | ||
route = routeRule.parseRequest(method, parsedUrl); | ||
if (Type.isArray(route) && route.length) { | ||
break; | ||
this.routes.forEach(function (routeRule) { | ||
all.push( | ||
new Promise(function (resolve, reject) { | ||
try { | ||
resolve(routeRule.parseRequest(method, parsedUrl)) | ||
} catch (e) { | ||
reject(e); | ||
} | ||
}) | ||
); | ||
}); | ||
return Promise.all(all).then(function (data) { | ||
var route; | ||
while (data.length) { | ||
route = data.shift(); | ||
if (Type.isArray(route) && route.length === 2) { | ||
return route; | ||
} | ||
} | ||
} | ||
if (!Type.isArray(route)) { | ||
route = []; | ||
} | ||
logger.print('Router.parseRequest', route); | ||
return route; | ||
return []; | ||
}); | ||
}, | ||
@@ -201,3 +230,3 @@ /** | ||
return new Promise(handlePromise.bind(this)) | ||
return this.parseRequest(method, parsedUrl) | ||
.then(function (routeRule) { | ||
@@ -212,10 +241,2 @@ if (Type.isArray(routeRule) && routeRule.length === 2) { | ||
}); | ||
function handlePromise(resolve, reject) { | ||
try { | ||
resolve(this.parseRequest(method, parsedUrl)); | ||
} catch (e) { | ||
reject(e); | ||
} | ||
} | ||
} | ||
@@ -222,0 +243,0 @@ }); |
@@ -12,4 +12,5 @@ "use strict"; | ||
ROUTE_MATCH = /<(\w+)>/ig, | ||
PATTERN_CAPTURE_REGEX = /\(((\?P?<(\w+)>)((:?(\(([^\/]+)\)))?|([^\)]+))|([^\)]+))\)/g, | ||
IS_NAMED = /\(\?P?<(\w+)>([^\)]+)\)/, | ||
PATTERN_CAPTURE_REGEX = /\(\??P?(<(\w+)>)?([^\)]+\)?)\)?\)/g, | ||
IS_NAMED = /<(\w+)>/, | ||
IS_GROUP = /^\(([^\)]+)\)$/, | ||
RouteRule; | ||
@@ -43,2 +44,3 @@ /** | ||
if (!config.pattern) { | ||
@@ -75,6 +77,8 @@ throw new error.HttpError(500, config, 'RouteRule: rule object must have an pattern property'); | ||
if (Array.isArray(matches) && matches.length) { | ||
matches.forEach(function (item) { | ||
var esc, nPattern; | ||
name = item[1]; | ||
nPattern = Type.isString(item[2]) ? item[2] : '[^\/]+'; | ||
nPattern = Type.isString(item[2]) ? item[2] : '([^\/]+)'; | ||
esc = { | ||
@@ -85,2 +89,3 @@ key: '<' + name + '>', | ||
escapePattern.push(esc); | ||
if (this.find(this.routeParams, name)) { | ||
@@ -91,3 +96,3 @@ escapeRule.push(esc); | ||
key: name, | ||
value: nPattern === '[^\/]+' ? '' : core.createRegex('^' + nPattern + '$') | ||
value: nPattern === '([^\/]+)' ? '' : core.createRegex('^' + nPattern + '$') | ||
}); | ||
@@ -315,2 +320,3 @@ } | ||
var matches = core.match(PATTERN_CAPTURE_REGEX, re), newRegex = re; | ||
if (matches.length) { | ||
@@ -320,9 +326,18 @@ matches = matches.map(this.toObject); | ||
var index = Object.keys(item).length - 3; | ||
item.isNamed = IS_NAMED.test(item[0]); | ||
item.isNamed = IS_NAMED.test(item[1]); | ||
if (item.isNamed) { | ||
item.pattern = '(' + item[index] + ')'; | ||
item.key = item[3]; | ||
if (IS_GROUP.test(item[index])) { | ||
item.pattern = item[index]; | ||
} else { | ||
item.pattern = '(' + item[index] + ')'; | ||
} | ||
item.key = item[2]; | ||
newRegex = newRegex.replace(item[0], item.pattern); | ||
} else { | ||
item.pattern = item[0]; | ||
if (IS_GROUP.test(item[index])) { | ||
item.pattern = item[index]; | ||
} else { | ||
item.pattern = '(' + item[index] + ')'; | ||
} | ||
item.key = null; | ||
@@ -354,3 +369,2 @@ } | ||
rgx = this.toRegex(rgx); | ||
} | ||
@@ -411,3 +425,3 @@ if (Type.isObject(rgx) && Type.isRegExp(rgx.regex)) { | ||
str = str.trim(); | ||
if (strip) { | ||
if (strip && str !== strip) { | ||
str = str.replace(core.createRegex('^' + strip), ''); | ||
@@ -414,0 +428,0 @@ str = str.replace(core.createRegex(strip + '$'), ''); |
@@ -6,2 +6,3 @@ "use strict"; | ||
ViewInterface = di.load('interface/view'), | ||
ModuleInterface = di.load('interface/module'), | ||
swig = di.load('swig'), | ||
@@ -27,3 +28,6 @@ error = di.load('error'), | ||
swig: Type.OBJECT, | ||
preloaded: Type.OBJECT | ||
preloaded: Type.OBJECT, | ||
paths: Type.ARRAY, | ||
aliasRegex: Type.REGEX, | ||
normalizers: Type.ARRAY | ||
}, | ||
@@ -34,3 +38,6 @@ { | ||
// extend | ||
this.aliasRegex = /@{.*}/g; | ||
this.preloaded = {}; | ||
this.paths = []; | ||
this.normalizers = []; | ||
this.config = core.extend({ | ||
@@ -44,12 +51,27 @@ cache: false, | ||
cacheComponent: false, | ||
themes: '@{appPath}/themes/', | ||
views: '@{appPath}/views/', | ||
suffix: '.twig', | ||
theme: false | ||
extensions: false, | ||
defaultTheme: 'default', | ||
themes: [] | ||
}, config); | ||
this.normalizers.push(this.config.views); | ||
di.setAlias('viewsPath', this.config.views); | ||
di.setAlias('themesPath', this.config.themes); | ||
if (Type.isArray(this.config.themes)) { | ||
if (this.config.themes.indexOf(this.config.defaultTheme) === -1) { | ||
this.config.themes.push(this.config.defaultTheme); | ||
} | ||
this.config.themes.forEach(function (name) { | ||
this.paths.push(this.config.views + name + '/'); | ||
}.bind(this)); | ||
} else { | ||
throw new error.HttpError(500, this.config, 'View.construct: themes are not array type'); | ||
} | ||
this.setModulesViewsPath(di.getAlias('modulesPath')); | ||
if (Type.assert(Type.STRING, this.config.suffix)) { | ||
@@ -67,12 +89,21 @@ this.suffix = new RegExp(this.config.suffix + '$'); | ||
defaults = core.extend({}, this.config); | ||
// don't use swig cache! | ||
if (this.config.cache) { | ||
this.preloadTemplates(di.getAlias('viewsPath')); | ||
this.preloadTemplates(di.getAlias('themesPath')); | ||
defaults.cache = { | ||
get: this.getPreloaded.bind(this), | ||
set: this.setPreloaded.bind(this) | ||
}; | ||
} | ||
defaults = core.extend({}, this.config); | ||
// don't use swig cache! | ||
defaults.cache = false; | ||
this.swig = new swig.Swig(defaults); | ||
if (this.config.extensions) { | ||
di.load(this.config.extensions)(this, di); | ||
} | ||
if (this.config.cache) { | ||
this.paths.forEach(this.preloadTemplates.bind(this)); | ||
} | ||
logger.print("View.construct", this.config); | ||
@@ -83,2 +114,48 @@ }, | ||
* @author Igor Ivanovic | ||
* @method View#setModulesViewsPath | ||
* | ||
* @description | ||
* Set modules path | ||
*/ | ||
setModulesViewsPath: function View_setModulesViewsPath(dir) { | ||
var list, name, moduleToLoad, LoadedModule, moduleInstance; | ||
if (this.isDir(dir)) { | ||
list = this.readDir(dir); | ||
while (true) { | ||
name = list.shift(); | ||
if (!name) { | ||
break; | ||
} | ||
moduleToLoad = dir + '/' + name; | ||
try { | ||
LoadedModule = di.load(moduleToLoad); | ||
} catch (e) { | ||
throw new error.HttpError(500, {path: moduleToLoad}, 'Missing module', e); | ||
} | ||
if (!Type.assert(Type.FUNCTION, LoadedModule)) { | ||
throw new error.HttpError(500, {path: moduleToLoad}, 'Module must be function type'); | ||
} | ||
moduleInstance = new LoadedModule(name); | ||
if (!(moduleInstance instanceof ModuleInterface)) { | ||
throw new error.HttpError(500, moduleInstance, 'Module must be instance of ModuleInterface "core/module"'); | ||
} | ||
this.normalizers.push(moduleInstance.getViewsPath()); | ||
this.config.themes.forEach(function (name) { | ||
this.paths.push(moduleInstance.getViewsPath() + name + '/'); | ||
}.bind(this)); | ||
} | ||
} | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method View#setPreloaded | ||
@@ -89,3 +166,3 @@ * | ||
*/ | ||
setPreloaded: function (key, value) { | ||
setPreloaded: function View_setPreloaded(key, value) { | ||
logger.log('View.setPreloaded: ', key + '\n' + value); | ||
@@ -101,5 +178,6 @@ this.preloaded[key] = value; | ||
* Get preloaded template | ||
* @return {string|boolean} | ||
*/ | ||
getPreloaded: function (key) { | ||
if (this.hasPreloaded(key)) { | ||
getPreloaded: function View_getPreloaded(key) { | ||
if (this.preloaded.hasOwnProperty(key)) { | ||
return this.preloaded[key]; | ||
@@ -112,13 +190,16 @@ } | ||
* @author Igor Ivanovic | ||
* @method View#hasPreloaded | ||
* @method View#isFile | ||
* | ||
* @description | ||
* Check if have preloaded | ||
* Is file | ||
* @return {string|boolean} | ||
*/ | ||
hasPreloaded: function (key) { | ||
var isPreloaded = this.preloaded.hasOwnProperty(key); | ||
if (!!this.config.cache && !isPreloaded) { | ||
throw new error.DataError({key: key}, "ENOENT, no such file or directory"); | ||
isFile: function View_isFile(path) { | ||
try { | ||
path = di.normalizePath(path); | ||
return fs.statSync(path).isFile(); | ||
} catch (e) { | ||
logger.print('View.isFile path is not valid file', {path: path}); | ||
} | ||
return isPreloaded; | ||
return false; | ||
}, | ||
@@ -128,34 +209,16 @@ /** | ||
* @author Igor Ivanovic | ||
* @method View#preloadTemplates | ||
* @method View#isDir | ||
* | ||
* @description | ||
* Preload all templates at bootstrap | ||
* Is directory | ||
* @return {string|boolean} | ||
*/ | ||
preloadTemplates: function View_preloadTemplates(dir) { | ||
var list, name; | ||
if (isDir(dir)) { | ||
list = readDir(dir); | ||
while (true) { | ||
name = list.shift(); | ||
if (!name) { | ||
break; | ||
} | ||
this.preloadTemplates(di.normalizePath(dir + '/' + name)); | ||
} | ||
} else if (isFile(dir) && this.suffix.test(dir)) { | ||
this.setPreloaded(dir, di.readFileSync(dir)); | ||
} | ||
function readDir(path) { | ||
return fs.readdirSync(path); | ||
} | ||
function isDir(path) { | ||
isDir: function View_isDir(path) { | ||
try { | ||
path = di.normalizePath(path); | ||
return fs.statSync(path).isDirectory(); | ||
} catch (e) { | ||
logger.print('View.isDir path is not valid path', {path: path}); | ||
} | ||
function isFile(path) { | ||
return fs.statSync(path).isFile(); | ||
} | ||
return false; | ||
}, | ||
@@ -165,15 +228,10 @@ /** | ||
* @author Igor Ivanovic | ||
* @method View#setThemesPath | ||
* @method View#readDir | ||
* | ||
* @description | ||
* Set theme path | ||
* Read directory | ||
*/ | ||
setTheme: function View_setTheme(name) { | ||
if (Type.assert(Type.STRING, name)) { | ||
this.config.theme = name; | ||
} else if(Type.isNull(name)) { | ||
this.config.theme = null; | ||
} else { | ||
throw new error.HttpError(500, {name: name}, "ViewLoader.setTheme: name must be string type"); | ||
} | ||
readDir: function View_readDir(path) { | ||
path = di.normalizePath(path); | ||
return fs.readdirSync(path); | ||
}, | ||
@@ -183,35 +241,28 @@ /** | ||
* @author Igor Ivanovic | ||
* @method View#getPath | ||
* @method View#preloadTemplates | ||
* | ||
* @description | ||
* Get path | ||
* Preload all templates at bootstrap | ||
*/ | ||
getPath: function View_getPath(skipTheme) { | ||
var path = '@{viewsPath}/'; | ||
if (Type.isString(this.config.theme) && !skipTheme) { | ||
path = '@{themesPath}/' + this.config.theme + '/'; | ||
preloadTemplates: function View_preloadTemplates(dir) { | ||
var list, name; | ||
if (this.isDir(dir)) { | ||
list = this.readDir(dir); | ||
while (true) { | ||
name = list.shift(); | ||
if (!name) { | ||
break; | ||
} | ||
this.preloadTemplates(di.normalizePath(dir + '/' + name)); | ||
} | ||
} else if (this.isFile(dir) && this.suffix.test(dir)) { | ||
this.setPreloaded(dir, this.swig.compileFile(dir)); | ||
} | ||
return di.normalizePath(path); | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method View#normalizeResolveValue | ||
* | ||
* @description | ||
* Normalize resolve value | ||
* @return {string} | ||
*/ | ||
normalizeResolveValue: function View_normalizeResolveValue(value) { | ||
var theme = this.getPath(), view = this.getPath(true); | ||
if (Type.isString(value) && value.match(theme)) { | ||
return value.replace(theme, "").replace(this.suffix, ""); | ||
} else if (Type.isString(value) && value.match(view)) { | ||
return value.replace(view, "").replace(this.suffix, ""); | ||
} | ||
return value; | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method View#resolve | ||
@@ -223,20 +274,68 @@ * | ||
*/ | ||
resolve: function View_resolve(to, from) { | ||
return this.getPath() + this.normalizeResolveValue(to) + this.config.suffix; | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method View#readTemplate | ||
* | ||
* @description | ||
* Read template | ||
* @return {string}; | ||
*/ | ||
readTemplate: function View_readTemplate(path) { | ||
if (this.hasPreloaded(path)) { | ||
return this.getPreloaded(path); | ||
} else { | ||
return di.readFileSync(path); | ||
resolve: function View_resolve(toPath, fromPath) { | ||
var file = di.normalizePath(toPath), | ||
themes = this.config.themes.slice(), | ||
theme, | ||
re, | ||
filePath, | ||
normalizers = this.normalizers.slice(), | ||
isNormalized = false, | ||
path, | ||
trace = []; | ||
// file name normalizers | ||
while (normalizers.length) { | ||
path = di.normalizePath(normalizers.shift()); | ||
if (file.match(path)) { | ||
file = file.replace(path, '').replace(this.suffix, ''); | ||
isNormalized = true; | ||
break; | ||
} | ||
} | ||
// try normalize fromPath | ||
if (!isNormalized && !!fromPath) { | ||
normalizers = this.normalizers.slice(); | ||
fromPath = di.normalizePath(fromPath); | ||
// file name normalizers | ||
while (normalizers.length) { | ||
path = di.normalizePath(normalizers.shift()); | ||
if (fromPath.match(path)) { | ||
isNormalized = true; | ||
break; | ||
} | ||
} | ||
} | ||
// check themes | ||
if (isNormalized) { | ||
while (themes.length) { | ||
theme = themes.shift(); | ||
re = new RegExp('^' + theme + '/'); | ||
file = file.replace(re, ''); | ||
filePath = di.normalizePath(path + theme + '/' + file + this.config.suffix); | ||
if (this.isFile(filePath)) { | ||
return filePath; | ||
} | ||
trace.push({ | ||
theme: theme, | ||
path: path, | ||
filePath: filePath | ||
}); | ||
} | ||
} | ||
throw new error.HttpError(500, { | ||
from: fromPath, | ||
load: toPath, | ||
filePath: filePath, | ||
paths: this.paths, | ||
isNormalized: isNormalized, | ||
file: file, | ||
path: path, | ||
trace: trace, | ||
themes: this.config.themes | ||
}, "View.resolve: template don't exists"); | ||
}, | ||
@@ -252,13 +351,15 @@ /** | ||
*/ | ||
load: function View_load(identifier, cb) { | ||
load: function View_load(path, cb) { | ||
var template = ''; | ||
try { | ||
template = this.readTemplate(identifier); | ||
template = di.readFileSync(path); | ||
} catch (e) { | ||
identifier = this.normalizeResolveValue(identifier); | ||
identifier = this.getPath(true) + identifier + this.config.suffix; | ||
template = this.readTemplate(identifier); | ||
logger.print('ViewLoader.load.error', { | ||
path: path, | ||
cb: cb, | ||
e: e | ||
}); | ||
} finally { | ||
logger.print('ViewLoader.load', { | ||
identifier: identifier, | ||
path: path, | ||
template: template, | ||
@@ -343,4 +444,7 @@ cb: cb | ||
*/ | ||
renderFile: function View_renderFile(pathName, locals) { | ||
return this.swig.renderFile(pathName, locals); | ||
renderFile: function View_renderFile(templateName, locals, viewsPath) { | ||
if (!viewsPath) { | ||
viewsPath = di.getAlias('viewsPath'); | ||
} | ||
return this.swig.renderFile(viewsPath + templateName, locals); | ||
} | ||
@@ -347,0 +451,0 @@ } |
@@ -87,6 +87,10 @@ "use strict"; | ||
normalizePath: function DI_normalizePath(value) { | ||
Object.keys(this.aliases).forEach(function (key) { | ||
value = value.replace('@{' + key + '}', this.aliases[key]); | ||
}.bind(this)); | ||
return path.normalize(value); | ||
if (Type.isString(value)) { | ||
Object.keys(this.aliases).forEach(function (key) { | ||
value = value.replace('@{' + key + '}', this.aliases[key]); | ||
}.bind(this)); | ||
return path.normalize(value); | ||
} else { | ||
throw new error.DataError({value: value}, 'DI.normalizePath: value is not string'); | ||
} | ||
}, | ||
@@ -127,2 +131,18 @@ /** | ||
* @author Igor Ivanovic | ||
* @method DI#refereshNodeModuleCache | ||
* | ||
* @description | ||
* Reset file cache | ||
*/ | ||
refereshNodeModuleCache: function DI_refereshNodeModuleCache(file) { | ||
var path = this.getFilePath(file), | ||
key = require.resolve(path + '.js'); | ||
// because all modules in node are cached while executing tests we want to delete cached version | ||
delete require.cache[key]; | ||
return path; | ||
}, | ||
/** | ||
* @since 0.0.1 | ||
* @author Igor Ivanovic | ||
* @method DI#mockLoad | ||
@@ -144,8 +164,4 @@ * | ||
if (Type.isString(file)) { | ||
// get file | ||
path = this.getFilePath(file); | ||
// because all modules in node are cached while executing tests we want to delete cached version | ||
delete require.cache[require.resolve(path + '.js')]; | ||
// do require | ||
return require(path); | ||
return require(this.refereshNodeModuleCache(file)); | ||
@@ -152,0 +168,0 @@ } else if (Type.isFunction(file)) { |
@@ -16,4 +16,6 @@ { | ||
"core/controller": "@{framework}/core/controller", | ||
"core/module": "@{framework}/core/module", | ||
"core/routeRule": "@{framework}/core/routeRule", | ||
"core/view": "@{framework}/core/view", | ||
"core/bodyParser": "@{framework}/core/bodyParser", | ||
@@ -25,3 +27,4 @@ "db/mongo": "@{framework}/db/mongo", | ||
"interface/requestHooks": "@{framework}/interface/requestHooks", | ||
"interface/cache": "@{framework}/interface/cache", | ||
"interface/storage": "@{framework}/interface/storage", | ||
"interface/controller": "@{framework}/interface/controller", | ||
@@ -32,6 +35,8 @@ "interface/routeRule": "@{framework}/interface/routeRule", | ||
"interface/view": "@{framework}/interface/view", | ||
"interface/module": "@{framework}/interface/module", | ||
"cache/memory": "@{framework}/cache/memory", | ||
"storage/memory": "@{framework}/storage/memory", | ||
"storage/session": "@{framework}/storage/session", | ||
"typejs": "static-type-js" | ||
} |
@@ -19,7 +19,20 @@ "use strict"; | ||
ControllerInterface = Type.create({ | ||
_request: Type.OBJECT | ||
__requestApi__: Type.OBJECT, | ||
__config__: Type.OBJECT | ||
}, { | ||
_invoke: function ControllerInterface(request) { | ||
this._request = request; | ||
["has", "get", "redirect", "forward", "addHeader", "onEnd", "createUrl"].forEach(function (method) { | ||
_invoke: function ControllerInterface(requestApi, config) { | ||
this.__requestApi__ = requestApi; | ||
this.__config__ = config; | ||
[ | ||
"has", "get", "redirect", | ||
"forward", "addHeader", "onEnd", | ||
"createUrl", "hasHeader", "getRequestHeader", | ||
"getHeaders", "getMethod", "getRequestHeaders", | ||
"isHeaderCacheUnModified", "sendNoChange", "getParsedUrl", | ||
"stopChain", "render", "renderFile", "setStatusCode", | ||
"getRequestBody", | ||
"getActionName", | ||
"getControllerName", | ||
"getModuleName" | ||
].forEach(function (method) { | ||
if (!(method in this)) { | ||
@@ -26,0 +39,0 @@ throw new error.DataError({method: method}, 'ControllerInterface: missing method in Controller object'); |
@@ -22,3 +22,3 @@ "use strict"; | ||
_invoke: function HttpServiceInterface() { | ||
["on", "listen", "close", "setTimeout"].forEach(function (method) { | ||
["on", "listen", "close", "setTimeout", "getEncoding"].forEach(function (method) { | ||
if (!(method in this)) { | ||
@@ -25,0 +25,0 @@ throw new error.DataError({method: method}, 'HttpServiceInterface: missing method in HttpService object'); |
@@ -25,4 +25,4 @@ "use strict"; | ||
"setLoader", "setFilter", "setTag", "setExtension", | ||
"render", "renderFile", "setTheme", "getPath", | ||
"normalizeResolveValue", "resolve", "load" | ||
"render", "renderFile", | ||
"resolve", "load" | ||
].forEach( | ||
@@ -29,0 +29,0 @@ function (method) { |
@@ -5,3 +5,3 @@ { | ||
"description": "Powerful lightweight mvc framework for nodejs", | ||
"version": "0.1.0-beta-6", | ||
"version": "0.1.0-beta-60", | ||
"dependencies" : { | ||
@@ -23,9 +23,9 @@ "mongoose": "3.8.x", | ||
"type": "git", | ||
"url": "git://github.com/igorzg/node-mvc.git" | ||
"url": "git://github.com/AdminJuwel191/node-mvc.git" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/igorzg/node-mvc/issues", | ||
"url": "https://github.com/AdminJuwel191/node-mvc/issues", | ||
"email": "igor.zg1987@gmail.com" | ||
}, | ||
"homepage": "https://github.com/igorzg/node-mvc", | ||
"homepage": "https://github.com/AdminJuwel191/node-mvc", | ||
"author": { | ||
@@ -36,2 +36,3 @@ "name": "Igor Ivanovic", | ||
"keywords": [ | ||
"node", | ||
"mvcjs", | ||
@@ -48,2 +49,2 @@ "framework", | ||
"license": "MIT" | ||
} | ||
} |
@@ -1,2 +0,3 @@ | ||
MVC JS [![Build Status](https://api.travis-ci.org/igorzg/node-mvc.svg?branch=master)](https://travis-ci.org/igorzg/node-mvc) beta | ||
MVC JS [![Build Status](https://api.travis-ci.org/AdminJuwel191/node-mvc.svg?branch=master)](https://travis-ci.org/igorzg/node-mvc) beta | ||
===== | ||
@@ -6,2 +7,4 @@ | ||
Mvcjs is a first nodejs framework which supports modules as bundles! | ||
Features | ||
@@ -17,5 +20,8 @@ ==== | ||
8. Logger | ||
9. Modules | ||
[Demo application](https://github.com/igorzg/mvcjs-testapp) | ||
[Docs and live app](http://mvcjs.igorivanovic.info) | ||
Getting started | ||
@@ -62,3 +68,2 @@ ==== | ||
"name": "core/view", | ||
"themes": "@{appPath}/themes/", | ||
"views": "@{appPath}/views/", | ||
@@ -65,0 +70,0 @@ "theme": "default", |
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
147913
36
4700
125