diff --git a/README.md b/README.md
index e1b7927..e00f21c 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,6 @@
+# This is for a proof-of-concept implementation of rack-tracker for our BillDoctor repo.
+[Proof-of-concept PR here](https://github.com/cpcmarketing/billdoctor-web/pull/1404)
+
# Rack::Tracker
[](https://codeclimate.com/github/railslove/rack-tracker) [](https://travis-ci.org/railslove/rack-tracker)
@@ -283,11 +286,11 @@ Google Tag manager code snippet supports the container id
end
```
-You can also use an experimental feature to track pageviews under turbolinks, which adds a `pageView` event with a `virtualUrl` of the current url.
+You can also use an experimental feature to track pageviews under turbo, which adds a `pageView` event with a `virtualUrl` of the current url.
```ruby
config.middleware.use(Rack::Tracker) do
- handler :google_tag_manager, { container: 'GTM-XXXXXX', turbolinks: true }
+ handler :google_tag_manager, { container: 'GTM-XXXXXX', turbo: true }
end
```
diff --git a/lib/rack/tracker.rb b/lib/rack/tracker.rb
index 4e10d87..3d843a5 100644
--- a/lib/rack/tracker.rb
+++ b/lib/rack/tracker.rb
@@ -1,9 +1,6 @@
require "rack"
require "tilt"
-require "active_support/core_ext/class/attribute"
-require "active_support/core_ext/hash"
-require "active_support/json"
-require "active_support/inflector"
+require "active_support/all"
require "rack/tracker/version"
require "rack/tracker/extensions"
@@ -27,6 +24,10 @@
require "rack/tracker/hubspot/hubspot"
require "rack/tracker/drift/drift"
require "rack/tracker/heap/heap"
+require "rack/tracker/tiktok_pixel/tiktok_pixel"
+require "rack/tracker/impact/impact"
+require "rack/tracker/cordial/cordial"
+require "rack/tracker/braze/braze"
module Rack
class Tracker
diff --git a/lib/rack/tracker/braze/braze.rb b/lib/rack/tracker/braze/braze.rb
new file mode 100644
index 0000000..7286f0c
--- /dev/null
+++ b/lib/rack/tracker/braze/braze.rb
@@ -0,0 +1,37 @@
+class Rack::Tracker::Braze < Rack::Tracker::Handler
+ class Event < OpenStruct
+ def write; end
+ end
+
+ class AutomaticallyShowInAppMessages < Event
+ def name
+ 'automaticallyShowInAppMessages'
+ end
+ end
+
+ class ChangeUser < Event
+ def name
+ 'changeUser'
+ end
+
+ def write
+ user_id.to_json
+ end
+ end
+
+ class LogCustomEvent < Event
+ def name
+ 'logCustomEvent'
+ end
+
+ def write
+ event_name.to_json << ", #{properties.to_json}"
+ end
+ end
+
+ class OpenSession < Event
+ def name
+ 'openSession'
+ end
+ end
+end
diff --git a/lib/rack/tracker/braze/template/braze.erb b/lib/rack/tracker/braze/template/braze.erb
new file mode 100644
index 0000000..0c256f8
--- /dev/null
+++ b/lib/rack/tracker/braze/template/braze.erb
@@ -0,0 +1,22 @@
+
+
+<% if events.any? %>
+
+<% end %>
diff --git a/lib/rack/tracker/cordial/cordial.rb b/lib/rack/tracker/cordial/cordial.rb
new file mode 100644
index 0000000..3780a80
--- /dev/null
+++ b/lib/rack/tracker/cordial/cordial.rb
@@ -0,0 +1,40 @@
+class Rack::Tracker::Cordial < Rack::Tracker::Handler
+ class Event < OpenStruct
+ def write
+ meta_data = action_name.present? ? action_name_to_json : ""
+ properties.present? ? meta_data << properties_to_json : meta_data
+ end
+
+ def action_name_to_json
+ "#{action_name.to_json}, "
+ end
+
+ def properties_to_json
+ # TODO: Set this up so we can pass in JavaScript variable names. Currently, we can only pass in strings since we're using to_json.
+ # Ex: crdl("event", "NPSSubmit", {"cookie_id":"8ded0052-4668-4b57-9e14-bd8501678f92","email":null,"first_name":null,"rating":"e.detail.recommendation_rating.value"});
+ props = properties.to_json
+ end
+ end
+
+ class Connect < Event
+ def name
+ 'connect'
+ end
+ end
+
+ class Contact < Event
+ def name
+ 'contact'
+ end
+
+ def write
+ "#{auth_data.to_json}, #{contact_data.to_json}"
+ end
+ end
+
+ class CustomEvent < Event
+ def name
+ 'event'
+ end
+ end
+end
diff --git a/lib/rack/tracker/cordial/template/cordial.erb b/lib/rack/tracker/cordial/template/cordial.erb
new file mode 100644
index 0000000..acdef19
--- /dev/null
+++ b/lib/rack/tracker/cordial/template/cordial.erb
@@ -0,0 +1,28 @@
+
+
+<% if events.any? %>
+
+<% end %>
diff --git a/lib/rack/tracker/criteo/criteo.rb b/lib/rack/tracker/criteo/criteo.rb
index 5a71c7b..9f538dd 100644
--- a/lib/rack/tracker/criteo/criteo.rb
+++ b/lib/rack/tracker/criteo/criteo.rb
@@ -30,4 +30,9 @@ def tracker_events
def self.track(name, event_name, event_args = {})
{ name.to_s => [{ 'class_name' => 'Event', 'event' => event_name.to_s.camelize(:lower) }.merge(event_args)] }
end
+
+ def turbo_event?
+ # TODO: Make this work so we can set eventListeners
+ options[:turbo_event]
+ end
end
diff --git a/lib/rack/tracker/criteo/template/criteo.erb b/lib/rack/tracker/criteo/template/criteo.erb
index 18ea6c6..3e8e751 100644
--- a/lib/rack/tracker/criteo/template/criteo.erb
+++ b/lib/rack/tracker/criteo/template/criteo.erb
@@ -1,9 +1,18 @@
<% if events.any? %>
-
-
+
<% end %>
diff --git a/lib/rack/tracker/facebook_pixel/facebook_pixel.rb b/lib/rack/tracker/facebook_pixel/facebook_pixel.rb
index f6015dd..ce81717 100644
--- a/lib/rack/tracker/facebook_pixel/facebook_pixel.rb
+++ b/lib/rack/tracker/facebook_pixel/facebook_pixel.rb
@@ -1,10 +1,11 @@
class Rack::Tracker::FacebookPixel < Rack::Tracker::Handler
- self.position = :body
self.allowed_tracker_options = [:id]
class Event < OpenStruct
def write
- options.present? ? type_to_json << options_to_json : type_to_json
+ meta_data = type_to_json
+ options.present? ? meta_data << options_to_json : meta_data
+ event_id.present? ? meta_data << event_id_to_json : meta_data
end
private
@@ -13,11 +14,25 @@ def type_to_json
type.to_json
end
+ def event_id_to_json
+ ", #{event_id.to_json}"
+ end
+
def options_to_json
", #{options.to_json}"
end
end
+ class Init < Event
+ def name
+ 'init'
+ end
+
+ def write
+ options.present? ? options_to_json : nil
+ end
+ end
+
class Track < Event
def name
'track'
@@ -29,4 +44,25 @@ def name
'trackCustom'
end
end
+
+ 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|
+ m.to_s << self.render_head
+ end
+ response.sub! %r{} do |m|
+ m.to_s << self.render_body
+ end
+ response
+ end
+
+ def render_head
+ Tilt.new( File.join( File.dirname(__FILE__), 'template', 'facebook_pixel_head.erb') ).render(self)
+ end
+
+ def render_body
+ Tilt.new( File.join( File.dirname(__FILE__), 'template', 'facebook_pixel_body.erb') ).render(self)
+ end
end
diff --git a/lib/rack/tracker/facebook_pixel/template/facebook_pixel_body.erb b/lib/rack/tracker/facebook_pixel/template/facebook_pixel_body.erb
new file mode 100644
index 0000000..0d95d12
--- /dev/null
+++ b/lib/rack/tracker/facebook_pixel/template/facebook_pixel_body.erb
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/lib/rack/tracker/facebook_pixel/template/facebook_pixel.erb b/lib/rack/tracker/facebook_pixel/template/facebook_pixel_head.erb
similarity index 67%
rename from lib/rack/tracker/facebook_pixel/template/facebook_pixel.erb
rename to lib/rack/tracker/facebook_pixel/template/facebook_pixel_head.erb
index 2901c52..7cbd2b0 100644
--- a/lib/rack/tracker/facebook_pixel/template/facebook_pixel.erb
+++ b/lib/rack/tracker/facebook_pixel/template/facebook_pixel_head.erb
@@ -6,19 +6,17 @@
n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0;
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', '<%= tracker_options[:id] %>');
- fbq('track', "PageView");
}
-
<% if events.any? %>
<% end %>
diff --git a/lib/rack/tracker/google_adwords_conversion/template/google_adwords_conversion.erb b/lib/rack/tracker/google_adwords_conversion/template/google_adwords_conversion.erb
index e91e75e..2c8e8ff 100644
--- a/lib/rack/tracker/google_adwords_conversion/template/google_adwords_conversion.erb
+++ b/lib/rack/tracker/google_adwords_conversion/template/google_adwords_conversion.erb
@@ -11,7 +11,7 @@
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 1a05489..d5098ef 100644
--- a/lib/rack/tracker/google_tag_manager/google_tag_manager.rb
+++ b/lib/rack/tracker/google_tag_manager/google_tag_manager.rb
@@ -19,10 +19,6 @@ def inject(response)
response
end
- def container
- options[:container].respond_to?(:call) ? options[:container].call(env) : options[:container]
- end
-
def render_head
Tilt.new( File.join( File.dirname(__FILE__), 'template', 'google_tag_manager_head.erb') ).render(self)
end
diff --git a/lib/rack/tracker/google_tag_manager/template/google_tag_manager_body.erb b/lib/rack/tracker/google_tag_manager/template/google_tag_manager_body.erb
index cc30f77..dbeb4d7 100644
--- a/lib/rack/tracker/google_tag_manager/template/google_tag_manager_body.erb
+++ b/lib/rack/tracker/google_tag_manager/template/google_tag_manager_body.erb
@@ -1,4 +1,2 @@
-<% if container %>
-
-<% end %>
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 2df1117..b47652e 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,34 +1,32 @@
-<% if container %>
-
- <% if options[:turbolinks] %>
- document.addEventListener('turbolinks:load', function(event) {
- var url = event.data.url;
- dataLayer.push({'event':'pageView','virtualUrl': url});
- });
- document.addEventListener('turbo:load', function(event) {
- var url = event.detail.url;
- dataLayer.push({'event':'pageView','virtualUrl': url});
- });
- <% end %>
+<% if options[:universal_analytics_id] %>
+
+<% end %>
- <% if events.any? %>
-
- <% end %>
-
+<% if events.any? %>
+ <%# Generates unique script tags to ensure that turbo includes %>
+ <%# them in the document even if the same event happens more than once %>
+ // Unique Script ID: <%= SecureRandom.base64(8) %>
+ dataLayer.push(<%= events.map(&:write).join(', ') %>);
+
<% end %>
-
-
diff --git a/lib/rack/tracker/heap/heap.rb b/lib/rack/tracker/heap/heap.rb
index dac9efb..2453174 100644
--- a/lib/rack/tracker/heap/heap.rb
+++ b/lib/rack/tracker/heap/heap.rb
@@ -1,2 +1,30 @@
class Rack::Tracker::Heap < Rack::Tracker::Handler
+ class Event < OpenStruct
+ def write
+ return if !properties.present?
+ properties.to_h
+ end
+ end
+
+ class Identify < Event
+ def name
+ 'identify'
+ end
+
+ def write
+ "#{id.to_s}"
+ end
+ end
+
+ class AddUser < Event
+ def name
+ 'addUserProperties'
+ end
+ end
+
+ class AddEvent < Event
+ def name
+ 'addEventProperties'
+ end
+ end
end
diff --git a/lib/rack/tracker/heap/template/heap.erb b/lib/rack/tracker/heap/template/heap.erb
index b3ac665..77aa26a 100644
--- a/lib/rack/tracker/heap/template/heap.erb
+++ b/lib/rack/tracker/heap/template/heap.erb
@@ -1,4 +1,19 @@
+
+<% if events.any? %>
+
+<% end %>
diff --git a/lib/rack/tracker/hotjar/hotjar.rb b/lib/rack/tracker/hotjar/hotjar.rb
index 482e5d3..fc73dcd 100644
--- a/lib/rack/tracker/hotjar/hotjar.rb
+++ b/lib/rack/tracker/hotjar/hotjar.rb
@@ -1,2 +1,10 @@
class Rack::Tracker::Hotjar < Rack::Tracker::Handler
+ class Event < OpenStruct
+ end
+
+ class Identify < Event
+ def name
+ 'identify'
+ end
+ end
end
diff --git a/lib/rack/tracker/hotjar/template/hotjar.erb b/lib/rack/tracker/hotjar/template/hotjar.erb
index 885ff26..c3fc660 100644
--- a/lib/rack/tracker/hotjar/template/hotjar.erb
+++ b/lib/rack/tracker/hotjar/template/hotjar.erb
@@ -1,10 +1,18 @@
+
+<% if events.any? %>
+
+<% end %>
diff --git a/lib/rack/tracker/impact/impact.rb b/lib/rack/tracker/impact/impact.rb
new file mode 100644
index 0000000..9144577
--- /dev/null
+++ b/lib/rack/tracker/impact/impact.rb
@@ -0,0 +1,2 @@
+class Rack::Tracker::Impact < Rack::Tracker::Handler
+end
diff --git a/lib/rack/tracker/impact/template/impact.erb b/lib/rack/tracker/impact/template/impact.erb
new file mode 100644
index 0000000..4a9507b
--- /dev/null
+++ b/lib/rack/tracker/impact/template/impact.erb
@@ -0,0 +1 @@
+
diff --git a/lib/rack/tracker/tiktok_pixel/template/tiktok_pixel.erb b/lib/rack/tracker/tiktok_pixel/template/tiktok_pixel.erb
new file mode 100644
index 0000000..afe90ea
--- /dev/null
+++ b/lib/rack/tracker/tiktok_pixel/template/tiktok_pixel.erb
@@ -0,0 +1,8 @@
+
diff --git a/lib/rack/tracker/tiktok_pixel/tiktok_pixel.rb b/lib/rack/tracker/tiktok_pixel/tiktok_pixel.rb
new file mode 100644
index 0000000..3adbb3f
--- /dev/null
+++ b/lib/rack/tracker/tiktok_pixel/tiktok_pixel.rb
@@ -0,0 +1,5 @@
+class Rack::Tracker::TiktokPixel < Rack::Tracker::Handler
+ self.position = :body
+ self.allowed_tracker_options = [:id]
+
+end
diff --git a/spec/handler/cordial_spec.rb b/spec/handler/cordial_spec.rb
new file mode 100644
index 0000000..9667b62
--- /dev/null
+++ b/spec/handler/cordial_spec.rb
@@ -0,0 +1 @@
+# TODO: this
diff --git a/spec/handler/facebook_pixel_spec.rb b/spec/handler/facebook_pixel_spec.rb
index 9716529..397d235 100644
--- a/spec/handler/facebook_pixel_spec.rb
+++ b/spec/handler/facebook_pixel_spec.rb
@@ -3,13 +3,8 @@ def env
{ 'PIXEL_ID' => 'DYNAMIC_PIXEL_ID' }
end
- it 'will be placed in the body' do
- expect(described_class.position).to eq(:body)
- expect(described_class.new(env).position).to eq(:body)
- end
-
describe 'with static id' do
- subject { described_class.new(env, id: 'PIXEL_ID').render }
+ subject { described_class.new(env, id: 'PIXEL_ID').render_head }
it 'will push the tracking events to the queue' do
expect(subject).to match(%r{fbq\('init', 'PIXEL_ID'\)})
@@ -21,7 +16,7 @@ def env
end
describe 'with dynamic id' do
- subject { described_class.new(env, id: lambda { |env| env['PIXEL_ID'] }).render }
+ subject { described_class.new(env, id: lambda { |env| env['PIXEL_ID'] }).render_head }
it 'will push the tracking events to the queue' do
expect(subject).to match(%r{fbq\('init', 'DYNAMIC_PIXEL_ID'\)})
@@ -59,7 +54,7 @@ def env
}
}
end
- subject { described_class.new(env).render }
+ subject { described_class.new(env).render_head }
it 'will push the tracking events to the queue' do
expect(subject).to match(%r{"track", "Purchase", \{"value":"23","currency":"EUR"\}})
@@ -70,4 +65,27 @@ def env
expect(subject).to match(%r{https://www.facebook.com/tr\?id=&ev=PageView&noscript=1})
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
diff --git a/spec/handler/impact_spec.rb b/spec/handler/impact_spec.rb
new file mode 100644
index 0000000..9667b62
--- /dev/null
+++ b/spec/handler/impact_spec.rb
@@ -0,0 +1 @@
+# TODO: this
diff --git a/spec/handler/tiktok_pixel_spec.rb b/spec/handler/tiktok_pixel_spec.rb
new file mode 100644
index 0000000..46b015a
--- /dev/null
+++ b/spec/handler/tiktok_pixel_spec.rb
@@ -0,0 +1,26 @@
+RSpec.describe Rack::Tracker::TiktokPixel do
+ def env
+ { 'PIXEL_ID' => 'DYNAMIC_PIXEL_ID' }
+ end
+
+ it 'will be placed in the body' do
+ expect(described_class.position).to eq(:body)
+ expect(described_class.new(env).position).to eq(:body)
+ end
+
+ describe 'with static id' do
+ subject { described_class.new(env, id: 'PIXEL_ID').render }
+
+ it 'will push the tracking events to the queue' do
+ expect(subject).to match(%r{ttq\.load\('PIXEL_ID'\)})
+ 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{ttq\.load\('DYNAMIC_PIXEL_ID'\)})
+ end
+ end
+end
diff --git a/spec/integration/cordial_integration_spec.rb b/spec/integration/cordial_integration_spec.rb
new file mode 100644
index 0000000..9667b62
--- /dev/null
+++ b/spec/integration/cordial_integration_spec.rb
@@ -0,0 +1 @@
+# TODO: this
diff --git a/spec/integration/google_tag_manager_integration_spec.rb b/spec/integration/google_tag_manager_integration_spec.rb
index 9d2b5aa..a25c35c 100644
--- a/spec/integration/google_tag_manager_integration_spec.rb
+++ b/spec/integration/google_tag_manager_integration_spec.rb
@@ -23,14 +23,13 @@
expect(page.find("body")).to have_xpath '//body/noscript/iframe[@src="https://www.googletagmanager.com/ns.html?id=GTM-ABCDEF"]'
end
- it "embeds turbolinks and turbo observers if requested" do
+ it "embeds turbo observers if requested" do
visit '/'
- expect(page.find("head")).to_not have_content "turbolinks:load"
+ expect(page.find("head")).to_not have_content "turbo:load"
setup_app(action: :google_tag_manager) do |tracker|
- tracker.handler :google_tag_manager, { container: 'GTM-ABCDEF', turbolinks: true }
+ tracker.handler :google_tag_manager, { container: 'GTM-ABCDEF', turbo: true }
end
visit '/'
- expect(page.find("head")).to have_content "turbolinks:load"
expect(page.find("head")).to have_content "turbo:load"
end
end
diff --git a/spec/integration/impact_integration_spec.rb b/spec/integration/impact_integration_spec.rb
new file mode 100644
index 0000000..9667b62
--- /dev/null
+++ b/spec/integration/impact_integration_spec.rb
@@ -0,0 +1 @@
+# TODO: this
diff --git a/spec/integration/tiktok_pixel_integration_spec.rb b/spec/integration/tiktok_pixel_integration_spec.rb
new file mode 100644
index 0000000..97f0d5c
--- /dev/null
+++ b/spec/integration/tiktok_pixel_integration_spec.rb
@@ -0,0 +1,16 @@
+require 'support/capybara_app_helper'
+
+RSpec.describe "Tiktok Pixel Integration" do
+ before do
+ setup_app(action: :tiktok_pixel) do |tracker|
+ tracker.handler :tiktok_pixel, { id: 'PIXEL_ID' }
+ end
+ visit '/'
+ end
+
+ subject { page }
+
+ it "embeds the script tag from the controller action" do
+ expect(page).to have_content("ttq.load('PIXEL_ID');")
+ end
+end
diff --git a/spec/support/metal_controller.rb b/spec/support/metal_controller.rb
index f22447e..336e749 100644
--- a/spec/support/metal_controller.rb
+++ b/spec/support/metal_controller.rb
@@ -132,4 +132,20 @@ def drift
def heap
render "metal/index"
end
+
+ def tiktok_pixel
+ render "metal/index"
+ end
+
+ def impact
+ render "metal/index"
+ end
+
+ def cordial
+ render "metal/index"
+ end
+
+ def braze
+ render "metal/index"
+ end
end