Tracking AJAX-driven events in Ruby on Rails for Google Analytics conversion goals »

Created at: 21.10.2009 21:09, source: Robby on Rails, tagged: Business Ruby on Rails programming ajax rubyonrails analytics javascript prototype kpi conversions

Tracking your KPI’s is extremely important in your online venture. At a minimum, you should be using something like Google Analytics to track conversions in your application. Setting up goals is actually quite simple, especially if you’re just tracking that specific pages are loaded. However, if some of your conversion points occur through AJAX, you might not be capturing those activities in Google Analytics.

Lucky for you, it’s actually quite simple to update this. I thought I’d show you a fairly simple example to help you along.

On our web site, we have a mini contact form at the bottom of many of our pages. When submitted, if JavaScript is enabled, it’ll perform an Ajax request to submit the form. If you fill out the main Get in Touch form that gets processed and we redirect people to a thank you page. The URL for that is unique and we’re able to track those in Google Analytics quite easily.

However, with the Ajax-form, the URL in the browser isn’t going to change so Google Analytics isn’t going to track that conversion. So, we needed to track that properly.

To do this, we just need to call a JavaScript function that the Google Analytics code provides you.

  pageTracker._trackPageview("/contact_requests/thanks");

Let’s look at some simple code from our controller action. If the request is from JavaScript, we currently replace the form area with the content in a partial. (note: if you’re curious about the _x, read Designers, Developers and the x_ factor)

  respond_to do |format|
    format.html { redirect_to :action => :thanks }
    format.js do
      render :update do |page|
        page.replace :x_mini_contact_form_module, :partial => 'mini_contact_form_thanks'
      end
    end
  end

As you can see, the redirect will within the format.html block will lead people to our conversion point. However, the format.js block will keep the user on the current page and it’ll not trigger Google Analytics to track the conversion. To make this happen, we’ll just sprinkle in the following line of code.

  page.call 'pageTracker._trackPageview("/contact_requests/thanks");'

However, if you need to do something like this in several locations in your application, you might want to just extend the JavaScriptGenerator page. GeneratorMethods. (you could toss this in lib/, create a plugin, etc…)

  module ActionView
    module Helpers
      module PrototypeHelper
        class JavaScriptGenerator #:nodoc:
          module GeneratorMethods
            # Calls the Google Analytics pageTracker._trackPageview function with +path+.
            #
            # Examples:
            #
            #
            #  # Triggers: pageTracker._trackPageview('/contact_requests/thanks');
            #  page.track_page_view '/contact_requests/thanks'
            #
            def track_page_view(path)
             record "pageTracker._trackPageview('#{path}');"
            end
          end
        end
      end
    end
  end

This will allow us to do the following:

  page.track_page_view "/contact_requests/thanks"

  # or using a route/path
  page.track_page_view thanks_contact_requests_path

So, our updated code now looks like:

render :update do |page|
  page.replace :x_mini_contact_form_module, :partial => 'mini_contact_form_thanks'
  page.track_page_view thanks_contact_requests_path
end

With this in place, we can sprinkle similar code for our various conversion points that are Ajax-driven and Google Analytics will pick it up.

Happy tracking!


more »

Post-Javascript DOM with Aptana Jaxer »

Created at: 25.08.2009 20:42, source: igvita.com, tagged: Architecture ajax dom javascript jaxer

It is hard to imagine the web as we know it today without AJAX. Within just a couple of years, it has transformed how we think about web applications, how we build them, and the technology stack underneath. Prototype, JQuery, YUI and dozens of others frameworks let us build rich and interactive applications, while Javascript and rendering engines are progressing in leaps and bounds when it comes to performance. Exciting stuff. However, this client-side functionality comes at a price. The AJAX testing experience is still painful (even with solutions like Selenium, Webrat, etc), and even more generally, working with, or even getting access to the post-Javascript DOM remains a challenge. Want to build a crawler which sees the page as the author intented? That's a tough problem.

Javascript is (Almost) Everywhere

Like it or not, disabling JavaScript in your browser today is likely to render a large section of the web as completely unusable, and that is unlikely to change anytime soon. For that reason, over the years there have been many recurring rumors of the Google bot rendering Javascript when it came to your page. Google denies this, but there are also theories that the V8 engine is a direct result of such initiatives - which, I think, is not completely unfounded.

In either case, if you ever attempted this yourself, you quickly realized just how non-trivial this problem actually is. A typical spider just downloads the HTML of the page, it has not knowledge of the DOM, or Javascript. So, at the end of the day, what you need is a browser: the content must be downloaded, the HTML needs to be interpreted into a DOM model, and then finally handed off to the Javascript runtime for further processing. That's a lot of work.

Jaxer, the AJAX Server

Having attempted at this problem many times with Rhino, SpiderMonkey and even raw Webkit bindings, I finally stumbled on Jaxer. Developed at Aptana, it is based directly on Gecko (Firefox rendering engine), minus the graphical rendering, which means that the DOM is already there and Javascript is free of charge. I never bought into the server-side Javascript movement, but seeing Jaxer in this light definitely opened my eyes to a lot of opportunities. The installation is as easy as unpacking the Apache runtime and then proxying your requests through the server. For example, to get the full post-Javascript DOM of any web-page, just drop this template into your Jaxer directory:

> post-js.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
  "http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Post JavaScript DOM</title>
  </head>
  <body>
 
    <!-- Render post-JS DOM: http://jaxer/post-js.html?www.google.com -->
    <script runat='server'>
       var url_query = Jaxer.request.queryString;
       var sb = new Jaxer.Sandbox(url_query);
       Jaxer.response.setContents(sb.toHTML());
    </script>
  </body>
</html>
 

Accessing the Server-Side DOM

The first thing you'll notice is just how slow most of the pages will come back when proxied through Jaxer. However, this has nothing to do with Jaxer and everything to do with the dozens of Javascript files and slow remote calls our browsers have to do on our behalf. Their asynchronous nature just happens to mask the abysmal performance of most pages. Which, incidentally, is also likely the reason why our search engines today do not look at the post-JS DOM.

If you haven't already, take a look at the Jaxer tutorials. You have full access to the DOM on the server, which means that you can use JQuery to alter the response, connect to a database, test your Javascript on the fly, etc. Having said that, I'm still secretly hoping that one day our curl command client will just have a flag to return the final post-Javascript DOM.


more »

JSONQuerying Your Rails Responses »

Created at: 23.06.2009 01:44, source: RailsTips - Home, tagged: javascript json jsonquery testing

In which I show how to use a Ruby implementation of JSONQuery to test JSON in Rails apps.

I’m writing an application right now that is really JSON heavy. Some of the functional tests are cucumber and some of them are just rails functional tests using shoulda.

I hit a point today where I wanted to verify that the JSON getting output was generally what I want. I could have just JSON parsed the response body and compared that with what I was looking for, but a little part of me thought this might be a cool application of JSONQuery.

JSONQuery provides a comprehensive set of data querying tools including filtering, recursive search, sorting, mapping, range selection, and flexible expressions with wildcard string comparisons and various operators.

The quote above is fancy and can be boiled down to “a query language for JSON”. If you want to read more about JSONPath and JSONQuery here are some posts:

Finding a Ruby JSONQuery Implementation

I knew there was a JavaScript implementation of JSONQuery and that Jon Crosby has been doing some cool stuff with it in CloudKit, but I couldn’t find a Ruby implemenation that didn’t require johnson.

After some googling and Github searching, I came across Siren. Siren was pretty much what I wanted, so I started playing around with it. I forked it, gem’d it and wrapped it with some shoulda goodness.

What I ended up with was pretty specific to my needs at the moment, but I post it here in hopes that it sparks some ideas.

Bringing It All Together

First, I added the following to my environments/test.rb file.

config.gem 'jnunemaker-siren',
            :lib     => 'siren',
            :version => '0.1.1',
            :source  => 'http://gems.github.com'

Then I added the following in my test helper (actually put it in separate module and file and included it but I’m going for simplicity in this post).

class ActiveSupport::TestCase
  def self.should_query_json(expression, string_or_regex)
    should "have json response matching #{expression}" do
      assert_jsonquery expression, string_or_regex
    end
  end

  def assert_jsonquery(expression, string_or_regex)
    json = ActiveSupport::JSON.decode(@response.body)
    query = Siren.query(expression, json)

    if string_or_regex.kind_of?(Regexp)
      assert_match string_or_regex, query, "JSONQuery expression #{expression} value did not match regex" 
    else
      assert_equal string_or_regex, query, "Expression #{expression} value #{query} did not equal #{string_or_regex}" 
    end
  end
end

The code is quick and dirty. The first thing you’ll notice is that assert_jsonquery actually uses @response.body, which means it can only be used in a controller test. I could easily expand it, but, like I said above, I just got it working for what I needed right now. The cool part is that now in my functional tests, I can do stuff like this:

context "on POST to :create" do
  setup { post :create, :status => {'action' => 'In', 'body' => 'Working on PB' }

  # ... code removed for brevity ...
  should_query_json "$['replace']['#current_status']", /Working on PB/
end

That is a really basic query. Trust me you can do a heck of a lot more. Check out the Siren tests if you don’t believe me.

Conclusion

Overall, working with siren was a little rough because I wasn’t familiar with JSONQuery syntax. Also, siren tends to return nil instead of a more helpful error message about my expression compilation failing, but I’m kind of excited to see how this works out in the long run.

What are you doing to test JSON in your apps? Does something like this seem cool or overkill? Just kind of curious.


more »

JavaScript gotcha: storing objects in an associative array »

Created at: 17.06.2009 23:51, source: Rail Spikes - Home, tagged: javascript tips

I just ran into a tricky gotcha in JavaScript.

I was trying to store some objects in an associative array. Based on my experience with Java, Ruby, and other languages, I expected that given code like this:

1
2
3
4
5
6
7
var dictionary = {};

var obj1 = {}; 
var obj2 = {};

dictionary[obj1] = 'foo'
dictionary[obj2] = 'bar'

The result of dictionary[obj1] would be ‘foo’ and dictionary[obj2] would be ‘bar’.

This is not the case!

The problem is that JavaScript objects are not really hash tables. They’re associative arrays, and the key can only be a String. When you insert an object into a associative array, toString() is called and that is used as the key. Unfortunately, the default toString implementation for JavaScript objects returns “[object Object]”...which is not only very unhelpful when debugging, but doesn’t provide you with a unique key for your associative array.

You can work around this problem by overriding toString. Or you can figure out another way to associate your object with a value. D’oh!


more »

Favicons for 37signals apps »

Created at: 13.01.2009 00:37, source: Robby on Rails, tagged: favicons greasemonkey firefox javascript userscripts

If you’re using Highrise or Basecamp and miss not having favicons load in your browser, you can install either of the following greasemonkey scripts that I created.

These will just add a little html to the page to load some favicons that I created from their logos. Will look like this:

Dashboard - Highrise

Hopefully 37signals will add favicons themselves in the future, but in the meantime. Here you go!


more »