rack-tracker 1.7.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 55705dd46c88e815dd7b0187c3d6d2a50b36a79f5ccc52e801a96d282ba348be
4
- data.tar.gz: 5cad1746a043405f4f0e1b2cf85904dd9a6daa50923199d77d71bb32b7a357ba
3
+ metadata.gz: e9d098ca10113dc7340ab5e268dec40e8b0143a0d1284d1caaeac155510c1bd6
4
+ data.tar.gz: c84e057fd1021e504ff749fd5a70e3a79ced5343d39720af987fb5b3783a38b3
5
5
  SHA512:
6
- metadata.gz: 0ea613048f9899f3c532eee40c7dd40ed9e4c6c6623f318df1522bbb11e3a05656bb3b96adf0b3b46bd10e33c34f9ab5774c7d48402e1830e3efff2042b144e2
7
- data.tar.gz: ea8b57893ebdcc970e55085f20cefc4cdb1e18f5172b6e44cc3c54a2189335109cafe0694e30854edaa90ed3f3d8330051dc2619b7826f3a5180cb168ea129d3
6
+ metadata.gz: 4a140e3e30f813d916adec3e6a6aed6919b53d72281d2880ae81ecc035d980c9c330fce2b0bd85497bb92e20172db0aa5bffa8edd0e9fd538f15380be66124aa
7
+ data.tar.gz: 357e0828908134f96ed5370554b17d9ecdce43a098493471dc9a5807e1af7f186ce05bb7a5674ac2f36a0f6eddefdf93742be93f8682573266065bd368c087d6
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ # 1.8.0
2
+
3
+ * [ENHANCEMENT] Google Global Site Tag: basic integration with support for pageviews to Google global tag #123 (thx @atd)
4
+
1
5
  # 1.7.0
2
6
 
3
7
  * [BUGFIX] dup response string in Rack::Tracker#inject to avoid RuntimeError #114 (thx @zpfled)
data/README.md CHANGED
@@ -20,6 +20,7 @@ to the middleware stack the second part are the service-handlers that you're goi
20
20
  in your application. It's easy to add your own [custom handlers](#custom-handlers),
21
21
  but to get you started we're shipping support for the following services out of the box:
22
22
 
23
+ * [Google Global Site Tag](#google-global)
23
24
  * [Google Analytics](#google-analytics)
24
25
  * [Google Adwords Conversion](#google-adwords-conversion)
25
26
  * [Google Tag Manager](#google-tag-manager)
@@ -111,6 +112,27 @@ request.env['tracker'] = {
111
112
  }
112
113
  ```
113
114
 
115
+ ### Google Global Site Tag (gtag.js)
116
+
117
+ * `: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.
118
+ * `: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.
119
+ * `: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.
120
+ * `:link_attribution` - Enables [Enhanced Link Attribution](https://developers.google.com/analytics/devguides/collection/gtagjs/enhanced-link-attribution).
121
+ * `:allow_display_features` - Can be used to disable [Display Features](https://developers.google.com/analytics/devguides/collection/gtagjs/display-features).
122
+ * `:custom_map` - Used to [Configure and send custom dimensions](https://developers.google.com/analytics/devguides/collection/gtagjs/custom-dims-mets)
123
+ * `:set` - Used in the [set command to configure multiple properties](https://developers.google.com/analytics/devguides/collection/gtagjs/setting-values)
124
+
125
+ #### Trackers
126
+
127
+ Google Global Site tag allows configuring multiple trackers. Use the tracker option to configure the ids:
128
+
129
+
130
+ ```ruby
131
+ config.middleware.use(Rack::Tracker) do
132
+ handler :google_global, { trackers: [ { id: 'U-XXXXX-Y' }, { id: 'U-WWWWWW-Z'} ] }
133
+ end
134
+ ```
135
+
114
136
  ### Google Analytics
115
137
 
116
138
  * `: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.
data/lib/rack/tracker.rb CHANGED
@@ -13,6 +13,7 @@ require "rack/tracker/handler"
13
13
  require "rack/tracker/handler_delegator"
14
14
  require "rack/tracker/controller"
15
15
  require "rack/tracker/google_analytics/google_analytics"
16
+ require "rack/tracker/google_global/google_global"
16
17
  require "rack/tracker/google_tag_manager/google_tag_manager"
17
18
  require "rack/tracker/google_adwords_conversion/google_adwords_conversion"
18
19
  require "rack/tracker/facebook/facebook"
@@ -0,0 +1,32 @@
1
+ class Rack::Tracker::GoogleGlobal < Rack::Tracker::Handler
2
+ self.allowed_tracker_options = [:cookie_domain, :user_id,
3
+ :link_attribution, :allow_display_features, :anonymize_ip,
4
+ :custom_map]
5
+
6
+ class Page < OpenStruct
7
+ def params
8
+ Hash[to_h.slice(:title, :location, :path).map { |key, value| ["page_#{key}", value] }]
9
+ end
10
+ end
11
+
12
+ def pages
13
+ events # TODO: Filter pages after Event is implemented
14
+ end
15
+
16
+ def trackers
17
+ options[:trackers].map { |tracker|
18
+ tracker[:id].respond_to?(:call) ? tracker.merge(id: tracker[:id].call(env)) : tracker
19
+ }.reject { |tracker| tracker[:id].nil? }
20
+ end
21
+
22
+ def set_options
23
+ @_set_options ||= build_set_options
24
+ end
25
+
26
+ private
27
+
28
+ def build_set_options
29
+ value = options[:set]
30
+ value.respond_to?(:call) ? value.call(env) : value
31
+ end
32
+ end
@@ -0,0 +1,19 @@
1
+ <% if trackers %>
2
+ <script async src='https://www.googletagmanager.com/gtag/js?id=<%= trackers[0][:id] %>'></script>
3
+ <script>
4
+ window.dataLayer = window.dataLayer || [];
5
+ function gtag(){dataLayer.push(arguments)};
6
+ gtag('js', new Date());
7
+
8
+ <% if set_options %>
9
+ gtag('set', <%= set_options.to_json %>);
10
+ <% end %>
11
+
12
+ <% trackers.each do |tracker| %>
13
+ <% pages.each do |page| %>
14
+ gtag('config', '<%= tracker[:id] %>', <%= tracker_options.merge(page.params).to_json %>);
15
+ <% end %>
16
+ gtag('config', '<%= tracker[:id] %>', <%= tracker_options.to_json %>);
17
+ <% end %>
18
+ </script>
19
+ <% end %>
@@ -68,7 +68,8 @@ class Rack::Tracker::Handler
68
68
  def tracker_options
69
69
  @_tracker_options ||= {}.tap do |tracker_options|
70
70
  options.slice(*allowed_tracker_options).each do |key, value|
71
- if option_value = value.respond_to?(:call) ? value.call(env) : value
71
+ option_value = value.respond_to?(:call) ? value.call(env) : value
72
+ unless option_value.nil?
72
73
  tracker_options[tracker_option_key(key)] = tracker_option_value(option_value)
73
74
  end
74
75
  end
@@ -1,5 +1,5 @@
1
1
  module Rack
2
2
  class Tracker
3
- VERSION = '1.7.0'
3
+ VERSION = '1.8.0'
4
4
  end
5
5
  end
@@ -0,0 +1,187 @@
1
+ RSpec.describe Rack::Tracker::GoogleGlobal do
2
+ def env
3
+ {
4
+ misc: 'foobar',
5
+ user_id: '123'
6
+ }
7
+ end
8
+
9
+ it 'will be placed in the head' do
10
+ expect(described_class.position).to eq(:head)
11
+ expect(described_class.new(env).position).to eq(:head)
12
+ expect(described_class.new(env, position: :body).position).to eq(:body)
13
+ end
14
+
15
+ describe '#tracker_options' do
16
+ context 'with an allowed option configured with a static value' do
17
+ subject { described_class.new(env, user_id: 'value') }
18
+
19
+ it 'returns hash with option set' do
20
+ expect(subject.tracker_options).to eql ({ user_id: 'value' })
21
+ end
22
+ end
23
+
24
+ context 'with an allowed option configured with a block' do
25
+ subject { described_class.new(env, user_id: lambda { |env| return env[:misc] }) }
26
+
27
+ it 'returns hash with option set' do
28
+ expect(subject.tracker_options).to eql ({ user_id: 'foobar' })
29
+ end
30
+ end
31
+
32
+ context 'with an allowed option configured with a block returning nil' do
33
+ subject { described_class.new(env, user_id: lambda { |env| return env[:non_existing_key] }) }
34
+
35
+ it 'returns an empty hash' do
36
+ expect(subject.tracker_options).to eql ({})
37
+ end
38
+ end
39
+
40
+ context 'with a non allowed option' do
41
+ subject { described_class.new(env, new_option: 'value') }
42
+
43
+ it 'returns an empty hash' do
44
+ expect(subject.tracker_options).to eql ({})
45
+ end
46
+ end
47
+ end
48
+
49
+ describe '#set_options' do
50
+ context 'with option configured with a static value' do
51
+ subject { described_class.new(env, set: { option: 'value' }) }
52
+
53
+ it 'returns hash with option set' do
54
+ expect(subject.set_options).to eql ({ option: 'value' })
55
+ end
56
+ end
57
+
58
+ context 'with option configured with a block' do
59
+ subject { described_class.new(env, set: lambda { |env| return { option: env[:misc] } }) }
60
+
61
+ it 'returns hash with option set' do
62
+ expect(subject.set_options).to eql ({ option: 'foobar' })
63
+ end
64
+ end
65
+
66
+ context 'with option configured with a block returning nil' do
67
+ subject { described_class.new(env, set: lambda { |env| return env[:non_existing_key] }) }
68
+
69
+ it 'returns nil' do
70
+ expect(subject.set_options).to be nil
71
+ end
72
+ end
73
+ end
74
+ describe "with custom domain" do
75
+ let(:tracker) { { id: 'somebody'}}
76
+ let(:options) { { cookie_domain: "railslabs.com", trackers: [tracker] } }
77
+ subject { described_class.new(env, options).render }
78
+
79
+ it "will show asyncronous tracker with cookie_domain" do
80
+ expect(subject).to match(%r{gtag\('config', 'somebody', {\"cookie_domain\":\"railslabs.com\"}\)})
81
+ end
82
+ end
83
+
84
+ describe "with user_id tracking" do
85
+ let(:tracker) { { id: 'somebody'}}
86
+ let(:options) { { user_id: lambda { |env| return env[:user_id] }, trackers: [tracker] } }
87
+ subject { described_class.new(env, options).render }
88
+
89
+ it "will show asyncronous tracker with userId" do
90
+ expect(subject).to match(%r{gtag\('config', 'somebody', {\"user_id\":\"123\"}\)})
91
+ end
92
+ end
93
+
94
+ describe "with link_attribution" do
95
+ let(:tracker) { { id: 'happy'}}
96
+ let(:options) { { link_attribution: true, trackers: [tracker] } }
97
+ subject { described_class.new(env, options).render }
98
+
99
+ it "will show asyncronous tracker with link_attribution" do
100
+ expect(subject).to match(%r{gtag\('config', 'happy', {\"link_attribution\":true}\)})
101
+ end
102
+ end
103
+
104
+ describe "with allow_display_features" do
105
+ let(:tracker) { { id: 'happy'}}
106
+ let(:options) { { allow_display_features: false, trackers: [tracker] } }
107
+ subject { described_class.new(env, options).render }
108
+
109
+ it "will disable display features" do
110
+ expect(subject).to match(%r{gtag\('config', 'happy', {\"allow_display_features\":false}\)})
111
+ end
112
+ end
113
+
114
+ describe "with anonymizeIp" do
115
+ let(:tracker) { { id: 'happy'}}
116
+ let(:options) { { anonymize_ip: true, trackers: [tracker] } }
117
+ subject { described_class.new(env, options).render }
118
+
119
+ it "will set anonymizeIp to true" do
120
+ expect(subject).to match(%r{gtag\('config', 'happy', {\"anonymize_ip\":true}\)})
121
+ end
122
+ end
123
+
124
+ describe "with dynamic tracker" do
125
+ let(:tracker) { { id: lambda { |env| return env[:misc] } }}
126
+ let(:options) { { trackers: [tracker] } }
127
+ subject { described_class.new(env, options).render }
128
+
129
+ it 'will call tracker lambdas to obtain tracking codes' do
130
+ expect(subject).to match(%r{gtag\('config', 'foobar', {}\)})
131
+ end
132
+ end
133
+
134
+ describe "with empty tracker" do
135
+ let(:present_tracker) { { id: 'present' }}
136
+ let(:empty_tracker) { { id: lambda { |env| return } }}
137
+ let(:options) { { trackers: [present_tracker, empty_tracker] } }
138
+ subject { described_class.new(env, options).render }
139
+
140
+ it 'will not render config' do
141
+ expect(subject).to match(%r{gtag\('config', 'present', {}\)})
142
+ expect(subject).not_to match(%r{gtag\('config', '', {}\)})
143
+ end
144
+ end
145
+
146
+ describe "with set options" do
147
+ let(:tracker) { { id: 'with_options' } }
148
+ let(:options) { { trackers: [tracker], set: { foo: 'bar' } } }
149
+ subject { described_class.new(env, options).render }
150
+
151
+ it 'will show set command' do
152
+ expect(subject).to match(%r{gtag\('set', {\"foo\":\"bar\"}\)})
153
+ end
154
+ end
155
+
156
+ describe "with virtual pages" do
157
+ subject { described_class.new(env, trackers: [{ id: 'somebody' }]).render }
158
+
159
+ describe "default" do
160
+ def env
161
+ {'tracker' => {
162
+ 'google_global' => [
163
+ { 'class_name' => 'Page', 'path' => '/virtual_page' }
164
+ ]
165
+ }}
166
+ end
167
+
168
+ it "will show virtual page" do
169
+ expect(subject).to match(%r{gtag\('config', 'somebody', {\"page_path\":\"/virtual_page\"}\);})
170
+ end
171
+ end
172
+
173
+ describe "with a event value" do
174
+ def env
175
+ {'tracker' => {
176
+ 'google_global' => [
177
+ { 'class_name' => 'Page', 'path' => '/virtual_page', 'location' => 'https://example.com/virtual_page', 'title' => 'Virtual Page' }
178
+ ]
179
+ }}
180
+ end
181
+
182
+ it "will show virtual page" do
183
+ expect(subject).to match(%r{gtag\('config', 'somebody', {\"page_title\":\"Virtual Page\",\"page_location\":\"https:\/\/example.com\/virtual_page\",\"page_path\":\"/virtual_page\"}\);})
184
+ end
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,30 @@
1
+ require 'support/capybara_app_helper'
2
+
3
+ RSpec.describe "Google Global Integration Integration" do
4
+ before do
5
+ setup_app(action: :google_global) do |tracker|
6
+ tracker.handler :google_global, trackers: [{ id: 'U-XXX-Y' }]
7
+ end
8
+ visit '/'
9
+ end
10
+
11
+ subject { page }
12
+
13
+ it "embeds the script tag with tracking event from the controller action" do
14
+ expect(page.find("head")).to have_content('U-XXX-Y')
15
+ end
16
+
17
+ describe 'adjust tracker position via options' do
18
+ before do
19
+ setup_app(action: :google_global) do |tracker|
20
+ tracker.handler :google_global, trackers: [{ id: 'U-XXX-Y' }], position: :body
21
+ end
22
+ visit '/'
23
+ end
24
+
25
+ it "will be placed in the specified tag" do
26
+ expect(page.find("head")).to_not have_content('U-XXX-Y')
27
+ expect(page.find("body")).to have_content('U-XXX-Y')
28
+ end
29
+ end
30
+ end
@@ -61,6 +61,10 @@ class MetalController < ActionController::Metal
61
61
  render "metal/index"
62
62
  end
63
63
 
64
+ def google_global
65
+ render "metal/index"
66
+ end
67
+
64
68
  def google_tag_manager
65
69
  unless params[:no_events]
66
70
  tracker do |t|
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-tracker
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.7.0
4
+ version: 1.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lars Brillert
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2018-07-19 00:00:00.000000000 Z
12
+ date: 2019-01-31 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rack
@@ -173,6 +173,8 @@ files:
173
173
  - lib/rack/tracker/google_adwords_conversion/template/google_adwords_conversion.erb
174
174
  - lib/rack/tracker/google_analytics/google_analytics.rb
175
175
  - lib/rack/tracker/google_analytics/template/google_analytics.erb
176
+ - lib/rack/tracker/google_global/google_global.rb
177
+ - lib/rack/tracker/google_global/template/google_global.erb
176
178
  - lib/rack/tracker/google_tag_manager/google_tag_manager.rb
177
179
  - lib/rack/tracker/google_tag_manager/template/google_tag_manager_body.erb
178
180
  - lib/rack/tracker/google_tag_manager/template/google_tag_manager_head.erb
@@ -201,6 +203,7 @@ files:
201
203
  - spec/handler/go_squared_spec.rb
202
204
  - spec/handler/google_adwords_conversion_spec.rb
203
205
  - spec/handler/google_analytics_spec.rb
206
+ - spec/handler/google_global_spec.rb
204
207
  - spec/handler/google_tag_manager_spec.rb
205
208
  - spec/handler/handler_spec.rb
206
209
  - spec/handler/hotjar_spec.rb
@@ -212,6 +215,7 @@ files:
212
215
  - spec/integration/go_squared_integration_spec.rb
213
216
  - spec/integration/google_adwords_conversion_integration_spec.rb
214
217
  - spec/integration/google_analytics_integration_spec.rb
218
+ - spec/integration/google_global_integration_spec.rb
215
219
  - spec/integration/google_tag_manager_integration_spec.rb
216
220
  - spec/integration/hotjar_integration_spec.rb
217
221
  - spec/integration/rails_integration_spec.rb
@@ -246,7 +250,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
246
250
  version: '0'
247
251
  requirements: []
248
252
  rubyforge_project:
249
- rubygems_version: 2.7.6
253
+ rubygems_version: 2.7.7
250
254
  signing_key:
251
255
  specification_version: 4
252
256
  summary: Tracking made easy
@@ -264,6 +268,7 @@ test_files:
264
268
  - spec/handler/go_squared_spec.rb
265
269
  - spec/handler/google_adwords_conversion_spec.rb
266
270
  - spec/handler/google_analytics_spec.rb
271
+ - spec/handler/google_global_spec.rb
267
272
  - spec/handler/google_tag_manager_spec.rb
268
273
  - spec/handler/handler_spec.rb
269
274
  - spec/handler/hotjar_spec.rb
@@ -275,6 +280,7 @@ test_files:
275
280
  - spec/integration/go_squared_integration_spec.rb
276
281
  - spec/integration/google_adwords_conversion_integration_spec.rb
277
282
  - spec/integration/google_analytics_integration_spec.rb
283
+ - spec/integration/google_global_integration_spec.rb
278
284
  - spec/integration/google_tag_manager_integration_spec.rb
279
285
  - spec/integration/hotjar_integration_spec.rb
280
286
  - spec/integration/rails_integration_spec.rb