From 29297c9f9966ca44c5876d3e6c101e7853b7d0ad Mon Sep 17 00:00:00 2001 From: bumi Date: Fri, 9 Jun 2017 17:41:27 +0200 Subject: [PATCH 1/2] Add support for different tracker locations for google tag manager This allows handlers to define an #inject(response) method to directly manipulate the response html. The google tag manager handler uses this to inject a snippet into the head and one snippet into the body (as required by google) --- lib/rack/tracker.rb | 7 +------ .../google_tag_manager/google_tag_manager.rb | 15 ++++++++++++--- .../template/google_tag_manager_body.erb | 4 ++++ ...ag_manager.erb => google_tag_manager_head.erb} | 5 +---- lib/rack/tracker/handler.rb | 8 ++++++++ spec/handler/google_tag_manager_spec.rb | 10 ++-------- .../google_tag_manager_integration_spec.rb | 5 +++-- 7 files changed, 31 insertions(+), 23 deletions(-) create mode 100644 lib/rack/tracker/google_tag_manager/template/google_tag_manager_body.erb rename lib/rack/tracker/google_tag_manager/template/{google_tag_manager.erb => google_tag_manager_head.erb} (64%) diff --git a/lib/rack/tracker.rb b/lib/rack/tracker.rb index a7e1780..640d971 100644 --- a/lib/rack/tracker.rb +++ b/lib/rack/tracker.rb @@ -58,12 +58,7 @@ def html?; @headers['Content-Type'] =~ /html/; end def inject(env, response) @handlers.each(env) do |handler| - # 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| - handler.render << m.to_s - end + handler.inject(response) end response end 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 4bac22c..677948a 100644 --- a/lib/rack/tracker/google_tag_manager/google_tag_manager.rb +++ b/lib/rack/tracker/google_tag_manager/google_tag_manager.rb @@ -6,9 +6,18 @@ def write end end - # It is strongly recommended to put the google_tag_manager snippet only in the body tag - # https://developers.google.com/tag-manager/quickstart - self.position = :body + 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 container options[:container].respond_to?(:call) ? options[:container].call(env) : options[:container] 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 new file mode 100644 index 0000000..cc30f77 --- /dev/null +++ b/lib/rack/tracker/google_tag_manager/template/google_tag_manager_body.erb @@ -0,0 +1,4 @@ +<% if container %> + +<% end %> diff --git a/lib/rack/tracker/google_tag_manager/template/google_tag_manager.erb b/lib/rack/tracker/google_tag_manager/template/google_tag_manager_head.erb similarity index 64% rename from lib/rack/tracker/google_tag_manager/template/google_tag_manager.erb rename to lib/rack/tracker/google_tag_manager/template/google_tag_manager_head.erb index 9a3743f..f096851 100644 --- a/lib/rack/tracker/google_tag_manager/template/google_tag_manager.erb +++ b/lib/rack/tracker/google_tag_manager/template/google_tag_manager_head.erb @@ -9,12 +9,9 @@ dataLayer.push( ); - - <% end %> diff --git a/lib/rack/tracker/handler.rb b/lib/rack/tracker/handler.rb index cab471d..238ab9d 100644 --- a/lib/rack/tracker/handler.rb +++ b/lib/rack/tracker/handler.rb @@ -21,6 +21,14 @@ def events def render raise NotImplementedError.new('needs implementation') + 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| + self.render << m.to_s + end + response end def self.track(name, event) diff --git a/spec/handler/google_tag_manager_spec.rb b/spec/handler/google_tag_manager_spec.rb index 3edaa14..ed1e147 100644 --- a/spec/handler/google_tag_manager_spec.rb +++ b/spec/handler/google_tag_manager_spec.rb @@ -7,12 +7,6 @@ def env } end - it 'will be placed in the body by default' do - expect(described_class.position).to eq(:body) - expect(described_class.new(env).position).to eq(:body) - expect(described_class.new(env, position: :head).position).to eq(:head) - end - describe "with events" do describe "default" do def env @@ -23,7 +17,7 @@ def env }} end - subject { described_class.new(env, container: 'somebody').render } + subject { described_class.new(env, container: 'somebody').render_head } it "will show events" do expect(subject).to match(%r{"page":"Cart","price":50,"content_ids":\["sku_1","sku_2","sku_3"\]}) end @@ -31,7 +25,7 @@ def env end describe "with dynamic tracker" do - subject { described_class.new(env, { container: lambda { |env| return env[:misc] }}).render } + subject { described_class.new(env, { container: lambda { |env| return env[:misc] }}).render_head } it 'will call tracker lambdas to obtain tracking codes' do expect(subject).to match(%r{\(window,document,'script','dataLayer','foobar'\)}) diff --git a/spec/integration/google_tag_manager_integration_spec.rb b/spec/integration/google_tag_manager_integration_spec.rb index e7c66e6..8bc6345 100644 --- a/spec/integration/google_tag_manager_integration_spec.rb +++ b/spec/integration/google_tag_manager_integration_spec.rb @@ -11,8 +11,9 @@ subject { page } it "embeds the script tag with tracking event from the controller action" do - expect(page.find("body")).to have_content 'GTM-ABCDEF' - expect(page.find("body")).to have_content "dataLayer.push( {\"click\":\"X\",\"price\":10}, {\"transactionProducts\":[{\"sku\":\"DD44\",\"name\":\"T-shirt\"},{\"sku\":\"DD66\",\"name\":\"Jeans\"}]} );" + expect(page.find("head")).to have_content 'GTM-ABCDEF' + expect(page.find("head")).to have_content "dataLayer.push( {\"click\":\"X\",\"price\":10}, {\"transactionProducts\":[{\"sku\":\"DD44\",\"name\":\"T-shirt\"},{\"sku\":\"DD66\",\"name\":\"Jeans\"}]} );" + expect(page.find("body")).to have_xpath '//body/noscript/iframe[@src="https://www.googletagmanager.com/ns.html?id=GTM-ABCDEF"]' end end \ No newline at end of file From 17fbf104adeba111600cc0807690c212ed3bf47f Mon Sep 17 00:00:00 2001 From: bumi Date: Fri, 9 Jun 2017 17:47:10 +0200 Subject: [PATCH 2/2] nothing to see herer :D --- lib/rack/tracker/google_tag_manager/google_tag_manager.rb | 8 ++++++-- lib/rack/tracker/handler.rb | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) 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 677948a..06d0c08 100644 --- a/lib/rack/tracker/google_tag_manager/google_tag_manager.rb +++ b/lib/rack/tracker/google_tag_manager/google_tag_manager.rb @@ -23,8 +23,12 @@ def container options[:container].respond_to?(:call) ? options[:container].call(env) : options[:container] end - def render - Tilt.new( File.join( File.dirname(__FILE__), 'template', 'google_tag_manager.erb') ).render(self) + def render_head + Tilt.new( File.join( File.dirname(__FILE__), 'template', 'google_tag_manager_head.erb') ).render(self) + end + + def render_body + Tilt.new( File.join( File.dirname(__FILE__), 'template', 'google_tag_manager_body.erb') ).render(self) end def self.track(name, *event) diff --git a/lib/rack/tracker/handler.rb b/lib/rack/tracker/handler.rb index 238ab9d..cf78dcc 100644 --- a/lib/rack/tracker/handler.rb +++ b/lib/rack/tracker/handler.rb @@ -21,6 +21,8 @@ def events def render raise NotImplementedError.new('needs implementation') + 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.