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/CHANGELOG.md b/CHANGELOG.md index 063ede4..4398e32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,29 @@ +# 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) +* [ENHANCEMENT] Make middleware thread safe #150 (thx @kspe) + +# 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) + +# 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) + # 1.10.0 * [ENHANCEMENT] Hubspot integration #136 (thx @ChrisCoffey) 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' diff --git a/README.md b/README.md index 515465c..e1b7927 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 @@ -604,6 +605,27 @@ 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 +``` + +### 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 diff --git a/lib/rack/tracker.rb b/lib/rack/tracker.rb index 6cce2fb..4e10d87 100644 --- a/lib/rack/tracker.rb +++ b/lib/rack/tracker.rb @@ -25,6 +25,8 @@ require "rack/tracker/hotjar/hotjar" require "rack/tracker/bing/bing" require "rack/tracker/hubspot/hubspot" +require "rack/tracker/drift/drift" +require "rack/tracker/heap/heap" module Rack class Tracker @@ -36,10 +38,14 @@ def initialize(app, &block) end def call(env) - @status, @headers, @body = @app.call(env) - return [@status, @headers, @body] unless html? - response = Rack::Response.new([], @status, @headers) + dup._call(env) + end + + def _call(env) + 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"] @@ -50,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| 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..e046404 --- /dev/null +++ b/lib/rack/tracker/drift/template/drift.erb @@ -0,0 +1,26 @@ + 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..d6d9d7b 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/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 %> - <% end %> - + + <% if events.any? %> + + <% end %> + + diff --git a/lib/rack/tracker/version.rb b/lib/rack/tracker/version.rb index b8a0953..3cf2d99 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.13.0' end end 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/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 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 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/drift_integration_spec.rb b/spec/integration/drift_integration_spec.rb new file mode 100644 index 0000000..498538f --- /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.load(\'DRIFT_ID\')') + end +end diff --git a/spec/integration/google_global_integration_spec.rb b/spec/integration/google_global_integration_spec.rb index 7079036..14df465 100644 --- a/spec/integration/google_global_integration_spec.rb +++ b/spec/integration/google_global_integration_spec.rb @@ -3,28 +3,39 @@ 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' }] + tracker.handler :google_global, tracker_options end visit '/' end - subject { page } + let(:tracker_options) { { trackers: [{ id: 'U-XXX-Y' }] } } 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 + let(:tracker_options) { { trackers: [{ id: 'U-XXX-Y' }], position: :body } } 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 + + describe "handles empty tracker id" do + let(:tracker_options) { { trackers: [{ id: nil }, { id: "" }, { id: " " }] } } + + it "does not inject scripts" do + expect(page.find("head")).to_not have_content(" - + +

welcome to metal#index

- + HTML diff --git a/spec/support/metal_controller.rb b/spec/support/metal_controller.rb index 13f0a61..f22447e 100644 --- a/spec/support/metal_controller.rb +++ b/spec/support/metal_controller.rb @@ -124,4 +124,12 @@ def hubspot def bing render "metal/index" end + + def drift + render "metal/index" + end + + def heap + render "metal/index" + end end