diff --git a/example/client-server/client.js b/example/client-server/client.js new file mode 100644 index 00000000..458785b7 --- /dev/null +++ b/example/client-server/client.js @@ -0,0 +1,21 @@ +var loopback = require('../../'); +var client = loopback(); +var CartItem = require('./models').CartItem; +var remote = loopback.createDataSource({ + connector: loopback.Remote, + root: 'http://localhost:3000', + remotes: client.remotes() +}); + +client.model(CartItem); +CartItem.attachTo(remote); + +// call the remote method +CartItem.sum(1, function(err, total) { + console.log('result:', err || total); +}); + +// call a built in remote method +CartItem.find(function(err, items) { + console.log(items); +}); diff --git a/example/client-server/models.js b/example/client-server/models.js new file mode 100644 index 00000000..a18ab2e2 --- /dev/null +++ b/example/client-server/models.js @@ -0,0 +1,35 @@ +var loopback = require('../../'); + +var CartItem = exports.CartItem = loopback.Model.extend('CartItem', { + tax: {type: Number, default: 0.1}, + price: Number, + item: String, + qty: {type: Number, default: 0}, + cartId: Number +}); + +CartItem.sum = function(cartId, callback) { + this.find({where: {cartId: 1}}, function(err, items) { + var total = items + .map(function(item) { + return item.total(); + }) + .reduce(function(cur, prev) { + return prev + cur; + }, 0); + + callback(null, total); + }); +} + +loopback.remoteMethod( + CartItem.sum, + { + accepts: {arg: 'cartId', type: 'number'}, + returns: {arg: 'total', type: 'number'} + } +); + +CartItem.prototype.total = function() { + return this.price * this.qty * 1 + this.tax; +} diff --git a/example/client-server/server.js b/example/client-server/server.js new file mode 100644 index 00000000..4ab292e7 --- /dev/null +++ b/example/client-server/server.js @@ -0,0 +1,24 @@ +var loopback = require('../../'); +var server = module.exports = loopback(); +var CartItem = require('./models').CartItem; +var memory = loopback.createDataSource({ + connector: loopback.Memory +}); + +server.use(loopback.rest()); +server.model(CartItem); + +CartItem.attachTo(memory); + +// test data +// CartItem.create([ +// {item: 'red hat', qty: 6, price: 19.99, cartId: 1}, +// {item: 'green shirt', qty: 1, price: 14.99, cartId: 1}, +// {item: 'orange pants', qty: 58, price: 9.99, cartId: 1} +// ]); + +// CartItem.sum(1, function(err, total) { +// console.log(total); +// }) + +server.listen(3000); diff --git a/index.js b/index.js index d9099a70..739fa4f5 100644 --- a/index.js +++ b/index.js @@ -12,6 +12,7 @@ var datasourceJuggler = require('loopback-datasource-juggler'); loopback.Connector = require('./lib/connectors/base-connector'); loopback.Memory = require('./lib/connectors/memory'); loopback.Mail = require('./lib/connectors/mail'); +loopback.Remote = require('./lib/connectors/remote'); /** * Types diff --git a/lib/connectors/remote.js b/lib/connectors/remote.js new file mode 100644 index 00000000..6f2f7e15 --- /dev/null +++ b/lib/connectors/remote.js @@ -0,0 +1,87 @@ +/** + * Dependencies. + */ + +var assert = require('assert') + , compat = require('../compat') + , _ = require('underscore'); + +/** + * Export the RemoteConnector class. + */ + +module.exports = RemoteConnector; + +/** + * Create an instance of the connector with the given `settings`. + */ + +function RemoteConnector(settings) { + assert(typeof settings === 'object', 'cannot initiaze RemoteConnector without a settings object'); + this.client = settings.client; + this.root = settings.root; + this.client = settings.client; + this.remotes = this.client.remotes(); + this.adapter = settings.adapter || 'rest'; + assert(this.client, 'RemoteConnector: settings.client is required'); + assert(this.root, 'RemoteConnector: settings.root is required'); + + // handle mixins here + this.DataAccessObject = function() {}; +} + +RemoteConnector.prototype.connect = function() { + this.remotes.connect(this.root, this.adapter); +} + +RemoteConnector.initialize = function(dataSource, callback) { + var connector = dataSource.connector = new RemoteConnector(dataSource.settings); + connector.connect(); + callback(); +} + +RemoteConnector.prototype.define = function(definition) { + var Model = definition.model; + var className = compat.getClassNameForRemoting(Model); + var sharedClass = getSharedClass(this.remotes, className); + + mixinRemoteMethods(this.remotes, Model, sharedClass.methods()); +} + +function getSharedClass(remotes, className) { + return _.find(remotes.classes(), function(sharedClass) { + return sharedClass.name === className; + }); +} + +function mixinRemoteMethods(remotes, Model, methods) { + methods.forEach(function(sharedMethod) { + var original = sharedMethod.fn; + var fn = createProxyFunction(remotes, sharedMethod.stringName); + for(var key in original) { + fn[key] = original[key]; + } + + if(sharedMethod.isStatic) { + Model[sharedMethod.name] = fn; + } else { + Model.prototype[sharedMethod.name] = fn; + } + }); +} + +function createProxyFunction(remotes, stringName) { + return function() { + var args = Array.prototype.slice.call(arguments); + var lastArgIsFunc = typeof args[args.length - 1] === 'function'; + var callback; + if(lastArgIsFunc) { + callback = args.pop(); + } else { + callback = noop; + } + remotes.invoke(stringName, args, callback); + } +} + +function noop() {}