├── .gitignore ├── Gemfile ├── LICENSE ├── README.rdoc ├── Rakefile ├── lib ├── rack-google_analytics.rb └── rack │ └── google_analytics.rb ├── rack-google_analytics.gemspec └── test └── rack └── google_analytics_test.rb /.gitignore: -------------------------------------------------------------------------------- 1 | .bundle 2 | Gemfile.lock 3 | pkg/* 4 | rdoc/* 5 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source :rubygems 2 | gemspec 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 Jason L Perry 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = Rack::GoogleAnalytics 2 | 3 | Rack middleware to embed Google Analytics tracking code. 4 | 5 | == Usage 6 | 7 | require "rack/google_analytics" 8 | 9 | use Rack::GoogleAnalytics, :web_property_id => "UA-000000-1" 10 | 11 | app = lambda { |env| [200, { 'Content-Type' => 'text/html' }, ''] } 12 | run app 13 | 14 | == Configuring for a Rails App 15 | 16 | In your `Gemfile`: 17 | 18 | gem 'rack-google_analytics' 19 | 20 | In your `config/application.rb` 21 | 22 | config.middleware.use "Rack::GoogleAnalytics", :web_property_id => "UA-0000000-1" 23 | 24 | == TODO and Motivations 25 | 26 | This isn't very "feature rich", because I've mostly written it as an 27 | excuse to learn more about rack. Hell, it's possible someone has already done 28 | it (and better); I didn't bother to look. See the notes below on contributing 29 | fixes for these deficiencies. 30 | 31 | * Support for the various tracking options (http://code.google.com/apis/analytics/docs/gaJS/gaJSApi.html) 32 | * Legacy tracking code 33 | 34 | == Note on Patches/Pull Requests 35 | 36 | * Fork the project. 37 | * Make your feature addition or bug fix. 38 | * Add tests for it. This is important so I don't break it in a 39 | future version unintentionally. 40 | * Commit, do not mess with rakefile, version, or history. 41 | (if you want to have your own version, that is fine but 42 | bump version in a commit by itself I can ignore when I pull) 43 | * Send me a pull request. Bonus points for topic branches. 44 | 45 | == Copyright 46 | 47 | Copyright (c) 2011 Jason L Perry. See LICENSE for details. 48 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler/gem_tasks' 2 | 3 | require 'rake/testtask' 4 | Rake::TestTask.new(:test) do |test| 5 | test.libs << 'lib' << 'test' 6 | test.pattern = 'test/**/*_test.rb' 7 | test.verbose = true 8 | end 9 | 10 | task :default => :test 11 | -------------------------------------------------------------------------------- /lib/rack-google_analytics.rb: -------------------------------------------------------------------------------- 1 | require 'rack/google_analytics' 2 | -------------------------------------------------------------------------------- /lib/rack/google_analytics.rb: -------------------------------------------------------------------------------- 1 | module Rack #:nodoc: 2 | class GoogleAnalytics < Struct.new :app, :options 3 | 4 | def call(env) 5 | status, headers, response = app.call(env) 6 | 7 | if headers["Content-Type"] =~ /text\/html|application\/xhtml\+xml/ 8 | body = "" 9 | response.each { |part| body << part } 10 | index = body.rindex("") 11 | if index 12 | body.insert(index, tracking_code(options[:web_property_id])) 13 | headers["Content-Length"] = body.length.to_s 14 | response = [body] 15 | end 16 | end 17 | 18 | [status, headers, response] 19 | end 20 | 21 | protected 22 | 23 | # Returns JS to be embeded. This takes one argument, a Web Property ID 24 | # (aka UA number). 25 | def tracking_code(web_property_id) 26 | returning_value = <<-EOF 27 | 33 | 51 | EOF 52 | returning_value 53 | end 54 | end 55 | end -------------------------------------------------------------------------------- /rack-google_analytics.gemspec: -------------------------------------------------------------------------------- 1 | Gem::Specification.new do |spec| 2 | spec.name = 'rack-google_analytics' 3 | spec.version = '1.0.2' 4 | spec.authors = ['Jason L Perry'] 5 | spec.date = '2011-08-18' 6 | spec.summary = 'Google Analytics for Rack applications' 7 | spec.description = 'Embeds Google Analytics tracking code in the bottom of HTML documents' 8 | spec.email = 'jasper@ambethia.com' 9 | spec.homepage = 'http://github.com/ambethia/rack-google_analytics' 10 | 11 | spec.extra_rdoc_files = [ 12 | "LICENSE", 13 | "README.rdoc" 14 | ] 15 | spec.files = [ 16 | "README.rdoc", 17 | "lib/rack-google_analytics.rb", 18 | "lib/rack/google_analytics.rb" 19 | ] 20 | spec.require_paths = %w[lib] 21 | spec.test_files = %w[test/rack/google_analytics_test.rb] 22 | 23 | spec.add_dependency 'rack' 24 | end 25 | -------------------------------------------------------------------------------- /test/rack/google_analytics_test.rb: -------------------------------------------------------------------------------- 1 | require 'test/unit' 2 | require 'rack/mock' 3 | 4 | require 'rack/google_analytics' 5 | 6 | class Rack::GoogleAnalyticsTest < Test::Unit::TestCase 7 | 8 | def test_embed_tracking_code_at_the_end_of_html_body 9 | assert_match TRACKER_EXPECT, request.body 10 | end 11 | 12 | def test_embed_tracking_code_in_xhtml_documents 13 | assert_match TRACKER_EXPECT, request(:content_type => "application/xhtml+xml").body 14 | end 15 | 16 | def test_dont_embed_code_in_non_html_documents 17 | assert_no_match TRACKER_EXPECT, request(:content_type => "text/xml", :body => [XML_DOC]).body 18 | end 19 | 20 | def test_should_not_raise_exception_if_theres_no_html_body_tag 21 | assert_nothing_raised { request(:body => [""]) } 22 | end 23 | 24 | def test_should_buff_content_length_by_the_size_of_tracker_code 25 | request do |app, req| 26 | assert_equal HTML_DOC.length + app.send(:tracking_code, WEB_PROPERTY_ID).length, req.content_length 27 | end 28 | end 29 | 30 | def test_should_include_pageTracker_definition 31 | assert_match( /#{Regexp.escape('var pageTracker = _gat.')}/, request.body) 32 | end 33 | 34 | def test_should_append_prefix_to_pageTracker_definition 35 | assert_match( /#{Regexp.escape('var conductor_pageTracker = _gat.')}/, request(:prefix => 'conductor_').body) 36 | end 37 | 38 | def test_should_allow_multiple_top_level_domains 39 | assert_match( /#{Regexp.escape('pageTracker._setDomainName("none")')}/, request(:multiple_top_level_domains => true).body) 40 | assert_match( /#{Regexp.escape('pageTracker._setAllowLinker(true)')}/, request(:multiple_top_level_domains => true).body) 41 | end 42 | 43 | def test_multiple_top_level_domains_should_supercede_domain_name 44 | request(:multiple_top_level_domains => true, :domain_name => '.test.com') do |app, req| 45 | assert_match( /#{Regexp.escape('pageTracker._setDomainName("none")')}/, req.body) 46 | assert_no_match( /#{Regexp.escape('pageTracker._setDomainName(".test.com")')}/, req.body) 47 | end 48 | end 49 | 50 | def test_should_allow_domain_name 51 | assert_match( /#{Regexp.escape('pageTracker._setDomainName(".test.com")')}/, request(:domain_name => '.test.com').body) 52 | end 53 | 54 | private 55 | WEB_PROPERTY_ID = "UA-0000000-1" 56 | 57 | TRACKER_EXPECT = /\s?<\/body>/m 58 | 59 | HTML_DOC = <<-EOF 60 | 61 | 62 | Rack::GoogleAnalytics 63 | 64 | 65 |

Rack::GoogleAnalytics

66 | 67 | 68 | EOF 69 | 70 | XML_DOC = <<-EOF 71 | 72 | 73 | Old Pond 74 | Matsuo Basho 75 | an ancient pond / a frog jumps in / the splash of water 76 | 77 | EOF 78 | 79 | def request(opts = {}) 80 | @application = app(opts) 81 | @request = Rack::MockRequest.new(@application).get("/") 82 | yield(@application, @request) if block_given? 83 | @request 84 | end 85 | 86 | def app(opts = {}) 87 | opts = opts.clone 88 | opts[:content_type] ||= "text/html" 89 | opts[:body] ||= [HTML_DOC] 90 | rack_app = lambda { |env| [200, { 'Content-Type' => opts.delete(:content_type) }, opts.delete(:body)] } 91 | opts[:web_property_id] ||= WEB_PROPERTY_ID 92 | Rack::GoogleAnalytics.new(rack_app, opts) 93 | end 94 | 95 | end 96 | --------------------------------------------------------------------------------