Google Event Tracking with jQuery

As with lots of little bits of code, if they are useful, you may write and re-write them a several times in several projects before you take the time to package them up into a reusable chunk. That finally happened to me around Google Analytics Event tracking.

Almost every project that I’ve been on in the past year has added Google Analytics (simple page tracking) right up front, and then near the end of the work, realized that they also want event/click tracking. On this last project, I decided to make it right, and package it up.

I’ve built a jQuery plugin (https://github.com/rcode5/ga_event_tracker) that makes it easy to setup click tracking all over your site. The event tracker takes 3 parameters: category, action, and label. The plugin will either find these values as attributes on the tracked DOM element (using data-category, data-action and data-label), or use values passed in when the plugin is initialized, or evaluate functions passed in on initialization. This allows for a lot of flexibility. We were able to setup tracking for most pages on a full site with a couple lines of Javascript.

Assuming you add the class ‘trackit’ to every element you want tracked, you can have something like this:

  $('.trackit').gaEventTracker();

And clicking all those elements will get tracked using the values found in the element attributes “data-category”, “data-action”, and “data-label”.

The source is available on Github with a few more examples. A test suite is included, in case you want to add your own extras and submit a pull request.

I hope this saves you a little time down the road, as I’m sure it will for me.

Handling Vanity URLs & Legacy Routes in Rails 3.2

Handling Vanity URLs is not a new problem. Website Owners want their URLs to be simple or catchy or elegant. For marketing purposes, they may need to be short(ish) and possibly temporary. Imagine a URL like

//example.com/party

which in April points to /parties/april, then May points to nothing (May’s a dull month) and then in June it points to /parties/june. We need something that acts sort of like Rack::Rewrite but that looks into an easily editable chunk of data (read: database) for the list of paths and redirects.

If you comb the web (as I did) looking for Vanity URL solutions, most of them solve the issue for pretty URLs for a given Model/Controller pair. They solve the issue that numeric IDs are not informative, but words with hyphens can be more informative. If that is the problem you’re hoping to solve here, read no further. Go straight to FriendlyID (or similar).

If, instead, you’re trying to build a simple redirect/route-fallback system that allows for root level temporary Vanity URLs that can be managed within the running application (not by a static configuration file), then you’ve come to the right place.

First, we need a place to store the paths and redirects. Let’s start by adding a simple VanityUrl model

class VanityUrl < ActiveRecord::Base
  attr_accessible :active, :path, :url
  validates :url, link: true, presence: true
  validates :path, presence: true, uniqueness: true
  class << self
    def active
      where(active: true)
    end

    def routes
      Hash[VanityUrl.active.map{|v| [v.path, v.url]}]
    end
  end
end

path is the path we will be watching for

url is the url we’ll redirect to, if we hit that path

active is a boolean (in case we want to easily turn the redirect off or on).

Looking ahead a bit, we’ve added an #active scoping method and #routes which returns all active VanityUrls. You’ll see where this is used shortly.

Exercise for the reader

Build controller and views to show/list/edit these guys in your application.

Now we want these to hit, only if other routes have not been matched. So at the end of our config/routes.rb file, we add a new catch-most route.

# in config/routes.rb

MyApp::Application.routes.draw do
  # your apps routes are defined here
  ...
  # then way down at the end... 
  # as the last defined route

  # this routes to the Vanity controller which handles the actual redirect
  # format:false allows us to recognize paths like '/this/old/page.htm
  match "/*vanity", to: 'vanity#routing', constraints: RoutingConstraints::VanityUrls.new, format: false
end

This last line is catch-most because it will only match if the constraint defined in RoutingConstraints::VanityUrls is hit. And with format: false is telling the router to ignore the any extension (as the comment says – so we can match page.htm if need be).

The RoutingConstraints::VanityUrls object needs to have a #matches? method (more in the Rails Routing Docs). So we can add the following:

# in app/lib/routing_constraints.rb
module RoutingConstraints
  class VanityUrls
    def matches?(request)
      routes = VanityUrl.routes
      vanity_url = request.params['vanity']
      if routes.has_key? vanity_url
        begin
          URI.parse(routes[vanity_url])
          return true
        rescue URI::InvalidURIError
          # vanity url is no good
          return false
        end
      end
      false
    end
  end
end

Notice here, we’re using the VanityUrl::routes method. Now, as routes are processed by Rails, *IF* they don’t match any of our normal routes *AND* they match one of our VanityURLs by path, *THEN* the router will pass them off to the VanityController#routing method which we told it to do in the routes.rb file.

Let’s add that method:

class VanityController
  def routing
    vanity_routes = VanityUrl.routes
    vanity_url = params['vanity']
    if vanity_routes.has_key? vanity_url
      redirect_to URI.parse(vanity_routes[vanity_url]).to_s and return
    end    
    raise "Unable to find requested VanityUrl for #{params}"
  end
end

Notice, this is not the VanityUrlController which you’ve written to handle VanityUrls in the database. We make a new controller which will only have this one method to handle the rerouting of requests.

That’s basically it. If you’re app gets a lot of traffic, you’ll probably want to cache the VanityUrl collection (since it should be read-often and write-occasionally). Without a cache, every request is doing a full table retrieval of the VanityUrl table.

Initially, we added this feature to support true VanityUrls for a client e.g. http://example.com/party, http://example.com/temporary_coupon etc. And for this purpose, it works great. But it also saved our butts in a way we hadn’t forseen. The application we built was replacing an existing site almost entirely. When we actually launched, we starting getting piles of 404’s. The bits of the old site that were still up were asking for resources, images, stylesheets etc that were not duplicated in the new site. With a handful of VanityUrls, we were able to point those misdirected requests to the old assets (or wherever they needed to go) without modifying code or adding Rack::Rewrite directives. It made launch day a relative breeze.