cannot denodeify methods in node-ftp module - javascript

I am new to both node.js and promise style function call. By looking at an denodeify example at http://runnable.com/Ulatc0QnzUgUAAAK/adapting-node-js-with-q-for-promises, I am trying to denodeify the methods of the node.js node-ftp module as following:
var ftp = require('ftp');
var q = require('q');
var ftpClient = new ftp();
ftpClient.on('ready', function() {
var ftpList = q.denodeify(ftpClient.list);
ftpList().then(function(list) {
console.log(list);
}.then(null, function(err) {
console.log(err);
}).done(function() {
ftpClient.end();
});
});
ftpClient.connect();
However, when running that code with node, it shows the error "list error: TypeError: Object # has no method '_pasv'"
I am not sure what's wrong with that piece of code. Does anyone know what's wrong with that? Can you point me some way to debug/troubleshoot the cause of that error message?
Thanks.

When you pass
ftpClient.list
to Q.denodefiy, you are getting the function object, list from the ftpClient object. It will be just a function and the relationship with the parent is lost. This is important because, the bound function list might be dependent on the ftpClient object. So, you must make sure that link is not broken.
Quoting from the Q.denodeify docs,
Note that if you have a method that uses the Node.js callback pattern,
as opposed to just a function, you will need to bind its this value
before passing it to denodeify, like so:
var Kitty = mongoose.model("Kitty");
var findKitties = Q.denodeify(Kitty.find.bind(Kitty));
The better strategy for methods would be to use Q.nbind, as shown below.
So, you can fix your code in two ways,
Using Q.denodeify and Function.prototype.bind, like this
var ftpList = q.denodeify(ftpClient.list.bind(ftpClient));
Using Q.nbind, like this
var ftpList = q.nbind(ftpClient.list, ftpClient);

you need to use q.nbind
q.nbind(ftpClient.list, ftpClient);

Related

Why functions in module are not passed back to the main process?

I need to load untrusted modules, written by third parties. I'm using vm for the sandbox and I was thinking to use threads (from npm: here) in order to load the module asynchronously and avoid blocking code.
I have stripped down the code to the minimum, because I'm stuck and I dont' understand if what I'm trying to achieve is impossible or it's just me messing with scopes.
Here is a dummy module:
exports.dummy = function () {
console.log('Dummy');
};
exports.val = 5;
And here is a module where I try to load this dummy module using threads:
var spawn = require('threads').spawn;
var mod;
var other;
var t = spawn(function (input, done) {
var m = require(input.dir + '/dummyMod');
done({m: m, other: 'hey'})
});
t.send({dir: __dirname}).on('message', function (result) {
mod = result.m;
other = result.other;
console.log(mod);
console.log(other);
t.kill();
});
The logged output is:
{ val: 5 }
hey
As you can see, the function in the dummy module has been skipped. If I try to load the module in the main process and log it, then the function is, of course, part of the object.
You need to properly serialize and deserialize the function. JSON.stringify ignores functions, probably because json is a format for storing data, not scripts.
Serialize the function by calling toString() on it. Then you can send it along as a string.
done({m: m.toString(), other: 'hey'})
Converting m to a string will give you something like this:
"function m(){console.log(\'called m()\')}"
On the receiving end, you will need to deserialize the function.
var m = new Function("return " + result.m)()

Getting element from pageobject in protractor

Im writing some Protractor testcases by using a pageobject. However im having trouble using elements directly from the Pageobject file, in the spec file.
Possibly im missing some JS nuance, since i havent written that much in JS.
i would like to use some elements defined in Pageobject like so:
var PageObject = function()
{
var loginUsername = element(by.id('loginusername'));
//other methods
};
module.exports = PageObject;
to be used in my spec file like so:
var PageObject = require('./pageObject.page.js');
describe( ' Login page ', function(){
it('type something in the usernamefield', function(){
var pageObject = new PageObject();
pageObject.get();
pageObject.loginUsername.sendKeys('Test');
});
});
Using methods (for example get but also others)works fine, but using elements directly causes some undefined errors.
Im trying to copy from something like this, where supposedly it should work.
https://ramonvictor.github.io/protractor/slides/#/33
Something you are missing,
bind your variable to object in pageObject.page.js, using this keyword
var PageObject = function()
{
this.loginUsername = element(by.id('loginusername'));
......
};
module.exports = PageObject;
In Spec first get the real application and do real work
it('type something in the usernamefield', function(){
browser.get('"https://....');
var pageObject = new PageObject();
pageObject.loginUsername.sendKeys('Test');
});
This works for me perfect,

Attempting to use MeteorJS w/twit Node Module, Error: [TypeError: Object #<Object> has no method 'request']

So I'm using MeteorJS w/the twit Node module to access the screen name of a tweet. Still just testing the code to see if I can retrieve the JSON from twitter.
Here is my code:
var Tget = Meteor.wrapAsync(T.get);
Meteor.methods({
'screenName' : function() {
try {
var result = Tget('search/tweets', {q:'#UCLA',count:1});
JSON.stringify(result);
console.log(result);
}
catch (e) {
console.log(e);
return false;
}
}
})
The error I'm receiving is :
[TypeError: Object #<Object> has no method 'request']
Here is the twit module git : https://github.com/ttezel/twit/blob/master/README.md
I think I understand. Here's the code of T.get:
Twitter.prototype.get = function (path, params, callback) {
return this.request('GET', path, params, callback)
}
As you can see, it expects this to have the method request. However, because we used wrapAsync without caring about the execution context (accessed with this), it fails.
Consider this example (you can copy/paste that in your browser console):
var obj = {
foo : 'foo',
logThis : function() {
console.log(this);
}
};
If we execute obj.logThis() we have: Object { foo: "foo", logThis: obj.logThis() }
But if we do the following...
var otherLogThis = obj.logThis;
otherLogThis();
It logs the Window object because we got the function out of its context!
How to solve that issue? Binding the function? Tricky call?
Nope, Meteor has the solution. wrapAsync can have two parameters... The second one is the context!
var Tget = Meteor.wrapAsync(T.get, T);
If you want to learn more about JavaScript contexts, I suggest this book :
https://github.com/getify/You-Dont-Know-JS/
It is free and open-source and I am not affiliated in any way other than my deepest affection and tender memories of feeling my brain growing in all kinds of funny ways when I read it.

Koa and Twitter - "Thunking" does not work

I've gotten some third party asynchronous functions to work with Koa through thunking, either by wrapping the function like so: var thunkedFunction = function(params) { return function(callback) { originalFunction(params, callback) }; ) or using the node thunkify library.
However when I try this with ntwitter's stream like so:
var streamThunk = thunkify(tw.stream);
var stream = yield streamThunk("statuses/filter", {track: track});
I get the following error: "Cannot read property stream_base of undefined".
Digging deeper into ntwitter (built on node-twitter) I see that the Twitter.prototype.stream function calls this.options.stream_base, and this.options is defined when I call it normally i.e. tw.stream(function(stream) {...}); but undefined when I thunk the function. Is there any reason that the function loses its scope when thunked, and is there a way to circumvent this?
Notice that thunkify doesn't see the tw object. So, the way it's designed, it has no way of knowing the context (tw in your case) of the function it's getting (tw.stream).
The function returned by thunkify will pass along whatever this context it's called with (source: node-thunkify/index.js).
This means you should be able to change the second line in your example to:
var stream = yield streamThunk.call(tw, "statuses/filter", {track: track});
Read more about call.

Javascript object member function referred to globally not recognized in callback

I'm having a problem with the following Javascript code (Phonegap in Eclipse):
function FileStore(onsuccess, onfail){
//chain of Phonegap File API handlers to get certain directories
function onGetSupportDirectorySuccess(dir){
//stuff
onsuccess();
}
function getDirectory(dir){
return "something" + dir;
}
}
var onFileStoreOpened = function(){
if (window.file_store instanceof FileStore){
console.log('window.file_store is a FileStore');
console.log(window.file_store.getDirectory('something'));
}
}
var onDeviceReady = function(){
window.file_store = new FileStore(onFileStoreOpened, onFileStoreFailure);
}
Here, I want to do some things to initialize file services for the app, and then use them in my initialization from the callback. I get the following error messages in LogCat:
07-03 06:26:54.942: D/CordovaLog(223): file:///android_asset/www/index.html: Line 40 : window.file_store is a FileStore
07-03 06:26:55.053: D/CordovaLog(223): file:///android_asset/www/cordova-1.8.1.js: Line 254 : Error in success callback: File7 = TypeError: Result of expression 'window.file_store.getDirectory' [undefined] is not a function.
After moving the code around and stripping out everything in getDirectory() to make sure it was valid, I'm not even sure I understand the error message, which suggested to me that getDirectory() is not seen as a member function of window.file_store, even though window.file_store is recognized as a FileStore object. That makes no sense to me, so I guess that interpretation is incorrect. Any enlightenment will be greatly appreciated.
I've since tried the following:
window.file_store = {
app_data_dir : null,
Init: function(onsuccess, onfail){
//chain of Phonegap File API handlers to get directories
function onGetSupportDirectorySuccess(dir){
window.file_store.app_data_dir = dir;
console.log("opened dir " + dir.name);
onsuccess();
}
},
GetDirectory : function(){
return window.file_store.app_data_dir; //simplified
}
}
var onFileStoreOpened = function(){
var docs = window.file_store.getDirectory();
console.log('APPDATA: ' + docs.fullPath);
}
var onDeviceReady = function() {
window.file_store.Init(onFileStoreOpened, onFileStoreFailure);
}
and I get
D/CordovaLog(224): file:///android_asset/www/base/device.js: Line 81 : opened dir AppData
D/CordovaLog(224): file:///android_asset/www/cordova-1.8.1.js: Line 254 : Error in success callback: File7 = TypeError: Result of expression 'docs' [null] is not an object.
All I want to do here is make sure certain directories exist (I've removed all but one) when I start, save the directory object for future use, and then retrieve and use it after all initialization is done, and I don't want everything in the global namespace. Of course I would like to be able to use specific instances when necessary, and I'm disturbed that I can't make it work that way since it demonstrates there is a problem with my understanding, but I can't even get this to work with a single, global one. Is this a Javascript problem or a Phonegap problem?
As it stands, your getDirectory function is a private function within FileStore. If you wanted to make it a 'member' or 'property' of FileStore, you would need to alter it a little within FileStore to make it like this:
this.getDirectory = function(dir){ };
or leave it how it is and then set a property....
this.getDirectory = getDirectory();
this way when new FileStore is called it will have getDirectory as a property because the 'this' keyword is always returned when calling a function with 'new'
Hope this quick answer helps. There's lots of stuff on the goog about constructor functions.
You understand it correctly. The getDirectory as it stands is a private function and cannot be called using the file_store instance.
Try this in the browser.
function FileStore(onsuccess, onfail){
function onGetSupportDirectorySuccess(dir){
//stuff
onsuccess();
}
this.getDirectory = function (dir){
return "something" + dir;
}
}
window.file_store = new FileStore('', ''); //the empty strings are just placeholders.
if (window.file_store instanceof FileStore){
console.log('window.file_store is a FileStore');
console.log(window.file_store.getDirectory('something'));
}
This will prove that the basic js code is working fine. If there still is a problem while using it in PhoneGap, comment.

Categories

Resources