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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4f96283c0c0a98829afd90e11bbd5e79d7286f52
4
- data.tar.gz: 5ad6fa93e9419ac106f7c53dc7b07afcab142db7
3
+ metadata.gz: c743ccee29e6faa8c5d71c07d205f3ae15be5919
4
+ data.tar.gz: efa4ee308684aabf373fb85d37a43d13605d6620
5
5
  SHA512:
6
- metadata.gz: 4006a72b6a23f2af2d29446842a93f0f6fc7392f75d539bb8344ffad99e81f8405ae365927bbcc58a7cf4848c90cfcbe95264ef86f5de6214895723a5d886caa
7
- data.tar.gz: 39be467029907b418117f7c8457b5268c1dbf0a8ea656bf346d5d8c92f9374c75745d2bdc74625c0c02316a1f7fc36ca1c3b3b0461476f7b2a3e2926bf45c3ff
6
+ metadata.gz: 579341d83332e1859080caf204e1ad92194c18e9e175194ca75701a23873dc4baa6e21fd97618d1b8d6bafdf70fdc3ee76c73fabbc2e62325c8824420ce4aa73
7
+ data.tar.gz: 2549534848b0a718337d61950f56b78e2def2c7e2d723f95dec60605bf868064b8ab15057e9d70f654f43acdadc44034d60ca8a02f6706af7be4c30c962c18d4
data/.travis.yml CHANGED
@@ -1,7 +1,11 @@
1
1
  language: ruby
2
+ sudo: false
2
3
  rvm:
3
4
  - 1.9.3
4
5
  - 2.0.0
5
- - 2.1.2
6
- - 2.2.1
6
+ - 2.1.5
7
+ - 2.2.3
7
8
  - jruby
9
+ gemfile:
10
+ - Gemfile.rails-3.2
11
+ - Gemfile.rails-4.2
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
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem 'activesupport', '~> 3.2.0'
6
+ gem 'actionpack', '~> 3.2.0'
data/Gemfile.rails-4.2 ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem 'activesupport', '~> 4.2.0'
6
+ gem 'actionpack', '~> 4.2.0'
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
- response.gsub!(%r{</#{handler.position}>}, handler.render + "</#{handler.position}>")
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 %> = <%= attr_name == :id ? attr_value : "'#{attr_value}'" %>;
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) %>&amp;guid=ON&amp;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]) %>&amp;guid=ON&amp;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
- ["set", self.to_h.to_a].flatten.map {|s| "'#{s}'" }.join(', ')
52
+ ['set', self.to_h.to_a].flatten.map { |v| %Q{'#{j(v)}'} }.join ', '
52
53
  end
53
54
  end
54
55
 
@@ -5,6 +5,9 @@ class Rack::Tracker::Handler
5
5
  attr_accessor :options
6
6
  attr_accessor :env
7
7
 
8
+ # Allow javascript escaping in view templates
9
+ include Rack::Tracker::JavaScriptHelper
10
+
8
11
  def initialize(env, options = {})
9
12
  self.env = env
10
13
  self.options = options
@@ -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!] = '&#x2028;'
17
+ JS_ESCAPE_MAP["\342\200\251".force_encoding(Encoding::UTF_8).encode!] = '&#x2029;'
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
@@ -1,5 +1,5 @@
1
1
  module Rack
2
2
  class Tracker
3
- VERSION = '1.0.1'
3
+ VERSION = '1.0.2'
4
4
  end
5
5
  end
@@ -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
- class Sale < OpenStruct
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&amp;guid=ON&amp;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
- end
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 &#x2028; newline) }
43
+ it { should escape(%(unicode \342\200\251 newline).force_encoding(Encoding::UTF_8).encode!).to %(unicode &#x2029; 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.1
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-08-10 00:00:00.000000000 Z
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.2.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