From 4b5f57a7df4755447661231ef8b220e51ccd1408 Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Tue, 24 Jun 2014 17:00:54 -0700 Subject: [PATCH 1/4] Update link to doc --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 55b0e4d..f8a380c 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ LoopBack Boot is a convention-based bootstrapper for LoopBack applications. **For full documentation, see the official StrongLoop documentation:** - * [Creating a LoopBack application](http://docs.strongloop.com/display/DOC/Creating+a+LoopBack+application) + * [Creating a LoopBack application](http://docs.strongloop.com/display/LB/Creating+a+LoopBack+application) ## Installation From 230360ef28651860a8e0e3ead2ad7db5d07f620e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Wed, 25 Jun 2014 14:10:16 +0200 Subject: [PATCH 2/4] executor: remove direct reference to loopback Modify the executor to access the loopback object via `app.loopback`. Fall back to `require('loopback')` only when `app.loopback` is not set (loopback versions before 1.9). --- lib/executor.js | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/lib/executor.js b/lib/executor.js index 245ae7f..f68d9b5 100644 --- a/lib/executor.js +++ b/lib/executor.js @@ -1,6 +1,5 @@ var assert = require('assert'); var _ = require('underscore'); -var loopback = require('loopback'); var semver = require('semver'); var debug = require('debug')('loopback:boot:executor'); @@ -14,6 +13,7 @@ var debug = require('debug')('loopback:boot:executor'); */ module.exports = function execute(app, instructions) { + patchAppLoopback(app); assertLoopBackVersion(app); setHost(app, instructions); @@ -23,25 +23,38 @@ module.exports = function execute(app, instructions) { setupDataSources(app, instructions); setupModels(app, instructions); - autoAttach(); + autoAttach(app); runBootScripts(app, instructions); enableAnonymousSwagger(app, instructions); }; +function patchAppLoopback(app) { + if (app.loopback) return; + // app.loopback was introduced in 1.9.0 + // patch the app object to make loopback-boot work with older versions too + try { + app.loopback = require('loopback'); + } catch(err) { + if (err.code === 'MODULE_NOT_FOUND') { + console.error( + 'When using loopback-boot with loopback <1.9, '+ + 'the loopback module must be available for `require(\'loopback\')`.'); + } + throw err; + } +} + function assertLoopBackVersion(app) { var RANGE = '1.x || 2.x'; - // app.loopback was introduced in 1.9.0 - var loopback = app.loopback || {}; - var version = loopback.version || '1.0.0'; - - if (!semver.satisfies(version, RANGE)) { + var loopback = app.loopback; + if (!semver.satisfies(loopback.version || '1.0.0', RANGE)) { throw new Error( 'The `app` is powered by an incompatible loopback version %s. ' + 'Supported versions: %s', - loopback.version || '<1.9', + loopback.version || '(unknown)', RANGE); } } @@ -134,14 +147,14 @@ function runScripts(app, list) { if (!list || !list.length) return; list.forEach(function(filepath) { var exports = tryRequire(filepath); - if (isFunctionNotModelCtor(exports)) + if (isFunctionNotModelCtor(exports, app.loopback.Model)) exports(app); }); } -function isFunctionNotModelCtor(fn) { +function isFunctionNotModelCtor(fn, Model) { return typeof fn === 'function' && - !(fn.prototype instanceof loopback.Model); + !(fn.prototype instanceof Model); } function tryRequire(modulePath) { @@ -158,9 +171,9 @@ function tryRequire(modulePath) { } // Deprecated, will be removed soon -function autoAttach() { +function autoAttach(app) { try { - loopback.autoAttach(); + app.loopback.autoAttach(); } catch(e) { if(e.name === 'AssertionError') { console.warn(e); From 961e04b379d156795bd3358da34a0f8861c33837 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Wed, 25 Jun 2014 10:16:24 +0200 Subject: [PATCH 3/4] docs: move hand-written content to README.md --- README.md | 290 +++++++++++++++++++++++++++++++++++++++++- docs.json | 7 +- docs/browserify.md | 74 ----------- docs/configuration.md | 50 -------- 4 files changed, 292 insertions(+), 129 deletions(-) delete mode 100644 docs/browserify.md delete mode 100644 docs/configuration.md diff --git a/README.md b/README.md index f8a380c..60d8a55 100644 --- a/README.md +++ b/README.md @@ -23,5 +23,293 @@ app.use(loopback.rest()); app.listen(); ``` -See [API docs](http://apidocs.strongloop.com/loopback-boot/) for +See [API docs](http://apidocs.strongloop.com/loopback-boot/#api) for complete API reference. + +## Configurations and conventions + +The bootstrapping process takes care of the following tasks: + + - Configuration of data-sources. + - Definition and configuration of custom Models, attaching models to + data-sources. + - Configuration of app settings like `host`, `port` or `restApiRoot`. + - Running additional boot scripts to keep the custom setup code in multiple + small files as opposed to keeping everything in the main app file. + +Below is the typical project layout. See the following sections for description +of the project files. + +``` +project/ + app.js + app.json + datasources.json + models.json + models/ + boot/ +``` + +### App settings + +The settings are loaded from the file `app.json` in the project root directory +and can be accessed via `app.get('option-name')` from the code. + +Additionally, the following files can provide values to override `app.json`: + + - `app.local.js` or `app.local.json` + - `app.{env}.js` or `app.{env}.json`, where `{env}` is the value of `NODE_ENV` + (typically `development` or `production`) + +**NOTE:** The additional files can override the top-level keys with +value-types (strings, numbers) only. Nested objects and arrays are +not supported at the moment. + +#### Example settings + +*app.json* + +```json +{ + "host": "localhost", + "port": 3000, + "restApiRoot": "/api" +} +``` + +*app.production.js* + +```js +module.exports = { + host: process.env.CUSTOM_HOST, + port: process.env.CUSTOM_PORT +}; +``` + +### Data sources + +The configuration of data sources is loaded from the file `datasources.json` +in the project root directory, the data sources can be accessed via +`app.datasources['datasource-name']` from the code. + +Additionally, the following files can provide values to override +`datasources.json`: + + - `datasources.local.js` or `datasources.local.json` + - `datasources.{env}.js` or `datasources.{env}.json`, + where `{env}` is the value of `NODE_ENV` + (typically `development` or `production`) + +**NOTE:** The additional files can override the top-level data-source options +with value-types (strings, numbers) only. Nested objects and arrays are +not supported at the moment. + +#### Example data sources + +*datasources.json* + +```js +{ + // the key is the datasource name + // the value is the config object to pass to + // app.dataSource(name, config). + db: { + connector: 'memory' + } +} +``` + +*datasources.production.json* + +```js +{ + db: { + connector: 'mongodb', + database: 'myapp', + user: 'myapp', + password: 'secret' + } +} +``` + +### Models + +App models are loaded from the file `models.json`. + +#### Example models + +The following is example JSON for two `Model` definitions: +`Dealership` and `Location`. + +```js +{ + // the key is the model name + "Dealership": { + // a reference, by name, to a dataSource definition + "dataSource": "my-db", + // the options passed to Model.extend(name, properties, options) + "options": { + "relations": { + "cars": { + "type": "hasMany", + "model": "Car", + "foreignKey": "dealerId" + } + } + }, + // the properties passed to Model.extend(name, properties, options) + "properties": { + "id": {"id": true}, + "name": "String", + "zip": "Number", + "address": "String" + } + }, + "Car": { + "dataSource": "my-db" + // options can be specified at the top level too + "relations": { + "dealer": { + "type": "belongsTo", + "model": "Dealership", + "foreignKey": "dealerId" + }, + } + "properties": { + "id": { + "type": "String", + "required": true, + "id": true + }, + "make": { + "type": "String", + "required": true + }, + "model": { + "type": "String", + "required": true + } + } + } +} +``` + +#### Adding custom methods to models + +The models created from `models.json` come with the set of built-in methods +like `find` and `create`. To implement your custom methods, you should +create a javascript file in `models/` directory named after the model +and define the methods there. + +Example: + +*models/car.js* + +```js +module.exports = function(app) { + var Car = app.models.Car; + + Car.prototype.honk = function(duration, cb) { + // make some noise for `duration` seconds + cb(); + }; +}; +``` + +### Boot scripts + +When the data sources and models are configured, the bootstrapper invokes +all scripts in the `boot/` folder. The scripts are sorted lexicographically +ingoring case. + +#### Example boot script + +*boot/authentication.js* + +```js +module.exports = function(app) { + app.enableAuth(); +}; +``` + +## Running in a browser + +The bootstrap process is implemented in two steps that can be called +independently. + +### Build + +The first step loads all configuration files, merges values from additional +config files like `app.local.js` and produces a set of instructions +that can be used to boot the application. + +These instructions must be included in the browser bundle together +with all configuration scripts from `models/` and `boot/`. + +Don't worry, you don't have to understand these details. +Just call `boot.compileToBrowserify`, it will take care of everything for you. + +*build file (Gruntfile.js, gulpfile.js)* + +```js +var browserify = require('browserify'); +var boot = require('loopback-boot'); + +var b = browserify({ + basedir: appDir, +}); + +// add the main application file +b.require('./browser-app.js', { expose: 'loopback-app' }); + +// add boot instructions +boot.compileToBrowserify(appDir, b); + +// create the bundle +var out = fs.createWriteStream('browser-bundle.js'); +b.bundle().pipe(out); +// handle out.on('error') and out.on('close') +``` + +### Run + +In the browser, the main application file should call loopback-boot +to setup the loopback application by executing the instructions +contained in the browser bundle: + +*browser-app.js* + +```js +var loopback = require('loopback'); +var boot = require('loopback-boot'); + +var app = module.exports = loopback(); +boot(app); +``` + +The app object created above can be accessed via `require('loopback-app')`, +where `loopback-app` is the identifier used for the main app file in +the browserify build shown above. + +Here is a simple example demonstrating the concept: + +*index.html* + +```xml + + +``` diff --git a/docs.json b/docs.json index 05f6402..c79ebcc 100644 --- a/docs.json +++ b/docs.json @@ -1,12 +1,11 @@ { "content": [ + "README.md", { - "title": "Bootstrap API", + "title": "API", "depth": 2 }, "index.js", - "browser.js", - "docs/configuration.md", - "docs/browserify.md" + "browser.js" ] } diff --git a/docs/browserify.md b/docs/browserify.md deleted file mode 100644 index 9873026..0000000 --- a/docs/browserify.md +++ /dev/null @@ -1,74 +0,0 @@ -## Running in a browser - -The bootstrap process is implemented in two steps that can be called -independently. - -### Build - -The first step loads all configuration files, merges values from additional -config files like `app.local.js` and produces a set of instructions -that can be used to boot the application. - -These instructions must be included in the browser bundle together -with all configuration scripts from `models/` and `boot/`. - -Don't worry, you don't have to understand these details. -Just call `boot.compileToBrowserify`, it will take care of everything for you. - -```js -/*-- build file --*/ -var browserify = require('browserify'); -var boot = require('loopback-boot'); - -var b = browserify({ - basedir: appDir, -}); - -// add the main application file -b.require('./app.js', { expose: 'loopback-app' }); - -// add boot instructions -boot.compileToBrowserify(appDir, b); - -// create the bundle -var out = fs.createWriteStream('app.bundle.js'); -b.bundle().pipe(out); -// handle out.on('error') and out.on('close') -``` - -### Run - -In the browser, the main application file should call loopback-boot -to setup the loopback application by executing the instructions -contained in the browser bundle: - -```js -/*-- app.js --*/ -var loopback = require('loopback'); -var boot = require('loopback-boot'); - -var app = module.exports = loopback(); -boot(app); -``` - -The app object created above can be accessed via `require('loopback-app')`, -where `loopback-app` is the identifier used for the main app file in -the browserify build shown above. - -Here is a simple example demonstrating the concept: - -```xml - - -``` diff --git a/docs/configuration.md b/docs/configuration.md deleted file mode 100644 index 082e959..0000000 --- a/docs/configuration.md +++ /dev/null @@ -1,50 +0,0 @@ -## Configuration and conventions - -### Model Definitions - -The following is example JSON for two `Model` definitions: -"dealership" and "location". - -```js -{ - "dealership": { - // a reference, by name, to a dataSource definition - "dataSource": "my-db", - // the options passed to Model.extend(name, properties, options) - "options": { - "relations": { - "cars": { - "type": "hasMany", - "model": "Car", - "foreignKey": "dealerId" - } - } - }, - // the properties passed to Model.extend(name, properties, options) - "properties": { - "id": {"id": true}, - "name": "String", - "zip": "Number", - "address": "String" - } - }, - "car": { - "dataSource": "my-db" - "properties": { - "id": { - "type": "String", - "required": true, - "id": true - }, - "make": { - "type": "String", - "required": true - }, - "model": { - "type": "String", - "required": true - } - } - } -} -``` From edd41be02f5da017bbc8ac337509ab5a24a49580 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Thu, 26 Jun 2014 10:59:49 +0200 Subject: [PATCH 4/4] 1.1.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6d2dc18..21522bb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "loopback-boot", - "version": "1.0.0", + "version": "1.1.0", "description": "Convention-based bootstrapper for LoopBack applications", "keywords": [ "StrongLoop",