ahoy_matey 0.1.2 → 0.1.3
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/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
|