Here is the error I'm getting when trying to test a basic Socket.io and Express set up (per the example on the socket.io website):
/Users/scottcorgan/Projects/sevenly/campaigns/node_modules/socket.io/lib/manager.js:659
var socket = this.namespaces[i].socket(data.id, true);
^
TypeError: Object function extend(another) {
var properties = Object.keys(another);
var object = this;
properties.forEach(function (property) {
object[property] = another[property];
});
return object;
} has no method 'socket'
at Manager.handleClient (/Users/scottcorgan/Projects/sevenly/campaigns/node_modules/socket.io/lib/manager.js:659:41)
at Manager.handleUpgrade (/Users/scottcorgan/Projects/sevenly/campaigns/node_modules/socket.io/lib/manager.js:588:8)
at HTTPServer.<anonymous> (/Users/scottcorgan/Projects/sevenly/campaigns/node_modules/socket.io/lib/manager.js:119:10)
at HTTPServer.emit (events.js:88:20)
at Socket.<anonymous> (http.js:1390:14)
at TCP.onread (net.js:334:27)
Appreciate any help I can get, please :)
This problem stems from the fact that you or a library you use are adding functions to Object.prototype.
Thus this code:
Object.prototype.foo = function() {};
Object.prototype.bar = function() {};
var myObj = { x: 1 };
for (var i in myObj) {
console.log(i)
}
will print: x, foo, bar (not necessarily in that order) and not just x as you expect.
In your case this happens in manager.js:
// initialize the socket for all namespaces
for (var i in this.namespaces) {
var socket = this.namespaces[i].socket(data.id, true);
// echo back connect packet and fire connection event
if (i === '') {
this.namespaces[i].handlePacket(data.id, { type: 'connect' });
}
}
This code doesn't expect to encounter the declared: extend key, as you can see from the stack trace of the error:
TypeError: Object function extend(another) {
var properties = Object.keys(another);
var object = this;
properties.forEach(function (property) {
object[property] = another[property];
});
return object;
} has no method 'socket'
the program is actually tried to invoke socket() on the extend function.
See Bob's rant here about adding functions to Object.prototype.
As for the solution, you can either add a conditional statement inside manager.js like so:
// initialize the socket for all namespaces
for (var i in this.namespaces) {
if ('extend' == i) continue; // ADDED
var socket = this.namespaces[i].socket(data.id, true);
// echo back connect packet and fire connection event
if (i === '') {
this.namespaces[i].handlePacket(data.id, { type: 'connect' });
}
}
or you can remove the Object.prototype.extend = function(...) {} declaration, which is my personal preference.
Your this.namespaces[i].socket(data.id, true); does not exist. Do something like console.log(typeof this.namespaces[i].socket(data.id, true)); You'll probably get an undefined
I bet one of the elements of your namespaces array is missing.
Related
Little info, i have an arp.js file which takes a subnet address "192.168.2" and gets all strings returned from arp -a and stores in an array.
I can't figure out why my arpList function is returning an undefined value in my index.js file.
All the console.logs are returning the correct values in the arp.js page when called from the index.js, but the ipObj is coming up undefined. Even the console.log before i return of ipObj works.
Any help would be greatly appreciated.
var { spawn } = require('child_process');
const arpLs = spawn('arp', ['-a']);
var bufferData;
module.exports = {
arpList: function (subnet) {
arpLs.stdout.on('data', data => {
bufferData += data
})
arpLs.stderr.on('data', data => {
console.log('error: ' + data);
});
arpLs.on('exit', function (code) {
if (code != 0) {
console.log("Error exiting"); //if error occurs
}
console.log("exit start 1"); // checking internal processes at stages
var dataArray = bufferData.split(' ');
var ipArray = [];
for (i = 0; i < dataArray.length; i++) {
if (dataArray[i].includes(subnet)) {
ipArray.push(dataArray[i]);
console.log("loop working");
}
}
var ipObj = { "lanIps": ipArray };
console.log("Object is there: "+ipObj)
return ipObj; // this obj should be returned to the index.js call using
})
},
sayMyName: function () {
return "Hello";
}
}
//arpList(ipSubnet);
//INDEX.js
//the index page looks like this
//var arp = require('./arp.js);
//var ipSubnet = "192.168.2";
//var lanIps = arp.arpList(ipSubnet);
//console.log(lanIps);
I ended up adding a callback function to arpList - function (subnet, callback)
Then instead of returning the value pass it into the callback
Then on the index.js side instead of
var lanIps = arp.arpList(value)
i used
arp.arpList(value, function(res){lanIps = res}
return ipObj; // this obj should be returned to the index.js call using
It won't be returned. The reference say nothing about return value. Node-style callbacks rarely work like that because they are potentially asynchronous and returned value cannot be taken into account.
This a special case of this well-known problem. The process is asynchronous and is finished after arp.arpList(ipSubnet) call, there's nothing to assign to lanIps. This is a use case for promises. There are already third-party promisified counterparts like child-process-promise.
The problem can be also solved by moving to synchronous API. child_process functions have synchronous counterparts, including spawnSync.
I can not figure out why the following code does not work:
var os = new Proxy(require('os'), {});
console.log( os.cpus() ); // TypeError: Illegal invocation
whereas
var os = require('os');
console.log(Reflect.apply(os.cpus, os, []));
or
var os = new Proxy(require('os'), {});
console.log( os.platform() );
works as expected.
Having just skim read the source for the os package in the Node repo, it appears that the cpus() is exported from binding.getCPUs which is a C hook in the Node runtime environment.
cpus() therefore has the binding object as a function context, which is then lost through the proxy, giving you the IllegalInvocation error because there is no context to the function when you call it — although I'm hazy on the details.
platform() on the other hand is exported as function () { return process.platform; }, and hence it's just a function that returns an object, and doesn't need to be run under a specific context because Node function contexts will have the process variable specified by default (unless it has been overridden).
The following behaviour shows that applying the os as a context to the cpus function will work — proxies on function objects evidently lose the function context when calling properties.
const os = require('os');
const proxy = new Proxy(os, {}); // proxy of object, functions called get proxy context rather than os context
const cpus = new Proxy(os.cpus, {}); // proxy of function, still has os context
console.log(os.cpus()); // works (duh)
console.log(cpus()); // works
console.log(proxy.cpus.apply(os, [])); // works
console.log(proxy.cpus()); // fails with IllegalInvocation
Note: If someone can clear up the details on the JS function context for an answer I'd love to read it too.
How about composition:
const os = require('os');
const proxy = new Proxy(os, {});
Object.getOwnPropertyNames(os).forEach(k => {
var v = os[k];
if(typeof v === "function") proxy[k] = v.bind(os);
});
//the `!!` because I don't want the actual print
//only a `true` or an `Error`
console.log(!!os.cpus());
console.log(!!proxy.cpus());
console.log(!!proxy.cpus.apply(proxy, []));
and all this as a utility function to "replace" new Proxy(), where handler.bindTargetFunctions can be
either an array of keyNames to be bound (so you can be specific)
or any truthy or falsy value to determine wether all functions on the target should be bound
the code:
function proxy(target, handler){
const _proxy = new Proxy(target, handler);
if(handler.bindTargetFunctions){
let bindTargetFunctions = handler.bindTargetFunctions;
if(!Array.isArray(bindTargetFunctions)){
bindTargetFunctions = Object.getOwnPropertyNames(target)
.filter(key => typeof target[key] === "function");
}
bindTargetFunctions.forEach(key => {
_proxy[key] = target[key].bind(target);
});
}
return _proxy;
}
const os = proxy(require('os'), { bindTargetFunctions: true });
//or
//const os = proxy(require('os'), { bindTargetFunctions: ["cpus"] });
console.log(os.cpus());
Edit:
Currently I try to bind functions directly in my get handler (see github.com/FranckFreiburger/module-invalidate/blob/master/…), the drawback of my solution is that each access to a function returns a new binding.
I entioned caching in the comments. This is how this cache could look like:
function createProxy(mod){
var cache = Object.create(null);
return new Proxy(function(){}, {
get(target, property, receiver) {
var val = Reflect.get(mod._exports, property, receiver);
if(typeof val === "function"){
if(!(property in cache) || cache[property].original !== val){
cache[property] = {
original: val,
bound: bal.bind(mod._exports)
}
}
val = cache[property].bound;
}else if(property in cache){
delete cache[property];
}
return val;
}
});
}
And No, I don't consider this cache a regular object. Not because it inherits from null, but because logically, to me this is a dictionary/map. And I don't know any reason why you would ever extend or proxy a particular dictionary.
I am trying to refactory my nodejs server using promises with Bluebird library, but I am stuck in a simple problem.
After to get the users from my db, I want to list all notification class associated with this user:
Bad Way (working...)
adapter.getUsers(function(users){
users.rows.forEach(function(item){
user = item.username;
adapter.getNotifications(user, function(notificationList){
console.log(notificationList);
})
});
});
Elegant Tentative Way (not working...)
var getNotifications = Promise.promisify(adapter.getNotifications);
adapter.getUsers().then(function(users) {
users.rows.forEach(function(item){
var dbUser = "sigalei/" + item.value.name;
console.log(dbUser);
return getNotifications(dbUser);
});
}).then(function(result){
console.log(result);
console.log("NOTIFICATIONLIST");
});
However when I execute this code I get this error inside my getNotification method:
Unhandled rejection TypeError: Cannot read property 'nano' of undefined
at Adapter.getNotifications (/Users/DaniloOliveira/Workspace/sigalei-api/api/tools/couchdb-adapter.js:387:30)
at tryCatcher (/Users/DaniloOliveira/Workspace/sigalei-api/node_modules/bluebird/js/main/util.js:26:23)
EDIT
After the user2864740`s precious comments, I noticed that the error is related with some scope problem. So, why after to use promisify method, the method dont getNotifications recognize the "this" env variable?
var Adapter = module.exports = function(config) {
this.nano = require('nano')({
url: url,
request_defaults: config.request_defaults
});
};
Adapter.prototype.getNotifications = function(userDb, done) {
var that = this;
console.log(that);
var userDbInstance = that.nano.use(userDb);
userDbInstance.view('_notificacao', 'lista',
{start_key: "[false]", end_key: "[false,{}]"},
function(err, body) {
if(err){ done(err); }
done(body);
});
};
This is just the very common problem of calling "unbound" methods.
You can pass the context as an option to Promise.promisify to have it bound:
var getNotifications = Promise.promisify(adapter.getNotifications, {context: adapter});
Alternatively, you'd need to .bind() the method, or call the new getNotifications function on the adapter (using .call()). You might also consider using Promise.promisifyAll(adapater) and then just calling adapter.getNotificationsAsync(…).
Notice that this still doesn't work. You cannot simply create promises in a loop - you need to await them explicitly and return a promise from the then callback, otherwise just the undefined value you returned will be passed to the next callback immediately.
adapter.getUsers().then(function(users) {
return Promise.all(users.rows.map(function(item){
var dbUser = "sigalei/" + item.value.name;
console.log(dbUser);
return getNotifications(dbUser);
}));
}).then(function(results) {
for (var i=0; i<results.length; i++)
console.log("result:", results[i]);
});
Instead of Promise.all(users.rows.map(…)), in Bluebird you can also use Promise.map(users.rows, …).
What about simply
var getNotifications = Promise.promisify(adapter.getNotifications.bind(adapter));
or possibly
var getNotifications = Promise.promisify(function () {
return adapter.getNotifications.apply(adapter, arguments);
});
?
I'm not sure I understand your problem well, but this should make sure this is bound and not undefined when you do return getNotifications(dbUser);
Is there a way to stub a virtual attribute of a Mongoose Model?
Assume Problem is a model class, and difficulty is a virtual attribute. delete Problem.prototype.difficulty returns false, and the attribute is still there, so I can't replace it with any value I want.
I also tried
var p = new Problem();
delete p.difficulty;
p.difficulty = Problem.INT_EASY;
It didn't work.
Assigning undefined to Problem.prototype.difficulty or using sinon.stub(Problem.prototype, 'difficulty').returns(Problem.INT_EASY);
would throw an exception "TypeError: Cannot read property 'scope' of undefined", while doing
var p = new Problem();
sinon.stub(p, 'difficulty').returns(Problem.INT_EASY);
would throw an error "TypeError: Attempted to wrap string property difficulty as function".
I am running out of ideas. Help me out! Thanks!
mongoose internally uses Object.defineProperty for all properties. Since they are defined as non-configurable, you can't delete them, and you can't re-configure them, either.
What you can do, though, is overwriting the model’s get and set methods, which are used to get and set any property:
var p = new Problem();
p.get = function (path, type) {
if (path === 'difficulty') {
return Problem.INT_EASY;
}
return Problem.prototype.get.apply(this, arguments);
};
Or, a complete example using sinon.js:
var mongoose = require('mongoose');
var sinon = require('sinon');
var problemSchema = new mongoose.Schema({});
problemSchema.virtual('difficulty').get(function () {
return Problem.INT_HARD;
});
var Problem = mongoose.model('Problem', problemSchema);
Problem.INT_EASY = 1;
Problem.INT_HARD = 2;
var p = new Problem();
console.log(p.difficulty);
sinon.stub(p, 'get').withArgs('difficulty').returns(Problem.INT_EASY);
console.log(p.difficulty);
As of the end of 2017 and the current Sinon version, stubbing only some of the arguments (e.g. only virtuals on mongoose models) can be achieved in the following manner
const ingr = new Model.ingredientModel({
productId: new ObjectID(),
});
// memorizing the original function + binding to the context
const getOrigin = ingr.get.bind(ingr);
const getStub = S.stub(ingr, 'get').callsFake(key => {
// stubbing ingr.$product virtual
if (key === '$product') {
return { nutrition: productNutrition };
}
// stubbing ingr.qty
else if (key === 'qty') {
return { numericAmount: 0.5 };
}
// otherwise return the original
else {
return getOrigin(key);
}
});
The solution is inspired by a bunch of different advises, including one by #Adrian Heine
I am trying to wrap the node-memcached api with deferred's promisify in order to simplify my nested callbacks.
When I try to call the promisified function I just get "TypeError: Cannot read property 'namespace' of undefined".
Memcached = require('memcached');
var memcache = new Memcached('localhost:11211');
var add = deferred.promisify(memcache.add);
add('myKey', 'myVal', 0)(function(result) {
...
});
I can't seem to find anyone else trying to wrap node-memcached, or getting my same error. Any insight into what may be going wrong? Or maybe even a push into a better direction if this is imperfect?
Thanks!
EDIT::
Just wanted to response that I found the best solution I could for now by doing some digging.
It seems that deferred.promisify calls the passed function with its own scope (this), instead of the context of the function that is passed in.
Using my own promisfy function appears to fix the issue (idea from http://howtonode.org/promises):
function promisify(fn, context) {
return function() {
var def = deferred();
var args = Array.prototype.slice.call(arguments);
args.push(function(err, val) {
if (err !== null) {
return def.reject(new Error(err));
}
return def.resolve(val);
});
fn.apply(context, args);
return def.promise;
};
}
When promisify instances members you should bind it to this instance like:
Memcached = require('memcached');
var memcache = new Memcached('localhost:11211');
var add = deferred.promisify(memcache.add.bind( memcache ) );
add('myKey', 'myVal', 0)(function(result) {
...
});