ga_events 0.1.0
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.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +93 -0
- data/REVIEW +2 -0
- data/Rakefile +2 -0
- data/TODO +4 -0
- data/app/assets/javascripts/ga_events.js.coffee +64 -0
- data/ga_events.gemspec +29 -0
- data/lib/ga_events.rb +1 -0
- data/lib/ga_events/engine.rb +5 -0
- data/lib/ga_events/event.rb +13 -0
- data/lib/ga_events/list.rb +26 -0
- data/lib/ga_events/middleware.rb +43 -0
- data/lib/ga_events/version.rb +3 -0
- metadata +83 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Nix-wie-weg GmbH & Co. KG
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
# GaEvents
|
2
|
+
|
3
|
+
Use Google Analytics' Event Tracking everywhere in your Rails app!
|
4
|
+
|
5
|
+
This gem alllows you to annotate events everywhere in the code of your Rails
|
6
|
+
app.
|
7
|
+
A rack middleware is automatically inserted into the stack. It transports
|
8
|
+
the event data to the client. Normal requests get a DIV injected, Ajax requests
|
9
|
+
get a data-pounded custom HTTP header appended.
|
10
|
+
The asset pipeline-ready CoffeeScript extracts this data on the client-side and
|
11
|
+
pushes it to Google Analytics via ga.js or Google Tag Manager.
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
GaEvents works with Rails 3.1 onwards. You can add it to your `Gemfile` with:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
gem 'ga_events'
|
19
|
+
```
|
20
|
+
|
21
|
+
Run the `bundle` command to install it.
|
22
|
+
|
23
|
+
Add to the top of your `application.js`:
|
24
|
+
|
25
|
+
```javascript
|
26
|
+
//= require ga_events.js
|
27
|
+
```
|
28
|
+
|
29
|
+
After requiring `ga_events.js`, choose an adapter.
|
30
|
+
|
31
|
+
For stock Google Analytics (ga.js) use:
|
32
|
+
|
33
|
+
```javascript
|
34
|
+
GaEvents.Event.adapter = function() {
|
35
|
+
return new GaEvents.GoogleAnalyticsAdapter();
|
36
|
+
}
|
37
|
+
```
|
38
|
+
|
39
|
+
If you are using Google Tag Manager you can add custom events which are then passed through to Google Analytics.
|
40
|
+
|
41
|
+
```javascript
|
42
|
+
GaEvents.Event.adapter = function() {
|
43
|
+
return new GaEvents.GoogleTagManagerAdapter("event_name"); // defaults to ga_event
|
44
|
+
}
|
45
|
+
```
|
46
|
+
|
47
|
+
## Usage
|
48
|
+
|
49
|
+
On the server-side a new event is added to a list, serialized into a container
|
50
|
+
element and then added to your HTML response. On Ajax requests a custom
|
51
|
+
HTTP header is added to the response.
|
52
|
+
|
53
|
+
You can create a new event like this:
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
GaEvents::Event.new(category, action, label, value)
|
57
|
+
```
|
58
|
+
|
59
|
+
On the client-side there is a similar interface to GaEvents:
|
60
|
+
|
61
|
+
```javascript
|
62
|
+
new GaEvents.Event(category, action, label, value)
|
63
|
+
```
|
64
|
+
|
65
|
+
We have taken special care of tracking events while the DOM is loading.
|
66
|
+
Events get collected until the DOM is ready and flushed afterwards.
|
67
|
+
|
68
|
+
### Too many events
|
69
|
+
|
70
|
+
Use something like this snippet to get informed of bloating HTTP headers with
|
71
|
+
event data:
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
class ApplicationController < ActionController::Base
|
75
|
+
after_filter :too_many_ga_events?
|
76
|
+
private
|
77
|
+
def too_many_ga_events?
|
78
|
+
if (serialized = GaEvents::List.to_s).length > 1_024
|
79
|
+
notify("GaEvents too big: #{serialized}")
|
80
|
+
end
|
81
|
+
true
|
82
|
+
end
|
83
|
+
end
|
84
|
+
```
|
85
|
+
|
86
|
+
## Contributing
|
87
|
+
|
88
|
+
Yes please! Use pull requests.
|
89
|
+
|
90
|
+
## More docs
|
91
|
+
|
92
|
+
* [Google Analytics: Event Tracking](https://developers.google.com/analytics/devguides/collection/gajs/eventTrackerGuide)
|
93
|
+
* [Google Tag Manager: Custom Events](http://support.google.com/tagmanager/answer/2574372#GoogleAnalytics)
|
data/REVIEW
ADDED
data/Rakefile
ADDED
data/TODO
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
# This file should be required as soon a possible to allow for
|
2
|
+
# early event tracking.
|
3
|
+
|
4
|
+
window.GaEvents = {}
|
5
|
+
|
6
|
+
class GaEvents.Event
|
7
|
+
adapter: null
|
8
|
+
@list: []
|
9
|
+
@may_flush: false
|
10
|
+
@header_key: "X-GA-Events"
|
11
|
+
@html_key: "ga-events"
|
12
|
+
klass: @
|
13
|
+
|
14
|
+
# Decompose a dom-string (ruby side) into an event object.
|
15
|
+
@from_string: (string) ->
|
16
|
+
$.map string.split("$"), (part) =>
|
17
|
+
[category, action, label, value] = part.split("|")
|
18
|
+
new @(category, action, label, value)
|
19
|
+
|
20
|
+
# Events should not be send to an adapter unless the DOM has finished loading.
|
21
|
+
@flush: ->
|
22
|
+
if @list.length > 0 and @may_flush
|
23
|
+
$.map @list, (event) -> event.push_to_adapter()
|
24
|
+
@list = []
|
25
|
+
|
26
|
+
# Add all events to a queue to flush them later
|
27
|
+
constructor: (@category, @action, @label, @value) ->
|
28
|
+
@klass.list.push @
|
29
|
+
@klass.flush()
|
30
|
+
|
31
|
+
push_to_adapter: ->
|
32
|
+
data =
|
33
|
+
action: @action
|
34
|
+
category: @category
|
35
|
+
data.label = @label if @is_valid_value(@label)
|
36
|
+
data.value = @value if @is_valid_value(@value)
|
37
|
+
@klass.adapter().push data
|
38
|
+
|
39
|
+
is_valid_value: (value) -> value? and value != ''
|
40
|
+
|
41
|
+
jQuery =>
|
42
|
+
@may_flush = true
|
43
|
+
@flush()
|
44
|
+
|
45
|
+
$(document).ajaxComplete (event, xhr) =>
|
46
|
+
xhr_events = xhr.getResponseHeader(@header_key)
|
47
|
+
@from_string(xhr_events) if xhr_events?
|
48
|
+
|
49
|
+
dom_events = $("div[data-#{@html_key}]").data(@html_key)
|
50
|
+
@from_string(dom_events) if dom_events?
|
51
|
+
|
52
|
+
class GaEvents.GoogleTagManagerAdapter
|
53
|
+
constructor: (@event = "ga_event") ->
|
54
|
+
push: (data) ->
|
55
|
+
data["event"] = @event
|
56
|
+
data["non_interaction"] = true
|
57
|
+
window.dataLayer.push(data)
|
58
|
+
|
59
|
+
class GaEvents.GoogleAnalyticsAdapter
|
60
|
+
push: (obj) ->
|
61
|
+
data = ["_trackEvent", obj["action"], obj["category"]]
|
62
|
+
data.push(obj["label"]) if obj.label?
|
63
|
+
data.push(obj["value"]) if obj.value?
|
64
|
+
window._qaq.push(data)
|
data/ga_events.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.expand_path('../lib/ga_events/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ['Florian Dütsch', 'Sven Winkler']
|
6
|
+
gem.email = ['florian.duetsch@nix-wie-weg.de',
|
7
|
+
'sven.winkler@nix-wie-weg.de']
|
8
|
+
gem.description =
|
9
|
+
%q{Google Analytics' Event Tracking everywhere in your Rails app}
|
10
|
+
gem.summary = 'This gem alllows you to annotate events everywhere in ' \
|
11
|
+
'the code of your Rails app. A rack middleware is ' \
|
12
|
+
'automatically inserted into the stack. It transports ' \
|
13
|
+
'the event data to the client. Normal requests get a ' \
|
14
|
+
'DIV injected, AJAX requests get a data-pounded custom ' \
|
15
|
+
'HTTP header appended. The asset pipeline-ready ' \
|
16
|
+
'CoffeeScript extracts this data on the client side ' \
|
17
|
+
'and pushes it to Google Analytics via ga.js or Google ' \
|
18
|
+
'Tag Manager.'
|
19
|
+
gem.homepage = 'https://github.com/Nix-wie-weg/ga_events'
|
20
|
+
|
21
|
+
gem.files = `git ls-files`.split($\)
|
22
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
23
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
24
|
+
gem.name = "ga_events"
|
25
|
+
gem.require_paths = ["lib"]
|
26
|
+
gem.version = GaEvents::VERSION
|
27
|
+
|
28
|
+
gem.add_dependency 'rails', '~> 3.1'
|
29
|
+
end
|
data/lib/ga_events.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
%w(middleware engine event list version).each { |f| require "ga_events/#{f}" }
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module GaEvents
|
2
|
+
class Event < Struct.new(:category, :action, :label, :value)
|
3
|
+
def initialize(category, action, label = nil, value = nil)
|
4
|
+
super
|
5
|
+
GaEvents::List << self
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_s
|
9
|
+
[category, action, label, value].join('|')
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# NOTE: Collecting the events is thread-safe, but will cause problems in an
|
2
|
+
# asynchronous environment.
|
3
|
+
|
4
|
+
module GaEvents::List
|
5
|
+
def self.<<(event)
|
6
|
+
data << event
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.to_s
|
10
|
+
data.collect(&:to_s).join('$')
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.present?
|
14
|
+
data.present?
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.init
|
18
|
+
Thread.current[:ga_events] = []
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.data
|
22
|
+
Thread.current[:ga_events]
|
23
|
+
end
|
24
|
+
|
25
|
+
private_class_method :data
|
26
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'rack/utils'
|
2
|
+
|
3
|
+
module GaEvents
|
4
|
+
class Middleware
|
5
|
+
|
6
|
+
def initialize(app)
|
7
|
+
@app = app
|
8
|
+
end
|
9
|
+
def call(env)
|
10
|
+
GaEvents::List.init
|
11
|
+
status, headers, response = @app.call(env)
|
12
|
+
headers = Rack::Utils::HeaderHash.new(headers)
|
13
|
+
|
14
|
+
if GaEvents::List.present?
|
15
|
+
request = Rack::Request.new(env)
|
16
|
+
|
17
|
+
# Can outgrow, headers might get too big
|
18
|
+
serialized = GaEvents::List.to_s
|
19
|
+
|
20
|
+
if request.xhr?
|
21
|
+
headers['X-GA-Events'] = serialized
|
22
|
+
elsif is_html?(status, headers)
|
23
|
+
new_body = response.body.sub('</body>',
|
24
|
+
"<div data-ga-events='#{serialized}'></div>\\0")
|
25
|
+
response = [new_body]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
[status, headers, response]
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
# Taken from:
|
35
|
+
# https://github.com/rack/rack-contrib/blob/master/lib/rack/contrib/jsonp.rb
|
36
|
+
def is_html?(status, headers)
|
37
|
+
!Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status.to_i) &&
|
38
|
+
headers.key?('Content-Type') &&
|
39
|
+
headers['Content-Type'].include?('text/html')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ga_events
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Florian Dütsch
|
9
|
+
- Sven Winkler
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2013-01-29 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rails
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ~>
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '3.1'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ~>
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '3.1'
|
31
|
+
description: Google Analytics' Event Tracking everywhere in your Rails app
|
32
|
+
email:
|
33
|
+
- florian.duetsch@nix-wie-weg.de
|
34
|
+
- sven.winkler@nix-wie-weg.de
|
35
|
+
executables: []
|
36
|
+
extensions: []
|
37
|
+
extra_rdoc_files: []
|
38
|
+
files:
|
39
|
+
- .gitignore
|
40
|
+
- Gemfile
|
41
|
+
- LICENSE
|
42
|
+
- README.md
|
43
|
+
- REVIEW
|
44
|
+
- Rakefile
|
45
|
+
- TODO
|
46
|
+
- app/assets/javascripts/ga_events.js.coffee
|
47
|
+
- ga_events.gemspec
|
48
|
+
- lib/ga_events.rb
|
49
|
+
- lib/ga_events/engine.rb
|
50
|
+
- lib/ga_events/event.rb
|
51
|
+
- lib/ga_events/list.rb
|
52
|
+
- lib/ga_events/middleware.rb
|
53
|
+
- lib/ga_events/version.rb
|
54
|
+
homepage: https://github.com/Nix-wie-weg/ga_events
|
55
|
+
licenses: []
|
56
|
+
post_install_message:
|
57
|
+
rdoc_options: []
|
58
|
+
require_paths:
|
59
|
+
- lib
|
60
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ! '>='
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0'
|
72
|
+
requirements: []
|
73
|
+
rubyforge_project:
|
74
|
+
rubygems_version: 1.8.24
|
75
|
+
signing_key:
|
76
|
+
specification_version: 3
|
77
|
+
summary: This gem alllows you to annotate events everywhere in the code of your Rails
|
78
|
+
app. A rack middleware is automatically inserted into the stack. It transports
|
79
|
+
the event data to the client. Normal requests get a DIV injected, AJAX requests
|
80
|
+
get a data-pounded custom HTTP header appended. The asset pipeline-ready CoffeeScript
|
81
|
+
extracts this data on the client side and pushes it to Google Analytics via ga.js
|
82
|
+
or Google Tag Manager.
|
83
|
+
test_files: []
|