ahoy_matey 4.1.0 → 5.0.2

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
  SHA256:
3
- metadata.gz: 021c0b07d7cddf6a22125683bb5b1d18a078f1ff1f504cca066d4069c6014357
4
- data.tar.gz: d49b52ce94f4c7436df4d054324639f9e5cd951fe459a11ca28aaab36231bd19
3
+ metadata.gz: f2da2e05c42ba50652c0fc6aada9a25bbaada8746a6802a7585790a751a2d9f4
4
+ data.tar.gz: 6ad4e393177de9f357957259082e0960ad8ea07e51d2c75e1b07826955a01456
5
5
  SHA512:
6
- metadata.gz: 6082fea0363bb2f25b7309876a36a1982894d3dcc2dc905da2278ab0ca41257f1c322001c86deb36168dbd1649c87199ec65990c9e7307b407b3f96fdb7a2bac
7
- data.tar.gz: d833375e6cdb800b51072ed3d3c269e55c21c06287cde4a351022c331b36dd4b9aa812afa7b33156ee7a7b745d08a53b0d726000a919fd616300c0f6149a51b4
6
+ metadata.gz: cbfcb708d2a2343b51f0eb9b20de751f9802695445049d09f15a09ee214151ac4b20705d4f7369d12cdf254018a3b5f3c3271ebdfef5fc2516c52ecd457f5203
7
+ data.tar.gz: 135303f5c3c1f04a0e08b38859f4ed67e0d530e83908c1d1e83af7d012c6c68dabd810a2800bec9e32249ea5605a3405a66969ebb55d8cd56eba0a66d7b55af9
data/CHANGELOG.md CHANGED
@@ -1,3 +1,28 @@
1
+ ## 5.0.2 (2023-10-05)
2
+
3
+ - Excluded visits from Rails health check
4
+
5
+ ## 5.0.1 (2023-10-01)
6
+
7
+ - Fixed error with geocoding with anonymity sets
8
+
9
+ ## 5.0.0 (2023-10-01)
10
+
11
+ - Changed visits to expire with anonymity sets
12
+ - Fixed error when Active Job is not available
13
+ - Fixed deprecation warning with Rails 7.1
14
+ - Dropped support for Ruby < 3 and Rails < 6.1
15
+ - Dropped support for Mongoid 6
16
+
17
+ ## 4.2.1 (2023-02-23)
18
+
19
+ - Updated Ahoy.js to 0.4.2
20
+
21
+ ## 4.2.0 (2023-02-07)
22
+
23
+ - Added primary key type to generated migration
24
+ - Updated Ahoy.js to 0.4.1
25
+
1
26
  ## 4.1.0 (2022-06-12)
2
27
 
3
28
  - Ensure `exclude_method` is only called once per request
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2014-2022 Andrew Kane
1
+ Copyright (c) 2014-2023 Andrew Kane
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  Track visits and events in Ruby, JavaScript, and native apps. Data is stored in your database by default, and you can customize it for any data store as you grow.
6
6
 
7
- **Ahoy 4.0 was recently released** - see [how to upgrade](#upgrading)
7
+ **Ahoy 5.0 was recently released** - see [how to upgrade](#upgrading)
8
8
 
9
9
  :postbox: Check out [Ahoy Email](https://github.com/ankane/ahoy_email) for emails and [Field Test](https://github.com/ankane/field_test) for A/B testing
10
10
 
@@ -48,7 +48,7 @@ And restart your web server.
48
48
 
49
49
  ### JavaScript
50
50
 
51
- For Rails 7 / Importmap, add to `config/importmap.rb`:
51
+ For Importmap (Rails 7 default), add to `config/importmap.rb`:
52
52
 
53
53
  ```ruby
54
54
  pin "ahoy", to: "ahoy.js"
@@ -60,7 +60,7 @@ And add to `app/javascript/application.js`:
60
60
  import "ahoy"
61
61
  ```
62
62
 
63
- For Rails 6 / Webpacker, run:
63
+ For Webpacker (Rails 6 default), run:
64
64
 
65
65
  ```sh
66
66
  yarn add ahoy.js
@@ -72,7 +72,7 @@ And add to `app/javascript/packs/application.js`:
72
72
  import ahoy from "ahoy.js"
73
73
  ```
74
74
 
75
- For Rails 5 / Sprockets, add to `app/assets/javascripts/application.js`:
75
+ For Sprockets, add to `app/assets/javascripts/application.js`:
76
76
 
77
77
  ```javascript
78
78
  //= require ahoy
@@ -212,7 +212,7 @@ visitable :sign_up_visit
212
212
 
213
213
  ### Users
214
214
 
215
- Ahoy automatically attaches the `current_user` to the visit. With [Devise](https://github.com/plataformatec/devise), it attaches the user even if they sign in after the visit starts.
215
+ Ahoy automatically attaches the `current_user` to the visit. With [Devise](https://github.com/heartcombo/devise), it attaches the user even if they sign in after the visit starts.
216
216
 
217
217
  With other authentication frameworks, add this to the end of your sign in method:
218
218
 
@@ -262,28 +262,6 @@ class ApplicationController < ActionController::Base
262
262
  end
263
263
  ```
264
264
 
265
- #### Knock
266
-
267
- To attach the user with [Knock](https://github.com/nsarno/knock), either include `Knock::Authenticable`in `ApplicationController`:
268
-
269
- ```ruby
270
- class ApplicationController < ActionController::API
271
- include Knock::Authenticable
272
- end
273
- ```
274
-
275
- Or include it in Ahoy:
276
-
277
- ```ruby
278
- Ahoy::BaseController.include Knock::Authenticable
279
- ```
280
-
281
- And use:
282
-
283
- ```ruby
284
- Ahoy.user_method = ->(controller) { controller.send(:authenticate_entity, "user") }
285
- ```
286
-
287
265
  ### Exclusions
288
266
 
289
267
  Bots are excluded from tracking by default. To include them, use:
@@ -308,6 +286,14 @@ By default, a new visit is created after 4 hours of inactivity. Change this with
308
286
  Ahoy.visit_duration = 30.minutes
309
287
  ```
310
288
 
289
+ ### Visitor Duration
290
+
291
+ By default, a new `visitor_token` is generated after 2 years. Change this with:
292
+
293
+ ```ruby
294
+ Ahoy.visitor_duration = 30.days
295
+ ```
296
+
311
297
  ### Cookies
312
298
 
313
299
  To track visits across multiple subdomains, use:
@@ -326,15 +312,15 @@ You can also [disable cookies](#anonymity-sets--cookies)
326
312
 
327
313
  ### Token Generation
328
314
 
329
- Ahoy uses random UUIDs for visit and visitor tokens by default, but you can use your own generator like [Druuid](https://github.com/recurly/druuid).
315
+ Ahoy uses random UUIDs for visit and visitor tokens by default, but you can use your own generator like [ULID](https://github.com/rafaelsales/ulid).
330
316
 
331
317
  ```ruby
332
- Ahoy.token_generator = -> { Druuid.gen }
318
+ Ahoy.token_generator = -> { ULID.generate }
333
319
  ```
334
320
 
335
321
  ### Throttling
336
322
 
337
- You can use [Rack::Attack](https://github.com/kickstarter/rack-attack) to throttle requests to the API.
323
+ You can use [Rack::Attack](https://github.com/rack/rack-attack) to throttle requests to the API.
338
324
 
339
325
  ```ruby
340
326
  class Rack::Attack
@@ -378,13 +364,17 @@ Ahoy.job_queue = :low_priority
378
364
 
379
365
  ### Local Geocoding
380
366
 
381
- For privacy and performance, we recommend geocoding locally. Add this line to your application’s Gemfile:
367
+ For privacy and performance, we recommend geocoding locally.
368
+
369
+ For city-level geocoding, download the [GeoLite2 City database](https://dev.maxmind.com/geoip/geolite2-free-geolocation-data).
370
+
371
+ Add this line to your application’s Gemfile:
382
372
 
383
373
  ```ruby
384
374
  gem "maxminddb"
385
375
  ```
386
376
 
387
- For city-level geocoding, download the [GeoLite2 City database](https://dev.maxmind.com/geoip/geoip2/geolite2/) and create `config/initializers/geocoder.rb` with:
377
+ And create `config/initializers/geocoder.rb` with:
388
378
 
389
379
  ```ruby
390
380
  Geocoder.configure(
@@ -401,6 +391,12 @@ For country-level geocoding, install the `geoip-database` package. It’s preins
401
391
  sudo apt-get install geoip-database
402
392
  ```
403
393
 
394
+ Add this line to your application’s Gemfile:
395
+
396
+ ```ruby
397
+ gem "geoip"
398
+ ```
399
+
404
400
  And create `config/initializers/geocoder.rb` with:
405
401
 
406
402
  ```ruby
@@ -450,7 +446,7 @@ class Ahoy::Store < Ahoy::DatabaseStore
450
446
  end
451
447
 
452
448
  Ahoy.mask_ips = true
453
- Ahoy.cookies = false
449
+ Ahoy.cookies = :none
454
450
  ```
455
451
 
456
452
  This:
@@ -488,20 +484,20 @@ end
488
484
 
489
485
  ### Anonymity Sets & Cookies
490
486
 
491
- Ahoy can switch from cookies to [anonymity sets](https://privacypatterns.org/patterns/Anonymity-set). Instead of cookies, visitors with the same IP mask and user agent are grouped together in an anonymity set.
487
+ Ahoy can switch from cookies to [anonymity sets](https://privacypatterns.org/patterns/Anonymity-set). Instead of cookies, visitors with the same IP mask and user agent are grouped together in an anonymity set.
492
488
 
493
489
  ```ruby
494
- Ahoy.cookies = false
490
+ Ahoy.cookies = :none
495
491
  ```
496
492
 
493
+ Note: If Ahoy was installed before v5, [add an index](#50) before making this change.
494
+
497
495
  Previously set cookies are automatically deleted. If you use JavaScript tracking, also set:
498
496
 
499
497
  ```javascript
500
498
  ahoy.configure({cookies: false});
501
499
  ```
502
500
 
503
- Note: With anonymity sets, visits no longer expire after 4 hours of inactivity. A new visit is only created when the IP mask or user agent changes (for instance, when a user updates their browser). There are plans to address this in the next major version.
504
-
505
501
  ## Data Retention
506
502
 
507
503
  Data should only be retained for as long as it’s needed. Delete older data with:
@@ -639,7 +635,7 @@ end
639
635
 
640
636
  [Blazer](https://github.com/ankane/blazer) is a great tool for exploring your data.
641
637
 
642
- With ActiveRecord, you can do:
638
+ With Active Record, you can do:
643
639
 
644
640
  ```ruby
645
641
  Ahoy::Visit.group(:search_keyword).count
@@ -777,19 +773,29 @@ Send a `POST` request to `/ahoy/events` with `Content-Type: application/json` an
777
773
 
778
774
  ## Upgrading
779
775
 
780
- ### 4.0
776
+ ### 5.0
781
777
 
782
- There are two notable changes to geocoding:
778
+ Visits now expire with anonymity sets. If using `Ahoy.cookies = false`, a new index is needed.
783
779
 
784
- 1. Geocoding is now disabled by default (this was already the case for new installations with 3.2.0+). Check out the instructions for [how to enable it](#geocoding).
780
+ For Active Record, create a migration with:
785
781
 
786
- 2. The `geocoder` gem is now an optional dependency. To use geocoding, add it to your Gemfile:
782
+ ```ruby
783
+ add_index :ahoy_visits, [:visitor_token, :started_at]
784
+ ```
787
785
 
788
- ```ruby
789
- gem "geocoder"
790
- ```
786
+ For Mongoid, set:
791
787
 
792
- Also, check out the [upgrade notes](https://github.com/ankane/ahoy.js#upgrading) for Ahoy.js.
788
+ ```ruby
789
+ class Ahoy::Visit
790
+ index({visitor_token: 1, started_at: 1})
791
+ end
792
+ ```
793
+
794
+ Create the index before upgrading, and set:
795
+
796
+ ```ruby
797
+ Ahoy.cookies = :none
798
+ ```
793
799
 
794
800
  ## History
795
801
 
@@ -813,26 +819,10 @@ bundle install
813
819
  bundle exec rake test
814
820
  ```
815
821
 
816
- To test Mongoid, use:
822
+ To test different adapters, use:
817
823
 
818
824
  ```sh
825
+ ADAPTER=postgresql bundle exec rake test
826
+ ADAPTER=mysql2 bundle exec rake test
819
827
  ADAPTER=mongoid bundle exec rake test
820
828
  ```
821
-
822
- To test query methods, use:
823
-
824
- ```sh
825
- # Postgres
826
- createdb ahoy_test
827
- bundle exec rake test:query_methods:postgresql
828
-
829
- # SQLite
830
- bundle exec rake test:query_methods:sqlite
831
-
832
- # MySQL and MariaDB
833
- mysqladmin create ahoy_test
834
- bundle exec rake test:query_methods:mysql
835
-
836
- # MongoDB
837
- bundle exec rake test:query_methods:mongoid
838
- ```
@@ -3,6 +3,7 @@ module Ahoy
3
3
  attr_writer :user
4
4
 
5
5
  def initialize(options)
6
+ @user = options[:user]
6
7
  @options = options
7
8
  end
8
9
 
@@ -36,7 +37,9 @@ module Ahoy
36
37
  end
37
38
 
38
39
  def exclude?
39
- (!Ahoy.track_bots && bot?) || exclude_by_method?
40
+ (!Ahoy.track_bots && bot?) ||
41
+ exclude_by_method? ||
42
+ (defined?(Rails::HealthController) && controller.is_a?(Rails::HealthController))
40
43
  end
41
44
 
42
45
  def generate_id
@@ -5,9 +5,8 @@ module Ahoy
5
5
  base.helper_method :current_visit
6
6
  base.helper_method :ahoy
7
7
  end
8
- base.before_action :set_ahoy_cookies, unless: -> { Ahoy.api_only }
9
8
  base.before_action :track_ahoy_visit, unless: -> { Ahoy.api_only }
10
- base.around_action :set_ahoy_request_store
9
+ base.around_action :set_ahoy_request_store, unless: -> { Ahoy.api_only }
11
10
  end
12
11
 
13
12
  def ahoy
@@ -19,7 +18,7 @@ module Ahoy
19
18
  end
20
19
 
21
20
  def set_ahoy_cookies
22
- if Ahoy.cookies
21
+ if Ahoy.cookies?
23
22
  ahoy.set_visitor_cookie
24
23
  ahoy.set_visit_cookie
25
24
  else
@@ -31,11 +30,17 @@ module Ahoy
31
30
  def track_ahoy_visit
32
31
  defer = Ahoy.server_side_visits != true
33
32
 
34
- if defer && !Ahoy.cookies
33
+ if defer && !Ahoy.cookies?
35
34
  # avoid calling new_visit?, which triggers a database call
35
+ elsif !Ahoy.cookies? && ahoy.exclude?
36
+ # avoid calling new_visit?, which triggers a database call
37
+ # may or may not be a new visit
38
+ Ahoy.log("Request excluded")
36
39
  elsif ahoy.new_visit?
37
40
  ahoy.track_visit(defer: defer)
38
41
  end
42
+
43
+ set_ahoy_cookies
39
44
  end
40
45
 
41
46
  def set_ahoy_request_store
@@ -53,11 +53,13 @@ module Ahoy
53
53
 
54
54
  def visit
55
55
  unless defined?(@visit)
56
- if defined?(Mongoid::Document) && visit_model < Mongoid::Document
57
- # find_by raises error by default when not found
58
- @visit = visit_model.where(visit_token: ahoy.visit_token).first if ahoy.visit_token
56
+ if ahoy.send(:existing_visit_token) || ahoy.instance_variable_get(:@visit_token)
57
+ # find_by raises error by default with Mongoid when not found
58
+ @visit = visit_model.where(visit_token: ahoy.visit_token).take if ahoy.visit_token
59
+ elsif !Ahoy.cookies? && ahoy.visitor_token
60
+ @visit = visit_model.where(visitor_token: ahoy.visitor_token).where(started_at: Ahoy.visit_duration.ago..).order(started_at: :desc).first
59
61
  else
60
- @visit = visit_model.find_by(visit_token: ahoy.visit_token) if ahoy.visit_token
62
+ @visit = nil
61
63
  end
62
64
  end
63
65
  @visit
data/lib/ahoy/tracker.rb CHANGED
@@ -99,7 +99,7 @@ module Ahoy
99
99
  end
100
100
 
101
101
  def new_visit?
102
- Ahoy.cookies ? !existing_visit_token : visit.nil?
102
+ Ahoy.cookies? ? !existing_visit_token : visit.nil?
103
103
  end
104
104
 
105
105
  def new_visitor?
@@ -145,6 +145,13 @@ module Ahoy
145
145
  delete_cookie("ahoy_track")
146
146
  end
147
147
 
148
+ def exclude?
149
+ unless defined?(@exclude)
150
+ @exclude = @store.exclude?
151
+ end
152
+ @exclude
153
+ end
154
+
148
155
  protected
149
156
 
150
157
  def api?
@@ -153,7 +160,7 @@ module Ahoy
153
160
 
154
161
  # private, but used by API
155
162
  def missing_params?
156
- if Ahoy.cookies && api? && Ahoy.protect_from_forgery
163
+ if Ahoy.cookies? && api? && Ahoy.protect_from_forgery
157
164
  !(existing_visit_token && existing_visitor_token)
158
165
  else
159
166
  false
@@ -162,7 +169,7 @@ module Ahoy
162
169
 
163
170
  def set_cookie(name, value, duration = nil, use_domain = true)
164
171
  # safety net
165
- return unless Ahoy.cookies && request
172
+ return unless Ahoy.cookies? && request
166
173
 
167
174
  cookie = Ahoy.cookie_options.merge(value: value)
168
175
  cookie[:expires] = duration.from_now if duration
@@ -184,13 +191,6 @@ module Ahoy
184
191
  end
185
192
  end
186
193
 
187
- def exclude?
188
- unless defined?(@exclude)
189
- @exclude = @store.exclude?
190
- end
191
- @exclude
192
- end
193
-
194
194
  def report_exception(e)
195
195
  if defined?(ActionDispatch::RemoteIp::IpSpoofAttackError) && e.is_a?(ActionDispatch::RemoteIp::IpSpoofAttackError)
196
196
  debug "Tracking excluded due to IP spoofing"
@@ -207,7 +207,7 @@ module Ahoy
207
207
  def visit_token_helper
208
208
  @visit_token_helper ||= begin
209
209
  token = existing_visit_token
210
- token ||= visit_anonymity_set unless Ahoy.cookies
210
+ token ||= visit&.visit_token unless Ahoy.cookies?
211
211
  token ||= generate_id unless Ahoy.api_only
212
212
  token
213
213
  end
@@ -216,7 +216,7 @@ module Ahoy
216
216
  def visitor_token_helper
217
217
  @visitor_token_helper ||= begin
218
218
  token = existing_visitor_token
219
- token ||= visitor_anonymity_set unless Ahoy.cookies
219
+ token ||= visitor_anonymity_set unless Ahoy.cookies?
220
220
  token ||= generate_id unless Ahoy.api_only
221
221
  token
222
222
  end
@@ -225,7 +225,7 @@ module Ahoy
225
225
  def existing_visit_token
226
226
  @existing_visit_token ||= begin
227
227
  token = visit_header
228
- token ||= visit_cookie if Ahoy.cookies && !(api? && Ahoy.protect_from_forgery)
228
+ token ||= visit_cookie if Ahoy.cookies? && !(api? && Ahoy.protect_from_forgery)
229
229
  token ||= visit_param if api?
230
230
  token
231
231
  end
@@ -234,16 +234,12 @@ module Ahoy
234
234
  def existing_visitor_token
235
235
  @existing_visitor_token ||= begin
236
236
  token = visitor_header
237
- token ||= visitor_cookie if Ahoy.cookies && !(api? && Ahoy.protect_from_forgery)
237
+ token ||= visitor_cookie if Ahoy.cookies? && !(api? && Ahoy.protect_from_forgery)
238
238
  token ||= visitor_param if api?
239
239
  token
240
240
  end
241
241
  end
242
242
 
243
- def visit_anonymity_set
244
- @visit_anonymity_set ||= Digest::UUID.uuid_v5(UUID_NAMESPACE, ["visit", Ahoy.mask_ip(request.remote_ip), request.user_agent].join("/"))
245
- end
246
-
247
243
  def visitor_anonymity_set
248
244
  @visitor_anonymity_set ||= Digest::UUID.uuid_v5(UUID_NAMESPACE, ["visitor", Ahoy.mask_ip(request.remote_ip), request.user_agent].join("/"))
249
245
  end
data/lib/ahoy/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Ahoy
2
- VERSION = "4.1.0"
2
+ VERSION = "5.0.2"
3
3
  end
data/lib/ahoy.rb CHANGED
@@ -7,27 +7,59 @@ require "active_support/core_ext"
7
7
  require "safely/core"
8
8
 
9
9
  # modules
10
- require "ahoy/utils"
11
- require "ahoy/base_store"
12
- require "ahoy/controller"
13
- require "ahoy/database_store"
14
- require "ahoy/helper"
15
- require "ahoy/model"
16
- require "ahoy/query_methods"
17
- require "ahoy/tracker"
18
- require "ahoy/version"
19
- require "ahoy/visit_properties"
20
-
21
- require "ahoy/engine" if defined?(Rails)
10
+ require_relative "ahoy/utils"
11
+ require_relative "ahoy/base_store"
12
+ require_relative "ahoy/controller"
13
+ require_relative "ahoy/database_store"
14
+ require_relative "ahoy/helper"
15
+ require_relative "ahoy/model"
16
+ require_relative "ahoy/query_methods"
17
+ require_relative "ahoy/tracker"
18
+ require_relative "ahoy/version"
19
+ require_relative "ahoy/visit_properties"
20
+
21
+ require_relative "ahoy/engine" if defined?(Rails)
22
22
 
23
23
  module Ahoy
24
+ # activejob optional
25
+ autoload :GeocodeV2Job, "ahoy/geocode_v2_job"
26
+
24
27
  mattr_accessor :visit_duration
25
28
  self.visit_duration = 4.hours
26
29
 
27
30
  mattr_accessor :visitor_duration
28
31
  self.visitor_duration = 2.years
29
32
 
30
- mattr_accessor :cookies
33
+ def self.cookies=(value)
34
+ if value == false
35
+ if defined?(Mongoid::Document) && defined?(Ahoy::Visit) && Ahoy::Visit < Mongoid::Document
36
+ raise <<~EOS
37
+ This feature requires a new index in Ahoy 5. Set:
38
+
39
+ class Ahoy::Visit
40
+ index({visitor_token: 1, started_at: 1})
41
+ end
42
+
43
+ Create the index before upgrading, and set:
44
+
45
+ Ahoy.cookies = :none
46
+ EOS
47
+ else
48
+ raise <<~EOS
49
+ This feature requires a new index in Ahoy 5. Create a migration with:
50
+
51
+ add_index :ahoy_visits, [:visitor_token, :started_at]
52
+
53
+ Run it before upgrading, and set:
54
+
55
+ Ahoy.cookies = :none
56
+ EOS
57
+ end
58
+ end
59
+ @@cookies = value
60
+ end
61
+
62
+ mattr_reader :cookies
31
63
  self.cookies = true
32
64
 
33
65
  # TODO deprecate in favor of cookie_options
@@ -94,6 +126,10 @@ module Ahoy
94
126
  logger.info { "[ahoy] #{message}" } if logger
95
127
  end
96
128
 
129
+ def self.cookies?
130
+ cookies && cookies != :none
131
+ end
132
+
97
133
  def self.mask_ip(ip)
98
134
  addr = IPAddr.new(ip)
99
135
  if addr.ipv4?
data/lib/ahoy_matey.rb CHANGED
@@ -1 +1 @@
1
- require "ahoy"
1
+ require_relative "ahoy"
@@ -33,6 +33,10 @@ module Ahoy
33
33
  properties_type == "text" || (properties_type == "json" && ActiveRecord::Base.connection.try(:mariadb?))
34
34
  end
35
35
 
36
+ def serialize_options
37
+ ActiveRecord::VERSION::STRING.to_f >= 7.1 ? "coder: JSON" : "JSON"
38
+ end
39
+
36
40
  # use connection_config instead of connection.adapter
37
41
  # so database connection isn't needed
38
42
  def adapter
@@ -43,13 +47,21 @@ module Ahoy
43
47
  end
44
48
  end
45
49
 
46
- def rails52?
47
- ActiveRecord::VERSION::STRING.to_f >= 5.2
48
- end
49
-
50
50
  def migration_version
51
51
  "[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
52
52
  end
53
+
54
+ def primary_key_type
55
+ ", id: :#{key_type}" if key_type
56
+ end
57
+
58
+ def foreign_key_type
59
+ ", type: :#{key_type}" if key_type
60
+ end
61
+
62
+ def key_type
63
+ Rails.configuration.generators.options.dig(:active_record, :primary_key_type)
64
+ end
53
65
  end
54
66
  end
55
67
  end
@@ -6,5 +6,5 @@ class Ahoy::Event < ApplicationRecord
6
6
  belongs_to :visit
7
7
  belongs_to :user, optional: true<% if serialize_properties? %>
8
8
 
9
- serialize :properties, JSON<% end %>
9
+ serialize :properties, <%= serialize_options %><% end %>
10
10
  end
@@ -1,6 +1,6 @@
1
1
  class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version %>
2
2
  def change
3
- create_table :ahoy_visits do |t|
3
+ create_table :ahoy_visits<%= primary_key_type %> do |t|
4
4
  t.string :visit_token
5
5
  t.string :visitor_token
6
6
 
@@ -8,7 +8,7 @@ class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version
8
8
  # simply remove any you don't want
9
9
 
10
10
  # user
11
- t.references :user
11
+ t.references :user<%= foreign_key_type %>
12
12
 
13
13
  # standard
14
14
  t.string :ip
@@ -45,18 +45,18 @@ class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version
45
45
  end
46
46
 
47
47
  add_index :ahoy_visits, :visit_token, unique: true
48
+ add_index :ahoy_visits, [:visitor_token, :started_at]
48
49
 
49
- create_table :ahoy_events do |t|
50
- t.references :visit
51
- t.references :user
50
+ create_table :ahoy_events<%= primary_key_type %> do |t|
51
+ t.references :visit<%= foreign_key_type %>
52
+ t.references :user<%= foreign_key_type %>
52
53
 
53
54
  t.string :name
54
55
  t.<%= properties_type %> :properties
55
56
  t.datetime :time
56
57
  end
57
58
 
58
- add_index :ahoy_events, [:name, :time]<% if properties_type == "jsonb" %><% if rails52? %>
59
- add_index :ahoy_events, :properties, using: :gin, opclass: :jsonb_path_ops<% else %>
60
- add_index :ahoy_events, "properties jsonb_path_ops", using: "gin"<% end %><% end %>
59
+ add_index :ahoy_events, [:name, :time]<% if properties_type == "jsonb" %>
60
+ add_index :ahoy_events, :properties, using: :gin, opclass: :jsonb_path_ops<% end %>
61
61
  end
62
62
  end
@@ -46,4 +46,5 @@ class Ahoy::Visit
46
46
  field :started_at, type: Time
47
47
 
48
48
  index({visit_token: 1}, {unique: true})
49
+ index({visitor_token: 1, started_at: 1})
49
50
  end
@@ -1,16 +1,15 @@
1
1
  /*!
2
- * Ahoy.js
2
+ * Ahoy.js v0.4.2
3
3
  * Simple, powerful JavaScript analytics
4
4
  * https://github.com/ankane/ahoy.js
5
- * v0.4.0
6
5
  * MIT License
7
6
  */
8
7
 
9
8
  (function (global, factory) {
10
9
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
11
10
  typeof define === 'function' && define.amd ? define(factory) :
12
- (global = global || self, global.ahoy = factory());
13
- }(this, (function () { 'use strict';
11
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.ahoy = factory());
12
+ })(this, (function () { 'use strict';
14
13
 
15
14
  // https://www.quirksmode.org/js/cookies.html
16
15
 
@@ -67,7 +66,7 @@
67
66
 
68
67
  ahoy.configure = function (options) {
69
68
  for (var key in options) {
70
- if (options.hasOwnProperty(key)) {
69
+ if (Object.prototype.hasOwnProperty.call(options, key)) {
71
70
  config[key] = options[key];
72
71
  }
73
72
  }
@@ -102,7 +101,7 @@
102
101
  function serialize(object) {
103
102
  var data = new FormData();
104
103
  for (var key in object) {
105
- if (object.hasOwnProperty(key)) {
104
+ if (Object.prototype.hasOwnProperty.call(object, key)) {
106
105
  data.append(key, object[key]);
107
106
  }
108
107
  }
@@ -170,6 +169,9 @@
170
169
  document.addEventListener(eventName, function (e) {
171
170
  var matchedElement = matchesSelector(e.target, selector);
172
171
  if (matchedElement) {
172
+ var skip = getClosest(matchedElement, "data-ahoy-skip");
173
+ if (skip !== null && skip !== "false") { return; }
174
+
173
175
  callback.call(matchedElement, e);
174
176
  }
175
177
  });
@@ -186,8 +188,9 @@
186
188
 
187
189
  // https://stackoverflow.com/a/2117523/1177228
188
190
  function generateId() {
189
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
190
- var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
191
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
192
+ var r = Math.random() * 16 | 0;
193
+ var v = c === 'x' ? r : (r & 0x3 | 0x8);
191
194
  return v.toString(16);
192
195
  });
193
196
  }
@@ -237,11 +240,11 @@
237
240
  xhr.withCredentials = config.withCredentials;
238
241
  xhr.setRequestHeader("Content-Type", "application/json");
239
242
  for (var header in config.headers) {
240
- if (config.headers.hasOwnProperty(header)) {
243
+ if (Object.prototype.hasOwnProperty.call(config.headers, header)) {
241
244
  xhr.setRequestHeader(header, config.headers[header]);
242
245
  }
243
246
  }
244
- xhr.onload = function() {
247
+ xhr.onload = function () {
245
248
  if (xhr.status === 200) {
246
249
  success();
247
250
  }
@@ -266,11 +269,11 @@
266
269
  }
267
270
 
268
271
  function trackEvent(event) {
269
- ahoy.ready( function () {
270
- sendRequest(eventsUrl(), eventData(event), function() {
272
+ ahoy.ready(function () {
273
+ sendRequest(eventsUrl(), eventData(event), function () {
271
274
  // remove from queue
272
275
  for (var i = 0; i < eventQueue.length; i++) {
273
- if (eventQueue[i].id == event.id) {
276
+ if (eventQueue[i].id === event.id) {
274
277
  eventQueue.splice(i, 1);
275
278
  break;
276
279
  }
@@ -281,7 +284,7 @@
281
284
  }
282
285
 
283
286
  function trackEventNow(event) {
284
- ahoy.ready( function () {
287
+ ahoy.ready(function () {
285
288
  var data = eventData(event);
286
289
  var param = csrfParam();
287
290
  var token = csrfToken();
@@ -303,7 +306,7 @@
303
306
 
304
307
  function cleanObject(obj) {
305
308
  for (var key in obj) {
306
- if (obj.hasOwnProperty(key)) {
309
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
307
310
  if (obj[key] === null) {
308
311
  delete obj[key];
309
312
  }
@@ -318,14 +321,14 @@
318
321
  id: presence(this.id),
319
322
  "class": presence(this.className),
320
323
  page: page(),
321
- section: getClosestSection(this)
324
+ section: getClosest(this, "data-section")
322
325
  });
323
326
  }
324
327
 
325
- function getClosestSection(element) {
326
- for ( ; element && element !== document; element = element.parentNode) {
327
- if (element.hasAttribute('data-section')) {
328
- return element.getAttribute('data-section');
328
+ function getClosest(element, attribute) {
329
+ for (; element && element !== document; element = element.parentNode) {
330
+ if (element.hasAttribute(attribute)) {
331
+ return element.getAttribute(attribute);
329
332
  }
330
333
  }
331
334
 
@@ -377,7 +380,7 @@
377
380
  }
378
381
 
379
382
  for (var key in config.visitParams) {
380
- if (config.visitParams.hasOwnProperty(key)) {
383
+ if (Object.prototype.hasOwnProperty.call(config.visitParams, key)) {
381
384
  data[key] = config.visitParams[key];
382
385
  }
383
386
  }
@@ -431,12 +434,12 @@
431
434
  js: true
432
435
  };
433
436
 
434
- ahoy.ready( function () {
437
+ ahoy.ready(function () {
435
438
  if (config.cookies && !ahoy.getVisitId()) {
436
439
  createVisit();
437
440
  }
438
441
 
439
- ahoy.ready( function () {
442
+ ahoy.ready(function () {
440
443
  log(event);
441
444
 
442
445
  event.visit_token = ahoy.getVisitId();
@@ -449,7 +452,7 @@
449
452
  saveEventQueue();
450
453
 
451
454
  // wait in case navigating to reduce duplicate events
452
- setTimeout( function () {
455
+ setTimeout(function () {
453
456
  trackEvent(event);
454
457
  }, 1000);
455
458
  }
@@ -467,8 +470,8 @@
467
470
  };
468
471
 
469
472
  if (additionalProperties) {
470
- for(var propName in additionalProperties) {
471
- if (additionalProperties.hasOwnProperty(propName)) {
473
+ for (var propName in additionalProperties) {
474
+ if (Object.prototype.hasOwnProperty.call(additionalProperties, propName)) {
472
475
  properties[propName] = additionalProperties[propName];
473
476
  }
474
477
  }
@@ -482,7 +485,7 @@
482
485
  }
483
486
  onEvent("click", selector, function (e) {
484
487
  var properties = eventProperties.call(this, e);
485
- properties.text = properties.tag == "input" ? this.value : (this.textContent || this.innerText || this.innerHTML).replace(/[\s\r\n]+/g, " ").trim();
488
+ properties.text = properties.tag === "input" ? this.value : (this.textContent || this.innerText || this.innerHTML).replace(/[\s\r\n]+/g, " ").trim();
486
489
  properties.href = this.href;
487
490
  ahoy.track("$click", properties);
488
491
  });
@@ -526,7 +529,7 @@
526
529
  ahoy.start = function () {};
527
530
  };
528
531
 
529
- documentReady(function() {
532
+ documentReady(function () {
530
533
  if (config.startOnReady) {
531
534
  ahoy.start();
532
535
  }
@@ -534,4 +537,4 @@
534
537
 
535
538
  return ahoy;
536
539
 
537
- })));
540
+ }));
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: 4.1.0
4
+ version: 5.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-06-12 00:00:00.000000000 Z
11
+ date: 2023-10-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -16,42 +16,42 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '5.2'
19
+ version: '6.1'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '5.2'
26
+ version: '6.1'
27
27
  - !ruby/object:Gem::Dependency
28
- name: safely_block
28
+ name: device_detector
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 0.2.1
33
+ version: '1'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: 0.2.1
40
+ version: '1'
41
41
  - !ruby/object:Gem::Dependency
42
- name: device_detector
42
+ name: safely_block
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: '0.4'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '0'
54
+ version: '0.4'
55
55
  description:
56
56
  email: andrew@ankane.org
57
57
  executables: []
@@ -65,14 +65,13 @@ files:
65
65
  - app/controllers/ahoy/base_controller.rb
66
66
  - app/controllers/ahoy/events_controller.rb
67
67
  - app/controllers/ahoy/visits_controller.rb
68
- - app/jobs/ahoy/geocode_job.rb
69
- - app/jobs/ahoy/geocode_v2_job.rb
70
68
  - config/routes.rb
71
69
  - lib/ahoy.rb
72
70
  - lib/ahoy/base_store.rb
73
71
  - lib/ahoy/controller.rb
74
72
  - lib/ahoy/database_store.rb
75
73
  - lib/ahoy/engine.rb
74
+ - lib/ahoy/geocode_v2_job.rb
76
75
  - lib/ahoy/helper.rb
77
76
  - lib/ahoy/model.rb
78
77
  - lib/ahoy/query_methods.rb
@@ -106,14 +105,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
106
105
  requirements:
107
106
  - - ">="
108
107
  - !ruby/object:Gem::Version
109
- version: '2.6'
108
+ version: '3'
110
109
  required_rubygems_version: !ruby/object:Gem::Requirement
111
110
  requirements:
112
111
  - - ">="
113
112
  - !ruby/object:Gem::Version
114
113
  version: '0'
115
114
  requirements: []
116
- rubygems_version: 3.3.7
115
+ rubygems_version: 3.4.10
117
116
  signing_key:
118
117
  specification_version: 4
119
118
  summary: Simple, powerful, first-party analytics for Rails
@@ -1,11 +0,0 @@
1
- # for smooth update from Ahoy 1 -> 2
2
- # TODO remove in 5.0
3
- module Ahoy
4
- class GeocodeJob < ActiveJob::Base
5
- queue_as { Ahoy.job_queue }
6
-
7
- def perform(visit)
8
- Ahoy::GeocodeV2Job.perform_now(visit.visit_token, visit.ip)
9
- end
10
- end
11
- end
File without changes