rack-tracker 1.0.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|