Thursday 19 July 2012

Node js: TypeError: Cannot read property 'prototype' of undefined at Object. (/http.js:49:35)

So I'm deploying my node app to the OpenStack framework using AppFog, but unfortunately even if you supply a package.json specifying your dependencies, you still need to manually copy over the files from your .npm directory
TypeError: Cannot read property 'prototype' of undefined at Object. (/http.js:49:35)

So I started copying over the dependencies manually, ie.


tchan@tommy:~/repo/afbirthday$ cp -a ~/.npm/cssom/0.2.4/package node_modules/cssom


I got to copying over express and its dependencies, and I had two versions of connect I could choose from. Naturally I chose the latest one, which turned out to be a bad idea when I tried to deploy the app.

I searched through forums, and eventually I came across a niffy feature of npm, which allows you to see the dependent versions of your dependencies.

npm ls

You will then see what version of connect is required by express. Turns out I used the newer version, which was causing issues.

Lesson of the day...use npm ls to see what versions your dependencies are before copying shiet over!

Monday 16 July 2012

Node and ampq plugin: /amqp/amqp.js:1229 this.channels[channel] = exchange; ^ TypeError: Cannot set property '1' of undefined

This is the error I got initially:



 /home/repo/404hound/node_modules/amqp/amqp.js:1229 
  this.channels[channel] = exchange;
              ^
 TypeError: Cannot set property '1' of undefined  


The solution is to wait until the connection is ready before invoking any start functions:




   var conn = createConnection(); 
   conn.on('ready', function () {
     conn.exchange(config.exchangeName, function (exchange) {
       console.log("Starting...");
       conn.queue(config.queueName, {durable:true, exclusive:true},
         function (q) {
           q.bind(config.exchangeName, '#'); //subscribe to all messages.
           q.subscribe(function (msg) {
             console.log(msg);
           });
         });
     });
   });  



Saturday 14 July 2012

Problems with Express js 3.x and EJS for node

So I upgraded to the latest version of Express, and to my dismay, they dropped support by the register() method in favor of the engine() method. Furthermore, they dropped partial view rendering (among others), which means one needs another plugin to restore the old functionality.

My solution:

 app.engine('.html', 

   require('ejs-locals') 

 );  

Saving to package.json and installing a node plugin in one step

I just found this from someone on Github:

$ npm install  --save
(--save automatically writes to your package.json file, tell your friends)
Pretty cool!

Using Node and RabbitMQ step-by-step tutorial

I spend a few hours today trying to configure the messaging part of my app using Rabbitmq on node js. I wanted to make sure it can be deployed to CloudFoundry. Anyways here is the finished module:



 var amqp = require('amqp'); 

 function rabbitUrl() { 

   if (process.env.VCAP_SERVICES) { 

     conf = JSON.parse(process.env.VCAP_SERVICES); 

     return conf['rabbitmq-2.4'][0].credentials.url; 

   } 

   else { 

     return "amqp://guest:@localhost";  

   } 

 } 



 var config = { 

   exchangeName:'404hound', 

   queueName:'ping.job' 

 }; 

 function init() { 

   var conn = createConnection(); 

   conn.on('ready', function () { 

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

     conn.queue(config.queueName, {durable:true, exclusive:true}, 

       function (q) { 

         q.bind(config.exchangeName, '#'); //subscribe to all messages. 

         q.subscribe(function (msg) { 

           console.log(msg); 

         }); 

       }); 

   }); 

 } 

 function publish(msg, conn) { 

   if (conn === undefined) { 

     conn = createConnection(); 

   } 

   conn.on('ready', function () { 

     console.log("Sending message..."); 

     conn.publish(config.queueName, {body:msg}); 

   }); 

 } 

 function createConnection() { 

   return amqp.createConnection({url:rabbitUrl()}, {defaultExchangeName:config.exchangeName}); 

 } 

 module.exports.init = init; 

 module.exports.publish = publish; 

 module.exports.createConnection = createConnection;  


The first function defines the url value. Notice that we are saying if it's defined by CloudFoundry, then we use it...otherwise we use our local config.

We then define an init() function that will create the connection with the exchange it we want. After that we wait until the connection is ready. Once it's ready we can then bind it to the messages we want to read. A '#' means we want to subscribe to all messages. Then we define what should happen when a message comes in. In this simply case we just log it.

The second part of this is the publish function. There are times where we don't want to create a connection for each message we're sending, so we can opt to pass in the connection. Again it's simple as publishing to the queue you want to send to.

Here's some sample output:


 Starting... 

 Sending message... 

 { body: 'hello' }  


Notice my code above differs from the sample code from CloudFoundry...after reading the ampq plugin for Node I realized that the guys at CF are using a deprecated way to sending/receiving messages.

Note #1: You will need to create your exchange on RabbitMQ first. In my case I created the exchange 404hound. You can also use the ampq plugin to create the exchange.

Unable to remove the tipsy tooltip plugin with using the fallback option

So I am using the tipsy tooltip plugin to alert a user that the form input in invalid if a XHR request returns false. Now I wanted to add the error text dynamically, so I used the 'fallback' option of the tipsy config, which looks like this:


   self.showTipsy = function() { 

     $('#keywords').tipsy({gravity: 'w', fallback: "Whoops. Did not find matching keywords."}); 

   };  

This is what the manual said if I want to remove it:
Tipsy tooltips are 'live' - if the source attribute's value changes, the tooltip text will be updated the next time the tooltip is shown. There's one caveat - if you wish to remove the tooltip by setting the title attribute to an empty string, set theoriginal-title attribute instead (this only applies if you're using the title attribute).
Unfortunately though, when I tried that it did not work. I also try to try to hide the tipsy as mention also in the manual. Still no luck. I did find a solution for it after doing a search on google:


   self.hideTipsy = function() { 

     $("#keywords").unbind('mouseenter mouseleave'); //workaround 

   }  

Hope that helps someone!

Wednesday 4 July 2012

Knockout binding doesn't work when using jquery $.load() method

So I encountered a problem today with knockout binding apparently not working when I load it via ajax using the $.load() method. I had the content loaded into a modal body, and the html content had data-bind elements in it, like so:




    <form id="create-form" class="form-horizontal"> 

     <fieldset> 

      <div class="control-group success"> 

       <label class="control-label">Email address</label> 

       <div class="controls"> 

        <span class="input-xlarge uneditable-input"><%= email %></span> 

       </div> 

      </div> 

      </div> 

     </fieldset> 

    </form>  



This is my js file that comes with it:




 ko.applyBindings(new CreatePageViewModel()); 

 function CreatePageViewModel() { 

   var self = this; 

   self.fetchContents = function() { 

     alert('hello'); 

   } 

 }  


Everything should work right? Wrong. Because we have already loaded the knockout library previously and it was activated, we need to specify the context in which to apply bindings, like so:



 ko.applyBindings(new CreatePageViewModel(), document.getElementById('create-form')); 

 function CreatePageViewModel() { 

   var self = this; 

   self.fetchContents = function() { 

     alert('hello'); 

   } 

 }  



Monday 2 July 2012

Knockout calling view model method twice

So I have a pretty simple use case, which involves disabling a button until the person types something in the text field. Here is my view model setup:



 var pageViewModel = new PageViewModel(); 
 ko.applyBindings(pageViewModel);
 function PageViewModel() {
   var self = this;
   self.email = ko.observable("");
   self.hasEmail = ko.computed(function () {
     return self.email().length != 0;
   });
   //called after user clicks the create button.
   self.create = function () {
     $('#modal-body').load('http://localhost:1337/create', function () {
       $('#modal').modal();
     });
   }
 }  

In my view, this is what I have:


       <div id="search"> 
         <form name="input" action="" method="get">
           <input type="text" data-bind="value: email" value="The admin's email address" name="field"
               class="text"/>
           <input type="submit" data-bind="enable: hasEmail(), click: create()" value="Start" name="search" class="search btn"
               data-toggle="modal" href="#myModal"/>
         </form>
       </div>  

Now for some reason before I found out my problem, it was also calling the create() method whenever the ko.applyBindings() method is called.

Upon looking at the docs, I realized that I have an extra bracket on the create, which causes KO to evaluate the create in the beginning. So the correct code should look like this:


       <div id="search"> 
         <form name="input" action="" method="get">
           <input type="text" data-bind="value: email" value="The admin's email address" name="field"
               class="text"/>
           <input type="submit" data-bind="enable: hasEmail(), click: create" value="Start" name="search" class="search btn"
               data-toggle="modal" href="#myModal"/>
         </form>
       </div>  



XMLHttpRequest cannot load http://localhost:1337/create. Origin null is not allowed by Access-Control-Allow-Origin.

I've started to do some backend Node work today. While my index.html is served locally on the file system, the node server actually runs on localhost with a port. This presented a problem when I try to make an ajax call. Here is the response

XMLHttpRequest cannot load http://localhost:1337/create. Origin null is not allowed by Access-Control-Allow-Origin.

If you get this error, that means that you are not allowed to cross site request, unless the server says you are allowed. Well how do you do this? Simple:


    res.header('Access-Control-Allow-Origin', '*');

For production though, depending on how secure you want your web service to be, you might not want to do '*' (allow everything from all domains) but rather restrict it to your own domain.

Starting my next project

I have completed part 1 of 4 of my project. But before I start part 2, I want to make sure that from now on, I know exactly when my app is down, since they will be all deployed on the cloud (and I have no idea if the cloud is happy or sad). Therefore I'm going to build http://www.404hound.com. The idea is simple -- use a screen scraping tool every X minute to look for certain keywords which would indicate that the site is up. If it isn't, then I will send a message to the site owner.

I'm going to do this on Node because I think it'll be great for this kind of app and I think Javascript is cool. I have my libraries picked out:

- Node.js / Express (for serving)
- Node.io (for screen scraping)
- Jade (for rendering templates)
- ampq (for messaging)
- node_mailer (for emailing using SendGrid)

Stay tuned for all the experiences I'll have with node =)