Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

First commit

  • Loading branch information...
commit 1ae6c608066cf7ec6b938ef316560b955ac72137 0 parents
lollo authored
1  .gitignore
@@ -0,0 +1 @@
+/node_modules/
20 LICENSE
@@ -0,0 +1,20 @@
+(The MIT License)
+
+Copyright (C) 2012 Lorenzo Tabacchini
+
+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.
4 Makefile
@@ -0,0 +1,4 @@
+test:
+ @./node_modules/.bin/mocha
+
+.PHONY: test
39 README.md
@@ -0,0 +1,39 @@
+#agni-framework
+
+Simple and intuitive MVC web framework for node.js.
+
+##Overview
+
+Agni is a web framework built on top of Express, based on the principle
+of _convention over configuration_.
+
+##Usage
+
+To create an application, use the command-line tool [agni](https://github.com/lortabac/agni).
+
+## Documentation
+
+http://lortabac.github.com/agni/
+
+## License
+
+(The MIT License)
+
+Copyright (C) 2012 Lorenzo Tabacchini
+
+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.
48 lib/config.js
@@ -0,0 +1,48 @@
+/**
+ * Module dependencies
+ */
+
+var path = require('path');
+
+/**
+ * Config constructor
+ *
+ * @param {String} Application directory
+ * @api public
+ */
+
+function Config(appDir) {
+ var configPath = path.join(appDir, 'config', 'config');
+ this.settings = require(configPath);
+}
+
+/**
+ * Get a configuration item
+ *
+ * @param {String} Item
+ * @api public
+ */
+
+Config.prototype.get = function(item) {
+ //Look for the environment-specific setting
+ var env = this.settings.current;
+ if(! env) {
+ //If no environment is specified, it defaults to 'development'
+ env = 'development';
+ }
+
+ if(this.settings.hasOwnProperty(env) &&
+ this.settings[env].hasOwnProperty(item)) {
+ return this.settings[env][item];
+ }
+
+ //Look for item in the 'all' settings
+ if(this.settings.hasOwnProperty('all') &&
+ this.settings.all.hasOwnProperty(item)) {
+ return this.settings.all[item];
+ }
+
+ return false;
+}
+
+module.exports = Config;
100 lib/controller.js
@@ -0,0 +1,100 @@
+/**
+ * Module dependencies
+ */
+
+var express = require('express');
+var h = require('./helpers');
+var NestedView = require('./nested_view');
+var factory = require('./factory');
+
+/**
+ * Controller constructor
+ *
+ * @param {String} Application directory
+ * @param {Object} Request object
+ * @param {Object} Response object
+ * @param {String} Controller name
+ * @api public
+ */
+
+function Controller(appDir, req, res, path) {
+ this._appDir = appDir;
+ this._path = path;
+ this.req = req;
+ this.res = res;
+ this.session = express.cookieSession;
+}
+
+/**
+ * Render a view
+ *
+ * @api public
+ */
+
+Controller.prototype.render = function(view, locals, callback) {
+ if((typeof view) === 'object') {
+ locals = view;
+ callback = locals;
+ } else if((typeof view) === 'function') {
+ callback = view;
+ view = '';
+ locals = {}
+ }
+
+ if(! view) {
+ view = h.ltrimSlash(this._path);
+ }
+
+ if(! locals) {
+ locals = {}
+ }
+
+ locals['agni'] = {
+ service: this.service,
+ model: this.model
+ };
+
+ this.res.render(view, locals, callback);
+}
+
+/**
+ * Nest a view inside another
+ *
+ * @api public
+ */
+
+Controller.prototype.nest = function(view, locals) {
+ if(! view) {
+ view = h.ltrimSlash(this._path);
+ }
+
+ return (new NestedView(this.res, view, locals));
+}
+
+/**
+ * Service factory
+ *
+ * @api public
+ */
+
+Controller.prototype.service = function(name) {
+ if(! name) {
+ name = h.ltrimSlash(this._path);
+ }
+ return factory.getService(this._appDir, name);
+}
+
+/**
+ * Model factory
+ *
+ * @api public
+ */
+
+Controller.prototype.model = function(name) {
+ if(! name) {
+ name = h.ltrimSlash(this._path);
+ }
+ return factory.getModel(this._appDir, name);
+}
+
+module.exports = Controller;
79 lib/controllers.js
@@ -0,0 +1,79 @@
+/**
+ * Module dependencies
+ */
+
+var fs = require('fs');
+var path = require('path');
+
+/**
+ * Controller constructor
+ *
+ * @param {String} Controllers directory
+ * @api public
+ */
+
+function Controllers(appDir) {
+ //Controller directory
+ this.controllerDir = path.join(appDir, 'controllers');
+ //Controller list
+ this.controllers = {};
+}
+
+/**
+ * Build controller list
+ *
+ * @param {Function} Callback
+ * @api public
+ */
+
+Controllers.prototype.buildList = function(done) {
+ this._processDir('');
+ done(this.controllers);
+}
+
+/**
+ * Iterate through a directory
+ *
+ * @api private
+ */
+
+Controllers.prototype._processDir = function(dir) {
+ var self = this;
+
+ //Read directory
+ var absDir = path.join(this.controllerDir, dir);
+ var list = fs.readdirSync(absDir);
+
+ //Iterate on directory contents
+ list.forEach(function(element, index, array) {
+ var elementPath = path.join(dir, element);
+ var stat = fs.statSync(path.join(self.controllerDir, elementPath));
+
+ if(stat.isFile()) {
+ //If it is a file, add to the controller list
+ self._processFile(elementPath);
+
+ } else if(stat.isDirectory()) {
+ //If it is a directory, call this function recursively
+ self._processDir(elementPath);
+ }
+ });
+ return true;
+}
+
+/**
+ * Add a file to the controller list
+ *
+ * @api private
+ */
+
+Controllers.prototype._processFile = function(file) {
+ //Remove extension
+ var basename = file.substring(0, file.lastIndexOf('.'));
+
+ //Store controller
+ var module = require(path.join(this.controllerDir, basename));
+ this.controllers[('/' + basename)] = module;
+}
+
+module.exports = Controllers;
57 lib/factory.js
@@ -0,0 +1,57 @@
+/**
+ * Module dependencies
+ */
+
+var path = require('path');
+
+/**
+ * Model factory
+ */
+
+var getModel = exports.getModel = function(appDir, name) {
+ var model = require(path.join(appDir, 'models', name));
+ return model;
+}
+
+/**
+ * Service factory
+ */
+
+var getService = exports.getService = function(appDir, name) {
+ var Service = require(path.join(appDir, 'services', name));
+
+ if((typeof Service) === 'function') {
+
+ Service.prototype.service = function(serviceName) {
+ return getService(appDir, serviceName);
+ }
+
+ Service.prototype.model = function(modelName) {
+ if(! modelName) {
+ modelName = name;
+ }
+ return getModel(name);
+ }
+
+ Service.construct = function() {
+ var self = Service.prototype;
+ Service.apply(self, arguments);
+ return self;
+ }
+
+ } else {
+
+ Service.service = function(serviceName) {
+ return getService(appDir, serviceName);
+ }
+
+ Service.model = function(modelName) {
+ if(! modelName) {
+ modelName = name;
+ }
+ return getModel(name);
+ }
+ }
+
+ return Service;
+}
102 lib/framework.js
@@ -0,0 +1,102 @@
+/**
+ * Module dependencies
+ */
+
+var express = require('express');
+var cons = require('consolidate');
+var path = require('path');
+var Config = require('./config');
+var Controllers = require('./controllers');
+var Router = require('./router');
+var util = require('util');
+
+/**
+ * Framework constructor
+ *
+ * @param {String} Application directory
+ * @api public
+ */
+
+function Framework(appDir) {
+ this.appDir = appDir;
+ this.config = new Config(appDir);
+}
+
+/*
+ * Boot framework
+ *
+ * @api public
+ */
+
+Framework.prototype.boot = function(app) {
+ //Express settings
+ var engine = this.config.get('engine');
+ var extension = this.config.get('extension');
+ app.engine(extension, cons[engine]);
+ app.set('view engine', extension);
+ app.set('views', path.join(this.appDir, 'views'));
+
+ app.use(express.bodyParser());
+ app.use(express.limit('5mb'));
+
+ app.use(express.favicon());
+
+ var loggerOptions = this.config.get('loggerOptions');
+ if(loggerOptions !== false) {
+ app.use(express.logger(loggerOptions));
+ }
+
+ var staticPath = path.join(this.appDir, 'static');
+ var staticOptions = this.config.get('staticOptions');
+ app.use('/static', express.static(staticPath, staticOptions));
+
+ //Error handling
+ app.use(logErrors);
+ app.use(errorHandler);
+
+ //User-defined initialization
+ var init = require(path.join(this.appDir, 'config', 'init'));
+ init(app);
+
+ //Set routes
+ this._bindRoutes(app);
+
+ //Listen to connections
+ app.listen(this.config.get('port'));
+}
+
+/**
+ * Route all requests
+ *
+ * @api private
+ */
+
+Framework.prototype._bindRoutes = function(app) {
+ var self = this;
+ //Build controller list and bind routes
+ var controllers = new Controllers(this.appDir);
+ controllers.buildList(function(controllers) {
+ app.all('*', function(req, res) {
+ var DI = {
+ config: self.config,
+ controllers: controllers
+ }
+ var router = new Router(self.appDir, req, res, DI);
+ router.route(function(err, found) {
+ if(err) throw err;
+ });
+ });
+ });
+}
+
+function logErrors(err, req, res, next) {
+ console.error(err);
+ next(err);
+}
+
+function errorHandler(err, req, res, next) {
+ res.status(500);
+ res.send('500 Internal server error');
+}
+
+module.exports = Framework;
41 lib/helpers.js
@@ -0,0 +1,41 @@
+/**
+ * Left trim slash
+ *
+ * @api public
+ */
+
+exports.ltrimSlash = function(string) {
+ if(string[0] === '/') {
+ return string.substr(1);
+ } else {
+ return string;
+ }
+}
+
+/**
+ * Right trim slash
+ *
+ * @api public
+ */
+
+exports.rtrimSlash = function(string) {
+ if(string[(string.length - 1)] === '/') {
+ return string.substr(0, (string.length - 1));
+ } else {
+ return string;
+ }
+}
+
+/**
+ * Adds final slash if not present
+ *
+ * @api public
+ */
+
+exports.addSlash = function(string) {
+ if(string[(string.length - 1)] !== '/') {
+ return string + '/';
+ } else {
+ return string;
+ }
+}
2  lib/index.js
@@ -0,0 +1,2 @@
+exports.Framework = require('./framework');
+exports.Config = require('./config');
51 lib/nested_view.js
@@ -0,0 +1,51 @@
+/**
+ * NestedView constructor
+ */
+
+function NestedView(res, child, locals) {
+ this.child = child;
+ this.locals = locals;
+ this.parent = 'layout';
+ this.name = 'content';
+ this.res = res;
+}
+
+/**
+ * Set child variable name
+ */
+
+NestedView.prototype.as = function(name) {
+ this.name = name;
+ return this;
+}
+
+/**
+ * Set layout name
+ */
+
+NestedView.prototype.within = function(parent) {
+ this.parent = parent;
+ return this;
+}
+
+/**
+ * Render
+ */
+
+NestedView.prototype.render = function() {
+ var self = this;
+
+ //Render child
+ this.res.render(this.child, this.locals, function(err, html) {
+ if(err) throw err;
+
+ //Parent locals
+ var locals = {}
+ locals[self.name] = html;
+
+ //Render parent
+ self.res.render(self.parent, locals);
+ });
+}
+
+module.exports = NestedView;
110 lib/router.js
@@ -0,0 +1,110 @@
+/**
+ * Module dependencies
+ */
+
+var Config = require('./config');
+var Controller = require('./controller');
+var Controllers = require('./controllers');
+
+/**
+ * Constants
+ */
+
+var NIL = 'nil';
+
+/**
+ * Router constructor
+ *
+ * @param {String} Application directory
+ * @param {Object} Request
+ * @param {Object} Response
+ * @param {Object} Dependency injection
+ * @api public
+ */
+
+function Router(appDir, req, res, DI) {
+ this.controllers = DI.controllers;
+ this.config = DI.config;
+ this.appDir = appDir;
+ this.req = req;
+ this.res = res;
+ this.segments = null;
+ this.origSegments = null;
+ this.i = null;
+}
+
+/**
+ * Execute the controller which corresponds to this path
+ *
+ * @api public
+ */
+
+Router.prototype.route = function(callback) {
+ var reqURL = this.req.url;
+
+ //Route '/' to home
+ if(reqURL === '/' || reqURL === '') {
+ reqURL = '/' + this.config.get('home');
+ }
+
+ this.segments = reqURL.split('/');
+ this.origSegments = this.segments.slice();
+
+ var methodName;
+
+ for(this.i = (this.segments.length - 1); this.i > 0; this.i--) {
+ //Look for nil function
+ var url = this.segments.join('/');
+ if(this._applyRouteIfExists(NIL)) {
+ callback(null, true);
+ return true;
+ }
+
+ //Or look for method
+ methodName = this.segments.pop();
+ var url = this.segments.join('/');
+ if(this._applyRouteIfExists(methodName)) {
+ callback(null, true);
+ return true;
+ }
+ }
+
+ this.res.status(404).send('404 Not found');
+ callback(null, false);
+}
+
+Router.prototype._applyRouteIfExists = function(methodName) {
+ var url = this.segments.join('/');
+ var fn = this._getMethod(url, methodName);
+ if(fn !== false) {
+ var args = this.origSegments.slice(this.i + 1);
+ var controllerScope = new Controller(this.appDir, this.req, this.res, url);
+ fn.apply(controllerScope, args);
+ this.res.status(200);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/**
+ * @api private
+ */
+
+Router.prototype._getMethod = function(url, method) {
+ if(this.controllers.hasOwnProperty(url)) {
+ var cntrl = this.controllers[url];
+ } else {
+ return false;
+ }
+
+ var fnExists = (cntrl.hasOwnProperty(method) &&
+ (typeof cntrl[method]) === 'function');
+ if(fnExists) {
+ return cntrl[method];
+ } else {
+ return false;
+ }
+}
+
+module.exports = Router;
28 package.json
@@ -0,0 +1,28 @@
+{
+ "name": "agni-framework",
+ "description": "Simple and intuitive MVC web framework for node.js",
+ "version": "0.1.0beta",
+ "author": "Lorenzo Tabacchini <lortabac@gmx.com>",
+ "main": "./lib/index",
+ "dependencies": {
+ "express": "3.x",
+ "consolidate": "*",
+ "forever": "*",
+ "ejs": "*",
+ "jade": "*"
+ },
+ "scripts": {
+ "test": "make test",
+ "start": "node ./server.js",
+ "stop": "node ./script/stop.js"
+ },
+ "devDependencies": {
+ "mocha": "*",
+ "sinon": "*"
+ },
+ "keywords": [
+ "framework",
+ "mvc",
+ "router"
+ ]
+}
20 test/config.js
@@ -0,0 +1,20 @@
+/**
+ * Config tests
+ */
+
+var assert = require('assert');
+var path = require('path');
+var Config = require('../lib/config');
+
+var appDir = path.join(__dirname, 'testdir');
+var config = new Config(appDir);
+
+describe('Config', function() {
+ describe('.get()', function() {
+ it('should return item value', function(done) {
+ var c = config.get('foo');
+ assert.strictEqual(c, 'foo');
+ done();
+ });
+ });
+});
36 test/controllers.js
@@ -0,0 +1,36 @@
+/**
+ * Controllers tests
+ */
+
+var assert = require('assert');
+var path = require('path');
+var Controllers = require('../lib/controllers');
+
+var appDir = path.join(__dirname, 'testdir');
+var controllers = new Controllers(appDir);
+
+describe('Controllers', function() {
+ describe('.buildList()', function() {
+ it('should build list correctly', function(done) {
+ controllers.buildList(function(list) {
+
+ var dirfile = {
+ method: 'method',
+ method2: 'method2'
+ }
+
+ var dir2dir3file3 = {
+ nil: 'nil',
+ method4: 'method4'
+ }
+
+ assert.deepEqual(list['/dir'], {file: 'file'});
+ assert.deepEqual(list['/dir/file'], dirfile);
+ assert.deepEqual(list['/dir/file/method'], {foo: 'foo'});
+ assert.deepEqual(list['/dir2/dir3/file3'], dir2dir3file3);
+
+ done();
+ });
+ });
+ });
+});
68 test/factory.js
@@ -0,0 +1,68 @@
+/**
+ * Factory tests
+ */
+
+var assert = require('assert');
+var util = require('util');
+var path = require('path');
+var factory = require('../lib/factory');
+
+var appDir = path.join(__dirname, 'testdir');
+
+describe('Factory', function() {
+ describe('.getModel()', function() {
+ it('should call an exported function', function(done) {
+ factory.getModel(appDir, 'foo').getFoo(function(value) {
+ assert.strictEqual(value, 'foo');
+ done();
+ });
+ });
+ });
+
+ describe('.getService()', function() {
+ it('should call an exported function', function(done) {
+ factory.getService(appDir, 'foo').getFoo(function(value) {
+ assert.strictEqual(value, 'foo');
+ done();
+ });
+ });
+
+ it('should construct object and call a method', function(done) {
+ var S = factory.getService(appDir, 'sum')
+ var s = new S(2, 3);
+ s.get(function(value) {
+ assert.strictEqual(value, 5);
+ done();
+ });
+ });
+ });
+
+ describe('service.service()', function() {
+ it('should call a service from the exported function of a service', function(done) {
+ factory.getService(appDir, 'foo').getBaz(function(value) {
+ assert.strictEqual(value, 'baz');
+ done();
+ });
+ });
+
+ it('should call a service from a constructed service object', function(done) {
+ var S = factory.getService(appDir, 'sum');
+ var s = new S(2, 3);
+ s.getBaz(function(value) {
+ assert.strictEqual(value, 'baz');
+ done();
+ });
+ });
+ });
+
+ describe('service.construct()', function() {
+ it('should construct object', function(done) {
+ factory.getService(appDir, 'sum')
+ .construct(2, 3)
+ .get(function(value) {
+ assert.strictEqual(value, 5);
+ done();
+ });
+ });
+ });
+});
140 test/router.js
@@ -0,0 +1,140 @@
+/**
+ * Router tests
+ */
+
+var assert = require('assert');
+var sinon = require('sinon');
+var path = require('path');
+var Router = require('../lib/router');
+var util = require('util');
+
+var appDir = path.join(__dirname, 'test', 'testdir');
+
+var DI = {}
+DI.config = {
+ get: function(item) {
+ return 'home';
+ }
+}
+DI.controllers = {
+ '/home': {
+ nil: function() {},
+ foo: function() {},
+ baz: function(arg1, arg2) {}
+ },
+ '/foo/foo2': {
+ bar: function() {}
+ }
+}
+
+var req = {}
+
+var res = {
+ status: function() {
+ return {
+ send: function() {}
+ }
+ },
+ render: function() {}
+}
+
+describe('Router', function() {
+ describe('.routeTo()', function() {
+ it('should call /home.foo()', function(done) {
+ req.url = '/home/foo';
+ var spy = sinon.spy(DI.controllers['/home'], 'foo');
+
+ var router = new Router(appDir, req, res, DI);
+ router.route(function(err, found) {
+ assert.ok(spy.called);
+ spy.restore();
+ done();
+ });
+ });
+
+ it('should call /foo/foo2.bar()', function(done) {
+ req.url = '/foo/foo2/bar';
+ var spy = sinon.spy(DI.controllers['/foo/foo2'], 'bar');
+
+ var router = new Router(appDir, req, res, DI);
+ router.route(function(err, found) {
+ assert.ok(spy.called);
+ spy.restore();
+ done();
+ });
+ });
+
+ it('should call /home.nil()', function(done) {
+ req.url = '/home';
+ var spy = sinon.spy(DI.controllers['/home'], 'nil');
+
+ var router = new Router(appDir, req, res, DI);
+ router.route(function(err, found) {
+ assert.ok(spy.called);
+ spy.restore();
+ done();
+ });
+ });
+
+ it('should call /home.nil() as default', function(done) {
+ req.url = '/';
+ var spy = sinon.spy(DI.controllers['/home'], 'nil');
+
+ var router = new Router(appDir, req, res, DI);
+ router.route(function(err, found) {
+ assert.ok(spy.called);
+ spy.restore();
+ done();
+ });
+ });
+
+ it('should pass false if controller does not exist', function(done) {
+ req.url = '/fsdf/hgd';
+ var spy = sinon.spy(DI.controllers['/home'], 'nil');
+
+ var router = new Router(appDir, req, res, DI);
+ router.route(function(err, found) {
+ assert.equal(spy.called, false);
+ assert.strictEqual(found, false);
+ spy.restore();
+ done();
+ });
+ })
+
+ it('should call /home.foo() with 2 arguments', function(done) {
+ req.url = '/home/foo/x/y';
+ var spy = sinon.spy(DI.controllers['/home'], 'foo');
+
+ var router = new Router(appDir, req, res, DI);
+ router.route(function(err, found) {
+ assert.ok(spy.calledWithExactly('x', 'y'));
+ spy.restore();
+ done();
+ });
+ });
+
+ it('should call /home.foo() with 3 arguments', function(done) {
+ req.url = '/home/foo/x/y/z';
+ var spy = sinon.spy(DI.controllers['/home'], 'foo');
+
+ var router = new Router(appDir, req, res, DI);
+ router.route(function(err, found) {
+ assert.ok(spy.calledWithExactly('x', 'y', 'z'));
+ spy.restore();
+ done();
+ });
+ });
+
+ it('should call /home.nil() with 2 arguments', function(done) {
+ req.url = '/home/x/y';
+ var spy = sinon.spy(DI.controllers['/home'], 'nil');
+
+ var router = new Router(appDir, req, res, DI);
+ router.route(function(err, found) {
+ assert.ok(spy.calledWithExactly('x', 'y'));
+ spy.restore();
+ done();
+ });
+ });
+ });
+});
13 test/testdir/config/config.js
@@ -0,0 +1,13 @@
+var env = module.exports = {
+ 'all': {},
+ 'development': {},
+ 'production': {}
+}
+
+env.current = 'development';
+
+env.all.foo = 'foo';
+
+env.development.bar = 'bar';
+
+env.production.baz = 'baz';
1  test/testdir/controllers/dir.js
@@ -0,0 +1 @@
+exports.file = 'file';
3  test/testdir/controllers/dir/file.js
@@ -0,0 +1,3 @@
+exports.method = 'method';
+
+exports.method2 = 'method2';
1  test/testdir/controllers/dir/file/method.js
@@ -0,0 +1 @@
+exports.foo = 'foo';
3  test/testdir/controllers/dir2/dir3/file3.js
@@ -0,0 +1,3 @@
+exports.nil = 'nil';
+
+exports.method4 = 'method4';
3  test/testdir/models/foo.js
@@ -0,0 +1,3 @@
+exports.getFoo = function(callback) {
+ callback('foo');
+}
3  test/testdir/services/bar.js
@@ -0,0 +1,3 @@
+exports.getBaz = function(callback) {
+ callback('baz');
+}
9 test/testdir/services/foo.js
@@ -0,0 +1,9 @@
+exports.getFoo = function(callback) {
+ callback('foo');
+}
+
+exports.getBaz = function(callback) {
+ this.service('bar').getBaz(function(bazValue) {
+ callback(bazValue);
+ });
+}
16 test/testdir/services/sum.js
@@ -0,0 +1,16 @@
+function Sum(arg1, arg2) {
+ this.arg1 = arg1;
+ this.arg2 = arg2;
+}
+
+Sum.prototype.get = function(callback) {
+ callback(this.arg1 + this.arg2);
+}
+
+Sum.prototype.getBaz = function(callback) {
+ this.service('bar').getBaz(function(bazValue) {
+ callback(bazValue);
+ });
+}
+
+module.exports = Sum;
Please sign in to comment.
Something went wrong with that request. Please try again.