ahoy_matey 1.4.2 → 1.5.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +20 -2
- data/ahoy_matey.gemspec +0 -1
- data/app/controllers/ahoy/base_controller.rb +7 -2
- data/lib/ahoy.rb +0 -9
- data/lib/ahoy/engine.rb +0 -5
- data/lib/ahoy/tracker.rb +19 -9
- data/lib/ahoy/version.rb +1 -1
- data/lib/ahoy/visit_properties.rb +5 -5
- data/vendor/assets/javascripts/ahoy.js +75 -53
- metadata +4 -18
- data/lib/ahoy/throttle.rb +0 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 40222ba157a73c4362b78a26fe1796a9bbc9fb50
|
4
|
+
data.tar.gz: 58a88b0ed382c126ceb141c902855f5c1492ee77
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e04afdc9015e4456e2062c718c38ef043777b3e7442435926420db88bbeecb5226b60d899d45b24d69faa9cadb4041f735a7460f6f8f0062180763c90fdadc8d
|
7
|
+
data.tar.gz: 08de05c386b58857803d8867f92512293f68d41146d1c96d3e0c7082596ab4db0f0b920284ae28a9ffa946877440abe96279d2765b2cc030d68a65b6da62f9b6
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
## 1.5.0
|
2
|
+
|
3
|
+
- Removed throttling due to unintended side effects with its implementation
|
4
|
+
- Ensure basic token requirements
|
5
|
+
- Fixed visit recreation on cookie expiration
|
6
|
+
- Fixed issue where `/ahoy/visits` is called indefinitely when `Ahoy.cookie_domain = :all`
|
7
|
+
|
1
8
|
## 1.4.2
|
2
9
|
|
3
10
|
- Fixed issues with `where_properties`
|
data/README.md
CHANGED
@@ -440,7 +440,9 @@ Ahoy.quiet = false
|
|
440
440
|
|
441
441
|
How you explore the data depends on the data store used.
|
442
442
|
|
443
|
-
|
443
|
+
For SQL databases, you can use [Blazer](https://github.com/ankane/blazer) to easily generate charts and dashboards.
|
444
|
+
|
445
|
+
With ActiveRecord, you can do:
|
444
446
|
|
445
447
|
```ruby
|
446
448
|
Visit.group(:search_keyword).count
|
@@ -448,7 +450,7 @@ Visit.group(:country).count
|
|
448
450
|
Visit.group(:referring_domain).count
|
449
451
|
```
|
450
452
|
|
451
|
-
[Chartkick](http://chartkick.com/) and [Groupdate](https://github.com/ankane/groupdate) make it
|
453
|
+
[Chartkick](http://chartkick.com/) and [Groupdate](https://github.com/ankane/groupdate) make it easy to visualize the data.
|
452
454
|
|
453
455
|
```erb
|
454
456
|
<%= line_chart Visit.group_by_day(:started_at).count %>
|
@@ -528,6 +530,18 @@ Send a `POST` request as `Content-Type: application/json` to `/ahoy/events` with
|
|
528
530
|
|
529
531
|
Use an array to pass multiple events at once.
|
530
532
|
|
533
|
+
## Throttling
|
534
|
+
|
535
|
+
To throttle requests to Ahoy endpoints, check out [Rack::Attack](https://github.com/kickstarter/ack-attack). Here’s a sample config:
|
536
|
+
|
537
|
+
```ruby
|
538
|
+
Rack::Attack.throttle("ahoy/ip", limit: 20, period: 1.minute) do |req|
|
539
|
+
if req.path.start_with?("/ahoy/")
|
540
|
+
req.ip
|
541
|
+
end
|
542
|
+
end
|
543
|
+
```
|
544
|
+
|
531
545
|
## Reference
|
532
546
|
|
533
547
|
By default, Ahoy create endpoints at `/ahoy/visits` and `/ahoy/events`. To disable, use:
|
@@ -538,6 +552,10 @@ Ahoy.mount = false
|
|
538
552
|
|
539
553
|
## Upgrading
|
540
554
|
|
555
|
+
### 1.5.0
|
556
|
+
|
557
|
+
There’s nothing to do, but it’s worth noting that simple throttling, which was added in `1.3.0`, was removed due to unintended side effects with its implementation. See the [Throttling](#throttling) section for how to properly add it by hand if needed.
|
558
|
+
|
541
559
|
### 1.4.0
|
542
560
|
|
543
561
|
There’s nothing to do, but it’s worth noting the default store was changed from `ActiveRecordStore` to `ActiveRecordTokenStore` for new installations.
|
data/ahoy_matey.gemspec
CHANGED
@@ -27,7 +27,6 @@ Gem::Specification.new do |spec|
|
|
27
27
|
spec.add_dependency "request_store"
|
28
28
|
spec.add_dependency "uuidtools"
|
29
29
|
spec.add_dependency "safely_block", ">= 0.1.1"
|
30
|
-
spec.add_dependency "rack-attack"
|
31
30
|
|
32
31
|
spec.add_development_dependency "bundler", "~> 1.5"
|
33
32
|
spec.add_development_dependency "rake"
|
@@ -2,8 +2,13 @@ module Ahoy
|
|
2
2
|
class BaseController < ApplicationController
|
3
3
|
# skip all filters except for authlogic
|
4
4
|
filters = _process_action_callbacks.map(&:filter) - [:load_authlogic]
|
5
|
-
if
|
6
|
-
|
5
|
+
if Rails::VERSION::MAJOR >= 5
|
6
|
+
skip_before_action(*filters, raise: false)
|
7
|
+
skip_after_action(*filters, raise: false)
|
8
|
+
skip_around_action(*filters, raise: false)
|
9
|
+
before_action :verify_request_size
|
10
|
+
elsif respond_to?(:skip_action_callback)
|
11
|
+
skip_action_callback *filters
|
7
12
|
before_action :verify_request_size
|
8
13
|
else
|
9
14
|
skip_filter *filters
|
data/lib/ahoy.rb
CHANGED
@@ -72,15 +72,6 @@ module Ahoy
|
|
72
72
|
mattr_accessor :mount
|
73
73
|
self.mount = true
|
74
74
|
|
75
|
-
mattr_accessor :throttle
|
76
|
-
self.throttle = true
|
77
|
-
|
78
|
-
mattr_accessor :throttle_limit
|
79
|
-
self.throttle_limit = 20
|
80
|
-
|
81
|
-
mattr_accessor :throttle_period
|
82
|
-
self.throttle_period = 1.minute
|
83
|
-
|
84
75
|
mattr_accessor :job_queue
|
85
76
|
self.job_queue = :ahoy
|
86
77
|
|
data/lib/ahoy/engine.rb
CHANGED
@@ -1,11 +1,6 @@
|
|
1
1
|
module Ahoy
|
2
2
|
class Engine < ::Rails::Engine
|
3
3
|
initializer "ahoy.middleware", after: "sprockets.environment" do |app|
|
4
|
-
if Ahoy.throttle
|
5
|
-
require "ahoy/throttle"
|
6
|
-
app.middleware.use Ahoy::Throttle
|
7
|
-
end
|
8
|
-
|
9
4
|
next unless Ahoy.quiet
|
10
5
|
|
11
6
|
# Parse PATH_INFO by assets prefix
|
data/lib/ahoy/tracker.rb
CHANGED
@@ -30,7 +30,7 @@ module Ahoy
|
|
30
30
|
debug "Visit excluded"
|
31
31
|
else
|
32
32
|
if options[:defer]
|
33
|
-
set_cookie("ahoy_track", true)
|
33
|
+
set_cookie("ahoy_track", true, nil, false)
|
34
34
|
else
|
35
35
|
options = options.dup
|
36
36
|
|
@@ -60,11 +60,11 @@ module Ahoy
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def visit_id
|
63
|
-
@visit_id ||= ensure_uuid(existing_visit_id ||
|
63
|
+
@visit_id ||= ensure_uuid(existing_visit_id || visit_token_helper)
|
64
64
|
end
|
65
65
|
|
66
66
|
def visitor_id
|
67
|
-
@visitor_id ||= ensure_uuid(existing_visitor_id ||
|
67
|
+
@visitor_id ||= ensure_uuid(existing_visitor_id || visitor_token_helper)
|
68
68
|
end
|
69
69
|
|
70
70
|
def new_visit?
|
@@ -90,25 +90,31 @@ module Ahoy
|
|
90
90
|
@visit_properties ||= Ahoy::VisitProperties.new(request, @options.slice(:api))
|
91
91
|
end
|
92
92
|
|
93
|
-
# for ActiveRecordTokenStore only - do not use
|
94
93
|
def visit_token
|
95
|
-
@visit_token ||=
|
94
|
+
@visit_token ||= ensure_token(visit_token_helper)
|
96
95
|
end
|
97
96
|
|
98
|
-
# for ActiveRecordTokenStore only - do not use
|
99
97
|
def visitor_token
|
100
|
-
@visitor_token ||=
|
98
|
+
@visitor_token ||= ensure_token(visitor_token_helper)
|
101
99
|
end
|
102
100
|
|
103
101
|
protected
|
104
102
|
|
105
|
-
def
|
103
|
+
def visit_token_helper
|
104
|
+
@visit_token_helper ||= existing_visit_id || (@options[:api] && request.params["visit_token"]) || generate_id
|
105
|
+
end
|
106
|
+
|
107
|
+
def visitor_token_helper
|
108
|
+
@visitor_token_helper ||= existing_visitor_id || (@options[:api] && request.params["visitor_token"]) || generate_id
|
109
|
+
end
|
110
|
+
|
111
|
+
def set_cookie(name, value, duration = nil, use_domain = true)
|
106
112
|
cookie = {
|
107
113
|
value: value
|
108
114
|
}
|
109
115
|
cookie[:expires] = duration.from_now if duration
|
110
116
|
domain = Ahoy.cookie_domain || Ahoy.domain
|
111
|
-
cookie[:domain] = domain if domain
|
117
|
+
cookie[:domain] = domain if domain && use_domain
|
112
118
|
request.cookie_jar[name] = cookie
|
113
119
|
end
|
114
120
|
|
@@ -151,6 +157,10 @@ module Ahoy
|
|
151
157
|
Ahoy.ensure_uuid(id)
|
152
158
|
end
|
153
159
|
|
160
|
+
def ensure_token(token)
|
161
|
+
token.to_s.gsub(/[^a-z0-9\-]/i, "").first(64)
|
162
|
+
end
|
163
|
+
|
154
164
|
def debug(message)
|
155
165
|
Rails.logger.debug { "[ahoy] #{message}" }
|
156
166
|
end
|
data/lib/ahoy/version.rb
CHANGED
@@ -8,11 +8,11 @@ module Ahoy
|
|
8
8
|
|
9
9
|
KEYS = REQUEST_KEYS + TRAFFIC_SOURCE_KEYS + UTM_PARAMETER_KEYS + TECHNOLOGY_KEYS + LOCATION_KEYS
|
10
10
|
|
11
|
-
delegate
|
12
|
-
delegate
|
13
|
-
delegate
|
14
|
-
delegate
|
15
|
-
delegate
|
11
|
+
delegate(*REQUEST_KEYS, to: :request_deckhand)
|
12
|
+
delegate(*TRAFFIC_SOURCE_KEYS, to: :traffic_source_deckhand)
|
13
|
+
delegate(*(UTM_PARAMETER_KEYS + [:landing_params]), to: :utm_parameter_deckhand)
|
14
|
+
delegate(*TECHNOLOGY_KEYS, to: :technology_deckhand)
|
15
|
+
delegate(*LOCATION_KEYS, to: :location_deckhand)
|
16
16
|
|
17
17
|
def initialize(request, options = {})
|
18
18
|
@request = request
|
@@ -20,8 +20,9 @@
|
|
20
20
|
var queue = [];
|
21
21
|
var canStringify = typeof(JSON) !== "undefined" && typeof(JSON.stringify) !== "undefined";
|
22
22
|
var eventQueue = [];
|
23
|
-
var visitsUrl = ahoy.visitsUrl || "/ahoy/visits"
|
24
|
-
var eventsUrl = ahoy.eventsUrl || "/ahoy/events"
|
23
|
+
var visitsUrl = ahoy.visitsUrl || "/ahoy/visits";
|
24
|
+
var eventsUrl = ahoy.eventsUrl || "/ahoy/events";
|
25
|
+
var canTrackNow = ahoy.trackNow && canStringify && typeof(window.navigator.sendBeacon) !== "undefined";
|
25
26
|
|
26
27
|
// cookies
|
27
28
|
|
@@ -122,6 +123,13 @@
|
|
122
123
|
});
|
123
124
|
}
|
124
125
|
|
126
|
+
function trackEventNow(event) {
|
127
|
+
ready( function () {
|
128
|
+
var payload = new Blob([JSON.stringify([event])], {type : "application/json; charset=utf-8"});
|
129
|
+
navigator.sendBeacon(eventsUrl, payload)
|
130
|
+
});
|
131
|
+
}
|
132
|
+
|
125
133
|
function page() {
|
126
134
|
return ahoy.page || window.location.pathname;
|
127
135
|
}
|
@@ -137,64 +145,66 @@
|
|
137
145
|
};
|
138
146
|
}
|
139
147
|
|
140
|
-
|
141
|
-
|
142
|
-
visitId = getCookie("ahoy_visit");
|
143
|
-
visitorId = getCookie("ahoy_visitor");
|
144
|
-
track = getCookie("ahoy_track");
|
148
|
+
function createVisit() {
|
149
|
+
isReady = false;
|
145
150
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
setReady();
|
150
|
-
} else {
|
151
|
-
if (track) {
|
152
|
-
destroyCookie("ahoy_track");
|
153
|
-
}
|
154
|
-
|
155
|
-
if (!visitId) {
|
156
|
-
visitId = generateId();
|
157
|
-
setCookie("ahoy_visit", visitId, visitTtl);
|
158
|
-
}
|
151
|
+
visitId = ahoy.getVisitId();
|
152
|
+
visitorId = ahoy.getVisitorId();
|
153
|
+
track = getCookie("ahoy_track");
|
159
154
|
|
160
|
-
|
161
|
-
|
162
|
-
log("
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
155
|
+
if (visitId && visitorId && !track) {
|
156
|
+
// TODO keep visit alive?
|
157
|
+
log("Active visit");
|
158
|
+
setReady();
|
159
|
+
} else {
|
160
|
+
if (track) {
|
161
|
+
destroyCookie("ahoy_track");
|
167
162
|
}
|
168
163
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
platform: ahoy.platform || "Web",
|
173
|
-
landing_page: window.location.href,
|
174
|
-
screen_width: window.screen.width,
|
175
|
-
screen_height: window.screen.height
|
176
|
-
};
|
177
|
-
|
178
|
-
// referrer
|
179
|
-
if (document.referrer.length > 0) {
|
180
|
-
data.referrer = document.referrer;
|
164
|
+
if (!visitId) {
|
165
|
+
visitId = generateId();
|
166
|
+
setCookie("ahoy_visit", visitId, visitTtl);
|
181
167
|
}
|
182
168
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
169
|
+
// make sure cookies are enabled
|
170
|
+
if (getCookie("ahoy_visit")) {
|
171
|
+
log("Visit started");
|
172
|
+
|
173
|
+
if (!visitorId) {
|
174
|
+
visitorId = generateId();
|
175
|
+
setCookie("ahoy_visitor", visitorId, visitorTtl);
|
176
|
+
}
|
177
|
+
|
178
|
+
var data = {
|
179
|
+
visit_token: visitId,
|
180
|
+
visitor_token: visitorId,
|
181
|
+
platform: ahoy.platform || "Web",
|
182
|
+
landing_page: window.location.href,
|
183
|
+
screen_width: window.screen.width,
|
184
|
+
screen_height: window.screen.height
|
185
|
+
};
|
186
|
+
|
187
|
+
// referrer
|
188
|
+
if (document.referrer.length > 0) {
|
189
|
+
data.referrer = document.referrer;
|
190
|
+
}
|
191
|
+
|
192
|
+
log(data);
|
193
|
+
|
194
|
+
$.post(visitsUrl, data, setReady, "json");
|
195
|
+
} else {
|
196
|
+
log("Cookies disabled");
|
197
|
+
setReady();
|
198
|
+
}
|
189
199
|
}
|
190
200
|
}
|
191
201
|
|
192
202
|
ahoy.getVisitId = ahoy.getVisitToken = function () {
|
193
|
-
return
|
203
|
+
return getCookie("ahoy_visit");
|
194
204
|
};
|
195
205
|
|
196
206
|
ahoy.getVisitorId = ahoy.getVisitorToken = function () {
|
197
|
-
return
|
207
|
+
return getCookie("ahoy_visitor");
|
198
208
|
};
|
199
209
|
|
200
210
|
ahoy.reset = function () {
|
@@ -215,22 +225,32 @@
|
|
215
225
|
};
|
216
226
|
|
217
227
|
ahoy.track = function (name, properties) {
|
228
|
+
if (!ahoy.getVisitId()) {
|
229
|
+
createVisit();
|
230
|
+
}
|
231
|
+
|
218
232
|
// generate unique id
|
219
233
|
var event = {
|
220
234
|
id: generateId(),
|
235
|
+
visit_token: ahoy.getVisitId(),
|
236
|
+
visitor_token: ahoy.getVisitorId(),
|
221
237
|
name: name,
|
222
238
|
properties: properties,
|
223
239
|
time: (new Date()).getTime() / 1000.0
|
224
240
|
};
|
225
241
|
log(event);
|
226
242
|
|
227
|
-
|
228
|
-
|
243
|
+
if (canTrackNow) {
|
244
|
+
trackEventNow(event);
|
245
|
+
} else {
|
246
|
+
eventQueue.push(event);
|
247
|
+
saveEventQueue();
|
229
248
|
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
249
|
+
// wait in case navigating to reduce duplicate events
|
250
|
+
setTimeout( function () {
|
251
|
+
trackEvent(event);
|
252
|
+
}, 1000);
|
253
|
+
}
|
234
254
|
};
|
235
255
|
|
236
256
|
ahoy.trackView = function () {
|
@@ -273,6 +293,8 @@
|
|
273
293
|
ahoy.trackChanges();
|
274
294
|
};
|
275
295
|
|
296
|
+
createVisit();
|
297
|
+
|
276
298
|
// push events from queue
|
277
299
|
try {
|
278
300
|
eventQueue = JSON.parse(getCookie("ahoy_events") || "[]");
|
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: 1.
|
4
|
+
version: 1.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-08-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: railties
|
@@ -136,20 +136,6 @@ dependencies:
|
|
136
136
|
- - ">="
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: 0.1.1
|
139
|
-
- !ruby/object:Gem::Dependency
|
140
|
-
name: rack-attack
|
141
|
-
requirement: !ruby/object:Gem::Requirement
|
142
|
-
requirements:
|
143
|
-
- - ">="
|
144
|
-
- !ruby/object:Gem::Version
|
145
|
-
version: '0'
|
146
|
-
type: :runtime
|
147
|
-
prerelease: false
|
148
|
-
version_requirements: !ruby/object:Gem::Requirement
|
149
|
-
requirements:
|
150
|
-
- - ">="
|
151
|
-
- !ruby/object:Gem::Version
|
152
|
-
version: '0'
|
153
139
|
- !ruby/object:Gem::Dependency
|
154
140
|
name: bundler
|
155
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -274,7 +260,6 @@ files:
|
|
274
260
|
- lib/ahoy/stores/log_store.rb
|
275
261
|
- lib/ahoy/stores/mongoid_store.rb
|
276
262
|
- lib/ahoy/subscribers/active_record.rb
|
277
|
-
- lib/ahoy/throttle.rb
|
278
263
|
- lib/ahoy/tracker.rb
|
279
264
|
- lib/ahoy/version.rb
|
280
265
|
- lib/ahoy/visit_properties.rb
|
@@ -335,7 +320,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
335
320
|
version: '0'
|
336
321
|
requirements: []
|
337
322
|
rubyforge_project:
|
338
|
-
rubygems_version: 2.
|
323
|
+
rubygems_version: 2.6.1
|
339
324
|
signing_key:
|
340
325
|
specification_version: 4
|
341
326
|
summary: Simple, powerful visit tracking for Rails
|
@@ -348,3 +333,4 @@ test_files:
|
|
348
333
|
- test/properties/postgresql_text_test.rb
|
349
334
|
- test/test_helper.rb
|
350
335
|
- test/visit_properties_test.rb
|
336
|
+
has_rdoc:
|
data/lib/ahoy/throttle.rb
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
require "rack/attack"
|
2
|
-
|
3
|
-
module Ahoy
|
4
|
-
class Throttle < Rack::Attack
|
5
|
-
throttle("ahoy/ip", limit: Ahoy.throttle_limit, period: Ahoy.throttle_period) do |req|
|
6
|
-
if req.path.start_with?("/ahoy/")
|
7
|
-
req.ip
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
def_delegators self, :whitelisted?, :blacklisted?, :throttled?, :tracked?
|
12
|
-
|
13
|
-
def self.throttled_response
|
14
|
-
Rack::Attack.throttled_response
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|