rack-tracker 1.0.1 → 1.0.2
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 +4 -4
- data/.travis.yml +6 -2
- data/CHANGELOG.md +6 -0
- data/Gemfile.rails-3.2 +6 -0
- data/Gemfile.rails-4.2 +6 -0
- data/README.md +39 -2
- data/lib/rack/tracker.rb +7 -1
- data/lib/rack/tracker/extensions.rb +31 -0
- data/lib/rack/tracker/google_adwords_conversion/template/google_adwords_conversion.erb +3 -3
- data/lib/rack/tracker/google_analytics/google_analytics.rb +2 -1
- data/lib/rack/tracker/handler.rb +3 -0
- data/lib/rack/tracker/javascript_helper.rb +34 -0
- data/lib/rack/tracker/version.rb +1 -1
- data/lib/rack/tracker/zanox/zanox.rb +1 -9
- data/spec/handler/google_adwords_conversion_spec.rb +30 -0
- data/spec/integration/google_analytics_integration_spec.rb +21 -1
- data/spec/support/metal_controller.rb +2 -0
- data/spec/tracker/javascript_helper_spec.rb +51 -0
- metadata +8 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c743ccee29e6faa8c5d71c07d205f3ae15be5919
|
4
|
+
data.tar.gz: efa4ee308684aabf373fb85d37a43d13605d6620
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 579341d83332e1859080caf204e1ad92194c18e9e175194ca75701a23873dc4baa6e21fd97618d1b8d6bafdf70fdc3ee76c73fabbc2e62325c8824420ce4aa73
|
7
|
+
data.tar.gz: 2549534848b0a718337d61950f56b78e2def2c7e2d723f95dec60605bf868064b8ab15057e9d70f654f43acdadc44034d60ca8a02f6706af7be4c30c962c18d4
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
# 1.0.2
|
2
|
+
|
3
|
+
* Back port deep_stringify_keys! from Rails 4 #55 (thx @jivagoalves)
|
4
|
+
* Javascript escape implementation to prevent XSS #54 (thx @fabn)
|
5
|
+
* setEmail event for criteo #51 (thx @florianeck)
|
6
|
+
|
1
7
|
# 1.0.1
|
2
8
|
|
3
9
|
* [BUGFIX] Fix for adjusted_bounce_rate_timeouts #49 (thx @fabn)
|
data/Gemfile.rails-3.2
ADDED
data/Gemfile.rails-4.2
ADDED
data/README.md
CHANGED
@@ -21,6 +21,7 @@ in your application. It's easy to add your own [custom handlers](#custom-handler
|
|
21
21
|
but to get you started we're shipping support for the following services out of the box:
|
22
22
|
|
23
23
|
* [Google Analytics](#google-analytics)
|
24
|
+
* [Google Adwords Conversion](#google-adwords-conversion)
|
24
25
|
* [Google Tag Manager](#google-tag-manager)
|
25
26
|
* [Facebook](#facebook)
|
26
27
|
* [Visual Website Optimizer (VWO)](#visual-website-optimizer-vwo)
|
@@ -187,8 +188,44 @@ take care of the plugin on your own.
|
|
187
188
|
config.middleware.use(Rack::Tracker) do
|
188
189
|
handler :google_analytics, { tracker: 'U-XXXXX-Y', ecommerce: true }
|
189
190
|
end
|
190
|
-
|
191
|
+
```
|
192
|
+
|
193
|
+
### Google Adwords Conversion
|
191
194
|
|
195
|
+
You can configure the handler with default options:
|
196
|
+
```ruby
|
197
|
+
config.middleware.use(Rack::Tracker) do
|
198
|
+
handler :google_adwords_conversion, { id: 123456,
|
199
|
+
language: "en",
|
200
|
+
format: "3",
|
201
|
+
color: "ffffff",
|
202
|
+
label: "Conversion label",
|
203
|
+
currency: "USD" }
|
204
|
+
end
|
205
|
+
```
|
206
|
+
|
207
|
+
To track adwords conversion from the server side just call the `tracker` method in your controller.
|
208
|
+
```ruby
|
209
|
+
def show
|
210
|
+
tracker do |t|
|
211
|
+
t.google_adwords_conversion :conversion, { value: 10.0 }
|
212
|
+
end
|
213
|
+
end
|
214
|
+
```
|
215
|
+
|
216
|
+
You can also specify a different value from default options:
|
217
|
+
```ruby
|
218
|
+
def show
|
219
|
+
tracker do |t|
|
220
|
+
t.google_adwords_conversion :conversion, { id: 123456,
|
221
|
+
language: 'en',
|
222
|
+
format: '3',
|
223
|
+
color: 'ffffff',
|
224
|
+
label: 'Conversion Label',
|
225
|
+
value: 10.0 }
|
226
|
+
end
|
227
|
+
end
|
228
|
+
```
|
192
229
|
|
193
230
|
### Google Tag Manager
|
194
231
|
|
@@ -198,7 +235,7 @@ Google Tag manager code snippet doesn't support any option other than the contai
|
|
198
235
|
config.middleware.use(Rack::Tracker) do
|
199
236
|
handler :google_tag_manager, { container: 'GTM-XXXXXX' }
|
200
237
|
end
|
201
|
-
|
238
|
+
```
|
202
239
|
|
203
240
|
#### Data Layer
|
204
241
|
|
data/lib/rack/tracker.rb
CHANGED
@@ -7,6 +7,7 @@ require "active_support/inflector"
|
|
7
7
|
|
8
8
|
require "rack/tracker/version"
|
9
9
|
require "rack/tracker/extensions"
|
10
|
+
require "rack/tracker/javascript_helper"
|
10
11
|
require 'rack/tracker/railtie' if defined?(Rails)
|
11
12
|
require "rack/tracker/handler"
|
12
13
|
require "rack/tracker/handler_delegator"
|
@@ -56,7 +57,12 @@ module Rack
|
|
56
57
|
|
57
58
|
def inject(env, response)
|
58
59
|
@handlers.each(env) do |handler|
|
59
|
-
|
60
|
+
# Sub! is enough, in well formed html there's only one head or body tag.
|
61
|
+
# Block syntax need to be used, otherwise backslashes in input will mess the output.
|
62
|
+
# @see http://stackoverflow.com/a/4149087/518204 and https://github.com/railslove/rack-tracker/issues/50
|
63
|
+
response.sub! %r{</#{handler.position}>} do |m|
|
64
|
+
handler.render << m.to_s
|
65
|
+
end
|
60
66
|
end
|
61
67
|
response
|
62
68
|
end
|
@@ -30,4 +30,35 @@ class Hash
|
|
30
30
|
end
|
31
31
|
self
|
32
32
|
end
|
33
|
+
|
34
|
+
# NOTE Back ported from Rails 4 to 3
|
35
|
+
# Destructively convert all keys by using the block operation.
|
36
|
+
# This includes the keys from the root hash and from all
|
37
|
+
# nested hashes.
|
38
|
+
def deep_transform_keys!(&block)
|
39
|
+
_deep_transform_keys_in_object!(self, &block)
|
40
|
+
end unless method_defined? :deep_transform_keys!
|
41
|
+
|
42
|
+
def _deep_transform_keys_in_object!(object, &block)
|
43
|
+
case object
|
44
|
+
when Hash
|
45
|
+
object.keys.each do |key|
|
46
|
+
value = object.delete(key)
|
47
|
+
object[yield(key)] = _deep_transform_keys_in_object!(value, &block)
|
48
|
+
end
|
49
|
+
object
|
50
|
+
when Array
|
51
|
+
object.map! {|e| _deep_transform_keys_in_object!(e, &block)}
|
52
|
+
else
|
53
|
+
object
|
54
|
+
end
|
55
|
+
end unless method_defined? :_deep_transform_keys_in_object!
|
56
|
+
|
57
|
+
# NOTE Back ported from Rails 4 to 3
|
58
|
+
# Destructively convert all keys to strings.
|
59
|
+
# This includes the keys from the root hash and from all
|
60
|
+
# nested hashes.
|
61
|
+
def deep_stringify_keys!
|
62
|
+
deep_transform_keys!{ |key| key.to_s }
|
63
|
+
end unless method_defined? :deep_stringify_keys!
|
33
64
|
end
|
@@ -2,8 +2,8 @@
|
|
2
2
|
|
3
3
|
<script type="text/javascript">
|
4
4
|
/* <![CDATA[ */
|
5
|
-
<% ev.to_h.each do |attr_name, attr_value| %>
|
6
|
-
var google_conversion_<%= attr_name %> = <%=
|
5
|
+
<% options.merge(ev.to_h).each do |attr_name, attr_value| %>
|
6
|
+
var google_conversion_<%= attr_name %> = <%= [:id, :value].include?(attr_name) ? attr_value : "'#{attr_value}'" %>;
|
7
7
|
<% end %>
|
8
8
|
var google_remarketing_only = false;
|
9
9
|
/* ]]> */
|
@@ -11,7 +11,7 @@
|
|
11
11
|
<script type="text/javascript" src="//www.googleadservices.com/pagead/conversion.js"></script>
|
12
12
|
<noscript>
|
13
13
|
<div style="display:inline;">
|
14
|
-
<img height="1" width="1" style="border-style:none;" alt="" src="//www.googleadservices.com/pagead/conversion/<%= ev.id %>/?label=<%= URI.escape(ev.label) %>&guid=ON&script=0"/>
|
14
|
+
<img height="1" width="1" style="border-style:none;" alt="" src="//www.googleadservices.com/pagead/conversion/<%= ev.id || options[:id] %>/?label=<%= URI.escape(ev.label || options[:label]) %>&guid=ON&script=0"/>
|
15
15
|
</div>
|
16
16
|
</noscript>
|
17
17
|
|
@@ -47,8 +47,9 @@ class Rack::Tracker::GoogleAnalytics < Rack::Tracker::Handler
|
|
47
47
|
end
|
48
48
|
|
49
49
|
class Parameter < OpenStruct
|
50
|
+
include Rack::Tracker::JavaScriptHelper
|
50
51
|
def write
|
51
|
-
[
|
52
|
+
['set', self.to_h.to_a].flatten.map { |v| %Q{'#{j(v)}'} }.join ', '
|
52
53
|
end
|
53
54
|
end
|
54
55
|
|
data/lib/rack/tracker/handler.rb
CHANGED
@@ -0,0 +1,34 @@
|
|
1
|
+
# This module is extracted from Rails to provide reliable javascript escaping.
|
2
|
+
#
|
3
|
+
# @see https://github.com/rails/rails/blob/master/actionview/lib/action_view/helpers/javascript_helper.rb
|
4
|
+
module Rack::Tracker::JavaScriptHelper
|
5
|
+
|
6
|
+
JS_ESCAPE_MAP = {
|
7
|
+
'\\' => '\\\\',
|
8
|
+
'</' => '<\/',
|
9
|
+
"\r\n" => '\n',
|
10
|
+
"\n" => '\n',
|
11
|
+
"\r" => '\n',
|
12
|
+
'"' => '\\"',
|
13
|
+
"'" => "\\'"
|
14
|
+
}
|
15
|
+
|
16
|
+
JS_ESCAPE_MAP["\342\200\250".force_encoding(Encoding::UTF_8).encode!] = '
'
|
17
|
+
JS_ESCAPE_MAP["\342\200\251".force_encoding(Encoding::UTF_8).encode!] = '
'
|
18
|
+
|
19
|
+
# Escapes carriage returns and single and double quotes for JavaScript segments.
|
20
|
+
#
|
21
|
+
# Also available through the alias j(). This is particularly helpful in JavaScript
|
22
|
+
# responses, like:
|
23
|
+
#
|
24
|
+
# $('some_element').replaceWith('<%=j render 'some/element_template' %>');
|
25
|
+
def escape_javascript(javascript)
|
26
|
+
if javascript
|
27
|
+
javascript.to_s.gsub(/(\\|<\/|\r\n|\342\200\250|\342\200\251|[\n\r"'])/u) { |match| JS_ESCAPE_MAP[match] }
|
28
|
+
else
|
29
|
+
''
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
alias_method :j, :escape_javascript
|
34
|
+
end
|
data/lib/rack/tracker/version.rb
CHANGED
@@ -3,7 +3,6 @@ class Rack::Tracker::Zanox < Rack::Tracker::Handler
|
|
3
3
|
# name of the handler
|
4
4
|
# everything after is passed as options
|
5
5
|
class Mastertag < OpenStruct
|
6
|
-
|
7
6
|
def write
|
8
7
|
to_h.except(:id).map do |k,v|
|
9
8
|
"var zx_#{k} = #{v.to_json};"
|
@@ -13,7 +12,6 @@ class Rack::Tracker::Zanox < Rack::Tracker::Handler
|
|
13
12
|
|
14
13
|
class Lead < OpenStruct
|
15
14
|
# Example: OrderID=[[DEFC-4321]]&CurrencySymbol=[[EUR]]&TotalPrice=[[23.40]]
|
16
|
-
|
17
15
|
def write
|
18
16
|
to_h.map do |k,v|
|
19
17
|
"#{k.to_s.camelize}=[[#{v}]]"
|
@@ -21,13 +19,7 @@ class Rack::Tracker::Zanox < Rack::Tracker::Handler
|
|
21
19
|
end
|
22
20
|
end
|
23
21
|
|
24
|
-
|
25
|
-
def write
|
26
|
-
to_h.map do |k,v|
|
27
|
-
"#{k.to_s.camelize}=[[#{v}]]"
|
28
|
-
end.join('&')
|
29
|
-
end
|
30
|
-
end
|
22
|
+
Sale = Class.new(Lead)
|
31
23
|
|
32
24
|
self.position = :body
|
33
25
|
|
@@ -35,4 +35,34 @@ RSpec.describe Rack::Tracker::GoogleAdwordsConversion do
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
+
describe 'with options' do
|
39
|
+
context 'when there are no params for the handler' do
|
40
|
+
let(:options) { { id: 123456,
|
41
|
+
language: 'en',
|
42
|
+
format: '3',
|
43
|
+
color: 'ffffff',
|
44
|
+
label: 'Conversion Label' } }
|
45
|
+
let(:env) do
|
46
|
+
{
|
47
|
+
'tracker' => {
|
48
|
+
'google_adwords_conversion' => [
|
49
|
+
{ 'class_name' => 'Conversion', 'value' => '10.0' }
|
50
|
+
]
|
51
|
+
}
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
subject { described_class.new(env, options).render }
|
56
|
+
|
57
|
+
it 'will show events by using default options' do
|
58
|
+
expect(subject).to match(%r{var google_conversion_id = 123456;})
|
59
|
+
expect(subject).to match(%r{var google_conversion_language = 'en';})
|
60
|
+
expect(subject).to match(%r{var google_conversion_format = '3';})
|
61
|
+
expect(subject).to match(%r{var google_conversion_color = 'ffffff';})
|
62
|
+
expect(subject).to match(%r{var google_conversion_label = 'Conversion Label';})
|
63
|
+
expect(subject).to match(%r{var google_conversion_value = 10.0;})
|
64
|
+
expect(subject).to match(%r{<img.*src=\"\/\/www.googleadservices.com\/pagead\/conversion\/123456\/\?label=Conversion%20Label&guid=ON&script=0\"/>})
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
38
68
|
end
|
@@ -29,4 +29,24 @@ RSpec.describe "Google Analytics Integration" do
|
|
29
29
|
expect(page.find("body")).to have_content('ga("ecommerce:addItem",{"id":"1234","name":"Fluffy Pink Bunnies","sku":"DD23444","category":"Party Toys","price":"11.99","quantity":"1"})')
|
30
30
|
end
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
|
+
describe 'values escaping' do
|
34
|
+
before do
|
35
|
+
setup_app(action: :google_analytics) do |tracker|
|
36
|
+
tracker.handler :google_analytics, { tracker: 'U-XXX-Y' }
|
37
|
+
end
|
38
|
+
visit '/'
|
39
|
+
end
|
40
|
+
|
41
|
+
it "will not mess up the html" do
|
42
|
+
expect(page.find('head')).to have_content('U-XXX-Y')
|
43
|
+
# Backslashes are also escaped, thus \' becomes in \\\' in output
|
44
|
+
expect(page.find('head')).to have_content %q{Some escaped \\\\\\'value}
|
45
|
+
end
|
46
|
+
|
47
|
+
it "automatically escape javascript in dimensions" do
|
48
|
+
expect(page.find('head')).to have_content('U-XXX-Y')
|
49
|
+
expect(page.find('head')).to have_content %q{Author\\'s name}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -34,6 +34,8 @@ class MetalController < ActionController::Metal
|
|
34
34
|
t.google_analytics :ecommerce, { type: 'addTransaction', id: 1234, affiliation: 'Acme Clothing', revenue: 11.99, shipping: 5, tax: 1.29 }
|
35
35
|
t.google_analytics :ecommerce, { type: 'addItem', id: 1234, name: 'Fluffy Pink Bunnies', sku: 'DD23444', category: 'Party Toys', price: 11.99, quantity: 1 }
|
36
36
|
t.google_analytics :send, { type: 'event', category: 'button', action: 'click', label: 'nav-buttons', value: 'X' }
|
37
|
+
t.google_analytics :parameter, dimension1: %q{Some escaped \\'value}
|
38
|
+
t.google_analytics :parameter, dimension2: %q{Author's name}
|
37
39
|
end
|
38
40
|
render "metal/index"
|
39
41
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
RSpec.describe Rack::Tracker do
|
2
|
+
|
3
|
+
let(:escaper) do
|
4
|
+
Class.new { include Rack::Tracker::JavaScriptHelper }.new
|
5
|
+
end
|
6
|
+
|
7
|
+
subject { escaper }
|
8
|
+
|
9
|
+
# Instance methods are mixed in
|
10
|
+
it { should respond_to :escape_javascript }
|
11
|
+
it { should respond_to :j }
|
12
|
+
|
13
|
+
context 'with plain values' do
|
14
|
+
it 'should return untouched values' do
|
15
|
+
expect(escaper.j('Hello')).to eq 'Hello'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# The following is a RSpec port of original rails's test suite for #j method.
|
20
|
+
# https://github.com/rails/rails/blob/master/actionview/test/template/javascript_helper_test.rb
|
21
|
+
describe '#escape_javascript' do
|
22
|
+
|
23
|
+
# Simple matcher to shorten specs
|
24
|
+
matcher :escape do |value|
|
25
|
+
|
26
|
+
match do |actual|
|
27
|
+
raise ArgumentError, 'macther syntax is escape("some value").to("expected value")' unless @expected
|
28
|
+
actual.j(value) == @expected
|
29
|
+
end
|
30
|
+
|
31
|
+
# Syntactic sugar for matcher
|
32
|
+
chain :to do |to|
|
33
|
+
@expected = to
|
34
|
+
self
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
it { should escape(nil).to '' }
|
39
|
+
it { should escape(%(This "thing" is really\n netos')).to %(This \\"thing\\" is really\\n netos\\') }
|
40
|
+
it { should escape(%(backslash\\test)).to %(backslash\\\\test) }
|
41
|
+
it { should escape(%(dont </close> tags)).to %(dont <\\/close> tags) }
|
42
|
+
it { should escape(%(unicode \342\200\250 newline).force_encoding(Encoding::UTF_8).encode!).to %(unicode 
 newline) }
|
43
|
+
it { should escape(%(unicode \342\200\251 newline).force_encoding(Encoding::UTF_8).encode!).to %(unicode 
 newline) }
|
44
|
+
|
45
|
+
it 'works with symbols' do
|
46
|
+
expect(subject).to escape(:dimension1).to 'dimension1'
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
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.0.
|
4
|
+
version: 1.0.2
|
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: 2015-
|
12
|
+
date: 2015-10-12 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rack
|
@@ -151,6 +151,8 @@ files:
|
|
151
151
|
- ".travis.yml"
|
152
152
|
- CHANGELOG.md
|
153
153
|
- Gemfile
|
154
|
+
- Gemfile.rails-3.2
|
155
|
+
- Gemfile.rails-4.2
|
154
156
|
- LICENSE.txt
|
155
157
|
- README.md
|
156
158
|
- Rakefile
|
@@ -171,6 +173,7 @@ files:
|
|
171
173
|
- lib/rack/tracker/google_tag_manager/template/google_tag_manager.erb
|
172
174
|
- lib/rack/tracker/handler.rb
|
173
175
|
- lib/rack/tracker/handler_delegator.rb
|
176
|
+
- lib/rack/tracker/javascript_helper.rb
|
174
177
|
- lib/rack/tracker/railtie.rb
|
175
178
|
- lib/rack/tracker/version.rb
|
176
179
|
- lib/rack/tracker/vwo/template/vwo.erb
|
@@ -207,6 +210,7 @@ files:
|
|
207
210
|
- spec/tracker/controller_spec.rb
|
208
211
|
- spec/tracker/handler_delegator_spec.rb
|
209
212
|
- spec/tracker/handler_set_spec.rb
|
213
|
+
- spec/tracker/javascript_helper_spec.rb
|
210
214
|
- spec/tracker/tracker_spec.rb
|
211
215
|
homepage: https://github.com/railslove/rack-tracker
|
212
216
|
licenses:
|
@@ -228,7 +232,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
228
232
|
version: '0'
|
229
233
|
requirements: []
|
230
234
|
rubyforge_project:
|
231
|
-
rubygems_version: 2.
|
235
|
+
rubygems_version: 2.4.5.1
|
232
236
|
signing_key:
|
233
237
|
specification_version: 4
|
234
238
|
summary: Tracking made easy
|
@@ -262,4 +266,5 @@ test_files:
|
|
262
266
|
- spec/tracker/controller_spec.rb
|
263
267
|
- spec/tracker/handler_delegator_spec.rb
|
264
268
|
- spec/tracker/handler_set_spec.rb
|
269
|
+
- spec/tracker/javascript_helper_spec.rb
|
265
270
|
- spec/tracker/tracker_spec.rb
|