I am studying Sails.js and I have a question.
I made a model object using "sails generate model site" command.
and I made init.js file in config directory for my cronjob.
everytime system launched, cronjob starts from this init.js.
this is init.js
var fs = require('fs'),
sails = require('sails'),
async = require('async');
exports.initSite = function () {
'use strict';
sails.log.debug('init method start!');
async.waterfall([
function (callback) {
Site.find().exec(function (err, sites) {
if (err) {
callback(err);
}
if (sites) {
async.forEachSeries(sites,
function (siteData, callback2) {
siteData.remove(function (err) {
if (err) {
callback(err);
}
callback2();
});
}, function (err) {
if (err) {
callback(err);
}
callback();
});
} else {
callback();
}
});
},
function (callback) {
var jsonData = fs.readFile('./urls.json', 'utf8', function (err, datas) {
if (err) {
callback(err);
}
callback(null, datas);
});
},
function (datas, callback) {
var urls = JSON.parse(datas);
for (var key in urls) {
var value = urls[key];
var site = new Site({
siteId: value.id,
name: value.name,
url: value.url,
category: value.category
});
site.save(function(err){
if (err) {
callback(err);
}
});
}
callback();
}
],function(err){
if (err) {
sails.log.error('ERROR!');
}
sails.log.info('INIT OK');
});
};
and I read this init.js from app.js like this.
in app.js
require(__dirname + '/config/init').initSite();
but everytime I launch application, console messages says ReferenceError: Site is not defined.
I don't know why init.js can't read 'Site (model object)'.
your advice is very thankful for me.
(sorry for my bad english.... ;_;)
Sailsjs does not load your models until it has been "lifted". You need to run sails lift first from within the directory that your app.js file has been defined, then you can run this chron job. After sails loads, all your models will be exposed as globals. The code in init.js should be called from app.js after sails lifts.
This is based on the discussion about the above topic, so it's not exactly a "cron job" but it can be executed like one and also have access to all the nice features that Sails.js provides, including models.
You can use node-schedule.
So this is what i did.
Install node-schedule
npm install –save node-schedule
Create a crontab file in config/crontab.js
Paste this code in the crontab.js. What i am doing here is creating a method where i require my cronjob, finally i append the method to the jsonArray, so the config/bootstrap.js will execute the file.
module.exports.crontab = {
/*
* The asterisks in the key are equivalent to the
* schedule setting in crontab, i.e.
* minute hour day month day-of-week year
* so in the example below it will run every minute
*/
crons:function()
{
var jsonArray = [];
jsonArray.push({interval:’*/1 * * * * * ‘,method:’mytest’});
// add more cronjobs if you want like below
// but dont forget to add a new method…
//jsonArray.push({interval:’*/1 * * * * * ‘,method:’anothertest’});
return jsonArray;
},
// declare the method mytest
// and add it in the crons function
mytest: function(){
require(‘../crontab/mytest.js’).run();
}
/*
anothertest:function(){
require(‘../crontab/anothertest.js’).run();
}
*/
};
Open your config/bootstrap.js and add this code
module.exports.bootstrap = function(cb) {
_.extend(sails.hooks.http.app.locals, sails.config.http.locals);
// add the lines from here
// bootstrapping all the cronjobs in the crontab folder
var schedule = require(‘node-schedule’);
sails.config.crontab.crons().forEach(function(item){
schedule.scheduleJob(item.interval,sails.config.crontab[item.method]);
});
// It’s very important to trigger this callback method when you are finished
// with the bootstrap! (otherwise your server will never lift, since it’s waiting on the bootstrap)
cb();
};
Finally run sails l and you should see a message running every minute.
Related
I am using protractor-cucumber frame work with protractor 5.2.2 and mssql 3.3.0.We have a requirement of get db values,i am able to get db data values into my protractor code, But i am not able to click on a element with that db value.My code is given below.
function ConnectDB() {
var config = {
user: 'sam',
password: 'Passw0rd',
server: 'xxxxxx',
port: '50866',
database: 'testDB',
options: {
trustedConnection: true
}
}
sql.connect(config, function (err) {
if (err) console.log(err+" db connection failed");
var request = new sql.Request();
request.query('SELECT * FROM Locations WHERE ID = 2', function (err, loc){
if (err) console.log(err+" loc_name failed");
var loc_count= loc.length;
console.log(loc_count);
console.log(loc[0].Location_name);
var i;
for (i=0; i<loc_count; i++){
if(loc_count!=0 & loc_count%10==0){
element(by.css(.publish)).click();
}
element(by.cssContainingText("span", loc[i].Location_name)).click();
console.log(loc[i].Location_name);
browser.sleep(4000);
element(by.css(.post)).sendKeys("test");
element(by.css(.submit)).click();
}
});
});
}
Here the click() and senkeys() functions inside of the for loop is not working.But i am able to console the values of loc_count and loc[i].Location_name.How can i solve this?Thanks in advance.
The root cause of your issue is that the scripts of loop body almost executed async. All Protractor APIs are async. Thus the sendKeys click(), sleep() inside the loop are executed async. But the loop iterate sync.
This lead to when the i==loc_count, the click()/sendKeys()/sleep() of the i==0 have not been executed (They are async).
One solution is to use Closure to keep the i for each iteration. Another solution is use ES6 async/await.
Below is the code example of solution one:
var i;
for (i=0; i<loc_count; i++){
(function(j){
if(loc_count!=0 && loc_count%10==0){
element(by.css('.publish')).click();
}
element(by.cssContainingText("span", loc[j].Location_name)).click();
console.log(loc[j].Location_name);
browser.sleep(4000);
element(by.css('.post')).sendKeys("test");
element(by.css('.submit')).click();
})(i)
}
As yong stated it is because your for loop is synchronous and the protractor actions are async. You either need to implement async/await or use the then callbacks to make it work correctly.
Async/Await option:
request.query('SELECT * FROM Locations WHERE ID = 2', async function (err, loc){
if (err) console.log(err+" loc_name failed");
var loc_count= loc.length;
console.log(loc_count);
console.log(loc[0].Location_name);
var i;
for (i=0; i<loc_count; i++){
if(loc_count!=0 & loc_count%10==0){
await element(by.css(.publish)).click();
}
await element(by.cssContainingText("span", loc[i].Location_name)).click();
console.log(loc[i].Location_name);
await browser.sleep(4000);
await element(by.css(.post)).sendKeys("test");
await element(by.css(.submit)).click();
}
});
To use the then callbacks it would be a little more difficult to handle your if block correctly but essentially you will need to do something like this to get it to work.
element(by.cssContainingText("span", loc[i].Location_name)).click().then(() -> {
console.log(loc[i].Location_name);
browser.sleep(4000).then(() => {
element(by.css(.post)).sendKeys("test").then(() => {
element(by.css(.submit)).click();
});
});
});
The first option is far easier to read and implement correctly.
Try to use :-
var elm = element(by.id("myid"));
browser.executeScript("arguments[0].click();", elm.getWebElement());
I have trying to create a database with a collection added to this database and the changes saved to a IndexedDB.
Below is my code
Two controllers SaveController and LoadController.
myApp.controller('SaveController', ['$scope', 'Loki', function ($scope, Loki) {
// SAVE : will save App/Key/Val as 'finance'/'test'/{serializedDb}
// if appContect ('finance' in this example) is omitted, 'loki' will be used
var idbAdapter = new LokiIndexedAdapter('finance');
var db = new loki('ProjectDb', { adapter: idbAdapter });
var coll = db.addCollection('SampleCollection');
coll.insert({ SampleId: 'Sample text.....' });
db.saveDatabase(); // could pass callback if needed for async complete
}]);
and then in my LoadController i use the
myApp.controller('LoadController', ['$scope', 'Loki', function ($scope, Loki) {
var idbAdapter = new LokiIndexedAdapter('finance');
var db = new loki('ProjectDb', { adapter: idbAdapter, autoload: true });
db.loadDatabase({}, function (result) {
console.log(result);
});
alert(db.getCollection("SampleCollection"));
}]);
I get a null when i alert "alert(db.getCollection("SampleCollection"));" . It never enters the call back of the "loadDatabase" method.
Is there something that i am missing ?
IndexedDB in Browser
Here the page html
Edit for default localstorage implementation
I use the default implementation for loki js and i try to load the offline db is shows result as null every time even though the db exist
var offlineDb = new loki('DbOfflineNew');
offlineDb.loadDatabase({},function (result) {
console.log(result);
if (result == null) {
alert('loading for first time..');
}
else {
alert('existing load..');
}
});
Every time the alert "loading for first time.. " is fired.. Any thing i am missing here..?
Basically all your logic needs to be in the loadDatabase callback. If you try to console.log the collection before it's loaded it will be null. Many people fall in this trap.
In other words:
myApp.controller('LoadController', ['$scope', 'Loki', function ($scope, Loki) {
var idbAdapter = new LokiIndexedAdapter('finance');
var db = new loki('ProjectDb', { adapter: idbAdapter, autoload: true });
db.loadDatabase({}, function (result) {
console.log(result);
// put your log call here.
alert(db.getCollection("SampleCollection"));
});
}]);
Hope this helps.
my sails.js app is embedded in php project where php generate some date for sails model. Can't find the way to fetch this date in beforeCreate callback or some custom method. I don't want use db to sync 2 models (model from sails & model from php). And i need to send some date to remote php app.
sails.js v.0.9.x
here is my controller:
module.exports = {
index: function (req, res) {},
create: function (req, res) {
if ( !req.param('login') )
throw new Error('Login is undefined');
if ( !req.param('message') )
throw new Error('Initial message is undefined');
var user, thread;
User.create({
id: 1,
name: req.param('login'),
ip: req.ip
}).done( function (err, model) {
user = model;
if (err) {
return res.redirect('/500', err);
}
user.fetch(); // my custom method
});
return res.view({ thread: thread });
}
};
and model:
module.exports = {
attributes: {
id: 'integer',
name: 'string',
ip: 'string',
fetch: function (url) {
var app = sails.express.app;
// suggest this but unlucky :)
app.get('/path/to/other/loc', function (req, res, next) {
console.log(req, res)
return next()
});
}
}
};
UPD My solution
Model:
beforeCreate: function (values, next) {
var options = 'http://localhost:1337/test',
get = require('http').get;
get(options, function (res) {
res.on('data', function (data) {
_.extend( values, JSON.parse( data.toString() ) );
next();
});
}).on('error', function () {
throw new Error('Unable to fetch remote data');
next();
});
}
Yikes--app.get is nowhere near what you need. That's binding a route handler inside of your app and has nothing to do with requesting remote data. Don't do this.
The easiest way to fetch remote data in Node is using the Request library. First install it in your project directory using npm install request. Then at the top of your model file:
var request = require('request');
and in your fetch method:
fetch: function(url, callback) {
request.get(url, function(error, response, body) {
return callback(error, body);
});
}
Note the added callback parameter for fetch; this is needed because the request is an asynchronous operation. That is, the call to fetch() will return immediately, but the request will take some time, and when it's done it will send the result back via the callback function. Seeing as fetch is at this point just a wrapper around request.get, I'm not sure why it's necessary to have it as a model method at all, but if the URL was based on something within the model instance then it would make sense.
I kept having this error when i deploy my app onto meteor cloud server.
Meteor code must always run within a Fiber
at _.extend.get (app/packages/meteor/dynamics_nodejs.js:14:13)
at _.extend.apply (app/packages/livedata/livedata_server.js:1268:57)
at _.extend.call (app/packages/livedata/livedata_server.js:1229:17)
at Meteor.startup.Meteor.methods.streamTwit (app/server/server.js:50:24)
however, I have already wrapped within Fibers
streamTwit: function (twit){
var userid = '1527228696';
twit.stream(
'statuses/filter',
{ follow: userid},
function(stream) {
stream.on('data', function(tweet) {
Fiber(function(){
if(tweet.user.id_str === userid)
{
Meteor.call('addQn', tweet);
}
}).run();
console.log(tweet);
console.log('---------------------------------------------------------');
console.log(tweet.user.screen_name);
console.log(tweet.user.name);
console.log(tweet.text);
});
}
);
}
I don't know what's the reason but someone suggested that i should wrap it with Meteor.bindEnvironment instead. Hence, I did this:
streamTwit: function (twit){
this.unblock(); // this doesn't seem to work
console.log('... ... trackTweets');
var _this = this;
var userid = '1527228696';
twit.stream(
'statuses/filter',
{ follow: userid},
function(stream) {
stream.on('data', function(tweet) {
Meteor.bindEnvironment(function () {
if(tweet.user.id_str === userid)
{
Meteor.call('addQn', tweet);
}
}, function(e) {
Meteor._debug("Exception from connection close callback:", e);
});
console.log(tweet);
console.log('---------------------------------------------------------');
console.log(tweet.user.screen_name);
console.log(tweet.user.name);
console.log(tweet.text);
});
}
);
}
//add question method
addQn:function(tweet){
questionDB.insert({'tweet': tweet, 'date': new Date()});
}
but now it doesn't even work. I realise that this only happened when I tried to insert some data into mongodb.
May I know what is the problem with my code? Thanks!
All these codes were written in app/server/server.js
You shouldn't need to use Meteor.call on the server side. That is for client-side code only. Just call addQn directly or better yet, inline it since it's just one line of code.
I've tried to understand this post regarding this concept, however, I'm failing to get it. I have the following simple setup:
/server/test.js
Meteor.methods({
abc: function() {
var result = {};
result.foo = "Hello ";
result.bar = "World!";
return result;
}
});
/client/myapp.js
var q = Meteor.call('abc');
console.log(q);
This structure returns to the console undefined.
If I change the myapp.js file to:
Meteor.call('abc', function(err, data) {
!err ? console.log(data) : console.log(err);
}
I receive the Object in my console.
Ideally this is what I'd like to be able to do, but it doesn't work, stating in the console: Cannot read property 'greeting' of undefined
/client/myapp.js
var q = Meteor.call('abc');
Template.hello.greeting = function() {
return q.foo;
}
Any help in passing the data from the server object into the template would be greatly appreciated. I'm still learning JavaScript & Meteor.
Thanks!
From the Meteor.call documentation:
On the client, if you do not pass a callback and you are not inside a stub, call will return undefined, and you will have no way to get the return value of the method. That is because the client doesn't have fibers, so there is not actually any way it can block on the remote execution of a method.
So, you'll want to do it like this:
Meteor.call('abc', function(err, data) {
if (err)
console.log(err);
Session.set('q', data);
});
Template.hello.greeting = function() {
return Session.get('q').foo;
};
This will reactively update the template once the data is available.
This happens because Npm.require has Async behavior. That's the reason that you have to write a callback for Meteor.call.
But there is a solution, just use install(mrt add npm) and you'll get a function named Meteor.sync(//...) with this you can do both games: sync and async in your Meteor.call().
Reference: http://www.sitepoint.com/create-a-meteor-app-using-npm-module/
You can get the return value of a Meteor method for use in a template by using a reactive variable. Check out the working demonstration on Meteorpad
I went for a ghetto solution. But, it works for me, which is what matters, to me. Below is my code, which, in concept, I think, solves OP's problem.
In the client's main.js:
Meteor.setInterval(function() {
confirmLogin();
}, 5000);
This runs the confirmLogin() function every five seconds.
The confirmLogin function (in the client's main.js):
function confirmLogin() {
Meteor.call('loggedIn', function (error, result) {
Session.set("loggedIn", result);
});
}
The loggedIn method (in the server's main.js):
loggedIn: function () {
var toReturn = false;
var userDetails = Meteor.user();
if (typeof userDetails["services"] !== "undefined") {
if (typeof userDetails["services"]["facebook"] != "undefined") {
toReturn = true;
}
}
return toReturn;
},
The relevant helper:
loggedIn: function () {
return Session.get("loggedIn");
}