ahoy_matey 4.0.0 → 5.4.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1fb8aecda5242614f518d06dbdc9449305f0ed30677d667e01eb1edefc627bc7
4
- data.tar.gz: 8f52fbe0bab0a0ac1c5722b06f342add3d31c0c441230b77f03ae566e07f746b
3
+ metadata.gz: df3e089d0f7806605ca6cb9215118d0a7791fdd9127f91dd13d0df59add4cb51
4
+ data.tar.gz: 69e7c817399a7e2488f54cbd4d4e136d383542de0194c5bc638bc191bdeae6d7
5
5
  SHA512:
6
- metadata.gz: '059927d7fe2344882860ae81ff23e142a09ed955da818da8d3e679b03173b39e64ffc611e4e682a912901678cd9b790b461805f788c423cd2b4331c2a340c226'
7
- data.tar.gz: f2d53c35500ee4d30485cfbf6c5d444cbf03c50cec4762783a972411343e19b8baf53b61286cc4aa78bbbe98a69df21da701989422263780447341fdce756db0
6
+ metadata.gz: cb08e2f80841ef62c2ad48881912c9cb2e81b40b03b7c4dead2ce006a49726571c3b9dee235cc9450bdbc7e72def56dbf201c2bf1d72d395aec849aca6cf323a
7
+ data.tar.gz: b92e534e57a6d3874e9b623a2bb1d55dc5d7fbfbd4ad16e94cb6d1350112b7623c56e569320baf2718a436db6991d1a1d943ace871d947d502f81b21b58b2fc7
data/CHANGELOG.md CHANGED
@@ -1,6 +1,78 @@
1
+ ## 5.4.1 (2025-09-30)
2
+
3
+ - Fixed deprecation warning with Rack 3.1+
4
+
5
+ ## 5.4.0 (2025-05-04)
6
+
7
+ - Dropped support for Ruby < 3.2 and Rails < 7.1
8
+
9
+ ## 5.3.0 (2025-02-01)
10
+
11
+ - Dropped support for Ruby < 3.1 and Rails < 7
12
+ - Dropped support for Mongoid < 8
13
+
14
+ ## 5.2.1 (2024-10-07)
15
+
16
+ - Fixed connection leasing for Active Record 7.2+
17
+
18
+ ## 5.2.0 (2024-09-04)
19
+
20
+ - Improved error handling for invalid API parameters
21
+
22
+ ## 5.1.0 (2024-03-26)
23
+
24
+ - Added support for Trilogy
25
+ - Updated Ahoy.js to 0.4.4
26
+
27
+ ## 5.0.2 (2023-10-05)
28
+
29
+ - Excluded visits from Rails health check
30
+
31
+ ## 5.0.1 (2023-10-01)
32
+
33
+ - Fixed error with geocoding with anonymity sets
34
+
35
+ ## 5.0.0 (2023-10-01)
36
+
37
+ - Changed visits to expire with anonymity sets
38
+ - Fixed error when Active Job is not available
39
+ - Fixed deprecation warning with Rails 7.1
40
+ - Dropped support for Ruby < 3 and Rails < 6.1
41
+ - Dropped support for Mongoid 6
42
+
43
+ ## 4.2.1 (2023-02-23)
44
+
45
+ - Updated Ahoy.js to 0.4.2
46
+
47
+ ## 4.2.0 (2023-02-07)
48
+
49
+ - Added primary key type to generated migration
50
+ - Updated Ahoy.js to 0.4.1
51
+
52
+ ## 4.1.0 (2022-06-12)
53
+
54
+ - Ensure `exclude_method` is only called once per request
55
+ - Fixed error with Mongoid when `Mongoid.raise_not_found_error` is `true`
56
+ - Fixed association for Mongoid
57
+
58
+ ## 4.0.3 (2022-01-15)
59
+
60
+ - Support for `importmap-rails` is no longer experimental
61
+ - Fixed asset precompilation error with `importmap-rails`
62
+
63
+ ## 4.0.2 (2021-11-06)
64
+
65
+ - Added experimental support for `importmap-rails`
66
+
67
+ ## 4.0.1 (2021-08-18)
68
+
69
+ - Added support for `where_event`, `where_props`, and `where_group` for SQLite
70
+ - Fixed results with `where_event` for MySQL, MariaDB, and Postgres `hstore`
71
+ - Fixed results with `where_props` and `where_group` when used with other scopes for MySQL, MariaDB, and Postgres `hstore`
72
+
1
73
  ## 4.0.0 (2021-08-14)
2
74
 
3
- - Disabled geocoding by default
75
+ - Disabled geocoding by default (this was already the case for new installations with 3.2.0+)
4
76
  - Made the `geocoder` gem an optional dependency
5
77
  - Updated Ahoy.js to 0.4.0
6
78
  - Updated API to return 400 status code when missing required parameters
@@ -303,3 +375,19 @@ Breaking changes
303
375
  ## 0.1.0 (2014-03-19)
304
376
 
305
377
  - First major release
378
+
379
+ ## 0.0.4 (2014-03-01)
380
+
381
+ - Added UTM parameters
382
+
383
+ ## 0.0.3 (2014-02-26)
384
+
385
+ - Renamed `ahoy_visit` method to `current_visit`
386
+
387
+ ## 0.0.2 (2014-02-26)
388
+
389
+ - Added `ahoy_visit` method to controllers
390
+
391
+ ## 0.0.1 (2014-02-26)
392
+
393
+ - First release
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2014-2021 Andrew Kane
1
+ Copyright (c) 2014-2025 Andrew Kane
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -4,20 +4,18 @@
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)
8
-
9
7
  :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
8
 
11
9
  :tangerine: Battle-tested at [Instacart](https://www.instacart.com/opensource)
12
10
 
13
- [![Build Status](https://github.com/ankane/ahoy/workflows/build/badge.svg?branch=master)](https://github.com/ankane/ahoy/actions)
11
+ [![Build Status](https://github.com/ankane/ahoy/actions/workflows/build.yml/badge.svg)](https://github.com/ankane/ahoy/actions)
14
12
 
15
13
  ## Installation
16
14
 
17
15
  Add this line to your application’s Gemfile:
18
16
 
19
17
  ```ruby
20
- gem 'ahoy_matey'
18
+ gem "ahoy_matey"
21
19
  ```
22
20
 
23
21
  And run:
@@ -48,19 +46,33 @@ And restart your web server.
48
46
 
49
47
  ### JavaScript
50
48
 
51
- For Rails 6 / Webpacker, run:
49
+ For Importmap (Rails 7+ default), add to `config/importmap.rb`:
50
+
51
+ ```ruby
52
+ pin "ahoy", to: "ahoy.js"
53
+ ```
54
+
55
+ And add to `app/javascript/application.js`:
56
+
57
+ ```javascript
58
+ import "ahoy"
59
+ ```
60
+
61
+ For Bun, esbuild, rollup.js, or Webpack, run:
52
62
 
53
63
  ```sh
64
+ bun add ahoy.js
65
+ # or
54
66
  yarn add ahoy.js
55
67
  ```
56
68
 
57
- And add to `app/javascript/packs/application.js`:
69
+ And add to `app/javascript/application.js`:
58
70
 
59
71
  ```javascript
60
- import ahoy from "ahoy.js";
72
+ import ahoy from "ahoy.js"
61
73
  ```
62
74
 
63
- For Rails 5 / Sprockets, add to `app/assets/javascripts/application.js`:
75
+ For Sprockets, add to `app/assets/javascripts/application.js`:
64
76
 
65
77
  ```javascript
66
78
  //= require ahoy
@@ -185,9 +197,9 @@ Order.joins(:ahoy_visit).group("device_type").count
185
197
  Here’s what the migration to add the `ahoy_visit_id` column should look like:
186
198
 
187
199
  ```ruby
188
- class AddVisitIdToOrders < ActiveRecord::Migration[6.1]
200
+ class AddAhoyVisitToOrders < ActiveRecord::Migration[8.0]
189
201
  def change
190
- add_column :orders, :ahoy_visit_id, :bigint
202
+ add_reference :orders, :ahoy_visit
191
203
  end
192
204
  end
193
205
  ```
@@ -200,7 +212,7 @@ visitable :sign_up_visit
200
212
 
201
213
  ### Users
202
214
 
203
- 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.
204
216
 
205
217
  With other authentication frameworks, add this to the end of your sign in method:
206
218
 
@@ -250,28 +262,6 @@ class ApplicationController < ActionController::Base
250
262
  end
251
263
  ```
252
264
 
253
- #### Knock
254
-
255
- To attach the user with [Knock](https://github.com/nsarno/knock), either include `Knock::Authenticable`in `ApplicationController`:
256
-
257
- ```ruby
258
- class ApplicationController < ActionController::API
259
- include Knock::Authenticable
260
- end
261
- ```
262
-
263
- Or include it in Ahoy:
264
-
265
- ```ruby
266
- Ahoy::BaseController.include Knock::Authenticable
267
- ```
268
-
269
- And use:
270
-
271
- ```ruby
272
- Ahoy.user_method = ->(controller) { controller.send(:authenticate_entity, "user") }
273
- ```
274
-
275
265
  ### Exclusions
276
266
 
277
267
  Bots are excluded from tracking by default. To include them, use:
@@ -296,6 +286,14 @@ By default, a new visit is created after 4 hours of inactivity. Change this with
296
286
  Ahoy.visit_duration = 30.minutes
297
287
  ```
298
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
+
299
297
  ### Cookies
300
298
 
301
299
  To track visits across multiple subdomains, use:
@@ -314,15 +312,15 @@ You can also [disable cookies](#anonymity-sets--cookies)
314
312
 
315
313
  ### Token Generation
316
314
 
317
- 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).
318
316
 
319
317
  ```ruby
320
- Ahoy.token_generator = -> { Druuid.gen }
318
+ Ahoy.token_generator = -> { ULID.generate }
321
319
  ```
322
320
 
323
321
  ### Throttling
324
322
 
325
- 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.
326
324
 
327
325
  ```ruby
328
326
  class Rack::Attack
@@ -349,7 +347,7 @@ Ahoy uses [Geocoder](https://github.com/alexreisner/geocoder) for geocoding. We
349
347
  To enable geocoding, add this line to your application’s Gemfile:
350
348
 
351
349
  ```ruby
352
- gem 'geocoder'
350
+ gem "geocoder"
353
351
  ```
354
352
 
355
353
  And update `config/initializers/ahoy.rb`:
@@ -366,13 +364,17 @@ Ahoy.job_queue = :low_priority
366
364
 
367
365
  ### Local Geocoding
368
366
 
369
- 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:
370
372
 
371
373
  ```ruby
372
- gem 'maxminddb'
374
+ gem "maxminddb"
373
375
  ```
374
376
 
375
- 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:
376
378
 
377
379
  ```ruby
378
380
  Geocoder.configure(
@@ -389,6 +391,12 @@ For country-level geocoding, install the `geoip-database` package. It’s preins
389
391
  sudo apt-get install geoip-database
390
392
  ```
391
393
 
394
+ Add this line to your application’s Gemfile:
395
+
396
+ ```ruby
397
+ gem "geoip"
398
+ ```
399
+
392
400
  And create `config/initializers/geocoder.rb` with:
393
401
 
394
402
  ```ruby
@@ -438,7 +446,7 @@ class Ahoy::Store < Ahoy::DatabaseStore
438
446
  end
439
447
 
440
448
  Ahoy.mask_ips = true
441
- Ahoy.cookies = false
449
+ Ahoy.cookies = :none
442
450
  ```
443
451
 
444
452
  This:
@@ -476,12 +484,14 @@ end
476
484
 
477
485
  ### Anonymity Sets & Cookies
478
486
 
479
- 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.
480
488
 
481
489
  ```ruby
482
- Ahoy.cookies = false
490
+ Ahoy.cookies = :none
483
491
  ```
484
492
 
493
+ Note: If Ahoy was installed before v5, [add an index](https://github.com/ankane/ahoy/tree/v5.0.0?tab=readme-ov-file#50) before making this change.
494
+
485
495
  Previously set cookies are automatically deleted. If you use JavaScript tracking, also set:
486
496
 
487
497
  ```javascript
@@ -625,7 +635,7 @@ end
625
635
 
626
636
  [Blazer](https://github.com/ankane/blazer) is a great tool for exploring your data.
627
637
 
628
- With ActiveRecord, you can do:
638
+ With Active Record, you can do:
629
639
 
630
640
  ```ruby
631
641
  Ahoy::Visit.group(:search_keyword).count
@@ -661,7 +671,7 @@ Group by properties with:
661
671
  Ahoy::Event.group_prop(:product_id, :category).count
662
672
  ```
663
673
 
664
- Note: MySQL and MariaDB always return string keys (include `"null"` for `nil`) for `group_prop`.
674
+ Note: MySQL and MariaDB always return string keys (including `"null"` for `nil`) for `group_prop`.
665
675
 
666
676
  ### Funnels
667
677
 
@@ -694,6 +704,24 @@ daily_visits = Ahoy::Visit.group_by_day(:started_at).count # uses Groupdate
694
704
  Prophet.forecast(daily_visits)
695
705
  ```
696
706
 
707
+ ### Anomaly Detection
708
+
709
+ To detect anomalies in visits and events, check out [AnomalyDetection.rb](https://github.com/ankane/AnomalyDetection.rb).
710
+
711
+ ```ruby
712
+ daily_visits = Ahoy::Visit.group_by_day(:started_at).count # uses Groupdate
713
+ AnomalyDetection.detect(daily_visits, period: 7)
714
+ ```
715
+
716
+ ### Breakout Detection
717
+
718
+ To detect breakouts in visits and events, check out [Breakout](https://github.com/ankane/breakout).
719
+
720
+ ```ruby
721
+ daily_visits = Ahoy::Visit.group_by_day(:started_at).count # uses Groupdate
722
+ Breakout.detect(daily_visits)
723
+ ```
724
+
697
725
  ### Recommendations
698
726
 
699
727
  To make recommendations based on events, check out [Disco](https://github.com/ankane/disco#ahoy).
@@ -737,28 +765,12 @@ Send a `POST` request to `/ahoy/events` with `Content-Type: application/json` an
737
765
  "properties": {
738
766
  "item_id": 123
739
767
  },
740
- "time": "2018-01-01T00:00:00-07:00"
768
+ "time": "2025-01-01T00:00:00-07:00"
741
769
  }
742
770
  ]
743
771
  }
744
772
  ```
745
773
 
746
- ## Upgrading
747
-
748
- ### 4.0
749
-
750
- There are two notable changes to geocoding:
751
-
752
- 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).
753
-
754
- 2. The `geocoder` gem is now an optional dependency. To use geocoding, add it to your Gemfile:
755
-
756
- ```ruby
757
- gem 'geocoder'
758
- ```
759
-
760
- Also, check out the [upgrade notes](https://github.com/ankane/ahoy.js#upgrading) for Ahoy.js.
761
-
762
774
  ## History
763
775
 
764
776
  View the [changelog](https://github.com/ankane/ahoy/blob/master/CHANGELOG.md)
@@ -781,10 +793,10 @@ bundle install
781
793
  bundle exec rake test
782
794
  ```
783
795
 
784
- To test query methods, start PostgreSQL, MySQL, and MongoDB and use:
796
+ To test different adapters, use:
785
797
 
786
798
  ```sh
787
- createdb ahoy_test
788
- mysqladmin create ahoy_test
789
- bundle exec rake test:query_methods
799
+ ADAPTER=postgresql bundle exec rake test
800
+ ADAPTER=mysql2 bundle exec rake test
801
+ ADAPTER=mongoid bundle exec rake test
790
802
  ```
@@ -36,7 +36,8 @@ module Ahoy
36
36
  def verify_request_size
37
37
  if request.content_length > Ahoy.max_content_length
38
38
  logger.info "[ahoy] Payload too large"
39
- render plain: "Payload too large\n", status: :payload_too_large
39
+ status = Rack::RELEASE.to_f >= 3.1 ? :content_too_large : :payload_too_large
40
+ render plain: "Payload too large\n", status: status
40
41
  end
41
42
  end
42
43
  end
@@ -17,12 +17,23 @@ module Ahoy
17
17
  begin
18
18
  ActiveSupport::JSON.decode(data)
19
19
  rescue ActiveSupport::JSON.parse_error
20
- # do nothing
20
+ # TODO change to nil in Ahoy 6
21
21
  []
22
22
  end
23
23
  end
24
24
 
25
- events.first(Ahoy.max_events_per_request).each do |event|
25
+ max_events_per_request = Ahoy.max_events_per_request
26
+
27
+ # check before creating any events
28
+ unless events.is_a?(Array) && events.first(max_events_per_request).all? { |v| v.is_a?(Hash) }
29
+ logger.info "[ahoy] Invalid parameters"
30
+ # :unprocessable_entity is probably more correct
31
+ # but keep consistent with missing parameters for now
32
+ render plain: "Invalid parameters\n", status: :bad_request
33
+ return
34
+ end
35
+
36
+ events.first(max_events_per_request).each do |event|
26
37
  time = Time.zone.parse(event["time"]) rescue nil
27
38
 
28
39
  # timestamp is deprecated
@@ -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,7 +53,14 @@ module Ahoy
53
53
 
54
54
  def visit
55
55
  unless defined?(@visit)
56
- @visit = visit_model.find_by(visit_token: ahoy.visit_token) 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
61
+ else
62
+ @visit = nil
63
+ end
57
64
  end
58
65
  @visit
59
66
  end
data/lib/ahoy/engine.rb CHANGED
@@ -26,5 +26,12 @@ module Ahoy
26
26
  alias_method :call, :call_with_quiet_ahoy
27
27
  end
28
28
  end
29
+
30
+ # for importmap
31
+ initializer "ahoy.importmap" do |app|
32
+ if app.config.respond_to?(:assets) && defined?(Importmap) && defined?(Sprockets)
33
+ app.config.assets.precompile << "ahoy.js"
34
+ end
35
+ end
29
36
  end
30
37
  end
@@ -8,47 +8,40 @@ module Ahoy
8
8
  end
9
9
 
10
10
  def where_props(properties)
11
- relation = self
12
- if respond_to?(:columns_hash)
13
- column_type = columns_hash["properties"].type
14
- adapter_name = connection.adapter_name.downcase
15
- else
16
- adapter_name = "mongoid"
17
- end
11
+ return all if properties.empty?
12
+
13
+ adapter_name = respond_to?(:connection_db_config) ? connection_db_config.adapter.to_s : "mongoid"
18
14
  case adapter_name
19
15
  when "mongoid"
20
- relation = where(Hash[properties.map { |k, v| ["properties.#{k}", v] }])
21
- when /mysql/
22
- column = column_type == :json || connection.try(:mariadb?) ? "properties" : "CAST(properties AS JSON)"
23
- properties.each do |k, v|
24
- if v.nil?
25
- v = "null"
26
- elsif v == true
27
- v = "true"
28
- end
29
-
30
- relation = relation.where("JSON_UNQUOTE(JSON_EXTRACT(#{column}, ?)) = ?", "$.#{k}", v.as_json)
31
- end
32
- when /postgres|postgis/
33
- case column_type
34
- when :jsonb
35
- relation = relation.where("properties @> ?", properties.to_json)
16
+ where(properties.to_h { |k, v| ["properties.#{k}", v] })
17
+ when /mysql|trilogy/i
18
+ where("JSON_CONTAINS(properties, ?, '$') = 1", properties.to_json)
19
+ when /postg/i
20
+ case columns_hash["properties"].type
36
21
  when :hstore
37
- properties.each do |k, v|
38
- relation =
39
- if v.nil?
40
- relation.where("properties -> ? IS NULL", k.to_s)
41
- else
42
- relation.where("properties -> ? = ?", k.to_s, v.to_s)
43
- end
22
+ properties.inject(all) do |relation, (k, v)|
23
+ if v.nil?
24
+ relation.where("properties -> ? IS NULL", k.to_s)
25
+ else
26
+ relation.where("properties -> ? = ?", k.to_s, v.to_s)
27
+ end
44
28
  end
29
+ when :jsonb
30
+ where("properties @> ?", properties.to_json)
45
31
  else
46
- relation = relation.where("properties::jsonb @> ?", properties.to_json)
32
+ where("properties::jsonb @> ?", properties.to_json)
33
+ end
34
+ when /sqlite/i
35
+ properties.inject(all) do |relation, (k, v)|
36
+ if v.nil?
37
+ relation.where("JSON_EXTRACT(properties, ?) IS NULL", "$.#{k}")
38
+ else
39
+ relation.where("JSON_EXTRACT(properties, ?) = ?", "$.#{k}", v.as_json)
40
+ end
47
41
  end
48
42
  else
49
43
  raise "Adapter not supported: #{adapter_name}"
50
44
  end
51
- relation
52
45
  end
53
46
  alias_method :where_properties, :where_props
54
47
 
@@ -56,32 +49,32 @@ module Ahoy
56
49
  # like with group
57
50
  props.flatten!
58
51
 
59
- relation = self
60
- if respond_to?(:columns_hash)
61
- column_type = columns_hash["properties"].type
62
- adapter_name = connection.adapter_name.downcase
63
- else
64
- adapter_name = "mongoid"
65
- end
52
+ relation = all
53
+ adapter_name = respond_to?(:connection_db_config) ? connection_db_config.adapter.to_s : "mongoid"
66
54
  case adapter_name
67
55
  when "mongoid"
68
56
  raise "Adapter not supported: #{adapter_name}"
69
- when /mysql/
70
- column = column_type == :json || connection.try(:mariadb?) ? "properties" : "CAST(properties AS JSON)"
57
+ when /mysql|trilogy/i
71
58
  props.each do |prop|
72
- quoted_prop = connection.quote("$.#{prop}")
73
- relation = relation.group("JSON_UNQUOTE(JSON_EXTRACT(#{column}, #{quoted_prop}))")
59
+ quoted_prop = connection_pool.with_connection { |c| c.quote("$.#{prop}") }
60
+ relation = relation.group("JSON_UNQUOTE(JSON_EXTRACT(properties, #{quoted_prop}))")
74
61
  end
75
- when /postgres|postgis/
62
+ when /postg/i
76
63
  # convert to jsonb to fix
77
64
  # could not identify an equality operator for type json
78
65
  # and for text columns
66
+ column_type = columns_hash["properties"].type
79
67
  cast = [:jsonb, :hstore].include?(column_type) ? "" : "::jsonb"
80
68
 
81
69
  props.each do |prop|
82
- quoted_prop = connection.quote(prop)
70
+ quoted_prop = connection_pool.with_connection { |c| c.quote(prop) }
83
71
  relation = relation.group("properties#{cast} -> #{quoted_prop}")
84
72
  end
73
+ when /sqlite/i
74
+ props.each do |prop|
75
+ quoted_prop = connection_pool.with_connection { |c| c.quote("$.#{prop}") }
76
+ relation = relation.group("JSON_EXTRACT(properties, #{quoted_prop})")
77
+ end
85
78
  else
86
79
  raise "Adapter not supported: #{adapter_name}"
87
80
  end
data/lib/ahoy/tracker.rb CHANGED
@@ -49,7 +49,7 @@ module Ahoy
49
49
  visit_token: visit_token,
50
50
  visitor_token: visitor_token,
51
51
  user_id: user.try(:id),
52
- started_at: trusted_time(started_at),
52
+ started_at: trusted_time(started_at)
53
53
  }.merge(visit_properties).select { |_, v| v }
54
54
 
55
55
  @store.track_visit(data)
@@ -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,10 +191,6 @@ module Ahoy
184
191
  end
185
192
  end
186
193
 
187
- def exclude?
188
- @store.exclude?
189
- end
190
-
191
194
  def report_exception(e)
192
195
  if defined?(ActionDispatch::RemoteIp::IpSpoofAttackError) && e.is_a?(ActionDispatch::RemoteIp::IpSpoofAttackError)
193
196
  debug "Tracking excluded due to IP spoofing"
@@ -204,7 +207,7 @@ module Ahoy
204
207
  def visit_token_helper
205
208
  @visit_token_helper ||= begin
206
209
  token = existing_visit_token
207
- token ||= visit_anonymity_set unless Ahoy.cookies
210
+ token ||= visit&.visit_token unless Ahoy.cookies?
208
211
  token ||= generate_id unless Ahoy.api_only
209
212
  token
210
213
  end
@@ -213,7 +216,7 @@ module Ahoy
213
216
  def visitor_token_helper
214
217
  @visitor_token_helper ||= begin
215
218
  token = existing_visitor_token
216
- token ||= visitor_anonymity_set unless Ahoy.cookies
219
+ token ||= visitor_anonymity_set unless Ahoy.cookies?
217
220
  token ||= generate_id unless Ahoy.api_only
218
221
  token
219
222
  end
@@ -222,7 +225,7 @@ module Ahoy
222
225
  def existing_visit_token
223
226
  @existing_visit_token ||= begin
224
227
  token = visit_header
225
- token ||= visit_cookie if Ahoy.cookies && !(api? && Ahoy.protect_from_forgery)
228
+ token ||= visit_cookie if Ahoy.cookies? && !(api? && Ahoy.protect_from_forgery)
226
229
  token ||= visit_param if api?
227
230
  token
228
231
  end
@@ -231,16 +234,12 @@ module Ahoy
231
234
  def existing_visitor_token
232
235
  @existing_visitor_token ||= begin
233
236
  token = visitor_header
234
- token ||= visitor_cookie if Ahoy.cookies && !(api? && Ahoy.protect_from_forgery)
237
+ token ||= visitor_cookie if Ahoy.cookies? && !(api? && Ahoy.protect_from_forgery)
235
238
  token ||= visitor_param if api?
236
239
  token
237
240
  end
238
241
  end
239
242
 
240
- def visit_anonymity_set
241
- @visit_anonymity_set ||= Digest::UUID.uuid_v5(UUID_NAMESPACE, ["visit", Ahoy.mask_ip(request.remote_ip), request.user_agent].join("/"))
242
- end
243
-
244
243
  def visitor_anonymity_set
245
244
  @visitor_anonymity_set ||= Digest::UUID.uuid_v5(UUID_NAMESPACE, ["visitor", Ahoy.mask_ip(request.remote_ip), request.user_agent].join("/"))
246
245
  end
data/lib/ahoy/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Ahoy
2
- VERSION = "4.0.0"
2
+ VERSION = "5.4.1"
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?
@@ -126,7 +162,6 @@ ActiveSupport.on_load(:action_view) do
126
162
  include Ahoy::Helper
127
163
  end
128
164
 
129
- # Mongoid
130
165
  ActiveSupport.on_load(:mongoid) do
131
166
  Mongoid::Document::ClassMethods.include(Ahoy::Model)
132
167
  end
data/lib/ahoy_matey.rb CHANGED
@@ -1 +1 @@
1
- require "ahoy"
1
+ require_relative "ahoy"
@@ -1,3 +1,4 @@
1
+ require "rails/generators"
1
2
  require "rails/generators/active_record"
2
3
 
3
4
  module Ahoy
@@ -20,7 +21,7 @@ module Ahoy
20
21
  case adapter
21
22
  when /postg/i # postgres, postgis
22
23
  "jsonb"
23
- when /mysql/i
24
+ when /mysql|trilogy/i
24
25
  "json"
25
26
  else
26
27
  "text"
@@ -32,23 +33,27 @@ module Ahoy
32
33
  properties_type == "text" || (properties_type == "json" && ActiveRecord::Base.connection.try(:mariadb?))
33
34
  end
34
35
 
35
- # use connection_config instead of connection.adapter
36
+ # use connection_db_config instead of connection.adapter
36
37
  # so database connection isn't needed
37
38
  def adapter
38
- if ActiveRecord::VERSION::STRING.to_f >= 6.1
39
- ActiveRecord::Base.connection_db_config.adapter.to_s
40
- else
41
- ActiveRecord::Base.connection_config[:adapter].to_s
42
- end
43
- end
44
-
45
- def rails52?
46
- ActiveRecord::VERSION::STRING.to_f >= 5.2
39
+ ActiveRecord::Base.connection_db_config.adapter.to_s
47
40
  end
48
41
 
49
42
  def migration_version
50
43
  "[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
51
44
  end
45
+
46
+ def primary_key_type
47
+ ", id: :#{key_type}" if key_type
48
+ end
49
+
50
+ def foreign_key_type
51
+ ", type: :#{key_type}" if key_type
52
+ end
53
+
54
+ def key_type
55
+ Rails.configuration.generators.options.dig(:active_record, :primary_key_type)
56
+ end
52
57
  end
53
58
  end
54
59
  end
@@ -11,12 +11,12 @@ module Ahoy
11
11
 
12
12
  selection =
13
13
  if activerecord && mongoid
14
- puts <<-MSG
14
+ puts <<~MSG
15
15
 
16
- Which data store would you like to use?
17
- 1. ActiveRecord (default)
18
- 2. Mongoid
19
- 3. Neither
16
+ Which data store would you like to use?
17
+ 1. ActiveRecord (default)
18
+ 2. Mongoid
19
+ 3. Neither
20
20
  MSG
21
21
 
22
22
  ask(">")
@@ -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, coder: JSON<% 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
@@ -2,7 +2,7 @@ class Ahoy::Event
2
2
  include Mongoid::Document
3
3
 
4
4
  # associations
5
- belongs_to :visit, index: true
5
+ belongs_to :visit, class_name: "Ahoy::Visit", index: true
6
6
  belongs_to :user, index: true, optional: true
7
7
 
8
8
  # fields
@@ -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.4
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,13 @@
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
+ if (window.crypto && window.crypto.randomUUID) {
192
+ return window.crypto.randomUUID();
193
+ }
194
+
195
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
196
+ var r = Math.random() * 16 | 0;
197
+ var v = c === 'x' ? r : (r & 0x3 | 0x8);
191
198
  return v.toString(16);
192
199
  });
193
200
  }
@@ -237,11 +244,11 @@
237
244
  xhr.withCredentials = config.withCredentials;
238
245
  xhr.setRequestHeader("Content-Type", "application/json");
239
246
  for (var header in config.headers) {
240
- if (config.headers.hasOwnProperty(header)) {
247
+ if (Object.prototype.hasOwnProperty.call(config.headers, header)) {
241
248
  xhr.setRequestHeader(header, config.headers[header]);
242
249
  }
243
250
  }
244
- xhr.onload = function() {
251
+ xhr.onload = function () {
245
252
  if (xhr.status === 200) {
246
253
  success();
247
254
  }
@@ -266,11 +273,11 @@
266
273
  }
267
274
 
268
275
  function trackEvent(event) {
269
- ahoy.ready( function () {
270
- sendRequest(eventsUrl(), eventData(event), function() {
276
+ ahoy.ready(function () {
277
+ sendRequest(eventsUrl(), eventData(event), function () {
271
278
  // remove from queue
272
279
  for (var i = 0; i < eventQueue.length; i++) {
273
- if (eventQueue[i].id == event.id) {
280
+ if (eventQueue[i].id === event.id) {
274
281
  eventQueue.splice(i, 1);
275
282
  break;
276
283
  }
@@ -281,7 +288,7 @@
281
288
  }
282
289
 
283
290
  function trackEventNow(event) {
284
- ahoy.ready( function () {
291
+ ahoy.ready(function () {
285
292
  var data = eventData(event);
286
293
  var param = csrfParam();
287
294
  var token = csrfToken();
@@ -303,7 +310,7 @@
303
310
 
304
311
  function cleanObject(obj) {
305
312
  for (var key in obj) {
306
- if (obj.hasOwnProperty(key)) {
313
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
307
314
  if (obj[key] === null) {
308
315
  delete obj[key];
309
316
  }
@@ -318,14 +325,14 @@
318
325
  id: presence(this.id),
319
326
  "class": presence(this.className),
320
327
  page: page(),
321
- section: getClosestSection(this)
328
+ section: getClosest(this, "data-section")
322
329
  });
323
330
  }
324
331
 
325
- function getClosestSection(element) {
326
- for ( ; element && element !== document; element = element.parentNode) {
327
- if (element.hasAttribute('data-section')) {
328
- return element.getAttribute('data-section');
332
+ function getClosest(element, attribute) {
333
+ for (; element && element !== document; element = element.parentNode) {
334
+ if (element.hasAttribute(attribute)) {
335
+ return element.getAttribute(attribute);
329
336
  }
330
337
  }
331
338
 
@@ -377,7 +384,7 @@
377
384
  }
378
385
 
379
386
  for (var key in config.visitParams) {
380
- if (config.visitParams.hasOwnProperty(key)) {
387
+ if (Object.prototype.hasOwnProperty.call(config.visitParams, key)) {
381
388
  data[key] = config.visitParams[key];
382
389
  }
383
390
  }
@@ -431,12 +438,12 @@
431
438
  js: true
432
439
  };
433
440
 
434
- ahoy.ready( function () {
441
+ ahoy.ready(function () {
435
442
  if (config.cookies && !ahoy.getVisitId()) {
436
443
  createVisit();
437
444
  }
438
445
 
439
- ahoy.ready( function () {
446
+ ahoy.ready(function () {
440
447
  log(event);
441
448
 
442
449
  event.visit_token = ahoy.getVisitId();
@@ -449,7 +456,7 @@
449
456
  saveEventQueue();
450
457
 
451
458
  // wait in case navigating to reduce duplicate events
452
- setTimeout( function () {
459
+ setTimeout(function () {
453
460
  trackEvent(event);
454
461
  }, 1000);
455
462
  }
@@ -467,8 +474,8 @@
467
474
  };
468
475
 
469
476
  if (additionalProperties) {
470
- for(var propName in additionalProperties) {
471
- if (additionalProperties.hasOwnProperty(propName)) {
477
+ for (var propName in additionalProperties) {
478
+ if (Object.prototype.hasOwnProperty.call(additionalProperties, propName)) {
472
479
  properties[propName] = additionalProperties[propName];
473
480
  }
474
481
  }
@@ -482,7 +489,7 @@
482
489
  }
483
490
  onEvent("click", selector, function (e) {
484
491
  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();
492
+ properties.text = properties.tag === "input" ? this.value : (this.textContent || this.innerText || this.innerHTML).replace(/[\s\r\n]+/g, " ").trim();
486
493
  properties.href = this.href;
487
494
  ahoy.track("$click", properties);
488
495
  });
@@ -526,7 +533,7 @@
526
533
  ahoy.start = function () {};
527
534
  };
528
535
 
529
- documentReady(function() {
536
+ documentReady(function () {
530
537
  if (config.startOnReady) {
531
538
  ahoy.start();
532
539
  }
@@ -534,4 +541,4 @@
534
541
 
535
542
  return ahoy;
536
543
 
537
- })));
544
+ }));
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ahoy_matey
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0
4
+ version: 5.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2021-08-14 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: activesupport
@@ -16,43 +15,42 @@ dependencies:
16
15
  requirements:
17
16
  - - ">="
18
17
  - !ruby/object:Gem::Version
19
- version: '5.2'
18
+ version: '7.1'
20
19
  type: :runtime
21
20
  prerelease: false
22
21
  version_requirements: !ruby/object:Gem::Requirement
23
22
  requirements:
24
23
  - - ">="
25
24
  - !ruby/object:Gem::Version
26
- version: '5.2'
25
+ version: '7.1'
27
26
  - !ruby/object:Gem::Dependency
28
- name: safely_block
27
+ name: device_detector
29
28
  requirement: !ruby/object:Gem::Requirement
30
29
  requirements:
31
30
  - - ">="
32
31
  - !ruby/object:Gem::Version
33
- version: 0.2.1
32
+ version: '1'
34
33
  type: :runtime
35
34
  prerelease: false
36
35
  version_requirements: !ruby/object:Gem::Requirement
37
36
  requirements:
38
37
  - - ">="
39
38
  - !ruby/object:Gem::Version
40
- version: 0.2.1
39
+ version: '1'
41
40
  - !ruby/object:Gem::Dependency
42
- name: device_detector
41
+ name: safely_block
43
42
  requirement: !ruby/object:Gem::Requirement
44
43
  requirements:
45
44
  - - ">="
46
45
  - !ruby/object:Gem::Version
47
- version: '0'
46
+ version: '0.4'
48
47
  type: :runtime
49
48
  prerelease: false
50
49
  version_requirements: !ruby/object:Gem::Requirement
51
50
  requirements:
52
51
  - - ">="
53
52
  - !ruby/object:Gem::Version
54
- version: '0'
55
- description:
53
+ version: '0.4'
56
54
  email: andrew@ankane.org
57
55
  executables: []
58
56
  extensions: []
@@ -65,14 +63,13 @@ files:
65
63
  - app/controllers/ahoy/base_controller.rb
66
64
  - app/controllers/ahoy/events_controller.rb
67
65
  - app/controllers/ahoy/visits_controller.rb
68
- - app/jobs/ahoy/geocode_job.rb
69
- - app/jobs/ahoy/geocode_v2_job.rb
70
66
  - config/routes.rb
71
67
  - lib/ahoy.rb
72
68
  - lib/ahoy/base_store.rb
73
69
  - lib/ahoy/controller.rb
74
70
  - lib/ahoy/database_store.rb
75
71
  - lib/ahoy/engine.rb
72
+ - lib/ahoy/geocode_v2_job.rb
76
73
  - lib/ahoy/helper.rb
77
74
  - lib/ahoy/model.rb
78
75
  - lib/ahoy/query_methods.rb
@@ -98,7 +95,6 @@ homepage: https://github.com/ankane/ahoy
98
95
  licenses:
99
96
  - MIT
100
97
  metadata: {}
101
- post_install_message:
102
98
  rdoc_options: []
103
99
  require_paths:
104
100
  - lib
@@ -106,15 +102,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
106
102
  requirements:
107
103
  - - ">="
108
104
  - !ruby/object:Gem::Version
109
- version: '2.6'
105
+ version: '3.2'
110
106
  required_rubygems_version: !ruby/object:Gem::Requirement
111
107
  requirements:
112
108
  - - ">="
113
109
  - !ruby/object:Gem::Version
114
110
  version: '0'
115
111
  requirements: []
116
- rubygems_version: 3.2.22
117
- signing_key:
112
+ rubygems_version: 3.6.9
118
113
  specification_version: 4
119
114
  summary: Simple, powerful, first-party analytics for Rails
120
115
  test_files: []
@@ -1,10 +0,0 @@
1
- # for smooth update from Ahoy 1 -> 2
2
- module Ahoy
3
- class GeocodeJob < ActiveJob::Base
4
- queue_as { Ahoy.job_queue }
5
-
6
- def perform(visit)
7
- Ahoy::GeocodeV2Job.perform_now(visit.visit_token, visit.ip)
8
- end
9
- end
10
- end
File without changes