From a36577e1483e11add2915d7320dacdfe597be1bf Mon Sep 17 00:00:00 2001 From: Szymon Madeja Date: Wed, 17 Jan 2018 16:07:23 +0100 Subject: [PATCH 01/54] Allow for dynamic Facebook Pixel options --- .../tracker/facebook_pixel/facebook_pixel.rb | 10 ++++++++++ .../facebook_pixel/template/facebook_pixel.erb | 4 ++-- spec/handler/facebook_pixel_spec.rb | 16 ++++++++++++++-- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/lib/rack/tracker/facebook_pixel/facebook_pixel.rb b/lib/rack/tracker/facebook_pixel/facebook_pixel.rb index 1acf5d5..98fbf89 100644 --- a/lib/rack/tracker/facebook_pixel/facebook_pixel.rb +++ b/lib/rack/tracker/facebook_pixel/facebook_pixel.rb @@ -1,6 +1,16 @@ class Rack::Tracker::FacebookPixel < Rack::Tracker::Handler self.position = :body + def tracker_options + @_tracker_options ||= {}.tap do |tracker_options| + options.each do |key, value| + if option_value = value.respond_to?(:call) ? value.call(env) : value + tracker_options[key] = option_value + end + end + end + end + class Event < OpenStruct def write options.present? ? type_to_json << options_to_json : type_to_json diff --git a/lib/rack/tracker/facebook_pixel/template/facebook_pixel.erb b/lib/rack/tracker/facebook_pixel/template/facebook_pixel.erb index 4209e21..2901c52 100644 --- a/lib/rack/tracker/facebook_pixel/template/facebook_pixel.erb +++ b/lib/rack/tracker/facebook_pixel/template/facebook_pixel.erb @@ -7,12 +7,12 @@ t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window, document,'script','//connect.facebook.net/en_US/fbevents.js'); - fbq('init', '<%= options[:id] %>'); + fbq('init', '<%= tracker_options[:id] %>'); fbq('track', "PageView"); } <% if events.any? %> diff --git a/spec/handler/facebook_pixel_spec.rb b/spec/handler/facebook_pixel_spec.rb index 7f70bee..9716529 100644 --- a/spec/handler/facebook_pixel_spec.rb +++ b/spec/handler/facebook_pixel_spec.rb @@ -1,6 +1,6 @@ RSpec.describe Rack::Tracker::FacebookPixel do def env - {} + { 'PIXEL_ID' => 'DYNAMIC_PIXEL_ID' } end it 'will be placed in the body' do @@ -8,7 +8,7 @@ def env expect(described_class.new(env).position).to eq(:body) end - describe 'with id' do + describe 'with static id' do subject { described_class.new(env, id: 'PIXEL_ID').render } it 'will push the tracking events to the queue' do @@ -20,6 +20,18 @@ def env end end + describe 'with dynamic id' do + subject { described_class.new(env, id: lambda { |env| env['PIXEL_ID'] }).render } + + it 'will push the tracking events to the queue' do + expect(subject).to match(%r{fbq\('init', 'DYNAMIC_PIXEL_ID'\)}) + end + + it 'will add the noscript fallback' do + expect(subject).to match(%r{https://www.facebook.com/tr\?id=DYNAMIC_PIXEL_ID&ev=PageView&noscript=1}) + end + end + describe 'with events' do def env { From 177e7de2eb7e6cb2eef08f2e8efd67534099d7f8 Mon Sep 17 00:00:00 2001 From: Szymon Madeja Date: Thu, 18 Jan 2018 10:22:57 +0100 Subject: [PATCH 02/54] Move `#tracker_options` to the parent class * Require `ALLOWED_TRACKER_OPTIONS` to be defined on all classes that use it * Extract key, and value transformations into separate methods that can be overriden in descendants --- .../tracker/facebook_pixel/facebook_pixel.rb | 10 +------- .../google_analytics/google_analytics.rb | 16 +++++-------- lib/rack/tracker/handler.rb | 24 +++++++++++++++++++ 3 files changed, 31 insertions(+), 19 deletions(-) diff --git a/lib/rack/tracker/facebook_pixel/facebook_pixel.rb b/lib/rack/tracker/facebook_pixel/facebook_pixel.rb index 98fbf89..341ab2f 100644 --- a/lib/rack/tracker/facebook_pixel/facebook_pixel.rb +++ b/lib/rack/tracker/facebook_pixel/facebook_pixel.rb @@ -1,15 +1,7 @@ class Rack::Tracker::FacebookPixel < Rack::Tracker::Handler self.position = :body - def tracker_options - @_tracker_options ||= {}.tap do |tracker_options| - options.each do |key, value| - if option_value = value.respond_to?(:call) ? value.call(env) : value - tracker_options[key] = option_value - end - end - end - end + ALLOWED_TRACKER_OPTIONS = [:id] class Event < OpenStruct def write diff --git a/lib/rack/tracker/google_analytics/google_analytics.rb b/lib/rack/tracker/google_analytics/google_analytics.rb index d490d20..7db883b 100644 --- a/lib/rack/tracker/google_analytics/google_analytics.rb +++ b/lib/rack/tracker/google_analytics/google_analytics.rb @@ -57,16 +57,6 @@ def tracker options[:tracker].respond_to?(:call) ? options[:tracker].call(env) : options[:tracker] end - def tracker_options - @tracker_options ||= {}.tap do |tracker_options| - options.slice(*ALLOWED_TRACKER_OPTIONS).each do |key, value| - if option_value = value.respond_to?(:call) ? value.call(env) : value - tracker_options[key.to_s.camelize(:lower).to_sym] = option_value.to_s - end - end - end - end - def ecommerce_events events.select {|e| e.kind_of?(Ecommerce) } end @@ -74,4 +64,10 @@ def ecommerce_events def enhanced_ecommerce_events events.select {|e| e.kind_of?(EnhancedEcommerce) } end + + private + + def tracker_option_key(key) + key.to_s.camelize(:lower).to_sym + end end diff --git a/lib/rack/tracker/handler.rb b/lib/rack/tracker/handler.rb index 7a0aa80..2f88852 100644 --- a/lib/rack/tracker/handler.rb +++ b/lib/rack/tracker/handler.rb @@ -56,4 +56,28 @@ def write_event(event) def handler_name self.class.name.demodulize.underscore end + + def tracker_options + @_tracker_options ||= {}.tap do |tracker_options| + options.slice(*self.class::ALLOWED_TRACKER_OPTIONS).each do |key, value| + if option_value = value.respond_to?(:call) ? value.call(env) : value + tracker_options[tracker_option_key(key)] = tracker_option_value(option_value) + end + end + end + end + + private + + # Transformations to be applied to tracker option keys. + # Override in descendants, if necessary. + def tracker_option_key(key) + key.to_sym + end + + # Transformations to be applied to tracker option values. + # Override in descendants, if necessary. + def tracker_option_value(value) + value.to_s + end end From b300ddc7ecafc80bd4e1c351febb759d701874da Mon Sep 17 00:00:00 2001 From: Szymon Madeja Date: Thu, 18 Jan 2018 10:51:56 +0100 Subject: [PATCH 03/54] Fix a typo --- spec/handler/google_analytics_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/handler/google_analytics_spec.rb b/spec/handler/google_analytics_spec.rb index a7cbc8f..02d98e2 100644 --- a/spec/handler/google_analytics_spec.rb +++ b/spec/handler/google_analytics_spec.rb @@ -192,7 +192,7 @@ def env describe "with custom domain" do subject { described_class.new(env, tracker: 'somebody', cookie_domain: "railslabs.com").render } - it "will show asyncronous tracker with cookieDomain" do + it "will show asynchronous tracker with cookieDomain" do expect(subject).to match(%r{ga\('create', 'somebody', {\"cookieDomain\":\"railslabs.com\"}\)}) expect(subject).to match(%r{ga\('send', 'pageview', window\.location\.pathname \+ window\.location\.search\)}) end @@ -201,7 +201,7 @@ def env describe "with user_id tracking" do subject { described_class.new(env, tracker: 'somebody', user_id: lambda { |env| return env[:user_id] } ).render } - it "will show asyncronous tracker with userId" do + it "will show asynchronous tracker with userId" do expect(subject).to match(%r{ga\('create', 'somebody', {\"userId\":\"123\"}\)}) expect(subject).to match(%r{ga\('send', 'pageview', window\.location\.pathname \+ window\.location\.search\)}) end From b70886d396dafbde3db3934c45187b01461b3733 Mon Sep 17 00:00:00 2001 From: Szymon Madeja Date: Fri, 19 Jan 2018 11:49:34 +0100 Subject: [PATCH 04/54] Remove the default transformation of tracker option values Put it in the Google Analytics tracker. --- lib/rack/tracker/google_analytics/google_analytics.rb | 4 ++++ lib/rack/tracker/handler.rb | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/rack/tracker/google_analytics/google_analytics.rb b/lib/rack/tracker/google_analytics/google_analytics.rb index 7db883b..20af077 100644 --- a/lib/rack/tracker/google_analytics/google_analytics.rb +++ b/lib/rack/tracker/google_analytics/google_analytics.rb @@ -70,4 +70,8 @@ def enhanced_ecommerce_events def tracker_option_key(key) key.to_s.camelize(:lower).to_sym end + + def tracker_option_value(value) + value.to_s + end end diff --git a/lib/rack/tracker/handler.rb b/lib/rack/tracker/handler.rb index 2f88852..c9ce356 100644 --- a/lib/rack/tracker/handler.rb +++ b/lib/rack/tracker/handler.rb @@ -78,6 +78,6 @@ def tracker_option_key(key) # Transformations to be applied to tracker option values. # Override in descendants, if necessary. def tracker_option_value(value) - value.to_s + value end end From 946d98a90efe736be2d059cb0931e9921cb1efdc Mon Sep 17 00:00:00 2001 From: Szymon Madeja Date: Fri, 19 Jan 2018 12:23:20 +0100 Subject: [PATCH 05/54] Add some specs for `Rack::Tracker::Handler#tracker_options` Base them off of the Google Analytics specs. --- spec/handler/handler_spec.rb | 43 ++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 spec/handler/handler_spec.rb diff --git a/spec/handler/handler_spec.rb b/spec/handler/handler_spec.rb new file mode 100644 index 0000000..35d13df --- /dev/null +++ b/spec/handler/handler_spec.rb @@ -0,0 +1,43 @@ +RSpec.describe Rack::Tracker::Handler do + def env + { misc: 'foobar' } + end + + describe '#tracker_options' do + before do + stub_const("#{described_class}::ALLOWED_TRACKER_OPTIONS", [:some_option]) + end + + context 'with an allowed option configured with a static value' do + subject { described_class.new(env, { some_option: 'value' }) } + + it 'returns hash with option set' do + expect(subject.tracker_options).to eql ({ some_option: 'value' }) + end + end + + context 'with an allowed option configured with a block' do + subject { described_class.new(env, { some_option: lambda { |env| return env[:misc] } }) } + + it 'returns hash with option set' do + expect(subject.tracker_options).to eql ({ some_option: 'foobar' }) + end + end + + context 'with an allowed option configured with a block returning nil' do + subject { described_class.new(env, { some_option: lambda { |env| return env[:non_existing_key] } }) } + + it 'returns an empty hash' do + expect(subject.tracker_options).to eql ({}) + end + end + + context 'with a non allowed option' do + subject { described_class.new(env, { new_option: 'value' }) } + + it 'returns an empty hash' do + expect(subject.tracker_options).to eql ({}) + end + end + end +end From 73fe6b8010f5a7f571f66fcd338a977e4ac8089d Mon Sep 17 00:00:00 2001 From: Szymon Madeja Date: Mon, 22 Jan 2018 16:21:23 +0100 Subject: [PATCH 06/54] Replace constants with class attributes * Define the :allowed_tracker_options attribute on the Handler class, and set it to an empty array by default. * Use the new class attribute in #tracker_options. * Remove allowed option stubbing from Google Analytics specs. Use a real option instead. * Redo the #tracker_options specs. --- .../tracker/facebook_pixel/facebook_pixel.rb | 3 +- .../google_analytics/google_analytics.rb | 2 +- lib/rack/tracker/handler.rb | 5 ++- spec/handler/google_analytics_spec.rb | 14 +++--- spec/handler/handler_spec.rb | 43 ++++++++----------- 5 files changed, 30 insertions(+), 37 deletions(-) diff --git a/lib/rack/tracker/facebook_pixel/facebook_pixel.rb b/lib/rack/tracker/facebook_pixel/facebook_pixel.rb index 341ab2f..f6015dd 100644 --- a/lib/rack/tracker/facebook_pixel/facebook_pixel.rb +++ b/lib/rack/tracker/facebook_pixel/facebook_pixel.rb @@ -1,7 +1,6 @@ class Rack::Tracker::FacebookPixel < Rack::Tracker::Handler self.position = :body - - ALLOWED_TRACKER_OPTIONS = [:id] + self.allowed_tracker_options = [:id] class Event < OpenStruct def write diff --git a/lib/rack/tracker/google_analytics/google_analytics.rb b/lib/rack/tracker/google_analytics/google_analytics.rb index 20af077..a359cc1 100644 --- a/lib/rack/tracker/google_analytics/google_analytics.rb +++ b/lib/rack/tracker/google_analytics/google_analytics.rb @@ -1,6 +1,6 @@ class Rack::Tracker::GoogleAnalytics < Rack::Tracker::Handler - ALLOWED_TRACKER_OPTIONS = [:cookie_domain, :user_id] + self.allowed_tracker_options = [:cookie_domain, :user_id] class Send < OpenStruct def initialize(attrs = {}) diff --git a/lib/rack/tracker/handler.rb b/lib/rack/tracker/handler.rb index c9ce356..f76832e 100644 --- a/lib/rack/tracker/handler.rb +++ b/lib/rack/tracker/handler.rb @@ -13,6 +13,9 @@ def track(name, *event) class_attribute :position self.position = :head + class_attribute :allowed_tracker_options + self.allowed_tracker_options = [] + attr_accessor :options attr_accessor :env @@ -59,7 +62,7 @@ def handler_name def tracker_options @_tracker_options ||= {}.tap do |tracker_options| - options.slice(*self.class::ALLOWED_TRACKER_OPTIONS).each do |key, value| + options.slice(*allowed_tracker_options).each do |key, value| if option_value = value.respond_to?(:call) ? value.call(env) : value tracker_options[tracker_option_key(key)] = tracker_option_value(option_value) end diff --git a/spec/handler/google_analytics_spec.rb b/spec/handler/google_analytics_spec.rb index a7cbc8f..93ce439 100644 --- a/spec/handler/google_analytics_spec.rb +++ b/spec/handler/google_analytics_spec.rb @@ -53,28 +53,24 @@ def env end describe '#tracker_options' do - before do - stub_const("#{described_class}::ALLOWED_TRACKER_OPTIONS", [:some_option]) - end - context 'with an allowed option configured with a static value' do - subject { described_class.new(env, { some_option: 'value' }) } + subject { described_class.new(env, { user_id: 'value' }) } it 'returns hash with option set' do - expect(subject.tracker_options).to eql ({ someOption: 'value' }) + expect(subject.tracker_options).to eql ({ userId: 'value' }) end end context 'with an allowed option configured with a block' do - subject { described_class.new(env, { some_option: lambda { |env| return env[:misc] } }) } + subject { described_class.new(env, { user_id: lambda { |env| return env[:misc] } }) } it 'returns hash with option set' do - expect(subject.tracker_options).to eql ({ someOption: 'foobar' }) + expect(subject.tracker_options).to eql ({ userId: 'foobar' }) end end context 'with an allowed option configured with a block returning nil' do - subject { described_class.new(env, { some_option: lambda { |env| return env[:non_existing_key] } }) } + subject { described_class.new(env, { user_id: lambda { |env| return env[:non_existing_key] } }) } it 'returns an empty hash' do expect(subject.tracker_options).to eql ({}) diff --git a/spec/handler/handler_spec.rb b/spec/handler/handler_spec.rb index 35d13df..2fabeff 100644 --- a/spec/handler/handler_spec.rb +++ b/spec/handler/handler_spec.rb @@ -4,39 +4,34 @@ def env end describe '#tracker_options' do - before do - stub_const("#{described_class}::ALLOWED_TRACKER_OPTIONS", [:some_option]) - end - - context 'with an allowed option configured with a static value' do + context 'without overriding allowed_tracker_options' do subject { described_class.new(env, { some_option: 'value' }) } - it 'returns hash with option set' do - expect(subject.tracker_options).to eql ({ some_option: 'value' }) + it 'returns an empty hash' do + expect(subject.tracker_options).to eql ({}) end end - context 'with an allowed option configured with a block' do - subject { described_class.new(env, { some_option: lambda { |env| return env[:misc] } }) } - - it 'returns hash with option set' do - expect(subject.tracker_options).to eql ({ some_option: 'foobar' }) - end - end + context 'with overridden allowed_tracker_options' do + subject do + handler = described_class.new(env, { + static_option: 'value', + dynamic_option: lambda { |env| return env[:misc] }, + dynamic_nil_option: lambda { |env| return env[:non_existent_key] }, + non_allowed_option: 'value' + }) - context 'with an allowed option configured with a block returning nil' do - subject { described_class.new(env, { some_option: lambda { |env| return env[:non_existing_key] } }) } + handler.allowed_tracker_options = + [:static_option, :dynamic_option, :dynamic_nil_option] - it 'returns an empty hash' do - expect(subject.tracker_options).to eql ({}) + handler end - end - context 'with a non allowed option' do - subject { described_class.new(env, { new_option: 'value' }) } - - it 'returns an empty hash' do - expect(subject.tracker_options).to eql ({}) + it 'evaluates dynamic options, rejecting nonallowed and nil ones' do + expect(subject.tracker_options).to eql ({ + static_option: 'value', + dynamic_option: 'foobar' + }) end end end From 1fae441a4b4f94e5cd291f7a92a6823617f81cc1 Mon Sep 17 00:00:00 2001 From: Suraj Vadgama Date: Mon, 5 Feb 2018 16:02:08 +0000 Subject: [PATCH 07/54] Add option to pass Google Optimize container ID to Google Analytics --- README.md | 1 + .../google_analytics/template/google_analytics.erb | 3 +++ spec/handler/google_analytics_spec.rb | 8 ++++++++ 3 files changed, 12 insertions(+) diff --git a/README.md b/README.md index 1550a64..4902569 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,7 @@ request.env['tracker'] = { * `:advertising` - Enables [Display Features](https://developers.google.com/analytics/devguides/collection/analyticsjs/display-features). * `:ecommerce` - Enables [Ecommerce Tracking](https://developers.google.com/analytics/devguides/collection/analyticsjs/ecommerce). * `:enhanced_ecommerce` - Enables [Enhanced Ecommerce Tracking](https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce) +* `:optimize` - pass [Google Optimize container ID](https://support.google.com/360suite/optimize/answer/6262084#example-combined-snippet) as value (e.g. `optimize: 'GTM-1234'`). #### Events diff --git a/lib/rack/tracker/google_analytics/template/google_analytics.erb b/lib/rack/tracker/google_analytics/template/google_analytics.erb index d56f043..96124b8 100644 --- a/lib/rack/tracker/google_analytics/template/google_analytics.erb +++ b/lib/rack/tracker/google_analytics/template/google_analytics.erb @@ -20,6 +20,9 @@ <% if options[:ecommerce] %> ga('require', 'ecommerce', 'ecommerce.js'); <% end %> +<% if options[:optimize] %> + ga('require', '<%= options[:optimize] %>'); +<% end %> <% if options[:anonymize_ip] %> ga('set', 'anonymizeIp', true); <% end %> diff --git a/spec/handler/google_analytics_spec.rb b/spec/handler/google_analytics_spec.rb index 79e4a92..027b7a8 100644 --- a/spec/handler/google_analytics_spec.rb +++ b/spec/handler/google_analytics_spec.rb @@ -235,6 +235,14 @@ def env end end + describe "with optimize" do + subject { described_class.new(env, tracker: 'happy', optimize: 'GTM-1234').render } + + it "will require the optimize plugin with container ID" do + expect(subject).to match(%r{ga\('require', 'GTM-1234'\)}) + end + end + describe "with anonymizeIp" do subject { described_class.new(env, tracker: 'happy', anonymize_ip: true).render } From 613569c67231f372e55671ca06c33665cae5267b Mon Sep 17 00:00:00 2001 From: Nicolas Leger Date: Thu, 15 Feb 2018 15:08:18 +0100 Subject: [PATCH 08/54] [CI] Test against Ruby 2.5 --- .travis.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8a42c15..59cfc04 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,9 +2,10 @@ language: ruby sudo: false rvm: - 2.1.10 - - 2.2.7 - - 2.3.4 - - 2.4.1 + - 2.2.9 + - 2.3.6 + - 2.4.3 + - 2.5.0 - jruby-9.1.10.0 gemfile: - Gemfile From b3d11e65dfc4ff60d7909c449a0e336f4da2ffb5 Mon Sep 17 00:00:00 2001 From: Jeremiah Date: Fri, 23 Mar 2018 15:29:43 -0700 Subject: [PATCH 09/54] set wildcard to non-greedy for GTM body insertion Change introduced in #86 causes issues with mismatches (normal HTML) of closing brackets - removing the greediness cleanly matches *just* the tag - regardless of what's contained in it. --- lib/rack/tracker/google_tag_manager/google_tag_manager.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rack/tracker/google_tag_manager/google_tag_manager.rb b/lib/rack/tracker/google_tag_manager/google_tag_manager.rb index 1fb286c..2db9d2b 100644 --- a/lib/rack/tracker/google_tag_manager/google_tag_manager.rb +++ b/lib/rack/tracker/google_tag_manager/google_tag_manager.rb @@ -13,7 +13,7 @@ def inject(response) response.sub! %r{} do |m| m.to_s << self.render_head end - response.sub! %r{} do |m| + response.sub! %r{} do |m| m.to_s << self.render_body end response From a7757b0dfbff07ab549aaff67dc9ff3b4f1eb82d Mon Sep 17 00:00:00 2001 From: Marco Schaden Date: Tue, 27 Mar 2018 08:50:13 +0200 Subject: [PATCH 10/54] finally a fresh release :fried_egg: --- CHANGELOG.md | 7 +++++++ lib/rack/tracker/version.rb | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d37e56b..e39ce1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# 1.6.0 + + * [BUGFIX] set wildcard to non-greedy for GTM body insertion #107 + * [ENHANCEMENT] Test against Ruby 2.5 #104 + * [ENHANCEMENT] Google Optimize container ID #103 + * [ENHANCEMENT] Allow for dynamic Facebook Pixel options #101 + # 1.5.0 * [ENHANCEMENT] facebook pixel now supports non-standard (custom) event names #93 diff --git a/lib/rack/tracker/version.rb b/lib/rack/tracker/version.rb index 8677a68..750100f 100644 --- a/lib/rack/tracker/version.rb +++ b/lib/rack/tracker/version.rb @@ -1,5 +1,5 @@ module Rack class Tracker - VERSION = '1.5.0' + VERSION = '1.6.0' end end From 39a475663e05bcd237cdbf82eb3fcb2e7b9aa508 Mon Sep 17 00:00:00 2001 From: Simon Knoll Date: Tue, 10 Apr 2018 17:39:07 +0200 Subject: [PATCH 11/54] Add a Rails based example on how to do a Dynamic FB-Pixel configuration --- README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/README.md b/README.md index 4902569..1d1d4d4 100644 --- a/README.md +++ b/README.md @@ -276,6 +276,23 @@ First, add the following to your config: end ``` +#### Dynamic Pixel Configuration +If you need to have different pixel ids e.g. based on the request or serving pages for different accounts, you have the possibility to achieve this with a [lambda expression](https://gist.github.com/Integralist/9994331). So simply change the config to something like: + +```ruby + config.middleware.use(Rack::Tracker) do + handler :facebook_pixel, {id: lambda {|env| env['PIXEL_ID']}} + end +``` + +Now you can set the pixel id per request in the request `env` variable: + +```ruby + def show + request.env['PIXEL_ID'] = 'DYNAMIC_PIXEL_ID' + end +``` + #### Standard Events To track Standard Events from the server side just call the `tracker` method in your controller. From ccc25abc7a1e33026c975c61454e520138c95023 Mon Sep 17 00:00:00 2001 From: Simon Knoll Date: Tue, 10 Apr 2018 17:47:45 +0200 Subject: [PATCH 12/54] Rephrase text and specify rails example --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1d1d4d4..1fd50dc 100644 --- a/README.md +++ b/README.md @@ -277,7 +277,7 @@ First, add the following to your config: ``` #### Dynamic Pixel Configuration -If you need to have different pixel ids e.g. based on the request or serving pages for different accounts, you have the possibility to achieve this with a [lambda expression](https://gist.github.com/Integralist/9994331). So simply change the config to something like: +If you need to have different pixel ids e.g. based on the request or serving pages for different accounts, you have the possibility to achieve this with a [lambda expression](https://gist.github.com/Integralist/9994331). Change the config to something like: ```ruby config.middleware.use(Rack::Tracker) do @@ -285,11 +285,13 @@ If you need to have different pixel ids e.g. based on the request or serving pag end ``` -Now you can set the pixel id per request in the request `env` variable: +and set the pixel id within the request `env` variable. Here an example how it can be done in a rails action: ```ruby - def show - request.env['PIXEL_ID'] = 'DYNAMIC_PIXEL_ID' + class MyController < ApplicationController + def show + request.env['PIXEL_ID'] = 'DYNAMIC_PIXEL_ID' + end end ``` From 199b0c8a8eb4953ff119aa236d161fe17e643938 Mon Sep 17 00:00:00 2001 From: Simon Knoll Date: Tue, 10 Apr 2018 18:01:18 +0200 Subject: [PATCH 13/54] Fix Grammar --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1fd50dc..6f2c380 100644 --- a/README.md +++ b/README.md @@ -285,7 +285,7 @@ If you need to have different pixel ids e.g. based on the request or serving pag end ``` -and set the pixel id within the request `env` variable. Here an example how it can be done in a rails action: +and set the pixel id within the request `env` variable. Here an example on how it can be done in a rails action: ```ruby class MyController < ApplicationController From 5d4cab5a757160b075a57dadfa2dbe0c5bdb3c2d Mon Sep 17 00:00:00 2001 From: bumi Date: Wed, 11 Apr 2018 13:12:52 +0200 Subject: [PATCH 14/54] Respect Do Not Track (DNT) headers by default The Do Not Track (DNT) header is the proposed HTTP header that requests that a web application disable its tracking of an individual user. This changes the default to respect the DNT header and no longer injects any tracker if the DNT header is set to 1 (user opt-out). This behaviour can be overwritten by setting the DO_NOT_RESPECT_THE_USERS_CHOICE_TO_OPT_OUT option on handlers that should still be injected. --- README.md | 26 ++++++++++++++++++++++++++ lib/rack/tracker/handler.rb | 10 ++++++++++ spec/tracker/tracker_spec.rb | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+) diff --git a/README.md b/README.md index 4902569..5f997da 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,32 @@ but to get you started we're shipping support for the following services out of * [Zanox](#zanox) * [Hotjar](#hotjar) +## Respecting the Do Not Track (DNT) HTTP header + +The Do Not Track (DNT) HTTP header is a HTTP header that requests the server to disable its tracking of the individual user. +This is an opt-out option supported by most browsers. This option is disabled by default and has to be explicitly enabled to indicate the user's request to opt-out. +We believe evey application should respect the user's choice to opt-out and respect this HTTP header. + +Since version 2.0.0 rack-tracker respects that request header by default. That means NO tracker is injected IF the DNT header is set to "1". + +This option can be overwriten using the `DO_NOT_RESPECT_THE_USERS_CHOICE_TO_OPT_OUT => true` option which must be set on any handler that should ignore the DNT header. (but please think twice before doing that) + +### Example on how to not respect the DNT header + +```ruby +use Rack::Tracker do + # this tracker will be injected EVEN IF the DNT header is set to 1 + handler :maybe_a_friendly_tracker, { tracker: 'U-XXXXX-Y', DO_NOT_RESPECT_THE_USERS_CHOICE_TO_OPT_OUT: true } + # this tracker will NOT be injected if the DNT header is set to 1 + handler :google_analytics, { tracker: 'U-XXXXX-Y' } +end +``` + +Further reading on the DNT header: + +* [Wikipedia Do Not Track](https://en.wikipedia.org/wiki/Do_Not_Track) +* [EFF: Do Not Track](https://www.eff.org/issues/do-not-track) + ## Installation diff --git a/lib/rack/tracker/handler.rb b/lib/rack/tracker/handler.rb index f76832e..b40628f 100644 --- a/lib/rack/tracker/handler.rb +++ b/lib/rack/tracker/handler.rb @@ -38,6 +38,11 @@ def render end def inject(response) + # default to not inject this tracker if the DNT HTTP header is set + # if the DO_NOT_RESPECT_THE_USERS_CHOICE_TO_OPT_OUT config is set the DNT header is ignored :( - please do respect the DNT header! + if self.dnt_header_opt_out? && !self.options.has_key?(:DO_NOT_RESPECT_THE_USERS_CHOICE_TO_OPT_OUT) + return response + end # Sub! is enough, in well formed html there's only one head or body tag. # Block syntax need to be used, otherwise backslashes in input will mess the output. # @see http://stackoverflow.com/a/4149087/518204 and https://github.com/railslove/rack-tracker/issues/50 @@ -70,6 +75,11 @@ def tracker_options end end + # the request has set the DO NOT TRACK (DNT) and has opted to get not tracked (DNT=1) + def dnt_header_opt_out? + self.env['DNT'] && self.env['DNT'].to_s == '1' + end + private # Transformations to be applied to tracker option keys. diff --git a/spec/tracker/tracker_spec.rb b/spec/tracker/tracker_spec.rb index 1f5a530..00bd024 100644 --- a/spec/tracker/tracker_spec.rb +++ b/spec/tracker/tracker_spec.rb @@ -20,6 +20,7 @@ def app use Rack::Tracker do handler DummyHandler, { foo: 'head' } handler BodyHandler, { foo: 'body' } + handler DummyHandler, { foo: 'I am evil', DO_NOT_RESPECT_THE_USERS_CHOICE_TO_OPT_OUT: true } end run lambda {|env| @@ -105,4 +106,39 @@ def app expect(last_response.body).to_not include("alert('this is a dummy class');") end end + + describe 'do not track' do + context 'DNT header set to 1' do + it 'will not inject any tracker' do + get '/', {}, {'DNT' => 1 } + + # the DummyHandler respects the DNT + expect(last_response.body).to_not include("console.log('head');") + end + + it 'will allow the DO_NOT_RESPECT_THE_USERS_CHOICE_TO_OPT_OUT overwrite' do + get '/', {}, {'DNT' => 1 } + + # the EvilHandler respects the DNT + expect(last_response.body).to include("console.log('I am evil');") + end + end + + context 'DNT header set to 0' do + it 'injects all trackers' do + get '/', {}, {'DNT' => 0 } + expect(last_response.body).to include("console.log('head');") + expect(last_response.body).to include("console.log('I am evil');") + end + end + + context 'DNT header is not set' do + it 'injects all trackers' do + get '/' + expect(last_response.body).to include("console.log('head');") + expect(last_response.body).to include("console.log('I am evil');") + end + end + + end end From d2e6ce504f5d41523d527472346b2e2d33b1e860 Mon Sep 17 00:00:00 2001 From: Marco Schaden Date: Thu, 12 Apr 2018 15:32:41 +0200 Subject: [PATCH 15/54] small adjustments --- README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 859556b..2b7f999 100644 --- a/README.md +++ b/README.md @@ -32,9 +32,9 @@ but to get you started we're shipping support for the following services out of ## Respecting the Do Not Track (DNT) HTTP header -The Do Not Track (DNT) HTTP header is a HTTP header that requests the server to disable its tracking of the individual user. -This is an opt-out option supported by most browsers. This option is disabled by default and has to be explicitly enabled to indicate the user's request to opt-out. -We believe evey application should respect the user's choice to opt-out and respect this HTTP header. +The Do Not Track (DNT) HTTP header is a HTTP header that requests the server to disable its tracking of the individual user. +This is an opt-out option supported by most browsers. This option is disabled by default and has to be explicitly enabled to indicate the user's request to opt-out. +We believe evey application should respect the user's choice to opt-out and respect this HTTP header. Since version 2.0.0 rack-tracker respects that request header by default. That means NO tracker is injected IF the DNT header is set to "1". @@ -51,7 +51,7 @@ use Rack::Tracker do end ``` -Further reading on the DNT header: +Further reading on the DNT header: * [Wikipedia Do Not Track](https://en.wikipedia.org/wiki/Do_Not_Track) * [EFF: Do Not Track](https://www.eff.org/issues/do-not-track) @@ -303,11 +303,12 @@ First, add the following to your config: ``` #### Dynamic Pixel Configuration -If you need to have different pixel ids e.g. based on the request or serving pages for different accounts, you have the possibility to achieve this with a [lambda expression](https://gist.github.com/Integralist/9994331). Change the config to something like: + +If you need to have different pixel ids e.g. based on the request or serving pages for different accounts, you have the possibility to achieve this by passing a lambda: ```ruby config.middleware.use(Rack::Tracker) do - handler :facebook_pixel, {id: lambda {|env| env['PIXEL_ID']}} + handler :facebook_pixel, { id: lambda { |env| env['PIXEL_ID'] } } end ``` From e9ed93b9c042b2239ae6b6a4af9e032d53b9afe3 Mon Sep 17 00:00:00 2001 From: bumi Date: Sat, 14 Apr 2018 16:38:12 +0200 Subject: [PATCH 16/54] Fix DNT to use the correct env variable HTTP headers are prefixed with HTTP_ --- lib/rack/tracker/handler.rb | 2 +- spec/tracker/tracker_spec.rb | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/rack/tracker/handler.rb b/lib/rack/tracker/handler.rb index b40628f..43312b1 100644 --- a/lib/rack/tracker/handler.rb +++ b/lib/rack/tracker/handler.rb @@ -77,7 +77,7 @@ def tracker_options # the request has set the DO NOT TRACK (DNT) and has opted to get not tracked (DNT=1) def dnt_header_opt_out? - self.env['DNT'] && self.env['DNT'].to_s == '1' + self.env['HTTP_DNT'] && self.env['HTTP_DNT'].to_s == '1' end private diff --git a/spec/tracker/tracker_spec.rb b/spec/tracker/tracker_spec.rb index 00bd024..588c571 100644 --- a/spec/tracker/tracker_spec.rb +++ b/spec/tracker/tracker_spec.rb @@ -110,14 +110,14 @@ def app describe 'do not track' do context 'DNT header set to 1' do it 'will not inject any tracker' do - get '/', {}, {'DNT' => 1 } + get '/', {}, {'HTTP_DNT' => 1 } # the DummyHandler respects the DNT expect(last_response.body).to_not include("console.log('head');") end it 'will allow the DO_NOT_RESPECT_THE_USERS_CHOICE_TO_OPT_OUT overwrite' do - get '/', {}, {'DNT' => 1 } + get '/', {}, {'HTTP_DNT' => 1 } # the EvilHandler respects the DNT expect(last_response.body).to include("console.log('I am evil');") @@ -126,7 +126,7 @@ def app context 'DNT header set to 0' do it 'injects all trackers' do - get '/', {}, {'DNT' => 0 } + get '/', {}, {'HTTP_DNT' => 0 } expect(last_response.body).to include("console.log('head');") expect(last_response.body).to include("console.log('I am evil');") end From 139063b030b93e4e935fa7b4c662f09ea5c9290c Mon Sep 17 00:00:00 2001 From: bumi Date: Tue, 17 Apr 2018 12:48:37 +0200 Subject: [PATCH 17/54] tone down option to ignore DNT headers --- README.md | 4 ++-- lib/rack/tracker/handler.rb | 4 ++-- spec/tracker/tracker_spec.rb | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 2b7f999..233d2b5 100644 --- a/README.md +++ b/README.md @@ -38,14 +38,14 @@ We believe evey application should respect the user's choice to opt-out and resp Since version 2.0.0 rack-tracker respects that request header by default. That means NO tracker is injected IF the DNT header is set to "1". -This option can be overwriten using the `DO_NOT_RESPECT_THE_USERS_CHOICE_TO_OPT_OUT => true` option which must be set on any handler that should ignore the DNT header. (but please think twice before doing that) +This option can be overwriten using the `DO_NOT_RESPECT_DNT_HEADER => true` option which must be set on any handler that should ignore the DNT header. (but please think twice before doing that) ### Example on how to not respect the DNT header ```ruby use Rack::Tracker do # this tracker will be injected EVEN IF the DNT header is set to 1 - handler :maybe_a_friendly_tracker, { tracker: 'U-XXXXX-Y', DO_NOT_RESPECT_THE_USERS_CHOICE_TO_OPT_OUT: true } + handler :maybe_a_friendly_tracker, { tracker: 'U-XXXXX-Y', DO_NOT_RESPECT_DNT_HEADER: true } # this tracker will NOT be injected if the DNT header is set to 1 handler :google_analytics, { tracker: 'U-XXXXX-Y' } end diff --git a/lib/rack/tracker/handler.rb b/lib/rack/tracker/handler.rb index 43312b1..25c7788 100644 --- a/lib/rack/tracker/handler.rb +++ b/lib/rack/tracker/handler.rb @@ -39,8 +39,8 @@ def render def inject(response) # default to not inject this tracker if the DNT HTTP header is set - # if the DO_NOT_RESPECT_THE_USERS_CHOICE_TO_OPT_OUT config is set the DNT header is ignored :( - please do respect the DNT header! - if self.dnt_header_opt_out? && !self.options.has_key?(:DO_NOT_RESPECT_THE_USERS_CHOICE_TO_OPT_OUT) + # if the DO_NOT_RESPECT_DNT_HEADER config is set the DNT header is ignored :( - please do respect the DNT header! + if self.dnt_header_opt_out? && !self.options.has_key?(:DO_NOT_RESPECT_DNT_HEADER) return response end # Sub! is enough, in well formed html there's only one head or body tag. diff --git a/spec/tracker/tracker_spec.rb b/spec/tracker/tracker_spec.rb index 588c571..1f9dfa1 100644 --- a/spec/tracker/tracker_spec.rb +++ b/spec/tracker/tracker_spec.rb @@ -20,7 +20,7 @@ def app use Rack::Tracker do handler DummyHandler, { foo: 'head' } handler BodyHandler, { foo: 'body' } - handler DummyHandler, { foo: 'I am evil', DO_NOT_RESPECT_THE_USERS_CHOICE_TO_OPT_OUT: true } + handler DummyHandler, { foo: 'I am evil', DO_NOT_RESPECT_DNT_HEADER: true } end run lambda {|env| @@ -116,7 +116,7 @@ def app expect(last_response.body).to_not include("console.log('head');") end - it 'will allow the DO_NOT_RESPECT_THE_USERS_CHOICE_TO_OPT_OUT overwrite' do + it 'will allow the DO_NOT_RESPECT_DNT_HEADER overwrite' do get '/', {}, {'HTTP_DNT' => 1 } # the EvilHandler respects the DNT From 477e3ce6d6c9452ba42b2c4822a5e21b04e2b188 Mon Sep 17 00:00:00 2001 From: Zach Pflederer Date: Thu, 26 Apr 2018 02:27:33 -0500 Subject: [PATCH 18/54] dup response string in Rack::Tracker#inject to avoid RuntimeError (#114) * dup response string in Rack::Tracker#inject to avoid RuntimeError * prefer frozen_string_literal: true over #freeze --- lib/rack/tracker.rb | 5 +++-- spec/tracker/tracker_spec.rb | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/rack/tracker.rb b/lib/rack/tracker.rb index d549a53..f2141f0 100644 --- a/lib/rack/tracker.rb +++ b/lib/rack/tracker.rb @@ -58,10 +58,11 @@ def call(env) def html?; @headers['Content-Type'] =~ /html/; end def inject(env, response) + duplicated_response = response.dup @handlers.each(env) do |handler| - handler.inject(response) + handler.inject(duplicated_response) end - response + duplicated_response end class HandlerSet diff --git a/spec/tracker/tracker_spec.rb b/spec/tracker/tracker_spec.rb index 1f9dfa1..664e6c1 100644 --- a/spec/tracker/tracker_spec.rb +++ b/spec/tracker/tracker_spec.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true class DummyHandler < Rack::Tracker::Handler def render Tilt.new( File.join( File.dirname(__FILE__), '../fixtures/dummy.erb') ).render(self) From bcce595a08b25347a7ee4e24edd937d7bf0883bc Mon Sep 17 00:00:00 2001 From: Kuntz Thomas Date: Wed, 6 Jun 2018 13:04:57 +0200 Subject: [PATCH 19/54] Allow to use custom pageview url script for GoogleAnalytics tracker. (#119) * Allow to use custom pageview url script for GoogleAnalytics tracker. --- .../google_analytics/google_analytics.rb | 4 ++++ .../template/google_analytics.erb | 2 +- spec/handler/google_analytics_spec.rb | 18 +++++++++++++++++ .../google_analytics_integration_spec.rb | 20 ++++++++++++++++++- 4 files changed, 42 insertions(+), 2 deletions(-) diff --git a/lib/rack/tracker/google_analytics/google_analytics.rb b/lib/rack/tracker/google_analytics/google_analytics.rb index a359cc1..a18d1e0 100644 --- a/lib/rack/tracker/google_analytics/google_analytics.rb +++ b/lib/rack/tracker/google_analytics/google_analytics.rb @@ -65,6 +65,10 @@ def enhanced_ecommerce_events events.select {|e| e.kind_of?(EnhancedEcommerce) } end + def pageview_url_script + options[:pageview_url_script] || 'window.location.pathname + window.location.search' + end + private def tracker_option_key(key) diff --git a/lib/rack/tracker/google_analytics/template/google_analytics.erb b/lib/rack/tracker/google_analytics/template/google_analytics.erb index 96124b8..000ff7c 100644 --- a/lib/rack/tracker/google_analytics/template/google_analytics.erb +++ b/lib/rack/tracker/google_analytics/template/google_analytics.erb @@ -39,6 +39,6 @@ ga('ecommerce:send'); <% end %> <% if tracker %> - ga('send', 'pageview', window.location.pathname + window.location.search); + ga('send', 'pageview', <%= pageview_url_script %>); <% end %> diff --git a/spec/handler/google_analytics_spec.rb b/spec/handler/google_analytics_spec.rb index 027b7a8..5d46e8d 100644 --- a/spec/handler/google_analytics_spec.rb +++ b/spec/handler/google_analytics_spec.rb @@ -267,4 +267,22 @@ def env expect(subject).to include %q{setTimeout(function() { ga('send', 'event', '30_seconds', 'read'); },30000)} end end + + describe '#pageview_url_script' do + context 'without custom pageview url script' do + subject { described_class.new(env, {} ) } + + it 'returns return the custom pageview url script' do + expect(subject.pageview_url_script).to eql ("window.location.pathname + window.location.search") + end + end + + context 'with a custom pageview url script' do + subject { described_class.new(env, { pageview_url_script: "{ 'page': location.pathname + location.search + location.hash }"}) } + + it 'returns return the custom pageview url script' do + expect(subject.pageview_url_script).to eql ("{ 'page': location.pathname + location.search + location.hash }") + end + end + end end diff --git a/spec/integration/google_analytics_integration_spec.rb b/spec/integration/google_analytics_integration_spec.rb index 7f5888a..ba59ea2 100644 --- a/spec/integration/google_analytics_integration_spec.rb +++ b/spec/integration/google_analytics_integration_spec.rb @@ -16,6 +16,10 @@ expect(page.find("head")).to have_content('ga("send",{"hitType":"event","eventCategory":"button","eventAction":"click","eventLabel":"nav-buttons","eventValue":"X"})') end + it "will have default pageview url script" do + expect(page.find("head")).to have_content("ga('send', 'pageview', window.location.pathname + window.location.search);") + end + describe 'adjust tracker position via options' do before do setup_app(action: :google_analytics) do |tracker| @@ -49,4 +53,18 @@ expect(page.find('head')).to have_content %q{Author\\'s name} end end -end \ No newline at end of file + + describe 'Use custom pageview script' do + before do + setup_app(action: :google_analytics) do |tracker| + tracker.handler :google_analytics, { tracker: 'U-XXX-Y', pageview_url_script: "{ 'page': location.pathname + location.search + location.hash }"} + end + visit '/' + end + + it "will use the custom pageview script for the pageview event" do + expect(page.find("head")).to have_content("ga('send', 'pageview', { 'page': location.pathname + location.search + location.hash });") + end + end + +end From ba42d14e03b2f1345f2f4b2668fb0d323f22f2c4 Mon Sep 17 00:00:00 2001 From: Thomas Kuntz Date: Mon, 11 Jun 2018 11:10:51 +0200 Subject: [PATCH 20/54] Update README.md for Google Analytics option `pageview_url_script`. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 233d2b5..297b516 100644 --- a/README.md +++ b/README.md @@ -123,6 +123,7 @@ request.env['tracker'] = { * `:ecommerce` - Enables [Ecommerce Tracking](https://developers.google.com/analytics/devguides/collection/analyticsjs/ecommerce). * `:enhanced_ecommerce` - Enables [Enhanced Ecommerce Tracking](https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce) * `:optimize` - pass [Google Optimize container ID](https://support.google.com/360suite/optimize/answer/6262084#example-combined-snippet) as value (e.g. `optimize: 'GTM-1234'`). +* `:pageview_url_script` - a String containing a custom js script evaluating to the url that shoudl be given to the pageview event. Default to `window.location.pathname + window.location.search`. #### Events From f2f62a771c98a11801b722da5ce401e405f6a8ad Mon Sep 17 00:00:00 2001 From: Marco Schaden Date: Thu, 19 Jul 2018 09:44:53 +0200 Subject: [PATCH 21/54] Y U NO release :rage: --- CHANGELOG.md | 5 +++++ lib/rack/tracker/version.rb | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e39ce1d..92e6213 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 1.7.0 + + * [BUGFIX] dup response string in Rack::Tracker#inject to avoid RuntimeError #114 (thx @zpfled) + * [ENHANCEMENT] Allow to use custom pageview url script for GoogleAnalytics tracker. #119 (thx @Haerezis) + # 1.6.0 * [BUGFIX] set wildcard to non-greedy for GTM body insertion #107 diff --git a/lib/rack/tracker/version.rb b/lib/rack/tracker/version.rb index 750100f..1ca3243 100644 --- a/lib/rack/tracker/version.rb +++ b/lib/rack/tracker/version.rb @@ -1,5 +1,5 @@ module Rack class Tracker - VERSION = '1.6.0' + VERSION = '1.7.0' end end From bf18ed67113135c3e3d31714f56ad6bd11f2ca7f Mon Sep 17 00:00:00 2001 From: Antonio Tapiador Date: Mon, 1 Oct 2018 11:11:00 +0200 Subject: [PATCH 22/54] Adds basic google global integration (continuation of #97) (#123) * adds basic google global integration * Support options with false boolean value * Use Rack::Tracker::Handler#tracker_options in GoogleGlobal * Extend documentation on Google global site tag * Add custom_map tracker option to GoogleGlobal * Add support of Google global tag set command * Filter dynamic tracker ids which return nil Allows more flexibility in tracker id configuration * Add support for pageviews to Google global tag * Fix a couple of style issues --- README.md | 22 +++ lib/rack/tracker.rb | 1 + .../tracker/google_global/google_global.rb | 32 +++ .../google_global/template/google_global.erb | 19 ++ lib/rack/tracker/handler.rb | 3 +- spec/handler/google_global_spec.rb | 187 ++++++++++++++++++ .../google_global_integration_spec.rb | 30 +++ spec/support/metal_controller.rb | 4 + 8 files changed, 297 insertions(+), 1 deletion(-) create mode 100644 lib/rack/tracker/google_global/google_global.rb create mode 100644 lib/rack/tracker/google_global/template/google_global.erb create mode 100644 spec/handler/google_global_spec.rb create mode 100644 spec/integration/google_global_integration_spec.rb diff --git a/README.md b/README.md index 297b516..4b7ac59 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ to the middleware stack the second part are the service-handlers that you're goi in your application. It's easy to add your own [custom handlers](#custom-handlers), but to get you started we're shipping support for the following services out of the box: +* [Google Global Site Tag](#google-global) * [Google Analytics](#google-analytics) * [Google Adwords Conversion](#google-adwords-conversion) * [Google Tag Manager](#google-tag-manager) @@ -111,6 +112,27 @@ request.env['tracker'] = { } ``` +### Google Global Site Tag (gtag.js) + +* `:anonymize_ip` - sets the tracker to remove the last octet from all IP addresses, see https://developers.google.com/analytics/devguides/collection/gtagjs/ip-anonymization for details. +* `:cookie_domain` - sets the domain name for the [GATC cookies](https://developers.google.com/analytics/devguides/collection/gtagjs/cookies-user-id). If not set its the website domain, with the www. prefix removed. +* `:user_id` - defines a proc to set the [userId](https://developers.google.com/analytics/devguides/collection/gtagjs/cookies-user-id). Ex: `user_id: lambda { |env| env['rack.session']['user_id'] }` would return the user_id from the session. +* `:link_attribution` - Enables [Enhanced Link Attribution](https://developers.google.com/analytics/devguides/collection/gtagjs/enhanced-link-attribution). +* `:allow_display_features` - Can be used to disable [Display Features](https://developers.google.com/analytics/devguides/collection/gtagjs/display-features). +* `:custom_map` - Used to [Configure and send custom dimensions](https://developers.google.com/analytics/devguides/collection/gtagjs/custom-dims-mets) +* `:set` - Used in the [set command to configure multiple properties](https://developers.google.com/analytics/devguides/collection/gtagjs/setting-values) + +#### Trackers + +Google Global Site tag allows configuring multiple trackers. Use the tracker option to configure the ids: + + +```ruby +config.middleware.use(Rack::Tracker) do + handler :google_global, { trackers: [ { id: 'U-XXXXX-Y' }, { id: 'U-WWWWWW-Z'} ] } +end +``` + ### Google Analytics * `:anonymize_ip` - sets the tracker to remove the last octet from all IP addresses, see https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApi_gat?hl=de#_gat._anonymizeIp for details. diff --git a/lib/rack/tracker.rb b/lib/rack/tracker.rb index f2141f0..1a7c899 100644 --- a/lib/rack/tracker.rb +++ b/lib/rack/tracker.rb @@ -13,6 +13,7 @@ require "rack/tracker/handler_delegator" require "rack/tracker/controller" require "rack/tracker/google_analytics/google_analytics" +require "rack/tracker/google_global/google_global" require "rack/tracker/google_tag_manager/google_tag_manager" require "rack/tracker/google_adwords_conversion/google_adwords_conversion" require "rack/tracker/facebook/facebook" diff --git a/lib/rack/tracker/google_global/google_global.rb b/lib/rack/tracker/google_global/google_global.rb new file mode 100644 index 0000000..5075906 --- /dev/null +++ b/lib/rack/tracker/google_global/google_global.rb @@ -0,0 +1,32 @@ +class Rack::Tracker::GoogleGlobal < Rack::Tracker::Handler + self.allowed_tracker_options = [:cookie_domain, :user_id, + :link_attribution, :allow_display_features, :anonymize_ip, + :custom_map] + + class Page < OpenStruct + def params + Hash[to_h.slice(:title, :location, :path).map { |key, value| ["page_#{key}", value] }] + end + end + + def pages + events # TODO: Filter pages after Event is implemented + end + + def trackers + options[:trackers].map { |tracker| + tracker[:id].respond_to?(:call) ? tracker.merge(id: tracker[:id].call(env)) : tracker + }.reject { |tracker| tracker[:id].nil? } + end + + def set_options + @_set_options ||= build_set_options + end + + private + + def build_set_options + value = options[:set] + value.respond_to?(:call) ? value.call(env) : value + end +end diff --git a/lib/rack/tracker/google_global/template/google_global.erb b/lib/rack/tracker/google_global/template/google_global.erb new file mode 100644 index 0000000..ea656a3 --- /dev/null +++ b/lib/rack/tracker/google_global/template/google_global.erb @@ -0,0 +1,19 @@ +<% if trackers %> + + +<% end %> diff --git a/lib/rack/tracker/handler.rb b/lib/rack/tracker/handler.rb index 25c7788..2e31195 100644 --- a/lib/rack/tracker/handler.rb +++ b/lib/rack/tracker/handler.rb @@ -68,7 +68,8 @@ def handler_name def tracker_options @_tracker_options ||= {}.tap do |tracker_options| options.slice(*allowed_tracker_options).each do |key, value| - if option_value = value.respond_to?(:call) ? value.call(env) : value + option_value = value.respond_to?(:call) ? value.call(env) : value + unless option_value.nil? tracker_options[tracker_option_key(key)] = tracker_option_value(option_value) end end diff --git a/spec/handler/google_global_spec.rb b/spec/handler/google_global_spec.rb new file mode 100644 index 0000000..d955bdd --- /dev/null +++ b/spec/handler/google_global_spec.rb @@ -0,0 +1,187 @@ +RSpec.describe Rack::Tracker::GoogleGlobal do + def env + { + misc: 'foobar', + user_id: '123' + } + end + + it 'will be placed in the head' do + expect(described_class.position).to eq(:head) + expect(described_class.new(env).position).to eq(:head) + expect(described_class.new(env, position: :body).position).to eq(:body) + end + + describe '#tracker_options' do + context 'with an allowed option configured with a static value' do + subject { described_class.new(env, user_id: 'value') } + + it 'returns hash with option set' do + expect(subject.tracker_options).to eql ({ user_id: 'value' }) + end + end + + context 'with an allowed option configured with a block' do + subject { described_class.new(env, user_id: lambda { |env| return env[:misc] }) } + + it 'returns hash with option set' do + expect(subject.tracker_options).to eql ({ user_id: 'foobar' }) + end + end + + context 'with an allowed option configured with a block returning nil' do + subject { described_class.new(env, user_id: lambda { |env| return env[:non_existing_key] }) } + + it 'returns an empty hash' do + expect(subject.tracker_options).to eql ({}) + end + end + + context 'with a non allowed option' do + subject { described_class.new(env, new_option: 'value') } + + it 'returns an empty hash' do + expect(subject.tracker_options).to eql ({}) + end + end + end + + describe '#set_options' do + context 'with option configured with a static value' do + subject { described_class.new(env, set: { option: 'value' }) } + + it 'returns hash with option set' do + expect(subject.set_options).to eql ({ option: 'value' }) + end + end + + context 'with option configured with a block' do + subject { described_class.new(env, set: lambda { |env| return { option: env[:misc] } }) } + + it 'returns hash with option set' do + expect(subject.set_options).to eql ({ option: 'foobar' }) + end + end + + context 'with option configured with a block returning nil' do + subject { described_class.new(env, set: lambda { |env| return env[:non_existing_key] }) } + + it 'returns nil' do + expect(subject.set_options).to be nil + end + end + end + describe "with custom domain" do + let(:tracker) { { id: 'somebody'}} + let(:options) { { cookie_domain: "railslabs.com", trackers: [tracker] } } + subject { described_class.new(env, options).render } + + it "will show asyncronous tracker with cookie_domain" do + expect(subject).to match(%r{gtag\('config', 'somebody', {\"cookie_domain\":\"railslabs.com\"}\)}) + end + end + + describe "with user_id tracking" do + let(:tracker) { { id: 'somebody'}} + let(:options) { { user_id: lambda { |env| return env[:user_id] }, trackers: [tracker] } } + subject { described_class.new(env, options).render } + + it "will show asyncronous tracker with userId" do + expect(subject).to match(%r{gtag\('config', 'somebody', {\"user_id\":\"123\"}\)}) + end + end + + describe "with link_attribution" do + let(:tracker) { { id: 'happy'}} + let(:options) { { link_attribution: true, trackers: [tracker] } } + subject { described_class.new(env, options).render } + + it "will show asyncronous tracker with link_attribution" do + expect(subject).to match(%r{gtag\('config', 'happy', {\"link_attribution\":true}\)}) + end + end + + describe "with allow_display_features" do + let(:tracker) { { id: 'happy'}} + let(:options) { { allow_display_features: false, trackers: [tracker] } } + subject { described_class.new(env, options).render } + + it "will disable display features" do + expect(subject).to match(%r{gtag\('config', 'happy', {\"allow_display_features\":false}\)}) + end + end + + describe "with anonymizeIp" do + let(:tracker) { { id: 'happy'}} + let(:options) { { anonymize_ip: true, trackers: [tracker] } } + subject { described_class.new(env, options).render } + + it "will set anonymizeIp to true" do + expect(subject).to match(%r{gtag\('config', 'happy', {\"anonymize_ip\":true}\)}) + end + end + + describe "with dynamic tracker" do + let(:tracker) { { id: lambda { |env| return env[:misc] } }} + let(:options) { { trackers: [tracker] } } + subject { described_class.new(env, options).render } + + it 'will call tracker lambdas to obtain tracking codes' do + expect(subject).to match(%r{gtag\('config', 'foobar', {}\)}) + end + end + + describe "with empty tracker" do + let(:present_tracker) { { id: 'present' }} + let(:empty_tracker) { { id: lambda { |env| return } }} + let(:options) { { trackers: [present_tracker, empty_tracker] } } + subject { described_class.new(env, options).render } + + it 'will not render config' do + expect(subject).to match(%r{gtag\('config', 'present', {}\)}) + expect(subject).not_to match(%r{gtag\('config', '', {}\)}) + end + end + + describe "with set options" do + let(:tracker) { { id: 'with_options' } } + let(:options) { { trackers: [tracker], set: { foo: 'bar' } } } + subject { described_class.new(env, options).render } + + it 'will show set command' do + expect(subject).to match(%r{gtag\('set', {\"foo\":\"bar\"}\)}) + end + end + + describe "with virtual pages" do + subject { described_class.new(env, trackers: [{ id: 'somebody' }]).render } + + describe "default" do + def env + {'tracker' => { + 'google_global' => [ + { 'class_name' => 'Page', 'path' => '/virtual_page' } + ] + }} + end + + it "will show virtual page" do + expect(subject).to match(%r{gtag\('config', 'somebody', {\"page_path\":\"/virtual_page\"}\);}) + end + end + + describe "with a event value" do + def env + {'tracker' => { + 'google_global' => [ + { 'class_name' => 'Page', 'path' => '/virtual_page', 'location' => 'https://example.com/virtual_page', 'title' => 'Virtual Page' } + ] + }} + end + + it "will show virtual page" do + expect(subject).to match(%r{gtag\('config', 'somebody', {\"page_title\":\"Virtual Page\",\"page_location\":\"https:\/\/example.com\/virtual_page\",\"page_path\":\"/virtual_page\"}\);}) + end + end + end +end diff --git a/spec/integration/google_global_integration_spec.rb b/spec/integration/google_global_integration_spec.rb new file mode 100644 index 0000000..7079036 --- /dev/null +++ b/spec/integration/google_global_integration_spec.rb @@ -0,0 +1,30 @@ +require 'support/capybara_app_helper' + +RSpec.describe "Google Global Integration Integration" do + before do + setup_app(action: :google_global) do |tracker| + tracker.handler :google_global, trackers: [{ id: 'U-XXX-Y' }] + end + visit '/' + end + + subject { page } + + it "embeds the script tag with tracking event from the controller action" do + expect(page.find("head")).to have_content('U-XXX-Y') + end + + describe 'adjust tracker position via options' do + before do + setup_app(action: :google_global) do |tracker| + tracker.handler :google_global, trackers: [{ id: 'U-XXX-Y' }], position: :body + end + visit '/' + end + + it "will be placed in the specified tag" do + expect(page.find("head")).to_not have_content('U-XXX-Y') + expect(page.find("body")).to have_content('U-XXX-Y') + end + end +end diff --git a/spec/support/metal_controller.rb b/spec/support/metal_controller.rb index b7241c7..2bb9493 100644 --- a/spec/support/metal_controller.rb +++ b/spec/support/metal_controller.rb @@ -61,6 +61,10 @@ def google_analytics render "metal/index" end + def google_global + render "metal/index" + end + def google_tag_manager unless params[:no_events] tracker do |t| From 13645a08a5694e0d3d553ec522c18523d09691b0 Mon Sep 17 00:00:00 2001 From: Nacho Abad Date: Mon, 8 Oct 2018 15:53:40 +0200 Subject: [PATCH 23/54] adds optimize_id to allowed tracker options --- README.md | 1 + lib/rack/tracker/google_global/google_global.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4b7ac59..499098a 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,7 @@ request.env['tracker'] = { * `:link_attribution` - Enables [Enhanced Link Attribution](https://developers.google.com/analytics/devguides/collection/gtagjs/enhanced-link-attribution). * `:allow_display_features` - Can be used to disable [Display Features](https://developers.google.com/analytics/devguides/collection/gtagjs/display-features). * `:custom_map` - Used to [Configure and send custom dimensions](https://developers.google.com/analytics/devguides/collection/gtagjs/custom-dims-mets) +* `:optimize_id` - Used to [Deploy Optimize using gtag](https://support.google.com/optimize/answer/7513085) * `:set` - Used in the [set command to configure multiple properties](https://developers.google.com/analytics/devguides/collection/gtagjs/setting-values) #### Trackers diff --git a/lib/rack/tracker/google_global/google_global.rb b/lib/rack/tracker/google_global/google_global.rb index 5075906..764ee02 100644 --- a/lib/rack/tracker/google_global/google_global.rb +++ b/lib/rack/tracker/google_global/google_global.rb @@ -1,7 +1,7 @@ class Rack::Tracker::GoogleGlobal < Rack::Tracker::Handler self.allowed_tracker_options = [:cookie_domain, :user_id, :link_attribution, :allow_display_features, :anonymize_ip, - :custom_map] + :custom_map, :optimize_id] class Page < OpenStruct def params From 3b162bd3728a843a6e5c4038d150481648d41eda Mon Sep 17 00:00:00 2001 From: Antonio Tapiador del Dujo Date: Tue, 15 Jan 2019 12:56:42 +0100 Subject: [PATCH 24/54] Support for google global events --- .../tracker/google_global/google_global.rb | 28 ++++++++++++++- .../google_global/template/google_global.erb | 4 +++ spec/handler/google_global_spec.rb | 36 +++++++++++++++++++ 3 files changed, 67 insertions(+), 1 deletion(-) diff --git a/lib/rack/tracker/google_global/google_global.rb b/lib/rack/tracker/google_global/google_global.rb index 5075906..ae4e52c 100644 --- a/lib/rack/tracker/google_global/google_global.rb +++ b/lib/rack/tracker/google_global/google_global.rb @@ -9,8 +9,30 @@ def params end end + class Event < OpenStruct + PREFIXED_PARAMS = %i[category label] + LITERAL_PARAMS = %i[value] + PARAMS = PREFIXED_PARAMS + LITERAL_PARAMS + + def params + Hash[to_h.slice(*PARAMS).map { |key, value| [param_key(key), value] }] + end + + private + + def param_key(key) + PREFIXED_PARAMS.include?(key) ? "event_#{key}" : key.to_s + end + end + def pages - events # TODO: Filter pages after Event is implemented + select_handler_events(Page) + end + + alias handler_events events + + def events + select_handler_events(Event) end def trackers @@ -29,4 +51,8 @@ def build_set_options value = options[:set] value.respond_to?(:call) ? value.call(env) : value end + + def select_handler_events(klass) + handler_events.select { |event| event.is_a?(klass) } + end end diff --git a/lib/rack/tracker/google_global/template/google_global.erb b/lib/rack/tracker/google_global/template/google_global.erb index ea656a3..b6141a8 100644 --- a/lib/rack/tracker/google_global/template/google_global.erb +++ b/lib/rack/tracker/google_global/template/google_global.erb @@ -15,5 +15,9 @@ <% end %> gtag('config', '<%= tracker[:id] %>', <%= tracker_options.to_json %>); <% end %> + + <% events.each do |event| %> + gtag('event', '<%= event.action %>', <%= event.params.to_json %>); + <% end %> <% end %> diff --git a/spec/handler/google_global_spec.rb b/spec/handler/google_global_spec.rb index d955bdd..dcf6fcc 100644 --- a/spec/handler/google_global_spec.rb +++ b/spec/handler/google_global_spec.rb @@ -184,4 +184,40 @@ def env end end end + + describe "with events" do + subject { described_class.new(env, trackers: [{ id: 'somebody' }]).render } + + describe "default" do + def env + {'tracker' => { + 'google_global' => [ + { 'class_name' => 'Event', 'action' => 'login' } + ] + }} + end + + it "will show the event" do + expect(subject).to match(%r{gtag\('event', 'login', {}\);}) + end + end + + describe "with event parameters" do + def env + {'tracker' => { + 'google_global' => [ + { 'class_name' => 'Event', + 'action' => 'login', + 'category' => 'engagement', + 'label' => 'Github', + 'value' => 5 } + ] + }} + end + + it "will show event" do + expect(subject).to match(%r{gtag\('event', 'login', {\"event_category\":\"engagement\",\"event_label\":\"Github\",\"value\":5}\);}) + end + end + end end From c92d81e61d0bb964b52b835e59f1943dc3402e42 Mon Sep 17 00:00:00 2001 From: Marco Schaden Date: Thu, 31 Jan 2019 07:43:40 +0100 Subject: [PATCH 25/54] version up :rocket: --- CHANGELOG.md | 4 ++++ lib/rack/tracker/version.rb | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 92e6213..3603277 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 1.8.0 + + * [ENHANCEMENT] Google Global Site Tag: basic integration with support for pageviews to Google global tag #123 (thx @atd) + # 1.7.0 * [BUGFIX] dup response string in Rack::Tracker#inject to avoid RuntimeError #114 (thx @zpfled) diff --git a/lib/rack/tracker/version.rb b/lib/rack/tracker/version.rb index 1ca3243..f6d5d5f 100644 --- a/lib/rack/tracker/version.rb +++ b/lib/rack/tracker/version.rb @@ -1,5 +1,5 @@ module Rack class Tracker - VERSION = '1.7.0' + VERSION = '1.8.0' end end From 240f45639077434390d31e4939240c413fd64788 Mon Sep 17 00:00:00 2001 From: Marco Date: Thu, 31 Jan 2019 11:13:58 +0100 Subject: [PATCH 26/54] update bundler dependency (#128) * update bundler dependency * update travis build matrix --- .travis.yml | 15 +++++---------- Gemfile.rails-5.1 | 6 ------ Gemfile.rails-5.2 | 6 ++++++ rack-tracker.gemspec | 2 +- 4 files changed, 12 insertions(+), 17 deletions(-) delete mode 100644 Gemfile.rails-5.1 create mode 100644 Gemfile.rails-5.2 diff --git a/.travis.yml b/.travis.yml index 59cfc04..b754765 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,18 +1,13 @@ language: ruby sudo: false rvm: - - 2.1.10 - - 2.2.9 - - 2.3.6 - - 2.4.3 - - 2.5.0 + - 2.3.8 + - 2.4.5 + - 2.5.3 + - 2.6.1 - jruby-9.1.10.0 gemfile: - Gemfile - Gemfile.rails-3.2 - Gemfile.rails-4.2 - - Gemfile.rails-5.1 -matrix: - exclude: - - rvm: 2.1.10 - gemfile: Gemfile.rails-5.1 # rails 5 needs ruby >= 2.2.2 + - Gemfile.rails-5.2 diff --git a/Gemfile.rails-5.1 b/Gemfile.rails-5.1 deleted file mode 100644 index f54ce1a..0000000 --- a/Gemfile.rails-5.1 +++ /dev/null @@ -1,6 +0,0 @@ -source "https://rubygems.org" - -gemspec - -gem 'activesupport', '~> 5.1.0' -gem 'actionpack', '~> 5.1.0' diff --git a/Gemfile.rails-5.2 b/Gemfile.rails-5.2 new file mode 100644 index 0000000..47e7c4c --- /dev/null +++ b/Gemfile.rails-5.2 @@ -0,0 +1,6 @@ +source "https://rubygems.org" + +gemspec + +gem 'activesupport', '~> 5.2.0' +gem 'actionpack', '~> 5.2.0' diff --git a/rack-tracker.gemspec b/rack-tracker.gemspec index 0aa7116..d5fa3d2 100644 --- a/rack-tracker.gemspec +++ b/rack-tracker.gemspec @@ -23,7 +23,7 @@ Gem::Specification.new do |spec| spec.add_dependency 'activesupport', '>= 3.0' spec.add_development_dependency 'actionpack', '>= 3.0' - spec.add_development_dependency "bundler", "~> 1.5" + spec.add_development_dependency "bundler", ">= 1.16" spec.add_development_dependency "rake" spec.add_development_dependency "rspec", "~> 3.2" spec.add_development_dependency "capybara", "~> 2.4" From 39112dfe0e222bde6b190f064398052e8b81a1f1 Mon Sep 17 00:00:00 2001 From: Patrick Craston Date: Thu, 7 Mar 2019 11:44:15 +0000 Subject: [PATCH 27/54] closes #130 add bing tracking --- README.md | 25 +++++++++++ lib/rack/tracker.rb | 1 + lib/rack/tracker/bing/bing.rb | 12 +++++ lib/rack/tracker/bing/template/bing.erb | 22 ++++++++++ spec/handler/bing_spec.rb | 53 +++++++++++++++++++++++ spec/integration/bing_integration_spec.rb | 17 ++++++++ spec/support/metal_controller.rb | 4 ++ 7 files changed, 134 insertions(+) create mode 100644 lib/rack/tracker/bing/bing.rb create mode 100644 lib/rack/tracker/bing/template/bing.erb create mode 100644 spec/handler/bing_spec.rb create mode 100644 spec/integration/bing_integration_spec.rb diff --git a/README.md b/README.md index 499098a..96ccd72 100644 --- a/README.md +++ b/README.md @@ -576,6 +576,31 @@ config.middleware.use(Rack::Tracker) do end ``` +### Bing + +[Bing](https://bingads.microsoft.com/) + +To add the tracking snippet: + +``` +config.middleware.use(Rack::Tracker) do + handler :bing, { tracker: '12345678' } +end +``` + +To send conversion events: +``` +tracker do |t| + t.bing :conversion, { + type: 'event', + category: 'Users', + action: 'Login', + label: 'Standard', + value: 10 + } +end +``` + ### Custom Handlers diff --git a/lib/rack/tracker.rb b/lib/rack/tracker.rb index 1a7c899..c3a72f4 100644 --- a/lib/rack/tracker.rb +++ b/lib/rack/tracker.rb @@ -23,6 +23,7 @@ require "rack/tracker/criteo/criteo" require "rack/tracker/zanox/zanox" require "rack/tracker/hotjar/hotjar" +require "rack/tracker/bing/bing" module Rack class Tracker diff --git a/lib/rack/tracker/bing/bing.rb b/lib/rack/tracker/bing/bing.rb new file mode 100644 index 0000000..f7e71c7 --- /dev/null +++ b/lib/rack/tracker/bing/bing.rb @@ -0,0 +1,12 @@ +class Rack::Tracker::Bing < Rack::Tracker::Handler + + class Conversion < OpenStruct + end + + self.position = :body + + def tracker + options[:tracker].respond_to?(:call) ? options[:tracker].call(env) : options[:tracker] + end + +end diff --git a/lib/rack/tracker/bing/template/bing.erb b/lib/rack/tracker/bing/template/bing.erb new file mode 100644 index 0000000..9c0d342 --- /dev/null +++ b/lib/rack/tracker/bing/template/bing.erb @@ -0,0 +1,22 @@ +<% if events.any? %> + + +<% end %> + + + \ No newline at end of file diff --git a/spec/handler/bing_spec.rb b/spec/handler/bing_spec.rb new file mode 100644 index 0000000..843752a --- /dev/null +++ b/spec/handler/bing_spec.rb @@ -0,0 +1,53 @@ +RSpec.describe Rack::Tracker::Bing do + + it 'will be placed in the body' do + expect(described_class.position).to eq(:body) + end + + describe "with events" do + subject { described_class.new(env, tracker: 'somebody').render } + + describe "default" do + def env + {'tracker' => { + 'bing' => [ + { 'class_name' => 'Conversion', 'category' => 'Users', 'action' => 'Login', 'label' => 'Standard', 'value' => 10 } + ] + }} + end + + it "will show event initialiser" do + expect(subject).to include "window.uetq = window.uetq || [];" + end + + it "will show events" do + expect(subject).to include "window.uetq.push({ 'ec': 'Users', 'ea': 'Login', 'el': 'Standard', 'ev': 10 });" + end + end + end + + describe "with multiple events" do + subject { described_class.new(env, tracker: 'somebody').render } + + describe "default" do + def env + {'tracker' => { + 'bing' => [ + { 'class_name' => 'Conversion', 'category' => 'Users', 'action' => 'Login', 'label' => 'Standard', 'value' => 10 }, + { 'class_name' => 'Conversion', 'category' => 'Users', 'action' => 'Logout', 'label' => 'Standard', 'value' => 5 } + ] + }} + end + + it "will show event initialiser" do + expect(subject).to include "window.uetq = window.uetq || [];" + end + + it "will show events" do + expect(subject).to include "window.uetq.push({ 'ec': 'Users', 'ea': 'Login', 'el': 'Standard', 'ev': 10 });" + expect(subject).to include "window.uetq.push({ 'ec': 'Users', 'ea': 'Logout', 'el': 'Standard', 'ev': 5 });" + end + end + end + +end \ No newline at end of file diff --git a/spec/integration/bing_integration_spec.rb b/spec/integration/bing_integration_spec.rb new file mode 100644 index 0000000..e68b8fd --- /dev/null +++ b/spec/integration/bing_integration_spec.rb @@ -0,0 +1,17 @@ +require 'support/capybara_app_helper' + +RSpec.describe "Bing Integration" do + before do + setup_app(action: :bing) do |tracker| + tracker.handler :bing, { tracker: '12345678' } + end + visit '/' + end + + subject { page } + + it "embeds the script tag with tracker" do + expect(page.find("body")).to have_content('var o = {ti: "12345678"};') + end + +end diff --git a/spec/support/metal_controller.rb b/spec/support/metal_controller.rb index 2bb9493..a666165 100644 --- a/spec/support/metal_controller.rb +++ b/spec/support/metal_controller.rb @@ -116,4 +116,8 @@ def zanox def hotjar render "metal/index" end + + def bing + render "metal/index" + end end From c6827aa8f4bbf52f8d6da02a2c84d5464a61dcdb Mon Sep 17 00:00:00 2001 From: Marco Schaden Date: Thu, 28 Mar 2019 10:14:23 +0100 Subject: [PATCH 28/54] fresh new release :coconut: --- CHANGELOG.md | 6 ++++++ lib/rack/tracker/version.rb | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3603277..2622f17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# 1.9.0 + + * [ENHANCEMENT] Integration for Bing tracking #131 (thx @pcraston) + * [ENHANCEMENT] Possibility to integrate Google Optimize ID into the allowed tracker options #127 (thx @nachoabad) + * [ENHANCEMENT] Support for google global events #126 (thx @atd) + # 1.8.0 * [ENHANCEMENT] Google Global Site Tag: basic integration with support for pageviews to Google global tag #123 (thx @atd) diff --git a/lib/rack/tracker/version.rb b/lib/rack/tracker/version.rb index f6d5d5f..c48fbdb 100644 --- a/lib/rack/tracker/version.rb +++ b/lib/rack/tracker/version.rb @@ -1,5 +1,5 @@ module Rack class Tracker - VERSION = '1.8.0' + VERSION = '1.9.0' end end From a7f566083f015029b41a9bfdf8599a6dcff9e177 Mon Sep 17 00:00:00 2001 From: Michael Bumann Date: Thu, 18 Apr 2019 19:06:36 +0000 Subject: [PATCH 29/54] Update README.md --- README.md | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 96ccd72..caa936e 100644 --- a/README.md +++ b/README.md @@ -18,18 +18,9 @@ rack middleware that can be hooked up to multiple services and exposing them in fashion. It comes in two parts, the first one is the actual middleware that you can add to the middleware stack the second part are the service-handlers that you're going to use in your application. It's easy to add your own [custom handlers](#custom-handlers), -but to get you started we're shipping support for the following services out of the box: - -* [Google Global Site Tag](#google-global) -* [Google Analytics](#google-analytics) -* [Google Adwords Conversion](#google-adwords-conversion) -* [Google Tag Manager](#google-tag-manager) -* [Facebook](#facebook) -* [Visual Website Optimizer (VWO)](#visual-website-optimizer-vwo) -* [GoSquared](#gosquared) -* [Criteo](#criteo) -* [Zanox](#zanox) -* [Hotjar](#hotjar) +but to get you started we're shipping support for the services [mentioned below](#services) +out of the box: + ## Respecting the Do Not Track (DNT) HTTP header @@ -112,6 +103,8 @@ request.env['tracker'] = { } ``` +## Services + ### Google Global Site Tag (gtag.js) * `:anonymize_ip` - sets the tracker to remove the last octet from all IP addresses, see https://developers.google.com/analytics/devguides/collection/gtagjs/ip-anonymization for details. From 5ddc22c134df62eda0535a0ebb046eacdf550db2 Mon Sep 17 00:00:00 2001 From: Chris Coffey Date: Thu, 27 Jun 2019 11:31:48 -0400 Subject: [PATCH 30/54] Add Hubspot integration --- README.md | 12 +++++++++++- lib/rack/tracker.rb | 1 + lib/rack/tracker/hubspot/hubspot.rb | 2 ++ lib/rack/tracker/hubspot/template/hubspot.erb | 1 + lib/rack/tracker/version.rb | 2 +- spec/handler/hubspot_spec.rb | 11 +++++++++++ spec/integration/hubspot_integration_spec.rb | 19 +++++++++++++++++++ spec/support/metal_controller.rb | 4 ++++ 8 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 lib/rack/tracker/hubspot/hubspot.rb create mode 100644 lib/rack/tracker/hubspot/template/hubspot.erb create mode 100644 spec/handler/hubspot_spec.rb create mode 100644 spec/integration/hubspot_integration_spec.rb diff --git a/README.md b/README.md index caa936e..515465c 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ rack middleware that can be hooked up to multiple services and exposing them in fashion. It comes in two parts, the first one is the actual middleware that you can add to the middleware stack the second part are the service-handlers that you're going to use in your application. It's easy to add your own [custom handlers](#custom-handlers), -but to get you started we're shipping support for the services [mentioned below](#services) +but to get you started we're shipping support for the services [mentioned below](#services) out of the box: @@ -594,6 +594,16 @@ tracker do |t| end ``` +### Hubspot + +[Hubspot](https://www.hubspot.com/) + +``` +config.middleware.use(Rack::Tracker) do + handler :hubspot, { site_id: '1234' } +end +``` + ### Custom Handlers diff --git a/lib/rack/tracker.rb b/lib/rack/tracker.rb index c3a72f4..6cce2fb 100644 --- a/lib/rack/tracker.rb +++ b/lib/rack/tracker.rb @@ -24,6 +24,7 @@ require "rack/tracker/zanox/zanox" require "rack/tracker/hotjar/hotjar" require "rack/tracker/bing/bing" +require "rack/tracker/hubspot/hubspot" module Rack class Tracker diff --git a/lib/rack/tracker/hubspot/hubspot.rb b/lib/rack/tracker/hubspot/hubspot.rb new file mode 100644 index 0000000..162b07c --- /dev/null +++ b/lib/rack/tracker/hubspot/hubspot.rb @@ -0,0 +1,2 @@ +class Rack::Tracker::Hubspot < Rack::Tracker::Handler +end diff --git a/lib/rack/tracker/hubspot/template/hubspot.erb b/lib/rack/tracker/hubspot/template/hubspot.erb new file mode 100644 index 0000000..ecf9556 --- /dev/null +++ b/lib/rack/tracker/hubspot/template/hubspot.erb @@ -0,0 +1 @@ + diff --git a/lib/rack/tracker/version.rb b/lib/rack/tracker/version.rb index c48fbdb..bd778d2 100644 --- a/lib/rack/tracker/version.rb +++ b/lib/rack/tracker/version.rb @@ -1,5 +1,5 @@ module Rack class Tracker - VERSION = '1.9.0' + VERSION = '1.9.1' end end diff --git a/spec/handler/hubspot_spec.rb b/spec/handler/hubspot_spec.rb new file mode 100644 index 0000000..ea36178 --- /dev/null +++ b/spec/handler/hubspot_spec.rb @@ -0,0 +1,11 @@ +RSpec.describe Rack::Tracker::Hubspot do + + def env + { misc: '42' } + end + + it 'will be placed in the head' do + expect(described_class.position).to eq(:head) + expect(described_class.new(env).position).to eq(:head) + end +end diff --git a/spec/integration/hubspot_integration_spec.rb b/spec/integration/hubspot_integration_spec.rb new file mode 100644 index 0000000..4414aa4 --- /dev/null +++ b/spec/integration/hubspot_integration_spec.rb @@ -0,0 +1,19 @@ +require 'support/capybara_app_helper' + +RSpec.describe "Hubspot Integration" do + before do + setup_app(action: :hubspot) do |tracker| + tracker.handler :hubspot, { site_id: '123456' } + end + + visit '/' + end + + + subject { page } + + it "embeds the site-specifc script tag" do + expect(page).to have_xpath("//script", id: "hs-script-loader" ) + expect(page.find("script")[:src]).to eq("//js.hs-scripts.com/123456.js") + end +end diff --git a/spec/support/metal_controller.rb b/spec/support/metal_controller.rb index a666165..13f0a61 100644 --- a/spec/support/metal_controller.rb +++ b/spec/support/metal_controller.rb @@ -117,6 +117,10 @@ def hotjar render "metal/index" end + def hubspot + render "metal/index" + end + def bing render "metal/index" end From 68eab20b49f5d4de6683a20487fddd0834a28e0d Mon Sep 17 00:00:00 2001 From: Marco Schaden Date: Thu, 27 Jun 2019 21:03:39 +0200 Subject: [PATCH 31/54] =?UTF-8?q?version=20up=20=F0=9F=8D=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 4 ++++ lib/rack/tracker/version.rb | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2622f17..063ede4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 1.10.0 + + * [ENHANCEMENT] Hubspot integration #136 (thx @ChrisCoffey) + # 1.9.0 * [ENHANCEMENT] Integration for Bing tracking #131 (thx @pcraston) diff --git a/lib/rack/tracker/version.rb b/lib/rack/tracker/version.rb index bd778d2..b8a0953 100644 --- a/lib/rack/tracker/version.rb +++ b/lib/rack/tracker/version.rb @@ -1,5 +1,5 @@ module Rack class Tracker - VERSION = '1.9.1' + VERSION = '1.10.0' end end From 57395d4e18993ab7dbc1207bbc6345ad0a03c154 Mon Sep 17 00:00:00 2001 From: Maris Veide Date: Wed, 10 Jul 2019 13:47:02 +0300 Subject: [PATCH 32/54] #137: Accepting all passed attributes for google_global tracking --- lib/rack/tracker/google_global/google_global.rb | 4 ++-- spec/handler/google_global_spec.rb | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/rack/tracker/google_global/google_global.rb b/lib/rack/tracker/google_global/google_global.rb index 4214ea3..36fb37b 100644 --- a/lib/rack/tracker/google_global/google_global.rb +++ b/lib/rack/tracker/google_global/google_global.rb @@ -12,10 +12,10 @@ def params class Event < OpenStruct PREFIXED_PARAMS = %i[category label] LITERAL_PARAMS = %i[value] - PARAMS = PREFIXED_PARAMS + LITERAL_PARAMS + SKIP_PARAMS = %i[action] def params - Hash[to_h.slice(*PARAMS).map { |key, value| [param_key(key), value] }] + Hash[to_h.except(*SKIP_PARAMS).map { |key, value| [param_key(key), value] }] end private diff --git a/spec/handler/google_global_spec.rb b/spec/handler/google_global_spec.rb index dcf6fcc..0858020 100644 --- a/spec/handler/google_global_spec.rb +++ b/spec/handler/google_global_spec.rb @@ -210,13 +210,15 @@ def env 'action' => 'login', 'category' => 'engagement', 'label' => 'Github', - 'value' => 5 } + 'value' => 5, + 'transaction_id' => 1001, + } ] }} end it "will show event" do - expect(subject).to match(%r{gtag\('event', 'login', {\"event_category\":\"engagement\",\"event_label\":\"Github\",\"value\":5}\);}) + expect(subject).to match(%r{gtag\('event', 'login', {\"event_category\":\"engagement\",\"event_label\":\"Github\",\"value\":5,\"transaction_id\":1001}\);}) end end end From 349d2087f3918a6b99f0051957e341f1d54306ff Mon Sep 17 00:00:00 2001 From: Maris Veide Date: Thu, 11 Jul 2019 10:27:04 +0300 Subject: [PATCH 33/54] #137: Removed unused constant --- lib/rack/tracker/google_global/google_global.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/rack/tracker/google_global/google_global.rb b/lib/rack/tracker/google_global/google_global.rb index 36fb37b..0631794 100644 --- a/lib/rack/tracker/google_global/google_global.rb +++ b/lib/rack/tracker/google_global/google_global.rb @@ -11,7 +11,6 @@ def params class Event < OpenStruct PREFIXED_PARAMS = %i[category label] - LITERAL_PARAMS = %i[value] SKIP_PARAMS = %i[action] def params From d2c6afc689656143886cf2ef53d101612a42aa28 Mon Sep 17 00:00:00 2001 From: Abby Sassel <3883855+sassela@users.noreply.github.com> Date: Wed, 31 Jul 2019 08:49:47 +0200 Subject: [PATCH 34/54] Add support for Drift (#139) * Add support for Drift * Add usage docs --- README.md | 9 ++++++++ lib/rack/tracker.rb | 1 + lib/rack/tracker/drift/drift.rb | 2 ++ lib/rack/tracker/drift/template/drift.erb | 26 ++++++++++++++++++++++ spec/handler/drift_spec.rb | 10 +++++++++ spec/integration/drift_integration_spec.rb | 18 +++++++++++++++ spec/support/metal_controller.rb | 4 ++++ 7 files changed, 70 insertions(+) create mode 100644 lib/rack/tracker/drift/drift.rb create mode 100644 lib/rack/tracker/drift/template/drift.erb create mode 100644 spec/handler/drift_spec.rb create mode 100644 spec/integration/drift_integration_spec.rb diff --git a/README.md b/README.md index 515465c..a0fdd89 100644 --- a/README.md +++ b/README.md @@ -604,6 +604,15 @@ config.middleware.use(Rack::Tracker) do end ``` +### Drift + +[Drift](https://www.drift.com/) + +``` +config.middleware.use(Rack::Tracker) do + handler :drift, account_id: 'DRIFT_ID' +end +``` ### Custom Handlers diff --git a/lib/rack/tracker.rb b/lib/rack/tracker.rb index 6cce2fb..2d3dc91 100644 --- a/lib/rack/tracker.rb +++ b/lib/rack/tracker.rb @@ -25,6 +25,7 @@ require "rack/tracker/hotjar/hotjar" require "rack/tracker/bing/bing" require "rack/tracker/hubspot/hubspot" +require "rack/tracker/drift/drift" module Rack class Tracker diff --git a/lib/rack/tracker/drift/drift.rb b/lib/rack/tracker/drift/drift.rb new file mode 100644 index 0000000..fb7fe34 --- /dev/null +++ b/lib/rack/tracker/drift/drift.rb @@ -0,0 +1,2 @@ +class Rack::Tracker::Drift < Rack::Tracker::Handler +end diff --git a/lib/rack/tracker/drift/template/drift.erb b/lib/rack/tracker/drift/template/drift.erb new file mode 100644 index 0000000..1b0d12a --- /dev/null +++ b/lib/rack/tracker/drift/template/drift.erb @@ -0,0 +1,26 @@ + diff --git a/spec/handler/drift_spec.rb b/spec/handler/drift_spec.rb new file mode 100644 index 0000000..5f96374 --- /dev/null +++ b/spec/handler/drift_spec.rb @@ -0,0 +1,10 @@ +RSpec.describe Rack::Tracker::Drift do + def env + { foo: 'bar' } + end + + it 'will be placed in the head' do + expect(described_class.position).to eq(:head) + expect(described_class.new(env).position).to eq(:head) + end +end diff --git a/spec/integration/drift_integration_spec.rb b/spec/integration/drift_integration_spec.rb new file mode 100644 index 0000000..3192d2c --- /dev/null +++ b/spec/integration/drift_integration_spec.rb @@ -0,0 +1,18 @@ +require 'support/capybara_app_helper' + +RSpec.describe 'Drift Integration' do + before do + setup_app(action: :drift) do |tracker| + tracker.handler :drift, account_id: 'DRIFT_ID' + end + + visit '/' + end + + subject { page } + + it 'embeds the script with account_id' do + expect(page.find('script')).to have_content('js.driftt.com') + expect(page.find('script')).to have_content('DRIFT_ID') + end +end diff --git a/spec/support/metal_controller.rb b/spec/support/metal_controller.rb index 13f0a61..644b6b7 100644 --- a/spec/support/metal_controller.rb +++ b/spec/support/metal_controller.rb @@ -124,4 +124,8 @@ def hubspot def bing render "metal/index" end + + def drift + render "metal/index" + end end From 21e6d1c27be5f33ce988b209ee29c03ba3742f92 Mon Sep 17 00:00:00 2001 From: Marco Schaden Date: Wed, 31 Jul 2019 08:56:07 +0200 Subject: [PATCH 35/54] fresh new release with new integration --- CHANGELOG.md | 4 ++++ lib/rack/tracker/version.rb | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 063ede4..6d63fc4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 1.11.0 + + * [ENHANCEMENT] Add support for Drift #139 (thx @sassela) + # 1.10.0 * [ENHANCEMENT] Hubspot integration #136 (thx @ChrisCoffey) diff --git a/lib/rack/tracker/version.rb b/lib/rack/tracker/version.rb index b8a0953..dc87117 100644 --- a/lib/rack/tracker/version.rb +++ b/lib/rack/tracker/version.rb @@ -1,5 +1,5 @@ module Rack class Tracker - VERSION = '1.10.0' + VERSION = '1.11.0' end end From 997b90fa6884d6ff6e8368144d782c6a3a70a145 Mon Sep 17 00:00:00 2001 From: Abby Sassel <3883855+sassela@users.noreply.github.com> Date: Thu, 1 Aug 2019 10:36:15 +0100 Subject: [PATCH 36/54] Uncaught ReferenceError Fix: wrap Drift account ID in quotes (#140) * Uncaught ReferenceError Fix: wrap Drift account ID in quotes * Update Drift integration test to verify fix --- lib/rack/tracker/drift/template/drift.erb | 2 +- spec/integration/drift_integration_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rack/tracker/drift/template/drift.erb b/lib/rack/tracker/drift/template/drift.erb index 1b0d12a..e046404 100644 --- a/lib/rack/tracker/drift/template/drift.erb +++ b/lib/rack/tracker/drift/template/drift.erb @@ -22,5 +22,5 @@ } }(); drift.SNIPPET_VERSION = '0.3.1'; -drift.load(<%= options[:account_id] %>); +drift.load('<%= options[:account_id] %>'); diff --git a/spec/integration/drift_integration_spec.rb b/spec/integration/drift_integration_spec.rb index 3192d2c..498538f 100644 --- a/spec/integration/drift_integration_spec.rb +++ b/spec/integration/drift_integration_spec.rb @@ -13,6 +13,6 @@ it 'embeds the script with account_id' do expect(page.find('script')).to have_content('js.driftt.com') - expect(page.find('script')).to have_content('DRIFT_ID') + expect(page.find('script')).to have_content('drift.load(\'DRIFT_ID\')') end end From a9f9eda5e40d4c12ea8bea1e781fb91ec3d41763 Mon Sep 17 00:00:00 2001 From: Marco Schaden Date: Thu, 1 Aug 2019 19:55:10 +0200 Subject: [PATCH 37/54] bugfix release --- CHANGELOG.md | 4 ++++ lib/rack/tracker/version.rb | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d63fc4..885993c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 1.11.1 + + * [BUGFIX] Uncaught ReferenceError Fix: wrap Drift account ID in quotes #140 (thx @sassela) + # 1.11.0 * [ENHANCEMENT] Add support for Drift #139 (thx @sassela) diff --git a/lib/rack/tracker/version.rb b/lib/rack/tracker/version.rb index dc87117..ce6f23f 100644 --- a/lib/rack/tracker/version.rb +++ b/lib/rack/tracker/version.rb @@ -1,5 +1,5 @@ module Rack class Tracker - VERSION = '1.11.0' + VERSION = '1.11.1' end end From 0616d5d088ded1541983688cb16367c6c6e0529c Mon Sep 17 00:00:00 2001 From: Chris Coffey Date: Sun, 25 Aug 2019 12:39:26 -0400 Subject: [PATCH 38/54] Allow disabling explicit pageview sends for GA (#141) * Enable disabling explicit pageview sends for GA * Bump version and update Changelog+Docs --- CHANGELOG.md | 4 ++++ README.md | 1 + .../tracker/google_analytics/google_analytics.rb | 5 +++++ .../template/google_analytics.erb | 2 +- lib/rack/tracker/version.rb | 2 +- spec/handler/google_analytics_spec.rb | 16 ++++++++++++++++ 6 files changed, 28 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 885993c..6523463 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 1.11.2 + + * [ENHANCEMENT] Allows disabling the Google Analytics pageview send. Defaults to true. + # 1.11.1 * [BUGFIX] Uncaught ReferenceError Fix: wrap Drift account ID in quotes #140 (thx @sassela) diff --git a/README.md b/README.md index a0fdd89..469998a 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,7 @@ end * `:enhanced_ecommerce` - Enables [Enhanced Ecommerce Tracking](https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce) * `:optimize` - pass [Google Optimize container ID](https://support.google.com/360suite/optimize/answer/6262084#example-combined-snippet) as value (e.g. `optimize: 'GTM-1234'`). * `:pageview_url_script` - a String containing a custom js script evaluating to the url that shoudl be given to the pageview event. Default to `window.location.pathname + window.location.search`. +* `:explicit_pageview` - A boolean that controls whether to send the `pageview` event on pageload. This defaults to true. #### Events diff --git a/lib/rack/tracker/google_analytics/google_analytics.rb b/lib/rack/tracker/google_analytics/google_analytics.rb index a18d1e0..f33663b 100644 --- a/lib/rack/tracker/google_analytics/google_analytics.rb +++ b/lib/rack/tracker/google_analytics/google_analytics.rb @@ -2,6 +2,11 @@ class Rack::Tracker::GoogleAnalytics < Rack::Tracker::Handler self.allowed_tracker_options = [:cookie_domain, :user_id] + def initialize(env, options = {}) + options[:explicit_pageview] = true if !options.has_key?(:explicit_pageview) + super(env, options) + end + class Send < OpenStruct def initialize(attrs = {}) attrs.reverse_merge!(type: 'event') diff --git a/lib/rack/tracker/google_analytics/template/google_analytics.erb b/lib/rack/tracker/google_analytics/template/google_analytics.erb index 000ff7c..673a671 100644 --- a/lib/rack/tracker/google_analytics/template/google_analytics.erb +++ b/lib/rack/tracker/google_analytics/template/google_analytics.erb @@ -38,7 +38,7 @@ <% if options[:ecommerce] && ecommerce_events.any? %> ga('ecommerce:send'); <% end %> -<% if tracker %> +<% if tracker && options[:explicit_pageview] %> ga('send', 'pageview', <%= pageview_url_script %>); <% end %> diff --git a/lib/rack/tracker/version.rb b/lib/rack/tracker/version.rb index ce6f23f..371b0c0 100644 --- a/lib/rack/tracker/version.rb +++ b/lib/rack/tracker/version.rb @@ -1,5 +1,5 @@ module Rack class Tracker - VERSION = '1.11.1' + VERSION = '1.11.2' end end diff --git a/spec/handler/google_analytics_spec.rb b/spec/handler/google_analytics_spec.rb index 5d46e8d..6951a02 100644 --- a/spec/handler/google_analytics_spec.rb +++ b/spec/handler/google_analytics_spec.rb @@ -284,5 +284,21 @@ def env expect(subject.pageview_url_script).to eql ("{ 'page': location.pathname + location.search + location.hash }") end end + + context 'with explicit_pageview disabled' do + subject { described_class.new(env, {tracker: 'afake', explicit_pageview: false }).render } + + it 'does not send a pageview event' do + expect(subject).not_to include %q{ga('send', 'pageview',} + end + end + + context 'defaults to sending the pageview event' do + subject { described_class.new(env, {tracker: 'afake'}).render } + + it 'does not send a pageview event' do + expect(subject).to include "ga('send', 'pageview'" + end + end end end From 8695ac65036017217722a7a428a5871deb297353 Mon Sep 17 00:00:00 2001 From: Marco Schaden Date: Sun, 25 Aug 2019 18:40:59 +0200 Subject: [PATCH 39/54] version up --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6523463..480708d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ -# 1.11.2 +# 1.12.0 - * [ENHANCEMENT] Allows disabling the Google Analytics pageview send. Defaults to true. + * [ENHANCEMENT] Allows disabling the Google Analytics pageview send. Defaults to true #131 (thx @ChrisCoffey) # 1.11.1 From f554f692be869ff937a4ea61eb8907e5630915f4 Mon Sep 17 00:00:00 2001 From: Marco Schaden Date: Sun, 25 Aug 2019 19:23:40 +0200 Subject: [PATCH 40/54] =?UTF-8?q?=F0=9F=99=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 480708d..e444877 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# 1.12.0 +# 1.11.2 * [ENHANCEMENT] Allows disabling the Google Analytics pageview send. Defaults to true #131 (thx @ChrisCoffey) From 09b46c67a6fc31109ae81a9c9ae757628727899e Mon Sep 17 00:00:00 2001 From: glaszig Date: Mon, 30 Sep 2019 09:20:33 +0200 Subject: [PATCH 41/54] test on rails 6.0 (#144) * test on rails 6.0 * do not test rails 6.0 on ruby < 2.5 on travis * test on jruby-9.2.7.0, do not test rails 6 on jruby-9.1.10.0 --- .travis.yml | 10 ++++++++++ Gemfile.rails-6.0 | 6 ++++++ 2 files changed, 16 insertions(+) create mode 100644 Gemfile.rails-6.0 diff --git a/.travis.yml b/.travis.yml index b754765..3c8e02f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,8 +6,18 @@ rvm: - 2.5.3 - 2.6.1 - jruby-9.1.10.0 + - jruby-9.2.7.0 gemfile: - Gemfile - Gemfile.rails-3.2 - Gemfile.rails-4.2 - Gemfile.rails-5.2 + - Gemfile.rails-6.0 +matrix: + exclude: + - rvm: 2.3.8 + gemfile: Gemfile.rails-6.0 + - rvm: 2.4.5 + gemfile: Gemfile.rails-6.0 + - rvm: jruby-9.1.10.0 + gemfile: Gemfile.rails-6.0 diff --git a/Gemfile.rails-6.0 b/Gemfile.rails-6.0 new file mode 100644 index 0000000..ee3fb05 --- /dev/null +++ b/Gemfile.rails-6.0 @@ -0,0 +1,6 @@ +source "https://rubygems.org" + +gemspec + +gem 'activesupport', '~> 6.0.0' +gem 'actionpack', '~> 6.0.0' From c10ea3ecbe4e8bab6af07a0bed9260d6bfa7c1c4 Mon Sep 17 00:00:00 2001 From: Mohan Zhang Date: Fri, 8 Nov 2019 04:46:01 -0400 Subject: [PATCH 42/54] Add support for Heap (#147) --- README.md | 12 ++++++++++++ lib/rack/tracker.rb | 1 + lib/rack/tracker/heap/heap.rb | 2 ++ lib/rack/tracker/heap/template/heap.erb | 4 ++++ lib/rack/tracker/version.rb | 2 +- spec/handler/heap_spec.rb | 10 ++++++++++ spec/integration/heap_integration_spec.rb | 17 +++++++++++++++++ spec/support/metal_controller.rb | 4 ++++ 8 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 lib/rack/tracker/heap/heap.rb create mode 100644 lib/rack/tracker/heap/template/heap.erb create mode 100644 spec/handler/heap_spec.rb create mode 100644 spec/integration/heap_integration_spec.rb diff --git a/README.md b/README.md index 469998a..e1b7927 100644 --- a/README.md +++ b/README.md @@ -615,6 +615,18 @@ config.middleware.use(Rack::Tracker) do end ``` +### Heap + +[Heap](https://heap.io/). Heap has Projects (e.g. "Main") which have multiple +Environments (e.g. "Production" or "Development"). `env_id` is therefore the numerical ID +that represents the Environment. See Settings -> Projects -> Environments in your dashboard. + +``` +config.middleware.use(Rack::Tracker) do + handler :heap, env_id: 'HEAP_ID' +end +``` + ### Custom Handlers Tough we give you handlers for a few tracking services right out of the box, you might diff --git a/lib/rack/tracker.rb b/lib/rack/tracker.rb index 2d3dc91..ed0a07e 100644 --- a/lib/rack/tracker.rb +++ b/lib/rack/tracker.rb @@ -26,6 +26,7 @@ require "rack/tracker/bing/bing" require "rack/tracker/hubspot/hubspot" require "rack/tracker/drift/drift" +require "rack/tracker/heap/heap" module Rack class Tracker diff --git a/lib/rack/tracker/heap/heap.rb b/lib/rack/tracker/heap/heap.rb new file mode 100644 index 0000000..dac9efb --- /dev/null +++ b/lib/rack/tracker/heap/heap.rb @@ -0,0 +1,2 @@ +class Rack::Tracker::Heap < Rack::Tracker::Handler +end diff --git a/lib/rack/tracker/heap/template/heap.erb b/lib/rack/tracker/heap/template/heap.erb new file mode 100644 index 0000000..b3ac665 --- /dev/null +++ b/lib/rack/tracker/heap/template/heap.erb @@ -0,0 +1,4 @@ + diff --git a/lib/rack/tracker/version.rb b/lib/rack/tracker/version.rb index 371b0c0..fb24a29 100644 --- a/lib/rack/tracker/version.rb +++ b/lib/rack/tracker/version.rb @@ -1,5 +1,5 @@ module Rack class Tracker - VERSION = '1.11.2' + VERSION = '1.12.0' end end diff --git a/spec/handler/heap_spec.rb b/spec/handler/heap_spec.rb new file mode 100644 index 0000000..7e48df4 --- /dev/null +++ b/spec/handler/heap_spec.rb @@ -0,0 +1,10 @@ +RSpec.describe Rack::Tracker::Heap do + def env + { foo: 'bar' } + end + + it 'will be placed in the head' do + expect(described_class.position).to eq(:head) + expect(described_class.new(env).position).to eq(:head) + end +end diff --git a/spec/integration/heap_integration_spec.rb b/spec/integration/heap_integration_spec.rb new file mode 100644 index 0000000..ff866a1 --- /dev/null +++ b/spec/integration/heap_integration_spec.rb @@ -0,0 +1,17 @@ +require 'support/capybara_app_helper' + +RSpec.describe "Heap Integration" do + before do + setup_app(action: :heap) do |tracker| + tracker.handler :heap, { env_id: '12341234' } + end + + visit '/' + end + + subject { page } + + it 'embeds the script with site_id' do + expect(page).to have_content('heap.load("12341234");') + end +end diff --git a/spec/support/metal_controller.rb b/spec/support/metal_controller.rb index 644b6b7..f22447e 100644 --- a/spec/support/metal_controller.rb +++ b/spec/support/metal_controller.rb @@ -128,4 +128,8 @@ def bing def drift render "metal/index" end + + def heap + render "metal/index" + end end From f3d4e65dee9527136c7613940f5cee16fba91f88 Mon Sep 17 00:00:00 2001 From: Marco Schaden Date: Thu, 14 Nov 2019 10:21:19 +0100 Subject: [PATCH 43/54] add integration spec for callable behaviour --- .../template/google_analytics.erb | 4 ++-- spec/integration/rails_integration_spec.rb | 23 +++++++++++++------ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/lib/rack/tracker/google_analytics/template/google_analytics.erb b/lib/rack/tracker/google_analytics/template/google_analytics.erb index 673a671..5aa0159 100644 --- a/lib/rack/tracker/google_analytics/template/google_analytics.erb +++ b/lib/rack/tracker/google_analytics/template/google_analytics.erb @@ -1,5 +1,5 @@ - +<% end %> diff --git a/spec/integration/rails_integration_spec.rb b/spec/integration/rails_integration_spec.rb index 368efbe..ec932f0 100644 --- a/spec/integration/rails_integration_spec.rb +++ b/spec/integration/rails_integration_spec.rb @@ -1,10 +1,19 @@ require 'support/capybara_app_helper' RSpec.describe "Rails Integration" do + let(:callable_skip_inject) do + lambda do |env| + # check for anything in the env hash to decide + # if you return the `tracker id` or `nil` to skip injection + nil + end + end + before do setup_app(action: :index) do |tracker| tracker.handler :track_all_the_things, { custom_key: 'SomeKey123' } tracker.handler :another_handler, { custom_key: 'AnotherKey42' } + tracker.handler :google_analytics, { tracker: callable_skip_inject } end visit '/' @@ -17,15 +26,15 @@ Metal Layout - - + +

welcome to metal#index

- + HTML From e22fb0f40762d1f7fb5f81e482f1631019ea83c1 Mon Sep 17 00:00:00 2001 From: Marco Schaden Date: Thu, 14 Nov 2019 10:23:32 +0100 Subject: [PATCH 44/54] heap release :sunny: --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e444877..3643e28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 1.12.0 + +* [ENHANCEMENT] Add support for Heap #147 (thx @mohanzhang) + # 1.11.2 * [ENHANCEMENT] Allows disabling the Google Analytics pageview send. Defaults to true #131 (thx @ChrisCoffey) From 42b8e5101ade142932e2a0e5dc96a81afff40ba4 Mon Sep 17 00:00:00 2001 From: glaszig Date: Sun, 8 Dec 2019 10:56:21 +0100 Subject: [PATCH 45/54] google global: better empty tracker handling (#142) * google global: better empty tracker handling * google global: improve integration test * google global: collect trackers only once * google global: improve tracker option validation and collection logic * google global: test if callable gets called * code style * google global: simplified invalid tracker check * warn about empty google global tracker --- .../tracker/google_global/google_global.rb | 29 +++++++++++++++++-- .../google_global/template/google_global.erb | 2 +- .../google_global_integration_spec.rb | 27 ++++++++++++----- 3 files changed, 46 insertions(+), 12 deletions(-) diff --git a/lib/rack/tracker/google_global/google_global.rb b/lib/rack/tracker/google_global/google_global.rb index 0631794..5ccb817 100644 --- a/lib/rack/tracker/google_global/google_global.rb +++ b/lib/rack/tracker/google_global/google_global.rb @@ -35,9 +35,7 @@ def events end def trackers - options[:trackers].map { |tracker| - tracker[:id].respond_to?(:call) ? tracker.merge(id: tracker[:id].call(env)) : tracker - }.reject { |tracker| tracker[:id].nil? } + @_trackers ||= build_trackers end def set_options @@ -46,6 +44,31 @@ def set_options private + def build_trackers + options[:trackers].map(&method(:call_tracker)).reject(&method(:invalid_tracker?)) + end + + def call_tracker(tracker) + if tracker[:id].respond_to?(:call) + tracker.merge(id: tracker[:id].call(env)) + else + tracker + end + end + + def invalid_tracker?(tracker) + if tracker[:id].to_s.strip == '' + $stdout.puts <<~WARN + WARNING: One of the trackers specified for Rack::Tracker handler 'google_global' is empty. + Trackers: #{options[:trackers]} + WARN + + true + else + false + end + end + def build_set_options value = options[:set] value.respond_to?(:call) ? value.call(env) : value diff --git a/lib/rack/tracker/google_global/template/google_global.erb b/lib/rack/tracker/google_global/template/google_global.erb index b6141a8..9103419 100644 --- a/lib/rack/tracker/google_global/template/google_global.erb +++ b/lib/rack/tracker/google_global/template/google_global.erb @@ -1,4 +1,4 @@ -<% if trackers %> +<% if trackers.any? %> <% end %> From 590bc95a0220f7249327fd3e662d92471274408c Mon Sep 17 00:00:00 2001 From: Ignacy Kasperowicz Date: Mon, 24 Feb 2020 13:46:07 +0100 Subject: [PATCH 47/54] Make middleware thread safe --- lib/rack/tracker.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/rack/tracker.rb b/lib/rack/tracker.rb index ed0a07e..e30cf98 100644 --- a/lib/rack/tracker.rb +++ b/lib/rack/tracker.rb @@ -38,6 +38,10 @@ def initialize(app, &block) end def call(env) + dup._call(env) + end + + def _call(env) @status, @headers, @body = @app.call(env) return [@status, @headers, @body] unless html? response = Rack::Response.new([], @status, @headers) From f599f7197fe2de0f1385c4690a0b3d8a6611bd8a Mon Sep 17 00:00:00 2001 From: Michael Bumann Date: Wed, 25 Mar 2020 05:58:16 +0000 Subject: [PATCH 48/54] Use local variables in rack middleware to prevent instance state changes (#151) see: https://github.com/railslove/rack-tracker/pull/150 Co-authored-by: Marco --- lib/rack/tracker.rb | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/rack/tracker.rb b/lib/rack/tracker.rb index e30cf98..4e10d87 100644 --- a/lib/rack/tracker.rb +++ b/lib/rack/tracker.rb @@ -42,10 +42,10 @@ def call(env) end def _call(env) - @status, @headers, @body = @app.call(env) - return [@status, @headers, @body] unless html? - response = Rack::Response.new([], @status, @headers) + status, headers, body = @app.call(env) + return [status, headers, body] unless headers['Content-Type'] =~ /html/ + response = Rack::Response.new([], status, headers) env[EVENT_TRACKING_KEY] ||= {} if session = env["rack.session"] @@ -56,16 +56,14 @@ def _call(env) session[EVENT_TRACKING_KEY] = env[EVENT_TRACKING_KEY] end - @body.each { |fragment| response.write inject(env, fragment) } - @body.close if @body.respond_to?(:close) + body.each { |fragment| response.write inject(env, fragment) } + body.close if body.respond_to?(:close) response.finish end private - def html?; @headers['Content-Type'] =~ /html/; end - def inject(env, response) duplicated_response = response.dup @handlers.each(env) do |handler| From 1ad0e20e03b8a6fc1230c773c47505ca929258ea Mon Sep 17 00:00:00 2001 From: Marco Schaden Date: Fri, 27 Mar 2020 06:12:14 +0100 Subject: [PATCH 49/54] version up --- CHANGELOG.md | 5 +++++ lib/rack/tracker/version.rb | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3643e28..2e06e08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 1.12.1 + +* [ENHANCEMENT] Use local variables to prevent instance state #151 (thx @bumi) +* [ENHANCEMENT] Make middleware thread safe #150 (thx @kspe) + # 1.12.0 * [ENHANCEMENT] Add support for Heap #147 (thx @mohanzhang) diff --git a/lib/rack/tracker/version.rb b/lib/rack/tracker/version.rb index fb24a29..7d12101 100644 --- a/lib/rack/tracker/version.rb +++ b/lib/rack/tracker/version.rb @@ -1,5 +1,5 @@ module Rack class Tracker - VERSION = '1.12.0' + VERSION = '1.12.1' end end From bba79d6fb87fdbb3f90da63ffee955aaafa5fdc9 Mon Sep 17 00:00:00 2001 From: yutoji Date: Wed, 8 Jul 2020 11:35:14 +0900 Subject: [PATCH 50/54] Use leftmost match to inject head to avoid one line html bug If the response html has no new line after tag, the gtm script tag will be placed on a invalid place. --- .../google_tag_manager/google_tag_manager.rb | 2 +- spec/handler/google_tag_manager_spec.rb | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/lib/rack/tracker/google_tag_manager/google_tag_manager.rb b/lib/rack/tracker/google_tag_manager/google_tag_manager.rb index 2db9d2b..1a05489 100644 --- a/lib/rack/tracker/google_tag_manager/google_tag_manager.rb +++ b/lib/rack/tracker/google_tag_manager/google_tag_manager.rb @@ -10,7 +10,7 @@ def inject(response) # Sub! is enough, in well formed html there's only one head or body tag. # Block syntax need to be used, otherwise backslashes in input will mess the output. # @see http://stackoverflow.com/a/4149087/518204 and https://github.com/railslove/rack-tracker/issues/50 - response.sub! %r{} do |m| + response.sub! %r{} do |m| m.to_s << self.render_head end response.sub! %r{} do |m| diff --git a/spec/handler/google_tag_manager_spec.rb b/spec/handler/google_tag_manager_spec.rb index ed1e147..d86568e 100644 --- a/spec/handler/google_tag_manager_spec.rb +++ b/spec/handler/google_tag_manager_spec.rb @@ -32,4 +32,27 @@ def env end end + describe '#inject' do + subject { handler_object.inject(example_response) } + let(:handler_object) { described_class.new(env, container: 'somebody') } + + before do + allow(handler_object).to receive(:render_head).and_return('') + allow(handler_object).to receive(:render_body).and_return('') + end + + context 'with one line html response' do + let(:example_response) { "" } + + it 'will have render_head content in head tag' do + expect(subject).to match(%r{.*.*}) + end + + it 'will have render_body content in body tag' do + expect(subject).to match(%r{.*.*}) + end + + end + end + end From 20009badc7d2bcd35d15adfad159602af372527c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Pierzcha=C5=82a?= Date: Wed, 10 Mar 2021 18:27:13 +0100 Subject: [PATCH 51/54] hotwired/turbo support in GTM --- .../template/google_tag_manager_head.erb | 7 +++++++ spec/integration/google_tag_manager_integration_spec.rb | 5 +++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/rack/tracker/google_tag_manager/template/google_tag_manager_head.erb b/lib/rack/tracker/google_tag_manager/template/google_tag_manager_head.erb index 3b059c0..cd930d0 100644 --- a/lib/rack/tracker/google_tag_manager/template/google_tag_manager_head.erb +++ b/lib/rack/tracker/google_tag_manager/template/google_tag_manager_head.erb @@ -17,6 +17,13 @@ <% end %> dataLayer.push({'event':'pageView','virtualUrl': url}); }); + document.addEventListener('turbo:load', function(event) { + var url = event.detail.url; + <% if events.any? %> + dataLayer.push(<%= events.map(&:write).join(', ') %>); + <% end %> + dataLayer.push({'event':'pageView','virtualUrl': url}); + }); <% end %> (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], diff --git a/spec/integration/google_tag_manager_integration_spec.rb b/spec/integration/google_tag_manager_integration_spec.rb index f8fe375..9d2b5aa 100644 --- a/spec/integration/google_tag_manager_integration_spec.rb +++ b/spec/integration/google_tag_manager_integration_spec.rb @@ -23,7 +23,7 @@ expect(page.find("body")).to have_xpath '//body/noscript/iframe[@src="https://www.googletagmanager.com/ns.html?id=GTM-ABCDEF"]' end - it "embeds the turbolinks observer if requested" do + it "embeds turbolinks and turbo observers if requested" do visit '/' expect(page.find("head")).to_not have_content "turbolinks:load" setup_app(action: :google_tag_manager) do |tracker| @@ -31,5 +31,6 @@ end visit '/' expect(page.find("head")).to have_content "turbolinks:load" + expect(page.find("head")).to have_content "turbo:load" end -end \ No newline at end of file +end From 7e1592a030f486cc17cb2bb1f7ef6967fb94415d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Pierzcha=C5=82a?= Date: Thu, 11 Mar 2021 15:09:41 +0100 Subject: [PATCH 52/54] Prevent duplicate events --- .../template/google_tag_manager_head.erb | 27 ++++++++----------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/lib/rack/tracker/google_tag_manager/template/google_tag_manager_head.erb b/lib/rack/tracker/google_tag_manager/template/google_tag_manager_head.erb index cd930d0..5032e60 100644 --- a/lib/rack/tracker/google_tag_manager/template/google_tag_manager_head.erb +++ b/lib/rack/tracker/google_tag_manager/template/google_tag_manager_head.erb @@ -1,30 +1,25 @@ <% if container %> - <% unless options[:turbolinks] %> - <% if events.any? %> - - <% end %> - <% end %> - + + <% if events.any? %> + + <% end %> + + <% end %> From de0da83dd0316ac6dc7f24faaab9b428751bdfa0 Mon Sep 17 00:00:00 2001 From: Marco Schaden Date: Mon, 12 Jul 2021 07:55:53 +0200 Subject: [PATCH 54/54] turbo release --- CHANGELOG.md | 5 +++++ lib/rack/tracker/version.rb | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e06e08..4398e32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 1.13.0 + +* [ENHANCEMENT] hotwired/turbo support #160 (thx @wrozka) +* [BUGFIX] Use leftmost match for gtm tag injection #156 (thx @yutoji) + # 1.12.1 * [ENHANCEMENT] Use local variables to prevent instance state #151 (thx @bumi) diff --git a/lib/rack/tracker/version.rb b/lib/rack/tracker/version.rb index 7d12101..3cf2d99 100644 --- a/lib/rack/tracker/version.rb +++ b/lib/rack/tracker/version.rb @@ -1,5 +1,5 @@ module Rack class Tracker - VERSION = '1.12.1' + VERSION = '1.13.0' end end