The following piece of code is a controller with crud operators for a model.
I cannot figure out what the Zone.find does when it calls itself in the function declaration and why it has to call itself.
var Zone = require('../models/Zone')
module.exports = {
find: function(params, callback){
Zone.find(params, function(err, zones){
if(err){
callback(err, null)
return
}
callback(null, zones)
})
},
findById: function(){
},
update: function(){
},
create: function(){
},
destroy: function(){
},
}
module.exports = {
find: function(params, callback){
and
Zone.find(params, function(err, zones){
are two seperate functions.
by calling Zone.find() invoke the find() function which is exported in Zone.js('../models/Zone') and the return value of that function is exported as the find() function of the controller
Related
I am trying to test async.waterfall by stubbing one of my functions using Sinon.js.
// functions.js
module.exports = {
// function I don't want to run
doBigThing: function() {
console.log("[doBigThing] was called");
},
// function I want to stub
myFunction: function(number, callback) {
console.log("[myFunction] was called");
doBigThing();
callback(null, number);
},
// function I want to test
waterfall: function(callback) {
return async.waterfall([
async.constant(5), // 5 just for the demo
myFunction
], callback);
}
}
And my test is:
describe('water', function() {
it ('successfully falls', function() {
// function under test
var waterfall = functions.waterfall;
var callback = function(err, number) {
expect(err).to.be.null;
expect(number).to.equal(5);
};
// I would like this stub to run instead of functions.myFunction
sinon.stub(functions, 'myFunction', function(number, callback) {
console.log("[myFunction] stub was called");
callback(null, number);
});
waterfall(callback);
// I suppose this is happening: myFunction(5, callback)
expect(functions.myFunction.withArgs(5, callback)).to.have.been.called;
expect(callback).to.have.been.called;
});
});
So the test passes, but the stub is ignored, because doBigThing was called:
Water
✓ successfully falls
[myFunction] was called
[doBigThing] was called
Instead I would like to see
Water
✓ successfully falls
[myFunction] stub was called
I am probably missing out on something and I would appreciate your help.
You're stubbing functions object's method myFunction, but in waterfall method you're calling a myFunction function (I actually can't run your code in my environment, I get "ReferenceError: myFunction is not defined"). So this should work:
// functions.js
var functions = {
// function I don't want to run
doBigThing: function() {
console.log("[doBigThing] was called");
},
// function I want to stub
myFunction: function(number, callback) {
console.log("[myFunction] was called");
functions.doBigThing(); // CHANGE HERE
callback(null, number);
},
// function I want to test
waterfall: function(callback) {
return async.waterfall([
async.constant(5), // 5 just for the demo
functions.myFunction // CHANGE HERE
], callback);
}
};
module.exports = functions;
The question:
As I understand in sails.js during initialization process Services are initialized before Models.
Is there any possibility to change this behavior? To make Models load before Services.
If it's not, then how can I load particular settings from the database to use them to build instance of my class described in some Service during this Service initialization?
A little bit code for solidity:
api/models/Model.js
console.log("Model Identified");
module.exports = {
attributes: {
name: { type: 'string', required: true, size: 15 },
//Some extra secret fields
}
};
...
api/services/MyCoolService.js
console.log('service inits');
function MyCoolService(options){
//some extraordinary constructor logic may be ommited
}
MyCoolService.prototype.setOptions = function(options){
//Set values for MyCoolService fields.
}
//Some other methods
var myCoolServiceWithSettingsFromDb = new MyCoolService();
//That's the place
model.findOne(sails.config.myApplication.settingsId).exec(function(err,result){
if(!err)
myCoolServiceWithSettingsFromDb.setOptions(result);
});
module.exports = myCoolServiceWithSettingsFromDb;
It's because you instantiate object in service with constructor that needs sails that not exist. Try use this at MyCoolService;
module.exports = {
someOption: null,
method: function () {
var that = this;
sails.models.model.findOne(sails.config.myApplication.settingsId)
.exec(function (err, result) {
if (!err)
that.someOption = result;
});
}
};
that method can be called by sails.services.mycoolservice.method() or simply MyCoolService.method() to give your service some option from DB.
If you want to initiate them at Sails start, call that method at config/bootstrap.js
Thanks to Andi Nugroho Dirgantara,
I ended up with this solution (I still don't like it much, but it works):
api/services/MyCoolService.js
console.log('service inits');
function MyCoolService(options){
//some extraordinary constructor logic may be ommited
}
//All the same as in question
//The instance
var instance;
module.exports = module.exports = {
init: function(options) {
instance = new MyCoolService(options);
},
get: function() {
return instance;
},
constructor: MyCoolService
};
config/bootstrap.js
...
Model.findOrCreate({ id: 1 }, sails.config.someDefaultSettings).exec(function(err, result) {
if (err)
return sails.log.error(err);
result = result || sails.config.someDefaultSettings;
MyCoolService.init(result);
return sails.log.verbose("MyCoolService Created: ", TbcPaymentProcessorService.get());
});
...
tests/unit/service/MyCoolService.test.js
...
describe('MyCoolService', function() {
it('check MyCoolService', function(done) {
assert.notDeepEqual(MyCoolService.get(), sails.config.someDefaultSettings);
done();
});
});
...
It works: the service is instantiated once while bootstraping and it's instance is avaliable everywhere.
But to me this solution still weird... I still don't understand how to globally instantiate instance of my service (for use in a lot of controllers) and make it the best way.
I am having trouble figuring out how to pass the objects method rather than sort "generic prototype" method when doing callback.
function Client() {
this.name = "hello";
}
Client.prototype.apiCall = function(method, params, callback) {
callback();
}
Client.prototype.onLogin = function(error, data) {
console.log(this.name);// undefined!!!!
}
Client.prototype.start = function() {
var self = this;
self.apiCall('rtm.start', {
}, self.onLogin) // passing of method like this does not work.
}
I am passing the onLogin method but well it does not work. This is code I have re-written. Previously I nested all methods inside the Client function but well, I learned that that is not the way to do it so now I am trying using prototype.
I know there is some solution "binding" the onLogin function inside the Client() function but well I want to understand the issue.
You need to bind the apiCalls context to the callback using bind:
Client.prototype.apiCall = function(method, params, callback) {
var bound = callback.bind(this);
bound();
}
Otherwise, the this within onLogin is set to the global object.
See Call, Apply And Bind for further details.
Basically .bind(obj) returns a function which, when called, will internally use (obj) as this.
So you create this bound and then you call it.
You can use call or apply to bind this, see snippet. I've modified your code for demonstration purposes. Hope it clarifies things for you
function Client() {
this.name = "hello";
}
Client.prototype = {
apiCall: function(method, params, callback) {
try {
var trial = method.call(this, params);
callback.apply(this, [null, trial]);
} catch (e) {
callback.apply(this, [e, null]);
}
},
onLogin: function(error, data) {
if (error) {
Helpers.report('<b style="color: red">' +
'An error occured!</b> <i>' +
error.message + '</i>')
} else {
Helpers.report(this.name, ' (data.result = ' + data.result + ')');
}
},
start: function() {
Helpers.useCSS(1);
// error from this.rtm.start
Helpers.report('Command: <code>', 'this.apiCall(this.rtm.start, {will: \'not work\'}, this.onLogin);','</code>');
this.apiCall(this.rtm.start, {will: 'not work'}, this.onLogin);
// this.rtm.works is ok
Helpers.report('<br>Command: <code>',
'this.apiCall(this.rtm.works, {will: \'work\'}, this.onLogin);',
'</code>');
this.apiCall(this.rtm.works, {
will: 'work'
}, this.onLogin);
},
// --------------------------------
// added rtm for snippet demo
rtm: {
start: function(params) {
return anerror;
},
works: function(params) {
return {
result: 'worked, <code>params: ' + JSON.stringify(params) + '</code>'
};
}
},
};
new Client().start(); //<= here
<script src="https://rawgit.com/KooiInc/Helpers/master/Helpers.js"></script>
I have set up a simple model with 2 instance methods. How can I call those methods in lifecycle callbacks?
module.exports = {
attributes: {
name: {
type: 'string',
required: true
}
// Instance methods
doSomething: function(cb) {
console.log('Lets try ' + this.doAnotherThing('this'));
cb();
},
doAnotherThing: function(input) {
console.log(input);
}
},
beforeUpdate: function(values, cb) {
// This doesn't seem to work...
this.doSomething(function() {
cb();
})
}
};
It looks like custom defined instance methods were not designed to be called in lifecycle but after querying a model.
SomeModel.findOne(1).done(function(err, someModel){
someModel.doSomething('dance')
});
Link to example in documentation - https://github.com/balderdashy/sails-docs/blob/0.9/models.md#custom-defined-instance-methods
Try defining the functions in regular javascript, this way they can be called from the entire model file like this:
// Instance methods
function doSomething(cb) {
console.log('Lets try ' + this.doAnotherThing('this'));
cb();
},
function doAnotherThing(input) {
console.log(input);
}
module.exports = {
attributes: {
name: {
type: 'string',
required: true
}
},
beforeUpdate: function(values, cb) {
// accessing the function defined above the module.exports
doSomething(function() {
cb();
})
}
};
doSomething and doAnotherThing aren't attributes, are methods and must be at Lifecycle callbacks level. Try something like this:
module.exports = {
attributes: {
name: {
type: 'string',
required: true
}
},
doSomething: function(cb) {
console.log('Lets try ' + "this.doAnotherThing('this')");
this.doAnotherThing('this')
cb();
},
doAnotherThing: function(input) {
console.log(input);
},
beforeCreate: function(values, cb) {
this.doSomething(function() {
cb();
})
}
};
On Second place, you're trying send to console this.doAnotherThing('this') but it is an instance of model so you can't pass it like parameter on the "Lets try" string. Instead of it try to exec this function apart and will work
I have this js class which has an anonymous function to retrieve some query results. Since the function which handles the results is anonymous I can't save the results in a this.var variable and use them somewhere else since this in the anonymous function references to the window object. I can't return it as a function return either so how do I do with those results to have them available somewhere else?
someObject = {
// this.db is created, no need to paste that code
dbGetAnimals: function () {
this.db.readTransaction(function(tx) {
tx.executeSql("SELECT * FROM animals", function(tx, results){
return results;
} )
});
},
printAllAnimals: function () {
var animals = this.dbGetAnimals();
alert (animals);// undefined
}
}
someObject.printAllAnimals();
You can create a local variable to hold your this reference. Your anonymous function will become a closure, so it will be able to see that local var.
dbGetAnimals: function () {
var myself = this;
this.db.readTransaction(function(tx) {
tx.executeSql("SELECT * FROM animals", function(tx, results){
myself.var = results;
} )
});
},
You're trying to do traditional synchronous/non-blocking programming where you want to do asynchronous programming.
var someObject = function()({
this.dbGetAnimals = function (callback) {
db.readTransaction(function(tx) {
tx.executeSql("SELECT * FROM animals", function(tx, results){
callback(results);
} )
});
},
this.printAllAnimals = function (callback) {
this.dbGetAnimals(callback);
}
})();
someObject.printAllAnimals(function(animals) {
alert(animals);
}
There are much cleaner ways to do this, but for async programming you have to learn to do everything on callbacks instead of direct returns.
This is happening because the anonymous function in executeSql is a callback function which is not executed until the query compmetes, which by definition will be after dbGetAnimals returns. That's why your call to dbGetAnimals is returning undefined.
You'll have to pass a callback function to receive the query resutls from within the executSql callback:
someObject = {
// this.db is created, no need to paste that code
dbGetAnimals: function (callback) {
this.db.readTransaction(function(tx) {
tx.executeSql("SELECT * FROM animals", function(tx, results){
callback(results);
} );
});
},
printAllAnimals: function () {
this.dbGetAnimals(function(animals) {
alert(animals)
});
}
}
someObject.printAllAnimals();