node-js-libs / node.io

MIT License
1.8k stars 140 forks source link

Setting jsdom to true and calling nodeio.start results in TypeError: undefined is not a function #129

Closed rowdyrabbit closed 11 years ago

rowdyrabbit commented 11 years ago

When I set jsdom:true in my options and create and start a new job like this (not on the command line):

 var jobOptions = {timeout : 10, jsdom:true}; 

 var jobName = { 
   input: false, 
   run: function () { 
        var phone = this.options.args[0]; 
        this.getHtml("http://www.gigpark.com/?search=" + phone + "&commit=Search&city", 
                               function (err, $) { 
                                  var results = $('h3 .search-results b').text.toLowerCase(); 
                                  this.emit('Results found: ' + results); 
                               }); 
   } 
}; 

 nodeio.start(new nodeio.Job(jobOptions, jobName), ['0789878'], 
                        function(err, output) {  
                            console.log('result is: ' + output); 
                        },
                        true); 

I get the following error:

var results = $('h3 .search-results b').text.toLowerCase(); 
                     ^
TypeError: undefined is not a function
    at jobName.run (/Users/myhome/Documents/workspace/project1/WebContent/tester.js:21:22)
    at Job.parseHtml (/Users/myhome/node_modules/node.io/lib/node.io/dom.js:66:22)
    at Job.getHtml (/Users/myhome/node_modules/node.io/lib/node.io/request.js:109:18)
    at Job.doRequest.callback (/Users/myhome/node_modules/node.io/lib/node.io/request.js:217:25)
    at Job.doRequest.parse_callback (/Users/myhome/node_modules/node.io/lib/node.io/request.js:339:13)
    at Request.Job.doRequest [as _callback] (/Users/myhome/node_modules/node.io/lib/node.io/request.js:349:13)
    at Request.init.self.callback (/Users/myhome/node_modules/node.io/node_modules/request/main.js:119:22)
    at Request.EventEmitter.emit (events.js:99:17)
    at Request.<anonymous> (/Users/myhome/node_modules/node.io/node_modules/request/main.js:521:16)
    at Request.EventEmitter.emit (events.js:96:17)

It looks like jsdom isn't actually being turned on and that I can't use jQuery.

Any idea what the problem is?

chriso commented 11 years ago

Note that jQuery has a different API to the default selector engine; text() is a function.

$('h3 .search-results b').text.toLowerCase() should instead be $('h3 .search-results b').text().toLowerCase()

Refer to the jQuery docs for more information

rowdyrabbit commented 11 years ago

Hi,

I've tried the code with the change you suggested, but I'm still getting the same error:

   var results = $('h3 .search-results b').text().toLowerCase(); 
                 ^

TypeError: undefined is not a function

I'll post the entire app below, so you can run it yourself.

rowdyrabbit commented 11 years ago
var http = require('http'); 

 var URL = require('url'); 

 var nodeio = require('node.io'); 

 var port = 8081; 

 var gigParkSearchJobOptions = {timeout : 10, jsdom:true}; 

 var gigParkSearchJob = { 

   input: false, 

   run: function (keyword) { 

        var phone = this.options.args[0]; 

     this.getHtml("http://www.gigpark.com/?search=" + phone + "&commit=Search&city", function (err, $) { 

       var results = $('h3 .search-results b').text().toLowerCase(); 

       this.emit('Results found: ' + results); 

     }); 

   } 

 }; 

 /** 
  * Main http serve method. 
  */ 

 function serve() { 

      var serv = http.createServer(function(req, res) { 

           var url = URL.parse(req.url); 

           if (req.method == 'GET' && url.pathname == '/gigpark_search') { 

                res.statusCode = 200; 

                //query looks like this: 'q=1234567890, so we split it. 

                var phoneNum = url.query.split('=')[1]; 

                console.log('query is: ' + phoneNum); 

                nodeio.start(new nodeio.Job(gigParkSearchJobOptions, gigParkSearchJob), [phoneNum], function(err, output) {  

                     console.log('result is: ' + output); 

                     res.write(JSON.stringify(output)); 

                     res.end(); 

                     }); 

           } else { 

                res.statusCode = 404; 

                res.end("Sorry, page not found."); 

           } 

      }); 
      serv.listen(port); 
 } 

 console.log("Starting server..."); 

 serve(); 

It's a pretty rough piece of code, not well structured, I know, but I just copied it off the web to try out node.io to see if it'll do what I want. I really need to be able to use the jQuery selectors as I have a webpage that needs this jQuery selector in order to select the correct part of the DOM:

$('.timetable:eq(' +day_of_week_index+ ') tbody tr')

I run the node server (which all lives in the one .js file called tester.js) like so:

$ node --debug tester.js

and I hit the URL which calls the node.io code like so:

http://localhost:8081/gigpark_search?q=7788911331

That's when I get the $ not defined error, which to me looks like the option I'm passing through to use jsdom isn't working.

chriso commented 11 years ago

Ok I've had a better look at your code. This is outside the scope of the library but I'll help you with your code this one time.

Here's what's wrong

Have a look at the jQuery and node.io documentation for more information.

Here's a working version of your initial snippet

var nodeio = require('node.io');

var options = {
    timeout: 10
  , jsdom: true
};

var job = {
    input: [ '0912341234' ],
    run: function (input) {
        var url = 'http://www.gigpark.com/?search=' + input + '&commit=Search&city';
        console.log(url);
        this.getHtml(url, function (err, $) {
            var result = $('h3.search-results b').first().text().toLowerCase();
            this.emit(result);
        });
    }
}; 

nodeio.start(new nodeio.Job(options, job), {}, function (err, output) {
    console.log(output);
}, true);
rowdyrabbit commented 11 years ago

I realise that this works from the command line, the problem is that I can't get the node.io job to be called from within the node.js app with jquery, the above code works fine if I don't try to set jsdom:true in the options.

Also, with the input hard-coded like that, it won't work for my app, I need it to be passed through from the request parameter.

Thanks for taking a look at the code.

This is where I got the example from: http://tommytcchan.blogspot.com.au/2012_05_01_archive.html

All I want to do is make it work with jquery by setting jsdom:true in the options.

rowdyrabbit commented 11 years ago

Updated the app to take into account all the changes you suggested, including the hard-coded input and still getting the 'undefined is not a function' error.

 var http = require('http'); 
 var URL = require('url'); 
 var nodeio = require('node.io'); 
 var port = 8081; 
 var options = {timeout : 10, jsdom:true}; 

 var job = { 
   input: ['0912341234'], 
   run: function (input) { 
       console.log("phone is: "+input);
       this.getHtml("http://www.gigpark.com/?search=" + input + "&commit=Search&city", function (err, $) { 
           var result = $('h3.search-results b').first().text().toLowerCase();
                   this.emit(result);
     }); 
   } 
 }; 

 /** 
  * Main http serve method. 
  */ 
 function serve() { 
      var serv = http.createServer(function(req, res) { 
           var url = URL.parse(req.url); 
           if (req.method == 'GET' && url.pathname == '/gigpark_search') { 
                res.statusCode = 200; 
                nodeio.start(new nodeio.Job(options, job), {}, function(err, output) {  
                     console.log('result is: ' + output); 
                     res.write(JSON.stringify(output)); 
                     res.end(); 
                     }, true); 
           } else { 
                res.statusCode = 404; 
                res.end("Sorry, page not found."); 
           } 
      }); 
      serv.listen(port); 
 } 

 console.log("Starting server..."); 
 serve();  

Cheers.

chriso commented 11 years ago

You're not checking if getHtml() returns an error. Cleaning up your code a bit I can only reproduce the error when the request times out.

var http = require('http')
  , URL = require('url')
  , nodeio = require('node.io')
  , port = 8081;

var options = {
    timeout: 30
  , jsdom: true
};

function getResult(phone_number, callback) {
    var job = new nodeio.Job(options, {
        input: [ phone_number ],
        run: function (input) {
            var url = 'http://www.gigpark.com/?search=' + input + '&commit=Search&city';
            this.getHtml(url, function (err, $) {
                if (err) {
                    return this.error(err);
                }
                var result = $('h3.search-results b').first().text().toLowerCase();
                this.emit(result);
            });
        }
    });
    nodeio.start(job, { debug: true }, callback, true);
}

function serve() {
    var serv = http.createServer(function(req, res) {
        var url = URL.parse(req.url);
        if (req.method == 'GET' && url.pathname == '/gigpark_search') {
            getResult('foo', function (err, result) {
                if (err) {
                    res.statusCode = 500;
                } else {
                    res.statusCode = 200;
                    res.write(JSON.stringify(result));
                }
                res.end();
            });
        } else {
            res.statusCode = 404;
            res.end();
        }
    });
    serv.listen(port);
}

console.log('Starting server...');
serve();
rowdyrabbit commented 11 years ago

Ok, I've taken your code above and modified it, this works (without jsdom:true):

var http = require('http')
  , URL = require('url')
  , nodeio = require('node.io')
  , port = 8081;

var options = {
    timeout: 30
//  , jsdom: true  //comment this out when not using jsdom
};

function getResult(search_str, callback) {
    var job = new nodeio.Job(options, {
        input: [ search_str ],
        run: function (input) {
            var url = 'http://www.gigpark.com/?search=' + input + '&commit=Search&city';
            this.getHtml(url, function (err, $) {
                console.log("url: "+url);
                if (err != undefined) {
                    console.log("error occurred: "+err);
                    return err;
                }
                console.log("got here");
//                var result = $('h3.search-results b').first().text().toLowerCase(); //use when jsdom:true property is set
                var result = $('h3 .search-results b').first().text.toLowerCase(); //use when not using jsdom
                console.log(result);
                this.emit(result);
            });
        }
    });
    nodeio.start(job, { debug: true }, callback, true);
}

function serve() {
    var serv = http.createServer(function(req, res) {
        var url = URL.parse(req.url);
        if (req.method == 'GET' && url.pathname == '/gigpark_search') {
            getResult('hairdresser', function (err, result) {
                console.log(result);
                if (err) {
                    res.statusCode = 500;
                } else {
                    res.statusCode = 200;
                    res.write(JSON.stringify(result));
                }
                res.end();
            });
        } else {
            res.statusCode = 404;
            res.end();
        }
    });
    serv.listen(port);
}

console.log('Starting server...');
serve();

However this, with jsdom: true does not:

var http = require('http')
  , URL = require('url')
  , nodeio = require('node.io')
  , port = 8081;

var options = {
    timeout: 30
  , jsdom: true
};

function getResult(search_str, callback) {
    var job = new nodeio.Job(options, {
        input: [ search_str ],
        run: function (input) {
            var url = 'http://www.gigpark.com/?search=' + input + '&commit=Search&city';
            this.getHtml(url, function (err, $) {
                console.log("url: "+url);
                if (err != undefined) {
                    console.log("error occurred: "+err);
                    return err;
                }
                console.log("got here");
                var result = $('h3.search-results b').first().text().toLowerCase();
//                var result = $('h3 .search-results b').first().text.toLowerCase(); // for when not using jsdom
                console.log(result);
                this.emit(result);
            });
        }
    });
    nodeio.start(job, { debug: true }, callback, true);
}

function serve() {
    var serv = http.createServer(function(req, res) {
        var url = URL.parse(req.url);
        if (req.method == 'GET' && url.pathname == '/gigpark_search') {
            getResult('hairdresser', function (err, result) {
                console.log(result);
                if (err) {
                    res.statusCode = 500;
                } else {
                    res.statusCode = 200;
                    res.write(JSON.stringify(result));
                }
                res.end();
            });
        } else {
            res.statusCode = 404;
            res.end();
        }
    });
    serv.listen(port);
}

console.log('Starting server...');
serve();

However, notice that the output of the

console.log("error occurred: "+err); 

log statement is:

error occurred: TypeError: Cannot read property 'prototype' of undefined

chriso commented 11 years ago

Try throw err instead of of console.log so you get a stack trace

rowdyrabbit commented 11 years ago

Ok, here's my stack trace:

url: http://www.gigpark.com/?search=hairdresser&commit=Search&city

error occurred: TypeError: Cannot read property 'prototype' of undefined

/Users/myhome/Documents/workspace/fitnessfirst/WebContent/tester.js:20 throw err; ^ TypeError: Cannot read property 'prototype' of undefined at create (/Users/myhome/node_modules/node.io/node_modules/jquery/lib/node-jquery.js:10:26) at /Users/myhome/node_modules/node.io/node_modules/jquery/lib/node-jquery.js:9471:18 at Object. (/Users/myhome/node_modules/node.io/node_modules/jquery/lib/node-jquery.js:9473:2) at Module._compile (module.js:449:26) at Object.Module._extensions..js (module.js:467:10) at Module.load (module.js:356:32) at Function.Module._load (module.js:312:12) at Module.require (module.js:362:17) at require (module.js:378:17) at Job.parseHtml (/Users/myhome/node_modules/node.io/lib/node.io/dom.js:60:22)

chriso commented 11 years ago

Try reinstalling jquery => npm install jquery

rowdyrabbit commented 11 years ago

Hmm, did that and still getting the same issue, here are some versions if it helps:

node -v = v0.8.14 npm -v = 1.1.65

jquery - A stupid-simple wrapper over jQuery for Node.JS (server). Currently 1.7.2.

Everything was installed via npm, and it's a fresh install of everything too, so should be picking up the latest packages.

chriso commented 11 years ago

Can you npm ls? You should get

└─┬ node.io@0.4.12
  ├── coffee-script@1.4.0
  ├── htmlparser@1.7.6
  ├─┬ jquery@1.8.2
  │ ├── location@0.0.1
  │ ├── navigator@1.0.1
  │ └── xmlhttprequest@1.4.2
  ├─┬ jsdom@0.2.19
  │ ├─┬ contextify@0.1.3
  │ │ └── bindings@1.0.0
  │ ├── cssom@0.2.5
  │ └── cssstyle@0.2.3
  └── request@2.9.202
rowdyrabbit commented 11 years ago

Ok, it looks like this:

/Users/myhome ├─┬ jquery@1.8.2 │ ├── htmlparser@1.7.6 │ ├── location@0.0.1 │ ├── navigator@1.0.1 │ └── xmlhttprequest@1.4.2 ├─┬ jsdom@0.2.19 │ ├─┬ contextify@0.1.3 │ │ └── bindings@1.0.0 │ ├── cssom@0.2.5 │ ├── cssstyle@0.2.3 │ ├── htmlparser@1.7.6 │ └─┬ request@2.12.0 │ ├─┬ form-data@0.0.3 │ │ ├── async@0.1.9 │ │ └─┬ combined-stream@0.0.3 │ │ └── delayed-stream@0.0.5 │ └── mime@1.2.7 ├─┬ node.io@0.4.12 │ ├── coffee-script@1.4.0 │ ├── htmlparser@1.7.6 │ ├─┬ jquery@1.8.2 │ │ ├── location@0.0.1 │ │ ├── navigator@1.0.1 │ │ └── xmlhttprequest@1.4.2 │ ├─┬ jsdom@0.2.19 │ │ ├── cssom@0.2.5 │ │ └── cssstyle@0.2.3 │ └── request@2.9.202 ├─┬ phantom@0.3.5 │ ├─┬ dnode@0.9.12 │ │ ├─┬ dnode-protocol@0.1.5 │ │ │ └── traverse@0.6.3 │ │ ├── jsonify@0.0.0 │ │ ├── lazy@1.0.8 │ │ ├─┬ socket.io@0.8.6 │ │ │ ├── policyfile@0.0.4 │ │ │ └── redis@0.6.7 │ │ └─┬ socket.io-client@0.8.6 │ │ ├── uglify-js@1.0.6 │ │ ├── websocket-client@1.0.0 │ │ └── xmlhttprequest@1.2.2 │ ├─┬ dnode-protocol@0.2.2 │ │ ├── jsonify@0.0.0 │ │ └── traverse@0.6.3 │ └─┬ express@3.0.3 │ ├── commander@0.6.1 │ ├─┬ connect@2.7.0 │ │ ├── bytes@0.1.0 │ │ ├── formidable@1.0.11 │ │ ├── pause@0.0.1 │ │ └── qs@0.5.1 │ ├── cookie@0.0.5 │ ├── cookie-signature@0.0.1 │ ├── crc@0.2.0 │ ├── debug@0.7.0 │ ├── fresh@0.1.0 │ ├── methods@0.0.1 │ ├── mkdirp@0.3.3 │ ├── range-parser@0.0.4 │ └─┬ send@0.1.0 │ └── mime@1.2.6 ├─┬ socket.io@0.8.7 │ ├── policyfile@0.0.4 │ ├── redis@0.6.7 │ └─┬ socket.io-client@0.8.7 │ ├── uglify-js@1.0.6 │ ├── websocket-client@1.0.0 │ └── xmlhttprequest@1.2.2 └─┬ socket.io-client@0.8.7 ├── uglify-js@1.0.6 ├── websocket-client@1.0.0 └── xmlhttprequest@1.2.2

rowdyrabbit commented 11 years ago

Ooh, ok, looks like contextify wasn't in my node.io jsdom install. I uninstalled node.io and jsdom and re-installed node.io, all seems to be working now!

Thanks so much for all your help, you rock :)

chriso commented 11 years ago

No probs ;) good luck