rack-tracker 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rspec +2 -0
- data/.travis.yml +2 -0
- data/README.md +39 -3
- data/Rakefile +5 -0
- data/lib/rack/tracker.rb +83 -2
- data/lib/rack/tracker/controller.rb +11 -0
- data/lib/rack/tracker/extensions.rb +8 -0
- data/lib/rack/tracker/facebook/facebook.rb +22 -0
- data/lib/rack/tracker/facebook/template/facebook.erb +40 -0
- data/lib/rack/tracker/google_analytics/google_analytics.rb +33 -0
- data/lib/rack/tracker/google_analytics/template/google_analytics.erb +43 -0
- data/lib/rack/tracker/handler.rb +24 -0
- data/lib/rack/tracker/handler_delegator.rb +48 -0
- data/lib/rack/tracker/railtie.rb +11 -0
- data/lib/rack/tracker/version.rb +2 -2
- data/rack-tracker.gemspec +9 -1
- data/spec/fixtures/another_handler.erb +3 -0
- data/spec/fixtures/dummy.erb +9 -0
- data/spec/fixtures/track_all_the_things.erb +3 -0
- data/spec/fixtures/views/layouts/application.html.erb +8 -0
- data/spec/fixtures/views/metal/index.html.erb +1 -0
- data/spec/handler/facebook_spec.rb +47 -0
- data/spec/handler/google_analytics_spec.rb +117 -0
- data/spec/integration/rails_integration_spec.rb +39 -0
- data/spec/spec_helper.rb +70 -0
- data/spec/support/fake_handler.rb +30 -0
- data/spec/support/metal_controller.rb +17 -0
- data/spec/tracker/controller_spec.rb +31 -0
- data/spec/tracker/handler_delegator_spec.rb +26 -0
- data/spec/tracker/handler_set_spec.rb +32 -0
- data/spec/tracker/tracker_spec.rb +100 -0
- metadata +150 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 42c882f19b618a8ce401d37e6dd5baba8dcc2335
|
4
|
+
data.tar.gz: b841bbdbac1fb8752bbddf6be2c526286a337c04
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ad51b4b1fd58c9dcc2f0985d8066fe13b58504cdf4cb6d1397285bb67ae729ea3fdffb119bcd41c2abec7924da3c8887a1668f03468b70b4f941bc8f7cb90e16
|
7
|
+
data.tar.gz: bb7a62cde667581a270cc4036b1413ce15be8c1ad490569210060763c572713611d5bc9ecf1ca3b35640402e0c2eee39c1a6fba29d6d027702eefda7b0d5fdb6
|
data/.gitignore
CHANGED
data/.rspec
ADDED
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
[![Code Climate](https://codeclimate.com/github/railslove/rack-tracker/badges/gpa.svg)](https://codeclimate.com/github/railslove/rack-tracker)
|
4
4
|
|
5
|
-
|
5
|
+
[![Build Status](https://travis-ci.org/railslove/rack-tracker.svg?branch=master)](https://travis-ci.org/railslove/rack-tracker)
|
6
6
|
|
7
7
|
## Installation
|
8
8
|
|
@@ -20,11 +20,47 @@ Or install it yourself as:
|
|
20
20
|
|
21
21
|
## Usage
|
22
22
|
|
23
|
-
|
23
|
+
Add it to your middleware stack
|
24
|
+
|
25
|
+
config.middleware.use(Rack::Tracker) do
|
26
|
+
handler :google_analytics, { tracker: 'U-XXXXX-Y' }
|
27
|
+
end
|
28
|
+
|
29
|
+
This will add Google Analytics as a tracking handler. `Rack::Tracker` some with
|
30
|
+
support for Google Analytics and Facebook. Others might be added in the future
|
31
|
+
but you can easily write your own handlers.
|
32
|
+
|
33
|
+
### Google Analytics
|
34
|
+
|
35
|
+
* `: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.
|
36
|
+
* `:cookie_domain` - sets the domain name for the GATC cookies. Defaults to `auto`.
|
37
|
+
* `:site_speed_sample_rate` - Defines a new sample set size for Site Speed data collection, see https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiBasicConfiguration?hl=de#_gat.GA_Tracker_._setSiteSpeedSampleRate
|
38
|
+
* `:adjusted_bounce_rate_timeouts` - An array of times in seconds that the tracker will use to set timeouts for adjusted bounce rate tracking. See http://analytics.blogspot.ca/2012/07/tracking-adjusted-bounce-rate-in-google.html for details.
|
39
|
+
* `:enhanced_link_attribution` - Enables [Enhanced Link Attribution](https://developers.google.com/analytics/devguides/collection/analyticsjs/advanced#enhancedlink).
|
40
|
+
* `:advertising` - Enables [Display Features](https://developers.google.com/analytics/devguides/collection/analyticsjs/display-features).
|
41
|
+
* `:ecommerce` - Enables [Ecommerce Tracking](https://developers.google.com/analytics/devguides/collection/analyticsjs/ecommerce).
|
42
|
+
|
43
|
+
### Facebook
|
44
|
+
|
45
|
+
* `custom_audience_id`
|
46
|
+
|
47
|
+
|
48
|
+
## Server side events
|
49
|
+
|
50
|
+
When using `rack-tracker` within a Rails application you can track events from
|
51
|
+
your controller and the tracking snippit will then populated with this data.
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
def show
|
55
|
+
tracker do
|
56
|
+
google_analytics category: 'foo'
|
57
|
+
end
|
58
|
+
end
|
59
|
+
```
|
24
60
|
|
25
61
|
## Contributing
|
26
62
|
|
27
|
-
1. Fork it ( http://github.com
|
63
|
+
1. Fork it ( http://github.com/railslove/rack-tracker/fork )
|
28
64
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
29
65
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
30
66
|
4. Push to the branch (`git push origin my-new-feature`)
|
data/Rakefile
CHANGED
data/lib/rack/tracker.rb
CHANGED
@@ -1,7 +1,88 @@
|
|
1
|
+
require "rack"
|
2
|
+
require "tilt"
|
3
|
+
require "active_support/core_ext/class/attribute"
|
4
|
+
require "active_support/core_ext/hash"
|
5
|
+
require "active_support/json"
|
6
|
+
|
1
7
|
require "rack/tracker/version"
|
8
|
+
require "rack/tracker/extensions"
|
9
|
+
require 'rack/tracker/railtie' if defined?(Rails)
|
10
|
+
require "rack/tracker/handler"
|
11
|
+
require "rack/tracker/handler_delegator"
|
12
|
+
require "rack/tracker/controller"
|
13
|
+
require "rack/tracker/google_analytics/google_analytics"
|
14
|
+
require "rack/tracker/facebook/facebook"
|
2
15
|
|
3
16
|
module Rack
|
4
|
-
|
5
|
-
|
17
|
+
class Tracker
|
18
|
+
EVENT_TRACKING_KEY = 'tracker'
|
19
|
+
|
20
|
+
def initialize(app, &block)
|
21
|
+
@app = app
|
22
|
+
@handlers = Rack::Tracker::HandlerSet.new(&block)
|
23
|
+
end
|
24
|
+
|
25
|
+
def call(env)
|
26
|
+
@status, @headers, @body = @app.call(env)
|
27
|
+
return [@status, @headers, @body] unless html?
|
28
|
+
response = Rack::Response.new([], @status, @headers)
|
29
|
+
|
30
|
+
env[EVENT_TRACKING_KEY] = {} unless env[EVENT_TRACKING_KEY]
|
31
|
+
|
32
|
+
session = env["rack.session"]
|
33
|
+
if response.ok?
|
34
|
+
# Write out the events now
|
35
|
+
|
36
|
+
# Get any stored events from a redirection
|
37
|
+
stored_events = session.delete(EVENT_TRACKING_KEY) if session
|
38
|
+
|
39
|
+
env[EVENT_TRACKING_KEY].deep_merge!(stored_events) { |key, old, new| Array.wrap(old) + Array.wrap(new) } unless stored_events.nil?
|
40
|
+
elsif response.redirection? && session
|
41
|
+
# Store the events until next time
|
42
|
+
env["rack.session"][EVENT_TRACKING_KEY] = env[EVENT_TRACKING_KEY]
|
43
|
+
end
|
44
|
+
|
45
|
+
@body.each { |fragment| response.write inject(env, fragment) }
|
46
|
+
@body.close if @body.respond_to?(:close)
|
47
|
+
|
48
|
+
response.finish
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def html?; @headers['Content-Type'] =~ /html/; end
|
54
|
+
|
55
|
+
def inject(env, response)
|
56
|
+
@handlers.each(env) do |handler|
|
57
|
+
response.gsub!(%r{</#{handler.position}>}, handler.render + "</#{handler.position}>")
|
58
|
+
end
|
59
|
+
response
|
60
|
+
end
|
61
|
+
|
62
|
+
class HandlerSet
|
63
|
+
class Handler
|
64
|
+
def initialize(name, options)
|
65
|
+
@name = name
|
66
|
+
@options = options
|
67
|
+
end
|
68
|
+
|
69
|
+
def init(env)
|
70
|
+
@name.new(env, @options)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def initialize(&block)
|
75
|
+
@handlers = []
|
76
|
+
self.instance_exec(&block) if block_given?
|
77
|
+
end
|
78
|
+
|
79
|
+
def handler(name, opts = {}, &block)
|
80
|
+
@handlers << Handler.new(Rack::Tracker::HandlerDelegator.handler(name), opts)
|
81
|
+
end
|
82
|
+
|
83
|
+
def each(env = {}, &block)
|
84
|
+
@handlers.map{|h| h.init(env)}.each(&block)
|
85
|
+
end
|
86
|
+
end
|
6
87
|
end
|
7
88
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class Rack::Tracker::Facebook < Rack::Tracker::Handler
|
2
|
+
self.position = :body
|
3
|
+
|
4
|
+
# options do
|
5
|
+
# locale 'de_DE'
|
6
|
+
# app_id
|
7
|
+
# custom_audience_id
|
8
|
+
# end
|
9
|
+
|
10
|
+
# event do
|
11
|
+
# id
|
12
|
+
# value
|
13
|
+
# currency
|
14
|
+
# end
|
15
|
+
|
16
|
+
# position :body
|
17
|
+
|
18
|
+
def render
|
19
|
+
Tilt.new( File.join( File.dirname(__FILE__), 'template/facebook.erb') ).render(self)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
<script type="text/javascript">
|
2
|
+
(function() {
|
3
|
+
var _fbq = window._fbq || (window._fbq = []);
|
4
|
+
if (!_fbq.loaded) {
|
5
|
+
var fbds = document.createElement('script');
|
6
|
+
fbds.async = true;
|
7
|
+
fbds.src = '//connect.facebook.net/en_US/fbds.js';
|
8
|
+
var s = document.getElementsByTagName('script')[0];
|
9
|
+
s.parentNode.insertBefore(fbds, s);
|
10
|
+
_fbq.loaded = true;
|
11
|
+
}
|
12
|
+
})();
|
13
|
+
window._fbq = window._fbq || [];
|
14
|
+
|
15
|
+
</script>
|
16
|
+
|
17
|
+
<% if options[:custom_audience_id] %>
|
18
|
+
<script type="text/javascript">
|
19
|
+
window._fbq.push(['addPixelId', '<%= options[:custom_audience_id] %>']);
|
20
|
+
window._fbq.push(["track", "PixelInitialized", {}]);
|
21
|
+
</script>
|
22
|
+
|
23
|
+
<noscript>
|
24
|
+
<img height="1" width="1" alt="" style="display:none" src="https://www.facebook.com/tr?id=<%= options[:custom_audience_id] %>&ev=PixelInitialized">
|
25
|
+
</noscript>
|
26
|
+
<% end %>
|
27
|
+
|
28
|
+
<% if events.any? %>
|
29
|
+
<script type="text/javascript">
|
30
|
+
<% events.each do |event| %>
|
31
|
+
window._fbq.push(['track', '<%= event["pixel_id"] %>', {'value': '<%= event["value"] %>', 'currency': '<%= event["currency"] %>'}]);
|
32
|
+
<% end %>
|
33
|
+
</script>
|
34
|
+
|
35
|
+
<noscript>
|
36
|
+
<% events.each do |event| %>
|
37
|
+
<img height="1" width="1" alt="" style="display:none" src="https://www.facebook.com/offsite_event.php?id=<%= event["pixel_id"] %>&value=<%= event["value"] %>&currency=<%= event["currency"] %>">
|
38
|
+
<% end %>
|
39
|
+
</noscript>
|
40
|
+
<% end %>
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class Rack::Tracker::GoogleAnalytics < Rack::Tracker::Handler
|
2
|
+
class Event < OpenStruct
|
3
|
+
def write
|
4
|
+
['send', event].to_json.gsub(/\[|\]/, '')
|
5
|
+
end
|
6
|
+
|
7
|
+
def event
|
8
|
+
{ hitType: 'event' }.merge(attributes).compact
|
9
|
+
end
|
10
|
+
|
11
|
+
def attributes
|
12
|
+
Hash[to_h.map { |k,v| ['event' + k.to_s.capitalize, v] }]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Ecommerce < Struct.new(:action, :payload)
|
17
|
+
def write
|
18
|
+
[self.action, self.payload.compact].to_json.gsub(/\[|\]/, '')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def tracker
|
23
|
+
options[:tracker].try(:call, env) || options[:tracker]
|
24
|
+
end
|
25
|
+
|
26
|
+
def render
|
27
|
+
Tilt.new( File.join( File.dirname(__FILE__), 'template', 'google_analytics.erb') ).render(self)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.track(name, event)
|
31
|
+
{ name.to_s => [Event.new(event)] }
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
<script type="text/javascript">
|
2
|
+
|
3
|
+
<% if tracker %>
|
4
|
+
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
5
|
+
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
6
|
+
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
7
|
+
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
8
|
+
|
9
|
+
ga('create', '<%= tracker %>', <%= options.slice(:cookieDomain).to_json %>);
|
10
|
+
|
11
|
+
<% if options[:enhanced_link_attribution] %>
|
12
|
+
ga('require', 'linkid', 'linkid.js');
|
13
|
+
<% end %>
|
14
|
+
|
15
|
+
<% if options[:advertising] %>
|
16
|
+
ga('require', 'displayfeatures');
|
17
|
+
<% end %>
|
18
|
+
|
19
|
+
<% if options[:ecommerce] %>
|
20
|
+
ga('require', 'ecommerce', 'ecommerce.js');
|
21
|
+
<% end %>
|
22
|
+
|
23
|
+
<% if options[:anonymize_ip] %>
|
24
|
+
ga('set', 'anonymizeIp', true);
|
25
|
+
<% end %>
|
26
|
+
|
27
|
+
<% if options[:adjusted_bounce_rate_timeouts] %>
|
28
|
+
<% options[:adjusted_bounce_rate_timeouts].each do |timeout| %>
|
29
|
+
setTimeout(ga('send', 'event', '<%= "#{timeout.to_s}_seconds" %>', 'read'),<%= timeout*1000 %>);
|
30
|
+
<% end %>
|
31
|
+
<% end %>
|
32
|
+
|
33
|
+
<% end %>
|
34
|
+
|
35
|
+
<% events.each do |var| %>
|
36
|
+
ga(<%= var.write() %>);
|
37
|
+
<% end %>
|
38
|
+
|
39
|
+
<% if tracker %>
|
40
|
+
ga('send', 'pageview');
|
41
|
+
<% end %>
|
42
|
+
|
43
|
+
</script>
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class Rack::Tracker::Handler
|
2
|
+
class_attribute :position
|
3
|
+
self.position = :head
|
4
|
+
|
5
|
+
attr_accessor :options
|
6
|
+
attr_accessor :env
|
7
|
+
|
8
|
+
def initialize(env, options = {})
|
9
|
+
self.env = env
|
10
|
+
self.options = options
|
11
|
+
end
|
12
|
+
|
13
|
+
def events
|
14
|
+
env.fetch('tracker', {})[self.class.to_s.demodulize.underscore] || []
|
15
|
+
end
|
16
|
+
|
17
|
+
def render
|
18
|
+
raise NotImplementedError.new('needs implementation')
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.track(name, event)
|
22
|
+
raise NotImplementedError.new("class method `#{__callee__}` is not implemented.")
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
class Rack::Tracker::HandlerDelegator
|
2
|
+
class << self
|
3
|
+
def handler(method_name)
|
4
|
+
new.handler(method_name)
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
attr_accessor :env
|
9
|
+
|
10
|
+
def initialize(env={})
|
11
|
+
@env = env
|
12
|
+
end
|
13
|
+
|
14
|
+
def method_missing(method_name, *args, &block)
|
15
|
+
if respond_to?(method_name)
|
16
|
+
write_event(handler(method_name).track(method_name, *args, &block))
|
17
|
+
else
|
18
|
+
super
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def write_event(event)
|
23
|
+
if env.key?('tracker')
|
24
|
+
self.env = env['tracker'].deep_merge!(event)
|
25
|
+
else
|
26
|
+
self.env['tracker'] = event
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def respond_to?(method_name, include_private=false)
|
31
|
+
handler(method_name).respond_to?(:track, include_private)
|
32
|
+
end
|
33
|
+
|
34
|
+
def handler(method_name)
|
35
|
+
return method_name if method_name.kind_of?(Class)
|
36
|
+
|
37
|
+
_handler = method_name.to_s.camelize
|
38
|
+
["Rack::Tracker::#{_handler}", _handler].detect do |const|
|
39
|
+
begin
|
40
|
+
return const.constantize
|
41
|
+
rescue NameError
|
42
|
+
false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
raise ArgumentError, "No such Handler: #{_handler}"
|
47
|
+
end
|
48
|
+
end
|
data/lib/rack/tracker/version.rb
CHANGED
data/rack-tracker.gemspec
CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.authors = ["Lars Brillert", "Marco Schaden"]
|
10
10
|
spec.email = ["lars@railslove.com", "marco@railslove.com"]
|
11
11
|
spec.summary = %q{Tracking made easy}
|
12
|
-
spec.description = %q{Don’t fool around with adding tracking partials to your app concentrate on the things that matter.}
|
12
|
+
spec.description = %q{Don’t fool around with adding tracking partials to your app and concentrate on the things that matter.}
|
13
13
|
spec.homepage = "https://github.com/railslove/rack-tracker"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
@@ -18,6 +18,14 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
+
spec.add_dependency "rack", "~> 1.5.2"
|
22
|
+
spec.add_dependency "tilt", "~> 1.4.1"
|
23
|
+
spec.add_dependency "activesupport", "~> 4.1.0"
|
24
|
+
|
21
25
|
spec.add_development_dependency "bundler", "~> 1.5"
|
22
26
|
spec.add_development_dependency "rake"
|
27
|
+
spec.add_development_dependency "rspec", "~> 3.0.0"
|
28
|
+
spec.add_development_dependency "capybara", "~> 2.4.1"
|
29
|
+
spec.add_development_dependency "actionpack", "~> 4.1.5"
|
30
|
+
spec.add_development_dependency "pry"
|
23
31
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
<h1>welcome to metal#index</h1>
|
@@ -0,0 +1,47 @@
|
|
1
|
+
RSpec.describe Rack::Tracker::Facebook do
|
2
|
+
def env
|
3
|
+
{}
|
4
|
+
end
|
5
|
+
|
6
|
+
it 'will be placed in the body' do
|
7
|
+
expect(described_class.position).to eq(:body)
|
8
|
+
expect(described_class.new(env).position).to eq(:body)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe 'with custom audience id' do
|
12
|
+
subject { described_class.new(env, custom_audience_id: 'custom_audience_id').render }
|
13
|
+
|
14
|
+
it 'will push the tracking events to the queue' do
|
15
|
+
expect(subject).to match(%r{window._fbq.push\(\['addPixelId', 'custom_audience_id'\]\)})
|
16
|
+
expect(subject).to match(%r{window._fbq.push\(\["track", "PixelInitialized", \{\}\]\)})
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'will add the noscript fallback' do
|
20
|
+
expect(subject).to match(%r{https://www.facebook.com/tr\?id=custom_audience_id&ev=PixelInitialized})
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe 'with events' do
|
25
|
+
def env
|
26
|
+
{
|
27
|
+
'tracker' => {
|
28
|
+
'facebook' =>
|
29
|
+
[
|
30
|
+
{'pixel_id' => '123456789',
|
31
|
+
'value' => '23',
|
32
|
+
'currency' => 'EUR'}
|
33
|
+
]
|
34
|
+
}
|
35
|
+
}
|
36
|
+
end
|
37
|
+
subject { described_class.new(env).render }
|
38
|
+
|
39
|
+
it 'will push the tracking events to the queue' do
|
40
|
+
expect(subject).to match(%r{\['track', '123456789', \{'value': '23', 'currency': 'EUR'\}\]})
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'will add the noscript fallback' do
|
44
|
+
expect(subject).to match(%r{https://www.facebook.com/offsite_event.php\?id=123456789&value=23&currency=EUR})
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
RSpec.describe Rack::Tracker::GoogleAnalytics do
|
2
|
+
|
3
|
+
def env
|
4
|
+
{misc: 'foobar'}
|
5
|
+
end
|
6
|
+
|
7
|
+
it 'will be placed in the head' do
|
8
|
+
expect(described_class.position).to eq(:head)
|
9
|
+
expect(described_class.new(env).position).to eq(:head)
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "with events" do
|
13
|
+
describe "default" do
|
14
|
+
def env
|
15
|
+
{'tracker' => {
|
16
|
+
'google_analytics' => [
|
17
|
+
Rack::Tracker::GoogleAnalytics::Event.new(category: "Users", action: "Login", label: "Standard")
|
18
|
+
]
|
19
|
+
}}
|
20
|
+
end
|
21
|
+
|
22
|
+
subject { described_class.new(env, tracker: 'somebody', cookieDomain: "railslabs.com").render }
|
23
|
+
it "will show events" do
|
24
|
+
expect(subject).to match(%r{ga\(\"send\",{\"hitType\":\"event\",\"eventCategory\":\"Users\",\"eventAction\":\"Login\",\"eventLabel\":\"Standard\"}\)})
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "with a event value" do
|
29
|
+
def env
|
30
|
+
{'tracker' => { 'google_analytics' => [
|
31
|
+
Rack::Tracker::GoogleAnalytics::Event.new(category: "Users", action: "Login", label: "Standard", value: 5)
|
32
|
+
]}}
|
33
|
+
end
|
34
|
+
|
35
|
+
subject { described_class.new(env, tracker: 'somebody', cookieDomain: "railslabs.com").render }
|
36
|
+
it "will show events with values" do
|
37
|
+
expect(subject).to match(%r{ga\(\"send\",{\"hitType\":\"event\",\"eventCategory\":\"Users\",\"eventAction\":\"Login\",\"eventLabel\":\"Standard\",\"eventValue\":5}\)},)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe 'with e-commerce events' do
|
43
|
+
describe "default" do
|
44
|
+
def env
|
45
|
+
{'tracker' => {
|
46
|
+
'google_analytics' => [
|
47
|
+
Rack::Tracker::GoogleAnalytics::Ecommerce.new('ecommerce:addItem', {id: '1234', affiliation: 'Acme Clothing', revenue: 11.99, shipping: '5', tax: '1.29', currency: 'EUR'})
|
48
|
+
]
|
49
|
+
}}
|
50
|
+
end
|
51
|
+
|
52
|
+
subject { described_class.new(env, tracker: 'somebody', cookieDomain: "railslabs.com").render }
|
53
|
+
it "will show events" do
|
54
|
+
expect(subject).to match(%r{ga\(\"ecommerce:addItem\",#{{id: '1234', affiliation: 'Acme Clothing', revenue: 11.99, shipping: '5', tax: '1.29', currency: 'EUR'}.to_json}})
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "with custom domain" do
|
60
|
+
subject { described_class.new(env, tracker: 'somebody', cookieDomain: "railslabs.com").render }
|
61
|
+
|
62
|
+
it "will show asyncronous tracker with cookieDomain" do
|
63
|
+
expect(subject).to match(%r{ga\('create', 'somebody', {\"cookieDomain\":\"railslabs.com\"}\)})
|
64
|
+
expect(subject).to match(%r{ga\('send', 'pageview'\)})
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "with enhanced_link_attribution" do
|
69
|
+
subject { described_class.new(env, tracker: 'happy', enhanced_link_attribution: true).render }
|
70
|
+
|
71
|
+
it "will embedded the linkid plugin script" do
|
72
|
+
expect(subject).to match(%r{linkid.js})
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "with advertising" do
|
77
|
+
subject { described_class.new(env, tracker: 'happy', advertising: true).render }
|
78
|
+
|
79
|
+
it "will require displayfeatures" do
|
80
|
+
expect(subject).to match(%r{ga\('require', 'displayfeatures'\)})
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "with e-commerce" do
|
85
|
+
subject { described_class.new(env, tracker: 'happy', ecommerce: true).render }
|
86
|
+
|
87
|
+
it "will require the ecommerce plugin" do
|
88
|
+
expect(subject).to match(%r{ga\('require', 'ecommerce', 'ecommerce\.js'\)})
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe "with anonymizeIp" do
|
93
|
+
subject { described_class.new(env, tracker: 'happy', anonymize_ip: true).render }
|
94
|
+
|
95
|
+
it "will set anonymizeIp to true" do
|
96
|
+
expect(subject).to match(%r{ga\('set', 'anonymizeIp', true\)})
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "with dynamic tracker" do
|
101
|
+
subject { described_class.new(env, { tracker: lambda { |env| return env[:misc] }}).render }
|
102
|
+
|
103
|
+
it 'will call tracker lambdas to obtain tracking codes' do
|
104
|
+
expect(subject).to match(%r{ga\('create', 'foobar', {}\)})
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe 'adjusted bounce rate' do
|
109
|
+
subject { described_class.new(env, tracker: 'afake', adjusted_bounce_rate_timeouts: [15, 30]).render }
|
110
|
+
|
111
|
+
it "will add timeouts to push read events" do
|
112
|
+
expect(subject).to match(%r{ga\('send', 'event', '15_seconds', 'read'\)})
|
113
|
+
expect(subject).to match(%r{ga\('send', 'event', '30_seconds', 'read'\)})
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'support/metal_controller'
|
2
|
+
require 'support/fake_handler'
|
3
|
+
|
4
|
+
Capybara.app = Rack::Builder.new do
|
5
|
+
use Rack::Tracker do
|
6
|
+
handler :track_all_the_things, { custom_key: 'SomeKey123' }
|
7
|
+
handler :another_handler, { custom_key: 'AnotherKey42' }
|
8
|
+
end
|
9
|
+
run MetalController.action(:index)
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
RSpec.describe "Rails Integration" do
|
14
|
+
before { visit '/' }
|
15
|
+
subject { page.html.gsub(/^\s*/, '') }
|
16
|
+
|
17
|
+
let(:expected_html) do
|
18
|
+
<<-HTML.gsub(/^\s*/, '')
|
19
|
+
<html>
|
20
|
+
<head>
|
21
|
+
<title>Metal Layout</title>
|
22
|
+
<script type="text/javascript">
|
23
|
+
myAwesomeFunction("tracks", "like", "no-one-else", "SomeKey123");
|
24
|
+
</script>
|
25
|
+
</head>
|
26
|
+
<body>
|
27
|
+
<h1>welcome to metal#index</h1>
|
28
|
+
<script type="text/javascript">
|
29
|
+
anotherFunction("tracks-event-from-down-under", "AnotherKey42");
|
30
|
+
</script>
|
31
|
+
</body>
|
32
|
+
</html>
|
33
|
+
HTML
|
34
|
+
end
|
35
|
+
|
36
|
+
it "embeds the script tag with tracking event from the controller action" do
|
37
|
+
expect(subject).to eql(expected_html)
|
38
|
+
end
|
39
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
|
4
|
+
require 'rspec'
|
5
|
+
require 'rack/test'
|
6
|
+
require 'capybara/rspec'
|
7
|
+
require 'rack/tracker'
|
8
|
+
|
9
|
+
# Requires supporting files with custom matchers and macros, etc,
|
10
|
+
# in ./support/ and its subdirectories.
|
11
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
12
|
+
|
13
|
+
Capybara.ignore_hidden_elements = false
|
14
|
+
|
15
|
+
RSpec.configure do |config|
|
16
|
+
config.include Rack::Test::Methods
|
17
|
+
config.include Capybara::DSL
|
18
|
+
|
19
|
+
# These two settings work together to allow you to limit a spec run
|
20
|
+
# to individual examples or groups you care about by tagging them with
|
21
|
+
# `:focus` metadata. When nothing is tagged with `:focus`, all examples
|
22
|
+
# get run.
|
23
|
+
config.filter_run :focus
|
24
|
+
config.run_all_when_everything_filtered = true
|
25
|
+
|
26
|
+
# Many RSpec users commonly either run the entire suite or an individual
|
27
|
+
# file, and it's useful to allow more verbose output when running an
|
28
|
+
# individual spec file.
|
29
|
+
if config.files_to_run.one?
|
30
|
+
# Use the documentation formatter for detailed output,
|
31
|
+
# unless a formatter has already been configured
|
32
|
+
# (e.g. via a command-line flag).
|
33
|
+
config.default_formatter = 'doc'
|
34
|
+
end
|
35
|
+
|
36
|
+
# Run specs in random order to surface order dependencies. If you find an
|
37
|
+
# order dependency and want to debug it, you can fix the order by providing
|
38
|
+
# the seed, which is printed after each run.
|
39
|
+
# --seed 1234
|
40
|
+
config.order = :random
|
41
|
+
|
42
|
+
# Seed global randomization in this process using the `--seed` CLI option.
|
43
|
+
# Setting this allows you to use `--seed` to deterministically reproduce
|
44
|
+
# test failures related to randomization by passing the same `--seed` value
|
45
|
+
# as the one that triggered the failure.
|
46
|
+
Kernel.srand config.seed
|
47
|
+
|
48
|
+
# rspec-expectations config goes here. You can use an alternate
|
49
|
+
# assertion/expectation library such as wrong or the stdlib/minitest
|
50
|
+
# assertions if you prefer.
|
51
|
+
config.expect_with :rspec do |expectations|
|
52
|
+
# Enable only the newer, non-monkey-patching expect syntax.
|
53
|
+
# For more details, see:
|
54
|
+
# - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
|
55
|
+
expectations.syntax = :expect
|
56
|
+
end
|
57
|
+
|
58
|
+
# rspec-mocks config goes here. You can use an alternate test double
|
59
|
+
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
60
|
+
config.mock_with :rspec do |mocks|
|
61
|
+
# Enable only the newer, non-monkey-patching expect syntax.
|
62
|
+
# For more details, see:
|
63
|
+
# - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
64
|
+
mocks.syntax = :expect
|
65
|
+
|
66
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
67
|
+
# a real object. This is generally recommended.
|
68
|
+
mocks.verify_partial_doubles = true
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class TrackAllTheThings < Rack::Tracker::Handler
|
2
|
+
def render
|
3
|
+
Tilt.new( File.join( File.dirname(__FILE__), '../fixtures/track_all_the_things.erb') ).render(self)
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.track(name, event)
|
7
|
+
{ name.to_s => event }
|
8
|
+
end
|
9
|
+
|
10
|
+
def track_me
|
11
|
+
env['tracker']['track_all_the_things']
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
class AnotherHandler < Rack::Tracker::Handler
|
17
|
+
self.position = :body
|
18
|
+
|
19
|
+
def render
|
20
|
+
Tilt.new( File.join( File.dirname(__FILE__), '../fixtures/another_handler.erb') ).render(self)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.track(name, event)
|
24
|
+
{ name.to_s => event }
|
25
|
+
end
|
26
|
+
|
27
|
+
def track_me
|
28
|
+
env['tracker']['another_handler']
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'action_controller'
|
2
|
+
|
3
|
+
class MetalController < ActionController::Metal
|
4
|
+
include Rack::Tracker::Controller
|
5
|
+
include AbstractController::Rendering
|
6
|
+
include ActionView::Layouts
|
7
|
+
append_view_path File.join(File.dirname(__FILE__), '../fixtures/views')
|
8
|
+
layout 'application'
|
9
|
+
|
10
|
+
def index
|
11
|
+
tracker do
|
12
|
+
track_all_the_things like: 'no-one-else'
|
13
|
+
another_handler likes: 'you'
|
14
|
+
end
|
15
|
+
render "metal/index"
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class SomeController
|
2
|
+
include Rack::Tracker::Controller
|
3
|
+
|
4
|
+
attr_accessor :env
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@env = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def index
|
11
|
+
tracker do
|
12
|
+
google_analytics category: 'foo'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
RSpec.describe Rack::Tracker::Controller do
|
19
|
+
context 'controller' do
|
20
|
+
let(:event) { Rack::Tracker::GoogleAnalytics::Event.new(category: 'foo') }
|
21
|
+
|
22
|
+
it 'writes the event into env' do
|
23
|
+
controller = SomeController.new
|
24
|
+
expect {
|
25
|
+
controller.index
|
26
|
+
}.to change {
|
27
|
+
controller.env
|
28
|
+
}.from({}).to('tracker' => {'google_analytics' => [event]})
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
Foo = Class.new
|
2
|
+
Bar = Class.new
|
3
|
+
|
4
|
+
RSpec.describe Rack::Tracker::HandlerDelegator do
|
5
|
+
|
6
|
+
describe '#handler' do
|
7
|
+
|
8
|
+
it 'will find handler in the Rack::Tracker namespace' do
|
9
|
+
expect(described_class.handler(:google_analytics)).to eq(Rack::Tracker::GoogleAnalytics)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'will find handler outside the Rack::Tracker namespace' do
|
13
|
+
expect(described_class.handler(:foo)).to eq(Foo)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'will just return a class' do
|
17
|
+
expect(described_class.handler(Bar)).to eq(Bar)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'will raise when no handler is found' do
|
21
|
+
expect { described_class.handler(:baz)}.to raise_error(ArgumentError, "No such Handler: Baz")
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class Dummy < Rack::Tracker::Handler
|
2
|
+
end
|
3
|
+
|
4
|
+
RSpec.describe Rack::Tracker::HandlerSet do
|
5
|
+
|
6
|
+
let(:set) do
|
7
|
+
Rack::Tracker::HandlerSet.new do
|
8
|
+
handler 'dummy', {foo: "bar"}
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '#each' do
|
13
|
+
subject { set.each }
|
14
|
+
specify { expect(subject.to_a.size).to eq(1) }
|
15
|
+
specify { expect(subject).to match_array(Dummy) }
|
16
|
+
end
|
17
|
+
|
18
|
+
describe Rack::Tracker::HandlerSet::Handler do
|
19
|
+
subject { described_class.new(Dummy, {foo: 'bar'}) }
|
20
|
+
|
21
|
+
describe '#init' do
|
22
|
+
it 'will initialize the handler with the given class' do
|
23
|
+
expect(subject.init({})).to be_kind_of(Dummy)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'will initialize the handler with the given options' do
|
27
|
+
expect(subject.init({}).options).to eq({foo: 'bar'})
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
class DummyHandler < Rack::Tracker::Handler
|
2
|
+
def render
|
3
|
+
Tilt.new( File.join( File.dirname(__FILE__), '../fixtures/dummy.erb') ).render(self)
|
4
|
+
end
|
5
|
+
|
6
|
+
def dummy_alert
|
7
|
+
env['tracker']['dummy']
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class BodyHandler < DummyHandler
|
12
|
+
self.position = :body
|
13
|
+
end
|
14
|
+
|
15
|
+
RSpec.describe Rack::Tracker do
|
16
|
+
def app
|
17
|
+
Rack::Builder.new do
|
18
|
+
use Rack::Session::Cookie, secret: "FOO"
|
19
|
+
|
20
|
+
use Rack::Tracker do
|
21
|
+
handler DummyHandler, { foo: 'head' }
|
22
|
+
handler BodyHandler, { foo: 'body' }
|
23
|
+
end
|
24
|
+
|
25
|
+
run lambda {|env|
|
26
|
+
request = Rack::Request.new(env)
|
27
|
+
case request.path
|
28
|
+
when '/' then
|
29
|
+
[200, {'Content-Type' => 'application/html'}, ['<head>Hello world</head>']]
|
30
|
+
when '/body' then
|
31
|
+
[200, {'Content-Type' => 'application/html'}, ['<body>bob here</body>']]
|
32
|
+
when '/body-head' then
|
33
|
+
[200, {'Content-Type' => 'application/html'}, ['<head></head><body></body>']]
|
34
|
+
when '/test.xml' then
|
35
|
+
[200, {'Content-Type' => 'application/xml'}, ['Xml here']]
|
36
|
+
when '/redirect' then
|
37
|
+
[302, {'Content-Type' => 'application/html', 'Location' => '/'}, ['<body>redirection</body>']]
|
38
|
+
else
|
39
|
+
[404, 'Nothing here']
|
40
|
+
end
|
41
|
+
}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
subject { app }
|
45
|
+
|
46
|
+
describe 'when head is present' do
|
47
|
+
it 'injects the handler code' do
|
48
|
+
get '/'
|
49
|
+
expect(last_response.body).to include("alert('this is a dummy class');")
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'will pass options to the Handler' do
|
53
|
+
get '/'
|
54
|
+
expect(last_response.body).to include("console.log('head');")
|
55
|
+
expect(last_response.body).to_not include("console.log('body');")
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'injects custom variables that was directly assigned' do
|
59
|
+
get '/', {}, {'tracker' => { 'dummy' => 'foo bar'}}
|
60
|
+
expect(last_response.body).to include("alert('foo bar');")
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'injects custom variables that lives in the session' do
|
64
|
+
get '/', {}, {'rack.session' => {'tracker' => { 'dummy' => 'bar foo'}}}
|
65
|
+
expect(last_response.body).to include("alert('bar foo');")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe 'when body is present' do
|
70
|
+
it 'will not inject the body handler code' do
|
71
|
+
get '/body'
|
72
|
+
expect(last_response.body).to include("console.log('body');")
|
73
|
+
expect(last_response.body).to_not include("console.log('head');")
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe 'when head and body is present' do
|
78
|
+
it 'will pass options to the Handler' do
|
79
|
+
get '/body-head'
|
80
|
+
expect(last_response.body).to include("console.log('head');")
|
81
|
+
expect(last_response.body).to include("console.log('body');")
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe 'when a redirect' do
|
86
|
+
it 'will keep the tracker attributes and show them on the new location' do
|
87
|
+
get '/redirect', {}, { 'tracker' => { 'dummy' => 'Keep this!' } }
|
88
|
+
follow_redirect!
|
89
|
+
expect(last_response.body).to include("alert('Keep this!');")
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe 'when not html' do
|
94
|
+
it 'will not inject the handler code' do
|
95
|
+
get '/test.xml'
|
96
|
+
|
97
|
+
expect(last_response.body).to_not include("alert('this is a dummy class');")
|
98
|
+
end
|
99
|
+
end
|
100
|
+
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: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lars Brillert
|
@@ -9,37 +9,135 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-08-
|
12
|
+
date: 2014-08-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rack
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: 1.5.2
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: 1.5.2
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: tilt
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 1.4.1
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: 1.4.1
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: activesupport
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: 4.1.0
|
49
|
+
type: :runtime
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: 4.1.0
|
14
56
|
- !ruby/object:Gem::Dependency
|
15
57
|
name: bundler
|
16
58
|
requirement: !ruby/object:Gem::Requirement
|
17
59
|
requirements:
|
18
|
-
- - ~>
|
60
|
+
- - "~>"
|
19
61
|
- !ruby/object:Gem::Version
|
20
62
|
version: '1.5'
|
21
63
|
type: :development
|
22
64
|
prerelease: false
|
23
65
|
version_requirements: !ruby/object:Gem::Requirement
|
24
66
|
requirements:
|
25
|
-
- - ~>
|
67
|
+
- - "~>"
|
26
68
|
- !ruby/object:Gem::Version
|
27
69
|
version: '1.5'
|
28
70
|
- !ruby/object:Gem::Dependency
|
29
71
|
name: rake
|
30
72
|
requirement: !ruby/object:Gem::Requirement
|
31
73
|
requirements:
|
32
|
-
- -
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: rspec
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - "~>"
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: 3.0.0
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - "~>"
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: 3.0.0
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
name: capybara
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - "~>"
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: 2.4.1
|
105
|
+
type: :development
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - "~>"
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: 2.4.1
|
112
|
+
- !ruby/object:Gem::Dependency
|
113
|
+
name: actionpack
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - "~>"
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: 4.1.5
|
119
|
+
type: :development
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - "~>"
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: 4.1.5
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: pry
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
requirements:
|
130
|
+
- - ">="
|
33
131
|
- !ruby/object:Gem::Version
|
34
132
|
version: '0'
|
35
133
|
type: :development
|
36
134
|
prerelease: false
|
37
135
|
version_requirements: !ruby/object:Gem::Requirement
|
38
136
|
requirements:
|
39
|
-
- -
|
137
|
+
- - ">="
|
40
138
|
- !ruby/object:Gem::Version
|
41
139
|
version: '0'
|
42
|
-
description: Don’t fool around with adding tracking partials to your app concentrate
|
140
|
+
description: Don’t fool around with adding tracking partials to your app and concentrate
|
43
141
|
on the things that matter.
|
44
142
|
email:
|
45
143
|
- lars@railslove.com
|
@@ -48,15 +146,40 @@ executables: []
|
|
48
146
|
extensions: []
|
49
147
|
extra_rdoc_files: []
|
50
148
|
files:
|
51
|
-
- .gitignore
|
52
|
-
- .
|
149
|
+
- ".gitignore"
|
150
|
+
- ".rspec"
|
151
|
+
- ".travis.yml"
|
53
152
|
- Gemfile
|
54
153
|
- LICENSE.txt
|
55
154
|
- README.md
|
56
155
|
- Rakefile
|
57
156
|
- lib/rack/tracker.rb
|
157
|
+
- lib/rack/tracker/controller.rb
|
158
|
+
- lib/rack/tracker/extensions.rb
|
159
|
+
- lib/rack/tracker/facebook/facebook.rb
|
160
|
+
- lib/rack/tracker/facebook/template/facebook.erb
|
161
|
+
- lib/rack/tracker/google_analytics/google_analytics.rb
|
162
|
+
- lib/rack/tracker/google_analytics/template/google_analytics.erb
|
163
|
+
- lib/rack/tracker/handler.rb
|
164
|
+
- lib/rack/tracker/handler_delegator.rb
|
165
|
+
- lib/rack/tracker/railtie.rb
|
58
166
|
- lib/rack/tracker/version.rb
|
59
167
|
- rack-tracker.gemspec
|
168
|
+
- spec/fixtures/another_handler.erb
|
169
|
+
- spec/fixtures/dummy.erb
|
170
|
+
- spec/fixtures/track_all_the_things.erb
|
171
|
+
- spec/fixtures/views/layouts/application.html.erb
|
172
|
+
- spec/fixtures/views/metal/index.html.erb
|
173
|
+
- spec/handler/facebook_spec.rb
|
174
|
+
- spec/handler/google_analytics_spec.rb
|
175
|
+
- spec/integration/rails_integration_spec.rb
|
176
|
+
- spec/spec_helper.rb
|
177
|
+
- spec/support/fake_handler.rb
|
178
|
+
- spec/support/metal_controller.rb
|
179
|
+
- spec/tracker/controller_spec.rb
|
180
|
+
- spec/tracker/handler_delegator_spec.rb
|
181
|
+
- spec/tracker/handler_set_spec.rb
|
182
|
+
- spec/tracker/tracker_spec.rb
|
60
183
|
homepage: https://github.com/railslove/rack-tracker
|
61
184
|
licenses:
|
62
185
|
- MIT
|
@@ -67,12 +190,12 @@ require_paths:
|
|
67
190
|
- lib
|
68
191
|
required_ruby_version: !ruby/object:Gem::Requirement
|
69
192
|
requirements:
|
70
|
-
- -
|
193
|
+
- - ">="
|
71
194
|
- !ruby/object:Gem::Version
|
72
195
|
version: '0'
|
73
196
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
197
|
requirements:
|
75
|
-
- -
|
198
|
+
- - ">="
|
76
199
|
- !ruby/object:Gem::Version
|
77
200
|
version: '0'
|
78
201
|
requirements: []
|
@@ -81,4 +204,19 @@ rubygems_version: 2.2.2
|
|
81
204
|
signing_key:
|
82
205
|
specification_version: 4
|
83
206
|
summary: Tracking made easy
|
84
|
-
test_files:
|
207
|
+
test_files:
|
208
|
+
- spec/fixtures/another_handler.erb
|
209
|
+
- spec/fixtures/dummy.erb
|
210
|
+
- spec/fixtures/track_all_the_things.erb
|
211
|
+
- spec/fixtures/views/layouts/application.html.erb
|
212
|
+
- spec/fixtures/views/metal/index.html.erb
|
213
|
+
- spec/handler/facebook_spec.rb
|
214
|
+
- spec/handler/google_analytics_spec.rb
|
215
|
+
- spec/integration/rails_integration_spec.rb
|
216
|
+
- spec/spec_helper.rb
|
217
|
+
- spec/support/fake_handler.rb
|
218
|
+
- spec/support/metal_controller.rb
|
219
|
+
- spec/tracker/controller_spec.rb
|
220
|
+
- spec/tracker/handler_delegator_spec.rb
|
221
|
+
- spec/tracker/handler_set_spec.rb
|
222
|
+
- spec/tracker/tracker_spec.rb
|