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 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