in this code: if i try to pass this.handler as parameter to server.createServer() , then i get no response (the page keeps loading in browser). but if i use server.createServer(function(req, res) { //same code here as in handler() }) , then it works. what am i doing wrong?
var Con = module.exports = function() {
process.EventEmitter.call(this);
}
var createServer = module.exports.createServer = function(options) {
console.log('start');
this.port = options.port || 9122;
this.secure = options.secure || false;
if(this.secure === true)
if(!options.key || !options.certificate)
this.secure = false;
else {
this.key = options.key;
this.certificate = options.certificate;
}
if(this.secure) {
this.server = require('https');
var fs = require('fs');
var opt = {
key: fs.readFileSync('privatekey.pem'),
cert: fs.readFileSync('certificate.pem')
};
this.server.createServer(opt, this.handler).listen(this.port);
} else {
this.server = require('http');
this.server.createServer(this.handler).listen(this.port);
}
}
Con.prototype.handler = function(req, res) {
console.log('request');
res.writeHead(200);
res.write(req.url);
res.end();
}
var Con = function() {
process.EventEmitter.call(this);
}
That's your constuctor
module.exports = new Con();
That's your instance
var createServer = module.exports.createServer = function(options) {
console.log('start');
this.port = options.port || 9122;
this.secure = options.secure || false;
if(this.secure === true)
if(!options.key || !options.certificate)
this.secure = false;
else {
this.key = options.key;
this.certificate = options.certificate;
}
if(this.secure) {
this.server = require('https');
var fs = require('fs');
var opt = {
key: fs.readFileSync('privatekey.pem'),
cert: fs.readFileSync('certificate.pem')
};
this.server.createServer(opt, this.handler).listen(this.port);
} else {
this.server = require('http');
this.server.createServer(this.handler).listen(this.port);
}
}
.createServer is now a method on the instance rather then the constructor.
Since it's on the instance it also has access to the .handler method defined on the instance through the prototype.
Related
I have following module.
var Sendcloud = require('sendcloud');
var sc = new Sendcloud("key1","key2","key3");
var service = {};
service.restorePassword = function (params, cb) {
if (!params.to || !params.name || !params.token) {
throw "Miss params"
}
var defaultTemplate = adminBaseUrl + "reset/token/" + params.token;
var subject = params.subject || "Letter";
var template = params.template || defaultTemplate;
// Send email
sc.send(params.to, subject, template).then(function (info) {
console.log(info)
if (info.message === "success") {
return cb(null, "success");
} else {
return cb("failure", null);
}
});
};
module.exports = service;
I experience problems stubbing sc.send method. How to correctly cover this point using sinon.js? Or maybe I need to replace sendcloud module?
You need to use proxyquire module or rewire module.
Here an example of using proxyquire
var proxyquire = require('proxyquire');
var sinon = require('sinon');
var Sendcloud = require('sendcloud');
require('sinon-as-promised');
describe('service', function() {
var service;
var sc;
beforeEach(function() {
delete require.cache['sendcloud'];
sc = sinon.createStubInstance(Sendcloud);
service = proxyquire('./service', {
'sendcloud': sinon.stub().returns(sc)
});
});
it('#restorePassword', function(done) {
sc.send.resolves({});
var obj = {
to: 'to',
name: 'name',
token: 'token'
};
service.restorePassword(obj, function() {
console.log(sc.send.args[0]);
done();
});
});
});
I am trying to build a slack bot, and I have come across an error I can't make sense of.
This is the error:
/Users/maecapozzi/Desktop/maebot/node_modules/vow/lib/vow.js:104
throw e;
^
Error: [Slack Bot Error] undefined
at assert (/Users/maecapozzi/Desktop/maebot/node_modules/slackbots/libs/utils.js:15:15)
at /Users/maecapozzi/Desktop/maebot/node_modules/slackbots/index.js:42:9
at Array.<anonymous> (/Users/maecapozzi/Desktop/maebot/node_modules/vow/lib/vow.js:712:56)
at Immediate.callFns [as _onImmediate] (/Users/maecapozzi/Desktop/maebot/node_modules/vow/lib/vow.js:23:35)
at tryOnImmediate (timers.js:534:15)
at processImmediate [as _immediateCallback] (timers.js:514:5)
I'll share my code with you as well, since that should help to make more sense of what has happened.
bot.js :
'use strict';
var MaeBot = require('../lib/maebot');
var token = process.env.BOT_API_KEY;
var dbPath = process.env.BOT_DB_PATH;
var name = process.env.BOT_NAME;
var maebot = new MaeBot({
token: token,
dbPath: dbPath,
name: name
});
maebot.run();
database.js:
var pg = require('pg');
var connectionString = process.env.DATABASE_URL || 'postgres://localhost:5432/maebot';
var client = new pg.Client(connectionString);
client.connect();
var query = client.query('CREATE TABLE dacts(id SERIAL PRIMARY KEY, text VARCHAR(40) not null)');
query.on('end', function() { client.end(); });
maebot.js:
'use strict';
var util = require('util');
var path = require('path');
var fs = require('fs');
var PostGres = require('postgresql');
var Bot = require('slackbots');
var MaeBot = function Constructor(settings) {
this.settings = settings;
this.settings.name = this.settings.name || 'maebot';
this.dbPath = settings.dbPath || path.resolve(process.cwd(), 'data', 'database.js');
this.user = null;
this.db = null;
};
MaeBot.prototype.run = function () {
MaeBot.super_.call(this, this.settings);
this.on('start', this._onStart);
this.on('message', this._onMessage);
};
MaeBot.prototype._onStart = function () {
this._loadBotUser();
this._connectDB();
};
MaeBot.prototype._loadBotUser = function () {
var self = this;
this.user = this.users.filter (function (user) {
return user.name === self.name;
})[0];
};
MaeBot.prototype._connectDB = function () {
if (!fs.existsSync(this.dbPath)) {
console.error('Database path ' + '"' + this.dbPath + '" does not exists or it\'s not readable."')
process.exit(1);
}
this.db = new PostGres.Database(this.dbPath);
};
MaeBot.prototype._welcomeMessage = function () {
this.postMessageToChannel(this.channels[0].name, 'Hi! Maebot here.' +
'\n I can tell you about my creator, Mae. Just say `Hi, maebot` or `' + this.name + '` to invoke me!',
{as_user: true});
};
MaeBot.prototype._onMessage = function (message) {
if (this._isChatMessage(message) &&
this._isChannelConversation(message) &&
!this._isFromMaeBot(message) &&
this._isMentioningMaeBot(message)
) {
this._replyWithRandomFact(message);
}
};
MaeBot.prototype._isChatMessage = function (message) {
return message.type === 'message' && Boolean(message.text);
};
MaeBot.prototype._isChannelConversation = function (message) {
return typeof message.channel === 'string' &&
message.channel[0] === 'C';
};
MaeBot.prototype._isFromMaeBot = function (message) {
return message.user === this.user.id;
};
MaeBot.prototype._isMentioningMaeBot = function (message) {
return message.text.toLowerCase().indexOf('maebot') > -1 ||
message.text.toLowerCase().indexOf(this.name) > -1;
};
MaeBot.prototype._replyWithRandomFact = function (originalMessage) {
var self = this;
self.db.get('SELECT id, fact FROM facts ORDER BY used ASC, RANDOM() LIMIT 1', function (err, record) {
if (err) {
return console.error('DATABASE ERROR:', err);
}
var channel = self._getChannelById(originalMessage.channel);
self.postMessageToChannel(channel.name, record.fact, {as_user: true});
self.db.run('UPDATE facts SET used = used + 1 WHERE id = ?', record.id);
});
};
MaeBot.prototype._getChannelById = function (channelId) {
return this.channels.filter(function (item) {
return item.id === channelId;
})[0];
};
util.inherits(MaeBot, Bot);
module.exports = MaeBot;
Your bot user and/or token is likely incorrect. Try re-creating your bot and ensure that you're using a valid token for your app.
I have a simple http client module(api.js) which is returning a promise as in the followings:
exports.endpoint = '';
exports.GET = function(args){
args.method = 'GET';
args.uri = this.endpoint + args.uri;
return asyncApiCall(args);
};
exports.POST = function(args){
args.method = 'POST';
args.uri = this.endpoint + args.uri;
return asyncApiCall(args);
};
exports.PUT = function(args){
args.method = 'PUT';
args.uri = this.endpoint + args.uri;
return asyncApiCall(args);
};
exports.DELETE= function(args){
args.method = 'DELETE';
args.uri = this.endpoint + args.uri;
return asyncApiCall(args);
};
var asyncApiCall = function(args){
var rp = require('request-promise');
var options = {
method: args.method,
uri: args.uri,
body : args.body,
json: args.json
}
return rp(options);
};
and I use the module like this:
var api = require('./api.js');
var args = {
uri : '/posts'
}
api.endpoint = 'http://localhost:3000';
api.GET(args)
.then(function(res){
console.log(res);
}, function(err){
console.log(err);
});
Now, I want to improve the module as much as possible. Is there any way to not repeat the export.functionName? I found the module.exports in NodeJS, but I am not sure how to use it in this case. How can I set the endpoint variable once in asyncApiCall function instead of all the other functions that return asyncApiCall?
Just another style:
var rp = require('request-promise'); // Put it here so you don't have to require 1 module so many times.
var asyncApiCall = function(args) {
var options = {
method: args.method,
uri: args.uri,
body : args.body,
json: args.json
};
return rp(options);
};
// Let's hack it.
var endpoint = '';
var apis = {};
['GET', 'POST', 'PUT', 'DELETE'].forEach(function(method) {
apis[method] = function(args) {
args.method = method;
args.uri = endpoint + args.uri;
return asyncApiCall(args);
}
});
module.exports = apis;
module.exports.endpoint = '';
A lot of people chose to put their export methods on a new object and export via module.exports, e.g.
var myExports = {
get: function () {},
post: function () {}
}
module.exports = myExports;
As for module.exports vs exports
It looks like it may be appropriate to set up a full constructor with your methods tied to it, like so:
var requests = function (endpoint) {
this.endpoint = endpoint;
}
requests.prototype.GET = function (args) {
args.method = 'GET';
args.uri = this.endpoint + args.uri;
return asyncApiCall(args);
}
// And so on
module.exports = requests;
And then call it like so:
var api = require('./api.js');
var endpoint = new api("http://localhost:3000");
endpoint.GET()
Wrap this in a class and export a new instance of it
function Module() {
}
Module.prototype.GET = function () {}
module.export = new Module()
// or
module.export = Module
// to call the constructor for your endpoint variable.
I am playing with NodeJS and for this purpose created an email extractor. Somehow when i create multiple http requests the node.exe memory useage in windows task manager keeps increasing. I understand that the node needs more memory to process the requests but what i noticed that this memory usage does not come down even after all requests have been successfully processed.
When i start nodejs it consumes about 35000K memory but after about 80-100 request this goes upto 50000K and stays.
Here is my simple email extractor module:
var request = require('request'),
cheerio = require('cheerio'),
async = require('async'),
urlHelper = require('url');
function Extractor(config) {
this.baseUrl = config.url;
this.parsedUrl = urlHelper.parse(config.url);
this.urls = [];
this.emails = [];
}
Extractor.prototype.getEmails = function getEmails(html) {
var foundEmails = html.match(/([a-zA-Z0-9._-]+#[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+)/gi) || [];
if(foundEmails.length) this.emails = this.emails.concat(foundEmails);
}
Extractor.prototype.extract = function extract(html) {
var $ = cheerio.load(html),
that = this;
if($('body')){
this.getEmails($('body').html());
}
if(!this.emails.length){
$("a[href^='http://" + this.parsedUrl.host + "'], a[href^='https://" + this.parsedUrl.host + "'], a[href^='/'], a[href^='./'], a[href^='../']").each(function(k, v) {
that.urls.push(urlHelper.resolve(that.baseUrl, $(v).attr('href')));
});
}
};
/**
* Process the base URL
*/
Extractor.prototype.processBase = function processBase(next) {
request(this.baseUrl, function(err, response, body) {
return next(err, body);
});
}
/**
* Process the internal pages
*/
Extractor.prototype.processInternal = function processInternal(cb) {
var that = this;
async.whilst(
// while this condition returns true
function () { return that.emails.length === 0 && that.urls.length > 0; },
// do this
function (callback) {
request(that.urls.shift(), function (err, response, body) {
var $ = cheerio.load(body);
if($(body)){
that.getEmails($('body').html());
}
callback(); // async internal, needs to be called after we are done with our thing
});
},
// call this if any errors occur. An error also stops the series
// this is also called on successful completion of the series
function (err) {
cb(that);
}
);
}
Extractor.prototype.process = function process(next) {
var that = this;
this.processBase(function(err, html) {
if(err) {
console.log(err);
} else {
that.extract(html);
if(!that.emails.length) {
that.processInternal(function(res) {
return next(null, that);
});
}
}
});
}
module.exports = Extractor;
and here is how i call it:
var express = require('express');
var router = express.Router();
var Extractor = require('../services/Extractor');
router.get('/', function(req, res) {
res.json({msg: 'okay'});
var extractor = new Extractor({url: 'http://lior-197784.use1-2.nitrousbox.com:4000/crawl'});
extractor.process(function(err, res) {});
});
module.exports = router;
for appfog I usually use this code:
var host = process.env.VCAP_APP_HOST || "127.0.0.1";
var port = process.env.VCAP_APP_PORT || 1337;
http.createServer(function (req, res) {
// ...
}).listen(port, host);
Now I found a very interesting forum software . Here is the app.js:
'use strict';
/*global global, require, process, module, jsGen, _restConfig*/
var fs = require('fs'),
path = require('path'),
zlib = require('zlib'),
util = require('util'),
http = require('http'),
domain = require('domain'),
serverDm = domain.create(),
processPath = path.dirname(process.argv[1]);
global.jsGen = {}; // 注册全局变量jsGen
jsGen.version = '0.6.6';
serverDm.on('error', function (err) {
delete err.domain;
jsGen.serverlog.error(err);
});
serverDm.run(function () {
jsGen.conf = module.exports.conf = require('./config/config'); // 注册rrestjs配置文件
jsGen.module = {};
jsGen.module.os = require('os');
jsGen.module.xss = require('xss');
jsGen.module.then = require('thenjs');
jsGen.module.marked = require('marked');
jsGen.module.rrestjs = require('rrestjs');
jsGen.module.mongoskin = require('mongoskin');
jsGen.module.nodemailer = require('nodemailer');
jsGen.serverlog = jsGen.module.rrestjs.restlog;
jsGen.lib = {};
jsGen.lib.msg = require('./lib/msg.js');
jsGen.lib.json = require('./lib/json.js');
jsGen.lib.tools = require('./lib/tools.js');
jsGen.lib.email = require('./lib/email.js');
jsGen.lib.redis = require('./lib/redis.js');
jsGen.lib.CacheLRU = require('./lib/cacheLRU.js');
jsGen.lib.converter = require('./lib/anyBaseConverter.js');
jsGen.Err = jsGen.lib.tools.Err;
jsGen.dao = {};
jsGen.dao.db = require('./dao/mongoDao.js').db;
jsGen.dao.tag = require('./dao/tagDao.js');
jsGen.dao.user = require('./dao/userDao.js');
jsGen.dao.index = require('./dao/indexDao.js');
jsGen.dao.article = require('./dao/articleDao.js');
jsGen.dao.message = require('./dao/messageDao.js');
jsGen.dao.collection = require('./dao/collectionDao.js');
jsGen.thenErrLog = function (defer, err) {
jsGen.serverlog.error(err);
};
var redis = jsGen.lib.redis,
then = jsGen.module.then,
each = jsGen.lib.tools.each,
CacheLRU = jsGen.lib.CacheLRU,
extend = jsGen.lib.tools.extend,
resJson = jsGen.lib.tools.resJson,
TimeLimitCache = jsGen.lib.redis.TimeLimitCache;
then(function (defer) {
redis.initConfig(jsGen.lib.json.GlobalConfig, defer); // 初始化config缓存
}).then(function (defer, config) {
jsGen.config = config;
if (!jsGen.config.date) { // config缓存未赋值,则从MongoDB取值
then(function (defer2) {
jsGen.dao.index.getGlobalConfig(defer2);
}).then(function (defer2, config) {
defer2(null, config);
}, function (defer2, err) {
// MongoDB无值,初始化数据库
require('./api/install.js')().then(function () {
defer2(null, jsGen.lib.json.GlobalConfig);
}).fail(defer2);
}).then(function (defer2, config) {
each(jsGen.config, function (value, key, list) {
if (key in config) {
list[key] = config[key]; // 写入config缓存
}
});
defer(null, jsGen.config);
}).fail(defer);
} else {
defer(null, config);
}
}).then(function (defer, config) {
var api = ['index', 'user', 'article', 'tag', 'collection', 'message', 'rebuild'];
jsGen.cache = {};
jsGen.cache.tag = new CacheLRU(config.tagCache);
jsGen.cache.user = new CacheLRU(config.userCache);
jsGen.cache.list = new CacheLRU(config.listCache);
jsGen.cache.article = new CacheLRU(config.articleCache);
jsGen.cache.comment = new CacheLRU(config.commentCache);
jsGen.cache.message = new CacheLRU(config.messageCache);
jsGen.cache.collection = new CacheLRU(config.collectionCache);
jsGen.cache.timeInterval = new TimeLimitCache(config.TimeInterval, 'string', 'interval', false);
jsGen.cache.pagination = new TimeLimitCache(config.paginationCache, 'array', 'pagination', true);
jsGen.robotReg = new RegExp(config.robots || 'Baiduspider|Googlebot|BingBot|Slurp!', 'i');
jsGen.api = {};
each(api, function (x) {
jsGen.api[x] = {}; // 初始化api引用,从而各api内部可提前获取其它api引用
});
each(api, function (x) {
extend(jsGen.api[x], require('./api/' + x + '.js')); // 扩展各api的具体方法
});
fs.readFile(processPath + '/package.json', 'utf8', defer); // 读取软件信息
}).then(function (defer, data) {
data = JSON.parse(data);
data.version = jsGen.version;
data.nodejs = process.versions.node;
data.rrestjs = _restConfig._version;
jsGen.config.info = data;
redis.userCache.index.total(defer); // 读取user缓存
}).then(function (defer, users) {
var rebuild = jsGen.api.rebuild;
if (!users) { // user缓存为空,则判断redis缓存为空,需要初始化
// 初始化redis缓存
then(function (defer2) {
rebuild.user().all(defer2);
}).then(function (defer2) {
rebuild.tag().all(defer2);
}).then(function (defer2) {
rebuild.article().all(defer);
}).fail(defer);
} else {
defer();
}
}).then(function (defer) {
http.createServer(function (req, res) {
var dm = domain.create();
function errHandler(err, res, dm) {
delete err.domain;
try {
res.on('finish', function () {
//jsGen.dao.db.close();
process.nextTick(function () {
dm.dispose();
});
});
if (err.hasOwnProperty('name')) {
res.sendjson(resJson(err));
} else {
jsGen.serverlog.error(err);
res.sendjson(resJson(jsGen.Err(jsGen.lib.msg.MAIN.requestDataErr)));
}
} catch (error) {
delete error.domain;
jsGen.serverlog.error(error);
dm.dispose();
}
}
function router(req, res) {
if (req.path[0] === 'api' && jsGen.api[req.path[1]]) {
jsGen.api[req.path[1]][req.method.toUpperCase()](req, res); // 处理api请求
} else if (req.path[0].toLowerCase() === 'sitemap.xml') {
jsGen.api.article.sitemap(req, res); // 响应搜索引擎sitemap,动态生成
} else if (req.path[0].slice(-3).toLowerCase() === 'txt') {
// 直接响应static目录的txt文件,如robots.txt
then(function (defer) {
fs.readFile(processPath + '/static/' + req.path[0], 'utf8', defer);
}).then(function (defer, txt) {
res.setHeader('Content-Type', 'text/plain');
res.send(txt);
}).fail(res.throwError);
} else if (jsGen.robotReg.test(req.useragent)) {
jsGen.api.article.robot(req, res); // 处理搜索引擎请求
} else {
jsGen.config.visitors = 1; // 访问次数+1
res.setHeader('Content-Type', 'text/html');
if (jsGen.cache.indexTpl) {
res.send(jsGen.cache.indexTpl); // 响应首页index.html
} else {
then(function (defer) {
fs.readFile(processPath + '/static/index.html', 'utf8', defer);
}).then(function (defer, tpl) {
jsGen.cache.indexTpl = tpl.replace(/_jsGenVersion_/g, jsGen.version);
res.send(jsGen.cache.indexTpl);
}).fail(res.throwError);
}
}
}
res.throwError = function (defer, err) { // 处理then.js捕捉的错误
if (!util.isError(err)) {
err = jsGen.Err(err);
}
errHandler(err, res, dm);
};
dm.on('error', function (err) { // 处理domain捕捉的错误
errHandler(err, res, dm);
});
dm.run(function () {
router(req, res); // 运行
});
}).listen(jsGen.module.rrestjs.config.listenPort);
console.log('jsGen start!');
}).fail(function (defer, err) {
throw err;
});
});
Do you know how to modify this code to deploy to appfog?
appfog and cloudfoundry support auto-reconfiguration for node.js applications. So the code should run on AppFog without any modifications.
However if you want to use those environment variables, you can start by modifying the line .listen(jsGen.module.rrestjs.config.listenPort); so it reads:
.listen(process.env.VCAP_APP_PORT, process.env.VCAP_APP_HOST);