diff --git a/README.rdoc b/README.rdoc index 521a72f..c3b5a1f 100644 --- a/README.rdoc +++ b/README.rdoc @@ -23,6 +23,7 @@ Service implementations include: * Quantcast[http://www.quantcast.com] * MixPanel[http://www.mixpanel.com] * Gauges[http://get.gaug.es] +* Segment.io[http://segment.io] == Usage @@ -40,7 +41,7 @@ Add a configuration file (config/analytical.yml) to declare your API keys, like development: test: -You can add different configurations for different environments. +You can add different configurations for different environments. By default, all the declared analytics modules are loaded. You can override this behavior in the controller. @@ -91,6 +92,29 @@ This can be useful for enabling the console logger optionally in your app, based controller.use_console_logger? ? [:console] : modules } +You can also configure modules in the controller by passing a hash to the :modules option + + analytical :modules => { :google => { key: 'UA-5555555-5' } } + +Finally it is also possible to dynamically specify what modules to use and their configurations, which can come in handy if your site uses multi-tenancy, where every tenant might not wish to use the same modules or configurations for each module. + + analytical :modules => lambda{ |controller| controller.analytical_modules } + +In this case we would then implement a method in the controller that could look like this: + + def analytical_modules + case current_tenant.name.parameterize + when "site-1" + { + :google => { key: 'UA-5555555-5' } + } + when "site-2" + { + :google => { key: 'UA-1111111-1' } + } + end + end + hide_action :analytical_modules == Adding new modules @@ -151,6 +175,7 @@ These fine folks have contributed patches and new features to this project: * {Kurt Werle}[http://github.com/kwerle] * {Olivier Lauzon}[http://github.com/olauzon] * {Chris Ricca}[https://github.com/ChrisRicca] +* {Thomas Dippel}[https://github.com/dipth] Thanks guys! diff --git a/lib/analytical.rb b/lib/analytical.rb index 694e08d..e8c8632 100644 --- a/lib/analytical.rb +++ b/lib/analytical.rb @@ -36,6 +36,15 @@ def analytical :ssl => request.ssl?, :controller => self, }) + if options[:modules] && (options[:modules].is_a?(Hash) || options[:modules].is_a?(Proc)) + items = options[:modules].is_a?(Hash) ? options[:modules] : options[:modules].call(self) + modules = [] + items.each do |k,v| + options[k.to_sym] = v.symbolize_keys + modules << k.to_sym + end + options[:modules] = modules + end if options[:disable_if] && options[:disable_if].call(self) options[:modules] = [] end diff --git a/lib/analytical/modules/segment_io.rb b/lib/analytical/modules/segment_io.rb new file mode 100644 index 0000000..600138d --- /dev/null +++ b/lib/analytical/modules/segment_io.rb @@ -0,0 +1,41 @@ +module Analytical + module Modules + class SegmentIo + include Analytical::Modules::Base + + def initialize(options={}) + super + @tracking_command_location = :head_prepend + end + + def init_javascript(location) + init_location(location) do + js = <<-HTML + + + HTML + js + end + end + + def track(*args) + if args.any? + %(window.analytics.pageview("#{args.first}");) + else + %(window.analytics.pageview();) + end + end + + def identify(id, attributes = {}) + %(window.analytics.identify("#{id}", #{attributes.to_json});) + end + + def event(name, attributes = {}) + %(window.analytics.track("#{name}", #{attributes.to_json});) + end + + end + end +end diff --git a/spec/analytical/modules/segment_io_spec.rb b/spec/analytical/modules/segment_io_spec.rb new file mode 100644 index 0000000..042bdca --- /dev/null +++ b/spec/analytical/modules/segment_io_spec.rb @@ -0,0 +1,61 @@ +require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') + +describe Analytical::Modules::SegmentIo do + before(:each) do + @parent = mock('api', :options=>{:segment_io=>{:key=>'abc'}}) + end + describe 'on initialize' do + it 'should set the command_location' do + a = described_class.new :parent=>@parent, :key=>'abc' + a.tracking_command_location.should eq :head_prepend + end + it 'should set the options' do + a = described_class.new :parent=>@parent, :key=>'abc' + a.options.should eq({:key=>'abc', :parent=>@parent}) + end + end + describe '#track' do + it 'should return the pageview javascript' do + @api = described_class.new :parent=>@parent, :key=>'abcdef' + @api.track.should eq "window.analytics.pageview();" + @api.track('pagename', {:some=>'data'}).should eq "window.analytics.pageview(\"pagename\");" + end + end + describe '#event' do + it 'should return the track javascript' do + @api = described_class.new :parent=>@parent, :key=>'abcdef' + @api.event('pagename').should eq "window.analytics.track(\"pagename\", {});" + end + + it 'should include data value' do + @api = described_class.new :parent=>@parent, :key=>'abcdef' + @api.event('pagename', {:value=>555, :more=>'info'}).should eq "window.analytics.track(\"pagename\", {\"value\":555,\"more\":\"info\"});" + end + it 'should not include data if there is no value' do + @api = described_class.new :parent=>@parent, :key=>'abcdef' + @api.event('pagename', {:more=>'info'}).should eq "window.analytics.track(\"pagename\", {\"more\":\"info\"});" + end + it 'should not include data if it is not a hash' do + @api = described_class.new :parent=>@parent, :key=>'abcdef' + @api.event('pagename', 555).should eq "window.analytics.track(\"pagename\", 555);" + end + end + describe '#identify' do + it 'should return the identify javascript' do + @api = described_class.new :parent=>@parent, :key=>'abcdef' + @api.identify('id', {:email=>'test@test.com'}).should == "window.analytics.identify(\"id\", {\"email\":\"test@test.com\"});" + end + end + + describe '#init_javascript' do + it 'should return the init javascript' do + @api = described_class.new :parent=>@parent, :key=>'abcdef' + @api.init_javascript(:head_append).should eq '' + @api.init_javascript(:head_prepend).should =~ /window.analytics=/ + @api.init_javascript(:head_prepend).should =~ /abcdef/ + @api.init_javascript(:head_prepend).should =~ /analytics.js\/v1/ + @api.init_javascript(:body_prepend).should eq '' + @api.init_javascript(:body_append).should eq '' + end + end +end diff --git a/spec/analytical_spec.rb b/spec/analytical_spec.rb index 29aedfa..9ebcf71 100644 --- a/spec/analytical_spec.rb +++ b/spec/analytical_spec.rb @@ -15,8 +15,8 @@ class DummyForInit def self.helper_method(*a); end def request - RSpec::Mocks::Mock.new 'request', - :'ssl?'=>true, + RSpec::Mocks::Mock.new 'request', + :'ssl?'=>true, :user_agent=>'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 GTB7.0' end end @@ -30,6 +30,27 @@ def request d.options[:string_option].should == "string" end + it 'should load module configurations from hash' do + options = { :modules => { :console => { :some_key => 'rubberduck' } }} + DummyForInit.analytical options + a = DummyForInit.new.analytical + a.options[:modules].should include(:console) + a.options[:console].should eq({ :some_key => 'rubberduck' }) + end + + it 'should load module configurations from a lambda' do + class DummyForInit + def analytical_modules + { :console => { :some_key => 'rubberduck' } } + end + end + options = { :modules => lambda { |controller| controller.analytical_modules }} + DummyForInit.analytical options + a = DummyForInit.new.analytical + a.options[:modules].should include(:console) + a.options[:console].should eq({ :some_key => 'rubberduck' }) + end + it 'should preserve :javascript_helpers option' do options = { :javascript_helpers => false, :modules => [] } DummyForInit.analytical options @@ -42,12 +63,12 @@ def request d = DummyForInit.new.analytical d.options[:modules].should == [:google] end - + describe 'conditionally disabled' do it 'should set the modules to []' do DummyForInit.analytical :disable_if => lambda { |x| true } d = DummyForInit.new - d.analytical.options[:modules].should == [] + d.analytical.options[:modules].should == [] end end