rack-tracker 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.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
|
[](https://codeclimate.com/github/railslove/rack-tracker)
|
4
4
|
|
5
|
-
|
5
|
+
[](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
|