diff --git a/api/app.api.js b/api/app.api.js
index 4447d90..909ceba 100644
--- a/api/app.api.js
+++ b/api/app.api.js
@@ -4,13 +4,13 @@ var loopback = require('loopback');
var explorer = require('loopback-explorer');
var boot = require('loopback-boot');
+// model definitions
+require('../models');
+
// server
var server = module.exports = loopback();
-boot(server, {
- appRootDir: __dirname,
- modelsRootDir: path.resolve(__dirname, '..'),
-});
+boot(server, __dirname);
// middleware
server.use(loopback.logger('dev'));
diff --git a/api/models.json b/api/models.json
new file mode 100644
index 0000000..ab1dc2f
--- /dev/null
+++ b/api/models.json
@@ -0,0 +1,8 @@
+{
+ "Todo": {
+ "dataSource": "db"
+ },
+ "user": {
+ "dataSource": "db"
+ }
+}
diff --git a/gulpfile.js b/gulpfile.js
index 3192ffc..0ebd928 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -24,6 +24,7 @@ gulp.task('run', function(cb) {
'**/node_modules/**',
'*/build/**',
'**/test/**',
+ '**/*.bundle.*',
'.*' // .git, .idea, etc.
],
watch: __dirname,
@@ -68,7 +69,7 @@ function findAndBuild(packageName, cb) {
writeGlobalConfigModule();
// build each
- async.each(packages, build, cb);
+ async.eachSeries(packages, build, cb);
}
var packageCache;
@@ -97,13 +98,27 @@ function listPackages() {
})
.map(function(filePath) {
return path.basename(path.dirname(filePath));
+ })
+ .sort(function(a, b) {
+ // temporary hack - ensure lbclient is built before html5
+ if (a === 'lbclient') return -1;
+ if (b === 'lbclient') return 1;
+ if (a < b) return -1;
+ if (a > b) return 1;
+ return 0;
});
}
function build(package, cb) {
- var buildConfig = config.build[package];
console.log('building', package);
- configure(package, 'build', cb);
+ configure(package, 'build', function(err) {
+ if (err) {
+ console.error('%s build failed', package, err.stack);
+ } else {
+ console.log('%s was built', package);
+ }
+ cb(err);
+ });
}
function configure(package, scope, cb) {
diff --git a/html5/app.html5.js b/html5/app.html5.js
index dfb6576..adad111 100644
--- a/html5/app.html5.js
+++ b/html5/app.html5.js
@@ -1,11 +1,8 @@
// dependencies
var LOCAL_CONFIG = require('local.config');
-var loopback = require('loopback');
-var boot = require('loopback-boot');
// loopback client
-var client = exports.client = loopback();
-boot(client);
+var client = exports.client = require('lbclient');
// angular.js dependencies
require('./bower_components/angular/angular.js');
@@ -23,6 +20,7 @@ var app = module.exports = angular.module('app', dependencies);
// providers
app.value('Todo', client.models.LocalTodo);
app.value('sync', client.sync);
+app.value('network', client.network);
// setup controllers
// must require controllers in order for browserify
diff --git a/html5/boot/client-models.js b/html5/boot/client-models.js
deleted file mode 100644
index 37b5d52..0000000
--- a/html5/boot/client-models.js
+++ /dev/null
@@ -1,11 +0,0 @@
-var loopback = require('loopback');
-
-// TODO(bajtos) This is a temporary workaround until we have
-// strongloop/loopback-example-full-stack#13
-
-module.exports = function(app) {
- var clientModels = require('../client.models.json');
- for (var name in clientModels) {
- app.model(name, clientModels[name]);
- }
-};
diff --git a/html5/client.models.json b/html5/client.models.json
deleted file mode 100644
index 226454e..0000000
--- a/html5/client.models.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "LocalTodo": {
- "dataSource": "local",
- "options": {
- "base": "Todo"
- }
- }
-}
diff --git a/html5/configure.js b/html5/configure.js
index 521f8bc..1f1ae63 100644
--- a/html5/configure.js
+++ b/html5/configure.js
@@ -1,11 +1,8 @@
-var gulp = require('gulp');
var async = require('async');
var path = require('path');
var pkg = require('./package.json');
var fs = require('fs');
var browserify = require('browserify');
-var sh = require('shelljs');
-var boot = require('loopback-boot');
var buildDir = path.resolve(__dirname, 'build');
@@ -47,7 +44,9 @@ exports.global = function(env, global) {
global.bundle += '.js';
global.html5Views = path.join(__dirname, 'views');
global.html5Bundle = path.join(buildDir, global.bundle);
+ global.clientBundle = path.join(buildDir, 'client.' + global.bundle);
global.bundleURL = '/' + global.bundle;
+ global.clientURL = '/client.' + global.bundle;
}
exports.local = function configure(env, global, local) {
@@ -65,17 +64,12 @@ exports.local = function configure(env, global, local) {
}
exports.build = function(env, global, local, cb) {
- async.waterfall([
- function createBuildDir(next) {
- fs.exists(buildDir, function(yes) {
- if (yes)
- next();
- else
- fs.mkdir(buildDir, next);
- });
- },
+ async.parallel([
function(next) {
createBundle(env, global, next);
+ },
+ function(next) {
+ copyClientBundle(global, next);
}
], cb);
};
@@ -83,16 +77,7 @@ exports.build = function(env, global, local, cb) {
function createBundle(env, global, cb) {
var b = browserify({basedir: __dirname});
b.add('./' + pkg.main);
-
- try {
- boot.compileToBrowserify({
- appRootDir: __dirname,
- modelsRootDir: path.resolve(__dirname, '..'),
- env: env
- }, b);
- } catch(err) {
- return cb(err);
- }
+ b.exclude('lbclient');
var bundleDir = path.dirname(global.html5Bundle);
if (!fs.existsSync(bundleDir))
@@ -100,7 +85,7 @@ function createBundle(env, global, cb) {
var out = fs.createWriteStream(global.html5Bundle);
- if(!isDev(env)) {
+ if (!isDev(env)) {
b.transform({
global: true
}, 'uglifyify');
@@ -110,12 +95,27 @@ function createBundle(env, global, cb) {
// TODO(bajtos) debug should be always true, the sourcemaps should be
// saved to a standalone file when !isDev(env)
debug: isDev(env)
- }).pipe(out);
+ })
+ .on('error', cb)
+ .pipe(out);
out.on('error', cb);
out.on('close', cb);
}
+function copyClientBundle(global, cb) {
+ var clientBundle = require.resolve('../lbclient/browser.bundle.js');
+ var client = fs.createReadStream(clientBundle);
+
+ var out = fs.createWriteStream(global.clientBundle);
+
+ client
+ .on('error', cb)
+ .pipe(out)
+ .on('error', cb)
+ .on('close', cb);
+}
+
function isDev(env) {
return ~['debug', 'development', 'test'].indexOf(env);
}
diff --git a/html5/controllers/todo.ctrl.js b/html5/controllers/todo.ctrl.js
index 789cb8e..058240f 100644
--- a/html5/controllers/todo.ctrl.js
+++ b/html5/controllers/todo.ctrl.js
@@ -2,7 +2,7 @@ module.exports = TodoCtrl;
var app = require('../app.html5');
var async = require('async');
-function TodoCtrl($scope, $routeParams, $filter, Todo, $location, sync) {
+function TodoCtrl($scope, $routeParams, $filter, Todo, $location, sync, network) {
var todos = $scope.todos = [];
$scope.newTodo = '';
@@ -97,16 +97,16 @@ function TodoCtrl($scope, $routeParams, $filter, Todo, $location, sync) {
};
$scope.connected = function() {
- return window.connected();
+ return network.isConnected;
};
$scope.connect = function() {
- window.isConnected = true;
+ network.isConnected = true;
sync();
};
$scope.disconnect = function() {
- window.isConnected = false;
+ network.isConnected = false;
};
Todo.on('conflicts', function(conflicts) {
diff --git a/lbclient/.gitignore b/lbclient/.gitignore
new file mode 100644
index 0000000..9f60abc
--- /dev/null
+++ b/lbclient/.gitignore
@@ -0,0 +1 @@
+browser.bundle.js
diff --git a/lbclient/app.client.js b/lbclient/app.client.js
new file mode 100644
index 0000000..ddc6006
--- /dev/null
+++ b/lbclient/app.client.js
@@ -0,0 +1,9 @@
+var loopback = require('loopback');
+var boot = require('loopback-boot');
+
+// model definitions
+require('../models');
+require('./models/index');
+
+var client = module.exports = loopback();
+boot(client);
diff --git a/html5/boot/replication.js b/lbclient/boot/replication.js
similarity index 71%
rename from html5/boot/replication.js
rename to lbclient/boot/replication.js
index 49ddce4..750e7d2 100644
--- a/html5/boot/replication.js
+++ b/lbclient/boot/replication.js
@@ -11,9 +11,20 @@ module.exports = function(client) {
var LocalTodo = client.models.LocalTodo;
var RemoteTodo = client.models.Todo;
+ client.network = {
+ _isConnected: true,
+ get isConnected() {
+ console.log('isConnected?', this._isConnected);
+ return this._isConnected;
+ },
+ set isConnected(value) {
+ this._isConnected = value;
+ }
+ };
+
// setup model replication
function sync(cb) {
- if (window.connected()) {
+ if (client.network.isConnected) {
RemoteTodo.replicate(LocalTodo, function() {
LocalTodo.replicate(RemoteTodo, cb);
});
@@ -25,11 +36,4 @@ module.exports = function(client) {
LocalTodo.on('deleted', sync);
client.sync = sync;
-
- window.isConnected = true;
-
- window.connected = function connected() {
- console.log('isConnected?', window.isConnected);
- return window.isConnected;
- };
};
diff --git a/lbclient/configure.js b/lbclient/configure.js
new file mode 100644
index 0000000..8c93d26
--- /dev/null
+++ b/lbclient/configure.js
@@ -0,0 +1,54 @@
+var path = require('path');
+var pkg = require('./package.json');
+var fs = require('fs');
+var browserify = require('browserify');
+var boot = require('loopback-boot');
+
+exports.global = function(env, global) {
+};
+
+exports.local = function configure(env, global, local) {
+ // NOTE: this config will be available in the browser
+ local.serverInfo = {
+ api: global.api,
+ url: global.api.protocol
+ + '://'
+ + global.api.host
+ + ':'
+ + global.api.port
+ + global.api.root
+ };
+ local.routes = global.routes;
+};
+
+exports.build = function(env, global, local, cb) {
+ var b = browserify({ basedir: __dirname });
+ b.require('./' + pkg.main, { expose: 'lbclient' });
+
+ try {
+ boot.compileToBrowserify({
+ appRootDir: __dirname,
+ env: env
+ }, b);
+ } catch(err) {
+ return cb(err);
+ }
+
+ var bundlePath = path.resolve(__dirname, 'browser.bundle.js');
+ var out = fs.createWriteStream(path.resolve(__dirname, bundlePath));
+
+ b.bundle({
+ // TODO(bajtos) debug should be always true, the sourcemaps should be
+ // saved to a standalone file when !isDev(env)
+ debug: isDev(env)
+ })
+ .on('error', cb)
+ .pipe(out);
+
+ out.on('error', cb);
+ out.on('close', cb);
+};
+
+function isDev(env) {
+ return ~['debug', 'development', 'test'].indexOf(env);
+}
diff --git a/html5/datasources.json b/lbclient/datasources.json
similarity index 88%
rename from html5/datasources.json
rename to lbclient/datasources.json
index 5ef7a4a..b26ed72 100644
--- a/html5/datasources.json
+++ b/lbclient/datasources.json
@@ -1,5 +1,5 @@
{
- "db": {
+ "remote": {
"connector": "remote"
},
"local": {
diff --git a/html5/datasources.local.js b/lbclient/datasources.local.js
similarity index 90%
rename from html5/datasources.local.js
rename to lbclient/datasources.local.js
index b578bc0..23a28e4 100644
--- a/html5/datasources.local.js
+++ b/lbclient/datasources.local.js
@@ -1,7 +1,7 @@
var LOCAL_CONFIG = require('local.config');
module.exports = {
- db: {
+ remote: {
url: LOCAL_CONFIG.serverInfo.url
}
};
diff --git a/lbclient/models.json b/lbclient/models.json
new file mode 100644
index 0000000..a868f13
--- /dev/null
+++ b/lbclient/models.json
@@ -0,0 +1,11 @@
+{
+ "Todo": {
+ "dataSource": "remote"
+ },
+ "LocalTodo": {
+ "dataSource": "local"
+ },
+ "user": {
+ "dataSource": "remote"
+ }
+}
diff --git a/lbclient/models/index.js b/lbclient/models/index.js
new file mode 100644
index 0000000..b970c7c
--- /dev/null
+++ b/lbclient/models/index.js
@@ -0,0 +1 @@
+exports.LocalTodo = require('./local-todo');
diff --git a/lbclient/models/local-todo.js b/lbclient/models/local-todo.js
new file mode 100644
index 0000000..134764b
--- /dev/null
+++ b/lbclient/models/local-todo.js
@@ -0,0 +1,4 @@
+var loopback = require('loopback');
+
+var LocalTodo = loopback.createModel(require('./local-todo.json'));
+module.exports = LocalTodo;
diff --git a/lbclient/models/local-todo.json b/lbclient/models/local-todo.json
new file mode 100644
index 0000000..5f93c88
--- /dev/null
+++ b/lbclient/models/local-todo.json
@@ -0,0 +1,4 @@
+{
+ "name": "LocalTodo",
+ "base": "Todo"
+}
diff --git a/lbclient/package.json b/lbclient/package.json
new file mode 100644
index 0000000..f00713f
--- /dev/null
+++ b/lbclient/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "client",
+ "main": "app.client.js"
+}
diff --git a/models.json b/models.json
deleted file mode 100644
index d1d94ec..0000000
--- a/models.json
+++ /dev/null
@@ -1,36 +0,0 @@
-{
- "Todo": {
- "options": {
- "trackChanges": true,
- "base": "DataModel"
- },
- "properties": {
- "id": {
- "id": true,
- "type": "string"
- },
- "title": "string",
- "completed": {
- "type": "boolean",
- "default": false
- },
- "created": {
- "type": "number"
- }
- },
- "dataSource": "db"
- },
- "user": {
- "options": {
- "base": "User"
- },
- "properties": {
- "title": "string",
- "done": {
- "type": "boolean",
- "default": false
- }
- },
- "dataSource": "db"
- }
-}
diff --git a/models/index.js b/models/index.js
new file mode 100644
index 0000000..096a804
--- /dev/null
+++ b/models/index.js
@@ -0,0 +1,2 @@
+exports.Todo = require('./todo');
+exports.User = require('./user');
diff --git a/models/todo.js b/models/todo.js
index 583b27f..502acc4 100644
--- a/models/todo.js
+++ b/models/todo.js
@@ -1,47 +1,45 @@
var loopback = require('loopback');
var async = require('async');
-module.exports = function(app) {
- var Todo = app.models.Todo;
-
- Todo.definition.properties.created.default = Date.now;
-
- Todo.beforeSave = function(next, model) {
- if (!model.id) model.id = 't-' + Math.floor(Math.random() * 10000).toString();
- next();
- };
-
- Todo.stats = function(filter, cb) {
- var stats = {};
- cb = arguments[arguments.length - 1];
- var Todo = this;
-
- async.parallel([
- countComplete,
- count
- ], function(err) {
- if (err) return cb(err);
- stats.remaining = stats.total - stats.completed;
- cb(null, stats);
- });
+var Todo = module.exports = loopback.createModel(require('./todo.json'));
+
+Todo.definition.properties.created.default = Date.now;
- function countComplete(cb) {
- Todo.count({completed: true}, function(err, count) {
- stats.completed = count;
- cb(err);
- });
- }
-
- function count(cb) {
- Todo.count(function(err, count) {
- stats.total = count;
- cb(err);
- });
- }
- };
-
- loopback.remoteMethod(Todo.stats, {
- accepts: {arg: 'filter', type: 'object'},
- returns: {arg: 'stats', type: 'object'}
+Todo.beforeSave = function(next, model) {
+ if (!model.id) model.id = 't-' + Math.floor(Math.random() * 10000).toString();
+ next();
+};
+
+Todo.stats = function(filter, cb) {
+ var stats = {};
+ cb = arguments[arguments.length - 1];
+ var Todo = this;
+
+ async.parallel([
+ countComplete,
+ count
+ ], function(err) {
+ if (err) return cb(err);
+ stats.remaining = stats.total - stats.completed;
+ cb(null, stats);
});
+
+ function countComplete(cb) {
+ Todo.count({completed: true}, function(err, count) {
+ stats.completed = count;
+ cb(err);
+ });
+ }
+
+ function count(cb) {
+ Todo.count(function(err, count) {
+ stats.total = count;
+ cb(err);
+ });
+ }
};
+
+loopback.remoteMethod(Todo.stats, {
+ accepts: {arg: 'filter', type: 'object'},
+ returns: {arg: 'stats', type: 'object'}
+});
diff --git a/models/todo.json b/models/todo.json
new file mode 100644
index 0000000..8d0e919
--- /dev/null
+++ b/models/todo.json
@@ -0,0 +1,19 @@
+{
+ "name": "Todo",
+ "base": "PersistedModel",
+ "trackChanges": true,
+ "properties": {
+ "id": {
+ "id": true,
+ "type": "string"
+ },
+ "title": "string",
+ "completed": {
+ "type": "boolean",
+ "default": false
+ },
+ "created": {
+ "type": "number"
+ }
+ }
+}
diff --git a/models/user.js b/models/user.js
new file mode 100644
index 0000000..367cfc6
--- /dev/null
+++ b/models/user.js
@@ -0,0 +1,3 @@
+var loopback = require('loopback');
+
+var User = module.exports = loopback.createModel(require('./user.json'));
diff --git a/models/user.json b/models/user.json
new file mode 100644
index 0000000..7b1b443
--- /dev/null
+++ b/models/user.json
@@ -0,0 +1,19 @@
+{
+ "name": "user",
+ "base": "PersistedModel",
+ "trackChanges": true,
+ "properties": {
+ "id": {
+ "id": true,
+ "type": "string"
+ },
+ "title": "string",
+ "completed": {
+ "type": "boolean",
+ "default": false
+ },
+ "created": {
+ "type": "number"
+ }
+ }
+}
diff --git a/package.json b/package.json
index 6f24a5f..b8b53d6 100644
--- a/package.json
+++ b/package.json
@@ -37,11 +37,11 @@
},
"dependencies": {
"async": "~0.7.0",
- "loopback": "2.0.0-beta3",
+ "loopback": "strongloop/loopback#2.0",
"loopback-explorer": "~1.1.0",
"loopback-connector-mongodb": "^1.2.4",
"loopback-datasource-juggler": "2.0.0-beta1",
- "loopback-boot": "strongloop/loopback-boot",
+ "loopback-boot": "strongloop/loopback-boot#2.0",
"ejs": "^1.0.0",
"morgan": "^1.1.1",
"compression": "^1.0.3"
diff --git a/web/views/footer.ejs b/web/views/footer.ejs
index d2e46c1..5b66f24 100644
--- a/web/views/footer.ejs
+++ b/web/views/footer.ejs
@@ -1 +1,2 @@
+