-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
1.9 #272
1.9 #272
Changes from all commits
4be3395
1a13a8d
2582c3f
a0e595d
cc49d67
e3d8005
ab8d1ae
13f6738
0bf7af1
867e3ca
e86a00d
b660f8a
83e74ab
e35309b
69cd6a8
918596f
1c7527a
012f880
e026a7f
ae2fb9d
f8b5fa1
a3a6828
5bf1f76
9082214
4bab424
1e2ad9f
64ea805
344601c
d875c51
2f21f4e
d58df51
4d5e788
558ea60
eec7bdd
2de33d4
52eb72d
77bd77e
1a8ba60
0c925f7
d237ae5
7a7f868
22670c7
90bdc73
1603a3e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,3 +11,4 @@ | |
*.swp | ||
*.swo | ||
node_modules | ||
dist |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
# Breaking Changes | ||
|
||
# 1.9 | ||
|
||
## Remote Method API | ||
|
||
`loopback.remoteMethod()` is now deprecated. | ||
|
||
Defining remote methods now should be done like this: | ||
|
||
```js | ||
// static | ||
MyModel.greet = function(msg, cb) { | ||
cb(null, 'greetings... ' + msg); | ||
} | ||
|
||
MyModel.remoteMethod( | ||
'greet', | ||
{ | ||
accepts: [{arg: 'msg', type: 'string'}], | ||
returns: {arg: 'greeting', type: 'string'} | ||
} | ||
); | ||
``` | ||
|
||
**NOTE: remote instance method support is also now deprecated... | ||
Use static methods instead. If you absolutely need it you can still set | ||
`options.isStatic = false`** We plan to drop support for instance methods in | ||
`2.0`. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
var loopback = require('../../'); | ||
var app = loopback(); | ||
var db = app.dataSource('db', {connector: loopback.Memory}); | ||
var Color = app.model('color', {dataSource: 'db', options: {trackChanges: true}}); | ||
var Color2 = app.model('color2', {dataSource: 'db', options: {trackChanges: true}}); | ||
var target = Color2; | ||
var source = Color; | ||
var SPEED = process.env.SPEED || 100; | ||
var conflicts; | ||
|
||
var steps = [ | ||
|
||
createSomeInitialSourceData, | ||
|
||
replicateSourceToTarget, | ||
list.bind(this, source, 'current SOURCE data'), | ||
list.bind(this, target, 'current TARGET data'), | ||
|
||
updateSomeTargetData, | ||
|
||
replicateSourceToTarget, | ||
list.bind(this, source, 'current SOURCE data '), | ||
list.bind(this, target, 'current TARGET data (includes conflicting update)'), | ||
|
||
updateSomeSourceDataCausingAConflict, | ||
|
||
replicateSourceToTarget, | ||
list.bind(this, source, 'current SOURCE data (now has a conflict)'), | ||
list.bind(this, target, 'current TARGET data (includes conflicting update)'), | ||
|
||
resolveAllConflicts, | ||
|
||
replicateSourceToTarget, | ||
list.bind(this, source, 'current SOURCE data (conflict resolved)'), | ||
list.bind(this, target, 'current TARGET data (conflict resolved)'), | ||
|
||
createMoreSourceData, | ||
|
||
replicateSourceToTarget, | ||
list.bind(this, source, 'current SOURCE data'), | ||
list.bind(this, target, 'current TARGET data'), | ||
|
||
createEvenMoreSourceData, | ||
|
||
replicateSourceToTarget, | ||
list.bind(this, source, 'current SOURCE data'), | ||
list.bind(this, target, 'current TARGET data'), | ||
|
||
deleteAllSourceData, | ||
|
||
replicateSourceToTarget, | ||
list.bind(this, source, 'current SOURCE data (empty)'), | ||
list.bind(this, target, 'current TARGET data (empty)'), | ||
|
||
createSomeNewSourceData, | ||
|
||
replicateSourceToTarget, | ||
list.bind(this, source, 'current SOURCE data'), | ||
list.bind(this, target, 'current TARGET data') | ||
]; | ||
|
||
run(steps); | ||
|
||
function createSomeInitialSourceData() { | ||
Color.create([ | ||
{name: 'red'}, | ||
{name: 'blue'}, | ||
{name: 'green'} | ||
]); | ||
} | ||
|
||
function replicateSourceToTarget() { | ||
Color.replicate(0, Color2, {}, function(err, replicationConflicts) { | ||
conflicts = replicationConflicts; | ||
}); | ||
} | ||
|
||
function resolveAllConflicts() { | ||
if(conflicts.length) { | ||
conflicts.forEach(function(conflict) { | ||
conflict.resolve(); | ||
}); | ||
} | ||
} | ||
|
||
function updateSomeTargetData() { | ||
Color2.findById(1, function(err, color) { | ||
color.name = 'conflict'; | ||
color.save(); | ||
}); | ||
} | ||
|
||
function createMoreSourceData() { | ||
Color.create({name: 'orange'}); | ||
} | ||
|
||
function createEvenMoreSourceData() { | ||
Color.create({name: 'black'}); | ||
} | ||
|
||
function updateSomeSourceDataCausingAConflict() { | ||
Color.findById(1, function(err, color) { | ||
color.name = 'red!!!!'; | ||
color.save(); | ||
}); | ||
} | ||
|
||
function deleteAllSourceData() { | ||
Color.destroyAll(); | ||
} | ||
|
||
function createSomeNewSourceData() { | ||
Color.create([ | ||
{name: 'violet'}, | ||
{name: 'amber'}, | ||
{name: 'olive'} | ||
]); | ||
} | ||
|
||
function list(model, msg) { | ||
console.log(msg); | ||
model.find(function(err, items) { | ||
items.forEach(function(item) { | ||
console.log(' -', item.name); | ||
}); | ||
console.log(); | ||
}); | ||
} | ||
|
||
function run(steps) { | ||
setInterval(function() { | ||
var step = steps.shift(); | ||
if(step) { | ||
console.log(step.name); | ||
step(); | ||
} | ||
}, SPEED); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,9 +2,10 @@ | |
* Dependencies. | ||
*/ | ||
|
||
var assert = require('assert') | ||
, compat = require('../compat') | ||
, _ = require('underscore'); | ||
var assert = require('assert'); | ||
var remoting = require('strong-remoting'); | ||
var compat = require('../compat'); | ||
var DataAccessObject = require('loopback-datasource-juggler/lib/dao'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't like the idea of depending on the internal layout of the juggler module. If var DataAccessObject = require('loopback-datasource-juggler').DataAccessObject; There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, |
||
|
||
/** | ||
* Export the RemoteConnector class. | ||
|
@@ -24,21 +25,25 @@ function RemoteConnector(settings) { | |
this.root = settings.root || ''; | ||
this.host = settings.host || 'localhost'; | ||
this.port = settings.port || 3000; | ||
this.remotes = remoting.create(); | ||
|
||
// TODO(ritch) make sure this name works with Model.getSourceId() | ||
this.name = 'remote-connector'; | ||
|
||
if(settings.url) { | ||
this.url = settings.url; | ||
} else { | ||
this.url = this.protocol + '://' + this.host + ':' + this.port + this.root; | ||
} | ||
|
||
// handle mixins here | ||
this.DataAccessObject = function() {}; | ||
// handle mixins in the define() method | ||
var DAO = this.DataAccessObject = function() {}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unused variable There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where is |
||
} | ||
|
||
RemoteConnector.prototype.connect = function() { | ||
this.remotes.connect(this.url, this.adapter); | ||
} | ||
|
||
|
||
RemoteConnector.initialize = function(dataSource, callback) { | ||
var connector = dataSource.connector = new RemoteConnector(dataSource.settings); | ||
connector.connect(); | ||
|
@@ -47,22 +52,40 @@ RemoteConnector.initialize = function(dataSource, callback) { | |
|
||
RemoteConnector.prototype.define = function(definition) { | ||
var Model = definition.model; | ||
var className = compat.getClassNameForRemoting(Model); | ||
var url = this.url; | ||
var adapter = this.adapter; | ||
|
||
Model.remotes(function(err, remotes) { | ||
var sharedClass = getSharedClass(remotes, className); | ||
remotes.connect(url, adapter); | ||
sharedClass | ||
.methods() | ||
.forEach(Model.createProxyMethod.bind(Model)); | ||
}); | ||
var remotes = this.remotes; | ||
var SharedClass; | ||
|
||
assert(Model.sharedClass, 'cannot attach ' + Model.modelName | ||
+ ' to a remote connector without a Model.sharedClass'); | ||
|
||
remotes.addClass(Model.sharedClass); | ||
|
||
Model | ||
.sharedClass | ||
.methods() | ||
.forEach(function(remoteMethod) { | ||
// TODO(ritch) more elegant way of ignoring a nested shared class | ||
if(remoteMethod.name !== 'Change' | ||
&& remoteMethod.name !== 'Checkpoint') { | ||
createProxyMethod(Model, remotes, remoteMethod); | ||
} | ||
}); | ||
} | ||
|
||
function getSharedClass(remotes, className) { | ||
return _.find(remotes.classes(), function(sharedClass) { | ||
return sharedClass.name === className; | ||
}); | ||
function createProxyMethod(Model, remotes, remoteMethod) { | ||
var scope = remoteMethod.isStatic ? Model : Model.prototype; | ||
var original = scope[remoteMethod.name]; | ||
|
||
scope[remoteMethod.name] = function remoteMethodProxy() { | ||
var args = Array.prototype.slice.call(arguments); | ||
var lastArgIsFunc = typeof args[args.length - 1] === 'function'; | ||
var callback; | ||
if(lastArgIsFunc) { | ||
callback = args.pop(); | ||
} | ||
|
||
remotes.invoke(remoteMethod.stringName, args, callback); | ||
} | ||
} | ||
|
||
function noop() {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The sample app hangs after the last step was executed.
I'd say the interval should be cleared when there are no more steps to run.