Thursday, July 12, 2012

5 minute guide to using JS PubSub and Noty

5 minute guide to using PubSubJS and Noty

A little background, Publish/Subscribe is a design pattern for decoupling components in software (or i guess anywhere in the world). The basic idea is that one thing publishes information to a well known place and another subscribes to it. There is lots of good writing about it out there, check it out on Wikipedia.

In my situation we wanted to display page level notifications. I would consider these to be messages that aren't tied to a web widget or element (like a message in a form). Page level notifications can increase the usability of your application (too many or badly placed one can reduce it :)

This tutorial will show you a simple way to create a subscriber that publishes messages to the browser using noty. And uses PubSubJS to handle the Publish/Subscribe work.

The tools

You will need a few things for this tutorial.
Grab them and download, or you can try this shell snippet:
  mkdir js
  wget https://raw.github.com/needim/noty/master/js/jquery.noty.js
  wget https://raw.github.com/mroderick/PubSubJS/master/src/pubsub.js
  cd ..
  mkdir css
  wget https://raw.github.com/needim/noty/master/css/jquery.noty.css
  wget https://raw.github.com/needim/noty/master/css/noty_theme_twitter.css

The whys

In our application, we needed to publish messages in the top of the application page. We use bootstrap and the noty plugin provided a perfect fit since it supports top (or anywhere else) notifications plus it matched the style of bootstrap alerts. It has an active developer as well.
There are many (many, many, many) Javascript PubSub implementations but PubSubJS seems the most lively and felt the most mature. In reality there are many good ones.

Getting Started

You will need to include PubSub and noty in your page with standard script and style html tags.

Also, all the code I"m including below should be put in a jQuery on load page closure. Example:

$(function() {
  // code goes here
});

Setup of noty options

First lets setup our noty library to display messages. After including the libraries in your page (using standard css and js html tags), In a page load closure set the following:

noty allows setup of most options when making the explicity call to the library:

$.noty({
  text: 'noty - a jquery notification library!',
  theme: 'noty_theme_twitter'
});

But this will become a pain, esp since we want all the notifications to use the same theme anyway. Nicely noty gives a way to setup all the options before creating individual notfications.

In the end, we only really need to set two default options:
  • theme - set everything to use the twitter option
  • closeButton - show button to close notification
Global Setup:
$.noty.defaultOptions.theme       = 'noty_theme_twitter';
$.noty.defaultOptions.closeButton = true;

You can see all the options at noty docs.

Using noty

Creating a noty is pretty quick and simple but before I drop the code in later in a subscriber, it seems best show and example an example now.

Example:
$.noty({ type: 'error', text: 'Arghhh getting eaten by zombie!' });

This will create a nice little alert at the top of your page. type parameter is used to figure what type of notification (and formatting to show) to show. text is what is shown to the user.

Setup of PubSubJS and message queues

Using pubsub requires two functions. A subscriber (that listens and acts on messages in a queue) and a publisher which sends a message (really just a data structure) to a queue. I guess there is a third item to setup but its more or less implicit that is the queue. In this situation the queue is created when either a publisher or subscriber specifics a queue.

The PubSubJS docs recommend using a variable name for a given queue name. I think that is often a good idea but in this case I don't plan on doing it :)

Subscriber

Since we have a pretty good idea of what the subscriber needs to do. I think creating the subscriber first makes sense.

The subscriber will need to listen on a queue and display each message coming in. In this situation, we want to be able to pass an array of messages.

Data format:
[
   { 'type': msgtype, 'text': msgtext },
   ... more if needed ...
]
Example:
PubSub.subscribe( 'site.messages', function( topic, msgs ) {
  var msgs = $.isArray(msgs) ? msgs : [ msgs ];
  $.each( msgs, function() { $.noty( this ) } );
});

that is it. That will listen to subscriber and show each message coming in. As I look at the code (after a few weeks), I added an auto convert msgs to an array.

Publisher

The publisher will be embedded in some other globber of code. I've used this type of call in ajax form handlers, click events and even some code that runs at page load time to show messages generated when the pages are produced from the server.

Example:
PubSub.publish( 'site.messages', 
   { 'type': "success", 'text': 'Successfully updated thingy' });
Use inside of events

I find it helpful to see an example of this in use i real-ish code. Here is a boring click handler for a button (or anything really) that will show a message

$('body').on('click', '.show-message', function(e) {
   PubSub.publish( 'site.message',
     { type: 'warning', "Watch for falling rocks!' });
   return false;
});

I hope you found some use in this guide, please let me know if you have any comments or questions

__END__

Tuesday, July 3, 2012

Using (Test::?)WWW::Mechanize to test AJAX calls

Test::WWW::Mechanize + AJAX = Love?

As we use more and more client side driven forms, testing in a standard way for unit/expanded unit tests becomes more and more difficult.

I've been in the process of creating more use case tests so as underlying components in our system changes those non-web people can verify that their changes don't break the webapp. 

This is often our REST API but can apply to DB changes or modules updates.

I've found this basic code block to work pretty well at using Test::WWW::Mechanize to simulate the situations where there are elements that use client side forms. (AJAX! But really AJAJ since we use JSON instead of XML).


BEGIN {
  use Test::More;
  use Test::Exception;
  use Test::WWW::Mechanize;
  use JSON;
}

#
# setup test data or load it from db
#

# 
# in this test, i login w/ many page, jump to account page
# then run ajax form and check json results
#
{
   diag "Running login and create user test";
   my $mech = Test::WWW::Mechanize->new;
   
   # base is defined above :)
   $mech->get_ok(sprintf('%s%s', $base, '/user/login'));

   # login
   $mech->submit_form_ok({
        #might be best to add name to enroll form
        form_number => 1,
        fields      => {
            email     => $test_email,
            password  => $test_password,
        }
    }, '/user/login');

    # goto next page
    $mech->get_ok( sprintf( '%s%s', $base, '/users' ));
    
    # in my case the form doesn't exist on the page until js 
    # code is run so we have to pretend to know it
 
    # pretend to be ajax client
    $mech->add_header( 'X-Requested-With' => 'XMLHttpRequest' );

    # form url
    my $create_url = sprintf '%s%s', $base, '/user/create';

    #
    # this is a failure case
    #
    my $resp  = $mech->post( $create_url, $bad_user_params );

    lives_ok { $jresp = from_json( $mech->content ) }
      'invalid user - json returned';
    
    # check response for errors and specific fields, YMMV
    is scalar @{$jresp->{errors}}, 5, 'missing fields';
    ok grep { qr/confirm_email/ } @{ $jresp->{errors} },
       'errors - confirm_email';
}

Its a bit of a long example but the real magic happens with adding the header to the request with $mech->add_header line, you will probably need to go back to standard requests by removing that header with $mech->delete_header('X-Requested-With').

Next series of my tests will be doing this with forms that exist in the page. I suspect that it may take a bit of fiddle with WWW::Mech.

 __END__