ahoy_matey 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +76 -25
- data/app/controllers/ahoy/visits_controller.rb +17 -4
- data/lib/ahoy/controller.rb +3 -2
- data/lib/ahoy/version.rb +1 -1
- data/lib/ahoy_matey.rb +5 -4
- data/lib/generators/ahoy/templates/install.rb +5 -0
- data/vendor/assets/javascripts/ahoy.js +42 -48
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5e7ae0f31293a7769778587395e0bd568e61f6f7
|
4
|
+
data.tar.gz: 8b941ee921205a3fd192b520934f7e57fb3ead7c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e6d0bae8709e973024253730e1a95479e6effefca95356b89d88e0019f69eefa2b7eeb8a3ea194e9ef75f80e3011dd64e021f4b82fbba0bc7adea1738e81a212
|
7
|
+
data.tar.gz: 04abbb97db3428069c283284b07168188e69e98babd8e95eb93245a49da42ea753b46b91aa32d1d5a0f0867dcc5438f4fc93b22681d927326a7b38a1c53c329e
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -17,6 +17,30 @@ See which campaigns generate the most revenue effortlessly.
|
|
17
17
|
Order.joins(:visit).group("utm_campaign").sum(:revenue)
|
18
18
|
```
|
19
19
|
|
20
|
+
## Installation
|
21
|
+
|
22
|
+
Add this line to your application’s Gemfile:
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
gem 'ahoy_matey'
|
26
|
+
```
|
27
|
+
|
28
|
+
And run the generator. This creates a model to store visits.
|
29
|
+
|
30
|
+
```sh
|
31
|
+
rails generate ahoy:install
|
32
|
+
rake db:migrate
|
33
|
+
```
|
34
|
+
|
35
|
+
Lastly, include the javascript file in `app/assets/javascripts/application.js` after jQuery.
|
36
|
+
|
37
|
+
```javascript
|
38
|
+
//= require jquery
|
39
|
+
//= require ahoy
|
40
|
+
```
|
41
|
+
|
42
|
+
We recommend using traditional analytics services like [Google Analytics](http://www.google.com/analytics/) as well.
|
43
|
+
|
20
44
|
## How It Works
|
21
45
|
|
22
46
|
When someone visits your website, Ahoy creates a visit with lots of useful information.
|
@@ -65,13 +89,15 @@ Order.joins(:visit).group("device_type").count
|
|
65
89
|
|
66
90
|
Ahoy automatically attaches the `current_user` to the `current_visit`.
|
67
91
|
|
92
|
+
If you define your own `current_user` method, be sure to add it to `ActionController::Base`, not `ApplicationController`.
|
93
|
+
|
68
94
|
With [Devise](https://github.com/plataformatec/devise), it will attach the user even if he / she signs in after the visit starts.
|
69
95
|
|
70
96
|
With other authentication frameworks, add this to the end of your sign in method:
|
71
97
|
|
72
98
|
```ruby
|
73
|
-
if current_visit
|
74
|
-
current_visit.user
|
99
|
+
if current_visit and !current_visit.user
|
100
|
+
current_visit.user = current_user
|
75
101
|
current_visit.save!
|
76
102
|
end
|
77
103
|
```
|
@@ -109,45 +135,64 @@ http://datakick.org/?utm_medium=twitter&utm_campaign=social&utm_source=tweet123
|
|
109
135
|
|
110
136
|
Ahoy uses [Geocoder](https://github.com/alexreisner/geocoder) for IP-based geocoding.
|
111
137
|
|
112
|
-
### Multiple Subdomains
|
138
|
+
### Multiple Subdomains
|
113
139
|
|
114
|
-
To track visits across multiple subdomains, add this
|
140
|
+
To track visits across multiple subdomains, add this **before** the javascript files.
|
115
141
|
|
116
|
-
```
|
117
|
-
|
118
|
-
var Ahoy = {"domain": "yourdomain.com"};
|
119
|
-
</script>
|
142
|
+
```javascript
|
143
|
+
var ahoy = {"domain": "yourdomain.com"};
|
120
144
|
```
|
121
145
|
|
122
|
-
###
|
123
|
-
|
124
|
-
- Excludes bots
|
125
|
-
- Degrades gracefully when cookies are disabled
|
126
|
-
- Don’t need a field? Just remove it from the migration
|
146
|
+
### Development
|
127
147
|
|
128
|
-
|
148
|
+
Ahoy is built with developers in mind. You can run the following code in your browser’s console.
|
129
149
|
|
130
|
-
|
150
|
+
Force a new visit
|
131
151
|
|
132
|
-
```
|
133
|
-
|
152
|
+
```javascript
|
153
|
+
ahoy.reset(); // then reload the page
|
134
154
|
```
|
135
155
|
|
136
|
-
|
156
|
+
Log messages
|
137
157
|
|
138
|
-
```
|
139
|
-
|
140
|
-
rake db:migrate
|
158
|
+
```javascript
|
159
|
+
ahoy.debug();
|
141
160
|
```
|
142
161
|
|
143
|
-
|
162
|
+
Turn off logging
|
144
163
|
|
145
164
|
```javascript
|
146
|
-
|
147
|
-
//= require ahoy
|
165
|
+
ahoy.debug(false);
|
148
166
|
```
|
149
167
|
|
150
|
-
|
168
|
+
### Native Apps [experimental]
|
169
|
+
|
170
|
+
When a user launches the app, create a visit. Send a `POST` request to `/ahoy/visits` with:
|
171
|
+
|
172
|
+
- platform - `iOS`, `Android`, etc.
|
173
|
+
- app_version - `1.0.0`
|
174
|
+
- os_version - `7.0.6`
|
175
|
+
- visitor_token - if you have one
|
176
|
+
|
177
|
+
The endpoint will return a JSON response like:
|
178
|
+
|
179
|
+
```json
|
180
|
+
{
|
181
|
+
"visit_token": "8tx2ziymkwa1WlppnkqxyaBaRlXrEQ3K",
|
182
|
+
"visitor_token": "hYBIV0rBfrIUAiArWweiECt4N9pyiygN"
|
183
|
+
}
|
184
|
+
```
|
185
|
+
|
186
|
+
Send the visit token in the `Ahoy-Visit` header for all requests.
|
187
|
+
|
188
|
+
After 4 hours, create another visit and use the updated visit token.
|
189
|
+
|
190
|
+
### More
|
191
|
+
|
192
|
+
- Excludes bots
|
193
|
+
- Degrades gracefully when cookies are disabled
|
194
|
+
- Don’t need a field? Just remove it from the migration
|
195
|
+
- Visits are 4 hours by default
|
151
196
|
|
152
197
|
## Reference
|
153
198
|
|
@@ -157,6 +202,12 @@ Use a different model
|
|
157
202
|
Ahoy.visit_model = UserVisit
|
158
203
|
```
|
159
204
|
|
205
|
+
Change the platform on the web
|
206
|
+
|
207
|
+
```javascript
|
208
|
+
var ahoy = {"platform": "Mobile Web"}
|
209
|
+
```
|
210
|
+
|
160
211
|
## TODO
|
161
212
|
|
162
213
|
- simple dashboard
|
@@ -1,25 +1,38 @@
|
|
1
1
|
module Ahoy
|
2
|
-
class VisitsController <
|
2
|
+
class VisitsController < ApplicationController
|
3
|
+
# skip all filters
|
4
|
+
skip_filter *_process_action_callbacks.map(&:filter)
|
5
|
+
|
3
6
|
before_filter :halt_bots
|
4
7
|
|
5
8
|
def create
|
9
|
+
visit_token = generate_token
|
10
|
+
visitor_token = params[:visitor_token] || generate_token
|
11
|
+
|
6
12
|
visit =
|
7
13
|
Ahoy.visit_model.new do |v|
|
8
|
-
v.visit_token =
|
9
|
-
v.visitor_token =
|
14
|
+
v.visit_token = visit_token
|
15
|
+
v.visitor_token = visitor_token
|
10
16
|
v.ip = request.remote_ip if v.respond_to?(:ip=)
|
11
17
|
v.user_agent = request.user_agent if v.respond_to?(:user_agent=)
|
12
18
|
v.referrer = params[:referrer] if v.respond_to?(:referrer=)
|
13
19
|
v.landing_page = params[:landing_page] if v.respond_to?(:landing_page=)
|
14
20
|
v.user = current_user if respond_to?(:current_user) and v.respond_to?(:user=)
|
21
|
+
v.platform = params[:platform] if v.respond_to?(:platform=)
|
22
|
+
v.app_version = params[:app_version] if v.respond_to?(:app_version=)
|
23
|
+
v.os_version = params[:os_version] if v.respond_to?(:os_version=)
|
15
24
|
end
|
16
25
|
|
17
26
|
visit.save!
|
18
|
-
render json: {
|
27
|
+
render json: {visit_token: visit.visit_token, visitor_token: visit.visitor_token}
|
19
28
|
end
|
20
29
|
|
21
30
|
protected
|
22
31
|
|
32
|
+
def generate_token
|
33
|
+
SecureRandom.urlsafe_base64(32).gsub(/[\-_]/, "").first(32)
|
34
|
+
end
|
35
|
+
|
23
36
|
def browser
|
24
37
|
@browser ||= Browser.new(ua: request.user_agent)
|
25
38
|
end
|
data/lib/ahoy/controller.rb
CHANGED
@@ -11,8 +11,9 @@ module Ahoy
|
|
11
11
|
protected
|
12
12
|
|
13
13
|
def current_visit
|
14
|
-
|
15
|
-
|
14
|
+
visit_token = cookies[:ahoy_visit] || request.headers["Ahoy-Visit"]
|
15
|
+
if visit_token
|
16
|
+
@current_visit ||= Ahoy.visit_model.where(visit_token: visit_token).first
|
16
17
|
end
|
17
18
|
end
|
18
19
|
|
data/lib/ahoy/version.rb
CHANGED
data/lib/ahoy_matey.rb
CHANGED
@@ -31,10 +31,11 @@ ActiveRecord::Base.send(:extend, Ahoy::Model) if defined?(ActiveRecord)
|
|
31
31
|
|
32
32
|
if defined?(Warden)
|
33
33
|
Warden::Manager.after_set_user except: :fetch do |user, auth, opts|
|
34
|
-
request =
|
35
|
-
|
36
|
-
|
37
|
-
|
34
|
+
request = ActionDispatch::Request.new(auth.env)
|
35
|
+
visit_token = request.cookies["ahoy_visit"] || request.headers["Ahoy-Visit"]
|
36
|
+
if visit_token
|
37
|
+
visit = Ahoy.visit_model.where(visit_token: visit_token).first
|
38
|
+
if visit and !visit.user
|
38
39
|
visit.user = user
|
39
40
|
visit.save!
|
40
41
|
end
|
@@ -3,24 +3,16 @@
|
|
3
3
|
(function (window) {
|
4
4
|
"use strict";
|
5
5
|
|
6
|
-
var
|
7
|
-
var options = window.Ahoy || {};
|
6
|
+
var ahoy = window.ahoy || window.Ahoy || {};
|
8
7
|
var $ = window.jQuery || window.Zepto || window.$;
|
9
8
|
var visitToken, visitorToken;
|
10
|
-
var visitTtl
|
11
|
-
|
12
|
-
if (debugMode) {
|
13
|
-
visitTtl = 0.2;
|
14
|
-
visitorTtl = 5; // 5 minutes
|
15
|
-
} else {
|
16
|
-
visitTtl = 4 * 60; // 4 hours
|
17
|
-
visitorTtl = 2 * 365 * 24 * 60; // 2 years
|
18
|
-
}
|
9
|
+
var visitTtl = 4 * 60; // 4 hours
|
10
|
+
var visitorTtl = 2 * 365 * 24 * 60; // 2 years
|
19
11
|
|
20
12
|
// cookies
|
21
13
|
|
22
14
|
// http://www.quirksmode.org/js/cookies.html
|
23
|
-
function setCookie(name, value, ttl
|
15
|
+
function setCookie(name, value, ttl) {
|
24
16
|
var expires = "";
|
25
17
|
var cookieDomain = "";
|
26
18
|
if (ttl) {
|
@@ -28,8 +20,8 @@
|
|
28
20
|
date.setTime(date.getTime() + (ttl * 60 * 1000));
|
29
21
|
expires = "; expires=" + date.toGMTString();
|
30
22
|
}
|
31
|
-
if (domain) {
|
32
|
-
cookieDomain = "; domain=" + domain;
|
23
|
+
if (ahoy.domain) {
|
24
|
+
cookieDomain = "; domain=" + ahoy.domain;
|
33
25
|
}
|
34
26
|
document.cookie = name + "=" + value + expires + cookieDomain + "; path=/";
|
35
27
|
}
|
@@ -50,26 +42,13 @@
|
|
50
42
|
return null;
|
51
43
|
}
|
52
44
|
|
53
|
-
|
54
|
-
|
55
|
-
// https://github.com/klughammer/node-randomstring
|
56
|
-
function generateToken() {
|
57
|
-
var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghiklmnopqrstuvwxyz';
|
58
|
-
var length = 32;
|
59
|
-
var string = '';
|
60
|
-
var i, randomNumber;
|
61
|
-
|
62
|
-
for (i = 0; i < length; i++) {
|
63
|
-
randomNumber = Math.floor(Math.random() * chars.length);
|
64
|
-
string += chars.substring(randomNumber, randomNumber + 1);
|
65
|
-
}
|
66
|
-
|
67
|
-
return string;
|
45
|
+
function destroyCookie(name) {
|
46
|
+
setCookie(name, "", -1);
|
68
47
|
}
|
69
48
|
|
70
|
-
function
|
71
|
-
if (
|
72
|
-
window.console.log(message
|
49
|
+
function log(message) {
|
50
|
+
if (getCookie("ahoy_debug")) {
|
51
|
+
window.console.log(message);
|
73
52
|
}
|
74
53
|
}
|
75
54
|
|
@@ -78,26 +57,18 @@
|
|
78
57
|
visitToken = getCookie("ahoy_visit");
|
79
58
|
visitorToken = getCookie("ahoy_visitor");
|
80
59
|
|
81
|
-
if (visitToken && visitorToken) {
|
60
|
+
if (visitToken && visitorToken && visitToken != "test") {
|
82
61
|
// TODO keep visit alive?
|
83
|
-
|
62
|
+
log("Active visit");
|
84
63
|
} else {
|
85
|
-
|
86
|
-
visitorToken = generateToken();
|
87
|
-
setCookie("ahoy_visitor", visitorToken, visitorTtl, options.domain);
|
88
|
-
}
|
89
|
-
|
90
|
-
// always generate a new visit id here
|
91
|
-
visitToken = generateToken();
|
92
|
-
setCookie("ahoy_visit", visitToken, visitTtl, options.domain);
|
64
|
+
setCookie("ahoy_visit", "test", 1);
|
93
65
|
|
94
66
|
// make sure cookies are enabled
|
95
67
|
if (getCookie("ahoy_visit")) {
|
96
|
-
|
68
|
+
log("Visit started");
|
97
69
|
|
98
70
|
var data = {
|
99
|
-
|
100
|
-
visitor_token: visitorToken,
|
71
|
+
platform: ahoy.platform || "Web",
|
101
72
|
landing_page: window.location.href
|
102
73
|
};
|
103
74
|
|
@@ -106,12 +77,35 @@
|
|
106
77
|
data.referrer = document.referrer;
|
107
78
|
}
|
108
79
|
|
109
|
-
|
80
|
+
if (visitorToken) {
|
81
|
+
data.visitor_token = visitorToken;
|
82
|
+
}
|
83
|
+
|
84
|
+
log(data);
|
110
85
|
|
111
|
-
$.post("/ahoy/visits", data)
|
86
|
+
$.post("/ahoy/visits", data, function(response) {
|
87
|
+
setCookie("ahoy_visit", response.visit_token, visitTtl);
|
88
|
+
setCookie("ahoy_visitor", response.visitor_token, visitorTtl);
|
89
|
+
}, "json");
|
112
90
|
} else {
|
113
|
-
|
91
|
+
log("Cookies disabled");
|
114
92
|
}
|
115
93
|
}
|
116
94
|
|
95
|
+
ahoy.reset = function () {
|
96
|
+
destroyCookie("ahoy_visit");
|
97
|
+
destroyCookie("ahoy_visitor");
|
98
|
+
return true;
|
99
|
+
};
|
100
|
+
|
101
|
+
ahoy.debug = function (enabled) {
|
102
|
+
if (enabled === false) {
|
103
|
+
destroyCookie("ahoy_debug");
|
104
|
+
} else {
|
105
|
+
setCookie("ahoy_debug", "t", 365 * 24 * 60); // 1 year
|
106
|
+
}
|
107
|
+
return true;
|
108
|
+
};
|
109
|
+
|
110
|
+
window.ahoy = ahoy;
|
117
111
|
}(window));
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ahoy_matey
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-04-
|
11
|
+
date: 2014-04-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|