Daily Code Reading #8 – Rack::GoogleAnalytics

Today I’m reading the code for Rack::GoogleAnalytics. It’s rack middleware I found on coderack.org that will automatically insert the Google Analytics tracking code into every page.

The Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
module Rack #:nodoc:
  class GoogleAnalytics < Struct.new :app, :options
 
    def call(env)
      status, headers, response = app.call(env)
 
      if headers["Content-Type"] =~ /text\/html|application\/xhtml\+xml/
        body = ""
        response.each { |part| body << part }
        index = body.rindex("")
        if index
          body.insert(index, tracking_code(options[:web_property_id]))
          headers["Content-Length"] = body.length.to_s
          response = [body]
        end
      end
 
      [status, headers, response]
    end
 
    protected
 
      # Returns JS to be embeded. This takes one argument, a Web Property ID
      # (aka UA number).
      def tracking_code(web_property_id)
        returning_value = <<-EOF
 
if (typeof gaJsHost == 'undefined') {
  var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
  document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
}
 
 
try {
var #{options[:prefix]}pageTracker = _gat._getTracker("#{web_property_id}");
EOF
        if options[:multiple_top_level_domains]
          returning_value << <<-EOF          
#{options[:prefix]}pageTracker._setDomainName("none");          
#{options[:prefix]}pageTracker._setAllowLinker(true);          
EOF
        elsif options[:domain_name]
                returning_value << <<-EOF          
      #{options[:prefix]}pageTracker._setDomainName("#{options[:domain_name]}");          
      EOF
        end
 
        returning_value << <<-EOF
#{options[:prefix]}pageTracker._trackPageview();
} catch(err) {}
EOF
      returning_value
      end
  end
end

Review

Rack::GoogleAnalytics does a few interesting things:

Struct

Instead of using an initialize method like Rack::MemoryBloat did yesterday, it’s using a new Struct class.

Generating tracking code

The protected #tracking_code method is responsible for generating the Google Analytics Javascript. There isn’t much going on here, just some string substitution and an if statement for the different domain options.

Inserting tracking code

How Rack::GoogleAnalytics inserts the tracking code is interesting. It’s running the rest of the application stack and then if the content type is HTML, it adds the tracking code to the string right before the closing body (using String#rindex). This line looks odd to me though:

1
response.each { |part| body << part }

I think this is collecting all of the response’s parts into a single string (body). I’m not sure how Rack works internally, but if it tries to send data to the client in chunks this code might block. At the very least, it will be creating a new string for every response that is the same size as the response body. I wonder if it’s possible to just do a string replacement in place.