ahoy_matey 1.4.2 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1aebb95363cb98c2b74dadb3b42f9cd04c17c421
4
- data.tar.gz: 08785d2aa638fddb3881d4365e74ded3049cfec8
3
+ metadata.gz: 40222ba157a73c4362b78a26fe1796a9bbc9fb50
4
+ data.tar.gz: 58a88b0ed382c126ceb141c902855f5c1492ee77
5
5
  SHA512:
6
- metadata.gz: 16c58280be2b7a51a4f41c837204f701a93897b509330a0ff648d06819e2b944373fcaf5e0b595009270f5b332ec1ef81626537b824deafbd2f89cca5ece4376
7
- data.tar.gz: 2aeb6f81dd3f0fdf09d0aee20ba52ed9349903636b9b392e87782ae135161520809aa45549dca292a479d5bae95ba893c4e6f95b29e13f6d32c601a39e204aae
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
- Here are ways to do it with ActiveRecord.
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 super easy to visualize the data.
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 respond_to?(:skip_action)
6
- skip_action *filters
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 || visit_token)
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 || visitor_token)
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 ||= existing_visit_id || (@options[:api] && request.params["visit_token"]) || generate_id
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 ||= existing_visitor_id || (@options[:api] && request.params["visitor_token"]) || generate_id
98
+ @visitor_token ||= ensure_token(visitor_token_helper)
101
99
  end
102
100
 
103
101
  protected
104
102
 
105
- def set_cookie(name, value, duration = nil)
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
@@ -1,3 +1,3 @@
1
1
  module Ahoy
2
- VERSION = "1.4.2"
2
+ VERSION = "1.5.0"
3
3
  end
@@ -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 *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
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
- // main
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
- if (visitId && visitorId && !track) {
147
- // TODO keep visit alive?
148
- log("Active visit");
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
- // make sure cookies are enabled
161
- if (getCookie("ahoy_visit")) {
162
- log("Visit started");
163
-
164
- if (!visitorId) {
165
- visitorId = generateId();
166
- setCookie("ahoy_visitor", visitorId, visitorTtl);
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
- var data = {
170
- visit_token: visitId,
171
- visitor_token: visitorId,
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
- log(data);
184
-
185
- $.post(visitsUrl, data, setReady, "json");
186
- } else {
187
- log("Cookies disabled");
188
- setReady();
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 visitId;
203
+ return getCookie("ahoy_visit");
194
204
  };
195
205
 
196
206
  ahoy.getVisitorId = ahoy.getVisitorToken = function () {
197
- return visitorId;
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
- eventQueue.push(event);
228
- saveEventQueue();
243
+ if (canTrackNow) {
244
+ trackEventNow(event);
245
+ } else {
246
+ eventQueue.push(event);
247
+ saveEventQueue();
229
248
 
230
- // wait in case navigating to reduce duplicate events
231
- setTimeout( function () {
232
- trackEvent(event);
233
- }, 1000);
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.2
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-06-21 00:00:00.000000000 Z
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.4.5.1
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