ahoy_matey 1.5.3 → 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +88 -0
  3. data/CONTRIBUTING.md +42 -0
  4. data/LICENSE.txt +1 -1
  5. data/README.md +369 -380
  6. data/app/controllers/ahoy/base_controller.rb +15 -15
  7. data/app/controllers/ahoy/events_controller.rb +8 -2
  8. data/app/controllers/ahoy/visits_controller.rb +8 -1
  9. data/app/jobs/ahoy/geocode_job.rb +10 -0
  10. data/app/jobs/ahoy/geocode_v2_job.rb +28 -0
  11. data/config/routes.rb +1 -1
  12. data/lib/ahoy.rb +68 -86
  13. data/lib/ahoy/base_store.rb +97 -0
  14. data/lib/ahoy/controller.rb +26 -17
  15. data/lib/ahoy/database_store.rb +89 -0
  16. data/lib/ahoy/engine.rb +7 -7
  17. data/lib/ahoy/helper.rb +40 -0
  18. data/lib/ahoy/model.rb +5 -27
  19. data/lib/ahoy/{properties.rb → query_methods.rb} +25 -9
  20. data/lib/ahoy/tracker.rb +101 -42
  21. data/lib/ahoy/utils.rb +7 -0
  22. data/lib/ahoy/version.rb +1 -1
  23. data/lib/ahoy/visit_properties.rb +99 -37
  24. data/lib/generators/ahoy/activerecord_generator.rb +41 -0
  25. data/lib/generators/ahoy/base_generator.rb +13 -0
  26. data/lib/generators/ahoy/install_generator.rb +44 -0
  27. data/lib/generators/ahoy/mongoid_generator.rb +16 -0
  28. data/lib/generators/ahoy/templates/active_record_event_model.rb.tt +10 -0
  29. data/lib/generators/ahoy/templates/active_record_migration.rb.tt +62 -0
  30. data/lib/generators/ahoy/templates/active_record_visit_model.rb.tt +6 -0
  31. data/lib/generators/ahoy/templates/base_store_initializer.rb.tt +20 -0
  32. data/lib/generators/ahoy/templates/database_store_initializer.rb.tt +5 -0
  33. data/lib/generators/ahoy/{stores/templates/mongoid_event_model.rb → templates/mongoid_event_model.rb.tt} +4 -2
  34. data/lib/generators/ahoy/{stores/templates/mongoid_visit_model.rb → templates/mongoid_visit_model.rb.tt} +15 -9
  35. data/vendor/assets/javascripts/ahoy.js +336 -113
  36. metadata +54 -144
  37. data/.gitignore +0 -17
  38. data/Gemfile +0 -6
  39. data/Rakefile +0 -8
  40. data/ahoy_matey.gemspec +0 -38
  41. data/lib/ahoy/deckhands/location_deckhand.rb +0 -49
  42. data/lib/ahoy/deckhands/request_deckhand.rb +0 -52
  43. data/lib/ahoy/deckhands/technology_deckhand.rb +0 -47
  44. data/lib/ahoy/deckhands/traffic_source_deckhand.rb +0 -22
  45. data/lib/ahoy/deckhands/utm_parameter_deckhand.rb +0 -23
  46. data/lib/ahoy/geocode_job.rb +0 -13
  47. data/lib/ahoy/logger_silencer.rb +0 -75
  48. data/lib/ahoy/stores/active_record_store.rb +0 -60
  49. data/lib/ahoy/stores/active_record_token_store.rb +0 -113
  50. data/lib/ahoy/stores/base_store.rb +0 -88
  51. data/lib/ahoy/stores/bunny_store.rb +0 -33
  52. data/lib/ahoy/stores/fluentd_store.rb +0 -17
  53. data/lib/ahoy/stores/kafka_store.rb +0 -40
  54. data/lib/ahoy/stores/kinesis_firehose_store.rb +0 -42
  55. data/lib/ahoy/stores/log_store.rb +0 -53
  56. data/lib/ahoy/stores/mongoid_store.rb +0 -63
  57. data/lib/ahoy/subscribers/active_record.rb +0 -19
  58. data/lib/ahoy/throttle.rb +0 -17
  59. data/lib/generators/ahoy/stores/active_record_events_generator.rb +0 -53
  60. data/lib/generators/ahoy/stores/active_record_generator.rb +0 -16
  61. data/lib/generators/ahoy/stores/active_record_visits_generator.rb +0 -43
  62. data/lib/generators/ahoy/stores/bunny_generator.rb +0 -15
  63. data/lib/generators/ahoy/stores/custom_generator.rb +0 -15
  64. data/lib/generators/ahoy/stores/fluentd_generator.rb +0 -15
  65. data/lib/generators/ahoy/stores/kafka_generator.rb +0 -15
  66. data/lib/generators/ahoy/stores/kinesis_firehose_generator.rb +0 -15
  67. data/lib/generators/ahoy/stores/log_generator.rb +0 -15
  68. data/lib/generators/ahoy/stores/mongoid_events_generator.rb +0 -19
  69. data/lib/generators/ahoy/stores/mongoid_generator.rb +0 -14
  70. data/lib/generators/ahoy/stores/mongoid_visits_generator.rb +0 -27
  71. data/lib/generators/ahoy/stores/templates/active_record_event_model.rb +0 -12
  72. data/lib/generators/ahoy/stores/templates/active_record_events_migration.rb +0 -19
  73. data/lib/generators/ahoy/stores/templates/active_record_initializer.rb +0 -3
  74. data/lib/generators/ahoy/stores/templates/active_record_visit_model.rb +0 -4
  75. data/lib/generators/ahoy/stores/templates/active_record_visits_migration.rb +0 -57
  76. data/lib/generators/ahoy/stores/templates/bunny_initializer.rb +0 -9
  77. data/lib/generators/ahoy/stores/templates/custom_initializer.rb +0 -10
  78. data/lib/generators/ahoy/stores/templates/fluentd_initializer.rb +0 -3
  79. data/lib/generators/ahoy/stores/templates/kafka_initializer.rb +0 -9
  80. data/lib/generators/ahoy/stores/templates/kinesis_firehose_initializer.rb +0 -17
  81. data/lib/generators/ahoy/stores/templates/log_initializer.rb +0 -3
  82. data/lib/generators/ahoy/stores/templates/mongoid_initializer.rb +0 -3
  83. data/test/properties/mysql_json_test.rb +0 -18
  84. data/test/properties/mysql_text_test.rb +0 -19
  85. data/test/properties/postgresql_hstore_test.rb +0 -18
  86. data/test/properties/postgresql_json_test.rb +0 -18
  87. data/test/properties/postgresql_jsonb_test.rb +0 -18
  88. data/test/properties/postgresql_text_test.rb +0 -19
  89. data/test/test_helper.rb +0 -99
  90. data/test/visit_properties_test.rb +0 -44
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 14b16720c567a2d253632b9a2a238273c1efb192
4
- data.tar.gz: fa9aa63ecb5aaa91c44c9a617c4bccfa3f37f6c1
2
+ SHA256:
3
+ metadata.gz: d90d64ae8641aa0bae1bc44acf29379a585ebbad0bbb87325380508d5a8ffcc7
4
+ data.tar.gz: 7c01ce8cbd2e92e3555479b251889d171b54be9d5c369fca51f413fdc912ab01
5
5
  SHA512:
6
- metadata.gz: a77b0b9f3ba3bfd134f848a531406778a46ae213b532945b80e0e2d11e81019ee22d1c55b8967e1913dd5b01aa6814d2e0b512dc500fe7a856e5f33adad35455
7
- data.tar.gz: 6f616904606aaf1af958a8defed7a2fd6ddf9297af5872a3746062ec74ce76cf8eb5c63c606ab45c10884d5119c6f3de45c1e618f892fa1495342a182b3adf28
6
+ metadata.gz: fe62cce407d17f736b616f11a742fefe338cdedd2457665cb3e578bafe0bd6fbb889dd315382dcb70d563a35dd5c5e05de908c069e12d21ab92e0cee637e42d7
7
+ data.tar.gz: f399c7708f6c1071c72db3d9164ab77bd3377fdc0eab7064707fb1202b240156be03669070763bf2f13bcf0bfcac134286eb9e9f9fb988d78267c71da9603386
data/CHANGELOG.md CHANGED
@@ -1,3 +1,90 @@
1
+ ## 3.0.1
2
+
3
+ - Made `Ahoy::Tracker` work outside of requests
4
+ - Fixed storage of `false` values with customized store
5
+ - Fixed error with `user_method` and `Rails::InfoController`
6
+ - Gracefully handle `ActionDispatch::RemoteIp::IpSpoofAttackError`
7
+
8
+ ## 3.0.0
9
+
10
+ - Made Device Detector the default user agent parser
11
+ - Made v2 the default bot detection version
12
+ - Removed a large number of dependencies
13
+ - Removed search keyword detection (most search engines today prevent this)
14
+ - Removed support for Rails < 5
15
+
16
+ ## 2.2.1
17
+
18
+ - Updated Ahoy.js to 0.3.4
19
+ - Fixed v2 bot detection
20
+ - Added latitude and longitude to installation
21
+
22
+ ## 2.2.0
23
+
24
+ - Added `amp_event` helper
25
+ - Improved bot detection for Device Detector
26
+
27
+ ## 2.1.0
28
+
29
+ - Added option for IP masking
30
+ - Added option to use anonymity sets instead of cookies
31
+ - Added `user_agent_parser` option
32
+ - Fixed `visitable` for Rails 4.2
33
+ - Removed `search_keyword` from new installs
34
+
35
+ ## 2.0.2
36
+
37
+ - Fixed error on duplicate records
38
+ - Fixed message when visit not found for geocoding
39
+ - Better compatibility with GeoLite2
40
+ - Better browser compatibility for Ahoy.js
41
+
42
+ ## 2.0.1
43
+
44
+ - Added `Ahoy.server_side_visits = :when_needed` to automatically create visits server-side when needed for events and `visitable`
45
+ - Better handling of visit duration and expiration in JavaScript
46
+
47
+ ## 2.0.0
48
+
49
+ - Removed dependency on jQuery
50
+ - Use `navigator.sendBeacon` by default in supported browsers
51
+ - Added `geocode` event
52
+ - Added `where_event` method for querying events
53
+ - Added support for `visitable` and `where_props` to Mongoid
54
+ - Added `preserve_callbacks` option
55
+ - Use `json` for MySQL by default
56
+ - Fixed log silencing
57
+
58
+ Breaking changes
59
+
60
+ - Simpler interface for data stores
61
+ - Renamed `track_visits_immediately` to `server_side_visits` and enabled by default
62
+ - Renamed `mount` option to `api` and disabled by default
63
+ - Enabled `protect_from_forgery` by default
64
+ - Removed deprecated options
65
+ - Removed throttling
66
+ - Removed most built-in stores
67
+ - Removed support for Rails < 4.2
68
+
69
+ ## 1.6.1
70
+
71
+ - Added `gin` index on properties for events
72
+ - Fixed `visitable` options when name not provided
73
+
74
+ ## 1.6.0
75
+
76
+ - Added support for Rails 5.1
77
+
78
+ ## 1.5.5
79
+
80
+ - Added support for Rails API
81
+ - Added NATS and NSQ stores
82
+
83
+ ## 1.5.4
84
+
85
+ - Fixed issue with duplicate events
86
+ - Added support for PostGIS for `where_properties`
87
+
1
88
  ## 1.5.3
2
89
 
3
90
  - Fixed error with Rails 5 and Mongoid 6
@@ -36,6 +123,7 @@
36
123
  - Detect database for `rails g ahoy:stores:active_record` for easier installation
37
124
  - Use `safely` as default exception handler
38
125
  - Fixed issue with log silencer
126
+ - Use multi-column indexes on `ahoy_events` table creation
39
127
 
40
128
  ## 1.3.1
41
129
 
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,42 @@
1
+ # Contributing
2
+
3
+ First, thanks for wanting to contribute. You’re awesome! :heart:
4
+
5
+ ## Help
6
+
7
+ We’re not able to provide support through GitHub Issues. If you’re looking for help with your code, try posting on [Stack Overflow](https://stackoverflow.com/).
8
+
9
+ All features should be documented. If you don’t see a feature in the docs, assume it doesn’t exist.
10
+
11
+ ## Bugs
12
+
13
+ Think you’ve discovered a bug?
14
+
15
+ 1. Search existing issues to see if it’s been reported.
16
+ 2. Try the `master` branch to make sure it hasn’t been fixed.
17
+
18
+ ```rb
19
+ gem "ahoy_matey", github: "ankane/ahoy"
20
+ ```
21
+
22
+ If the above steps don’t help, create an issue. Include:
23
+
24
+ - Detailed steps to reproduce
25
+ - Complete backtraces for exceptions
26
+
27
+ ## New Features
28
+
29
+ If you’d like to discuss a new feature, create an issue and start the title with `[Idea]`.
30
+
31
+ ## Pull Requests
32
+
33
+ Fork the project and create a pull request. A few tips:
34
+
35
+ - Keep changes to a minimum. If you have multiple features or fixes, submit multiple pull requests.
36
+ - Follow the existing style. The code should read like it’s written by a single person.
37
+
38
+ Feel free to open an issue to get feedback on your idea before spending too much time on it.
39
+
40
+ ---
41
+
42
+ This contributing guide is released under [CCO](https://creativecommons.org/publicdomain/zero/1.0/) (public domain). Use it for your own project without attribution.
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2014 Andrew Kane
1
+ Copyright (c) 2014-2019 Andrew Kane
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -1,10 +1,14 @@
1
1
  # Ahoy
2
2
 
3
- Ahoy provides a solid foundation to track visits and events in Ruby, JavaScript, and native apps. Works with any data store so you can easily scale.
3
+ :fire: Simple, powerful analytics for Rails
4
+
5
+ Track visits and events in Ruby, JavaScript, and native apps. Data is stored in your database by default so you can easily combine it with other data.
6
+
7
+ :postbox: To track emails, check out [Ahoy Email](https://github.com/ankane/ahoy_email), and for A/B testing, check out [Field Test](https://github.com/ankane/field_test)
4
8
 
5
9
  :tangerine: Battle-tested at [Instacart](https://www.instacart.com/opensource)
6
10
 
7
- :postbox: To track emails, check out [Ahoy Email](https://github.com/ankane/ahoy_email).
11
+ [![Build Status](https://travis-ci.org/ankane/ahoy.svg?branch=master)](https://travis-ci.org/ankane/ahoy)
8
12
 
9
13
  ## Installation
10
14
 
@@ -14,159 +18,124 @@ Add this line to your application’s Gemfile:
14
18
  gem 'ahoy_matey'
15
19
  ```
16
20
 
17
- And add the javascript file in `app/assets/javascripts/application.js` after jQuery.
18
-
19
- ```javascript
20
- //= require jquery
21
- //= require ahoy
22
- ```
23
-
24
- ## Choose a Data Store
25
-
26
- Ahoy supports a number of data stores out of the box. You can start with one of them and customize as needed, or create your own store from scratch.
27
-
28
- - [PostgreSQL, MySQL, or SQLite](#postgresql-mysql-or-sqlite)
29
- - [MongoDB](#mongodb)
30
- - [Kafka](#kafka)
31
- - [Fluentd](#fluentd)
32
- - [RabbitMQ](#rabbitmq)
33
- - [Amazon Kinesis Firehose](#amazon-kinesis-firehose)
34
- - [Logs](#logs)
35
- - [Custom](#custom)
36
-
37
- ### PostgreSQL, MySQL, or SQLite
38
-
39
- Run:
21
+ And run:
40
22
 
41
23
  ```sh
42
- rails generate ahoy:stores:active_record
43
- rake db:migrate
24
+ bundle install
25
+ rails generate ahoy:install
26
+ rails db:migrate
44
27
  ```
45
28
 
46
- ### MongoDB
29
+ Restart your web server, open a page in your browser, and a visit will be created :tada:
47
30
 
48
- ```sh
49
- rails generate ahoy:stores:mongoid
31
+ Track your first event from a controller with:
32
+
33
+ ```ruby
34
+ ahoy.track "My first event", language: "Ruby"
50
35
  ```
51
36
 
52
- ### Kafka
37
+ ### JavaScript, Native Apps, & AMP
53
38
 
54
- Add [ruby-kafka](https://github.com/zendesk/ruby-kafka) to your Gemfile.
39
+ Enable the API in `config/initializers/ahoy.rb`:
55
40
 
56
41
  ```ruby
57
- gem 'ruby-kafka'
42
+ Ahoy.api = true
58
43
  ```
59
44
 
60
- And run:
45
+ And restart your web server.
46
+
47
+ ### JavaScript
48
+
49
+ For Rails 6 / Webpacker, run:
61
50
 
62
51
  ```sh
63
- rails generate ahoy:stores:kafka
52
+ yarn add ahoy.js
64
53
  ```
65
54
 
66
- Use `ENV["KAFKA_URL"]` to configure.
55
+ And add to `app/javascript/packs/application.js`:
67
56
 
68
- ### Fluentd
57
+ ```javascript
58
+ import ahoy from "ahoy.js";
59
+ ```
69
60
 
70
- Add [fluent-logger](https://github.com/fluent/fluent-logger-ruby) to your Gemfile.
61
+ For Rails 5 / Sprockets, add to `app/assets/javascripts/application.js`:
71
62
 
72
- ```ruby
73
- gem 'fluent-logger'
63
+ ```javascript
64
+ //= require ahoy
74
65
  ```
75
66
 
76
- And run:
67
+ Track an event with:
77
68
 
78
- ```sh
79
- rails generate ahoy:stores:fluentd
69
+ ```javascript
70
+ ahoy.track("My second event", {language: "JavaScript"});
80
71
  ```
81
72
 
82
- Use `ENV["FLUENTD_HOST"]` and `ENV["FLUENTD_PORT"]` to configure.
73
+ ### GDPR Compliance
83
74
 
84
- ### RabbitMQ
75
+ Ahoy provides a number of options to help with GDPR compliance. See the [GDPR section](#gdpr-compliance-1) for more info.
85
76
 
86
- Add [bunny](https://github.com/ruby-amqp/bunny) to your Gemfile.
87
-
88
- ```ruby
89
- gem 'bunny'
90
- ```
77
+ ## How It Works
91
78
 
92
- And run:
79
+ ### Visits
93
80
 
94
- ```sh
95
- rails generate ahoy:stores:bunny
96
- ```
81
+ When someone visits your website, Ahoy creates a visit with lots of useful information.
97
82
 
98
- Use `ENV["RABBITMQ_URL"]` to configure.
83
+ - **traffic source** - referrer, referring domain, landing page
84
+ - **location** - country, region, city, latitude, longitude
85
+ - **technology** - browser, OS, device type
86
+ - **utm parameters** - source, medium, term, content, campaign
99
87
 
100
- ### Amazon Kinesis Firehose
88
+ Use the `current_visit` method to access it.
101
89
 
102
- Add [aws-sdk](https://github.com/aws/aws-sdk-ruby) to your Gemfile.
90
+ Prevent certain Rails actions from creating visits with:
103
91
 
104
92
  ```ruby
105
- gem 'aws-sdk', '>= 2.0.0'
93
+ skip_before_action :track_ahoy_visit
106
94
  ```
107
95
 
108
- And run:
96
+ This is typically useful for APIs. If your entire Rails app is an API, you can use:
109
97
 
110
- ```sh
111
- rails generate ahoy:stores:kinesis_firehose
98
+ ```ruby
99
+ Ahoy.api_only = true
112
100
  ```
113
101
 
114
- Configure delivery streams and credentials in the initializer.
102
+ You can also defer visit tracking to JavaScript. This is useful for preventing bots (that aren’t detected by their user agent) and users with cookies disabled from creating a new visit on each request. `:when_needed` will create visits server-side only when needed by events, and `false` will disable server-side creation completely, discarding events without a visit.
115
103
 
116
- ### Logs
117
-
118
- ```sh
119
- rails generate ahoy:stores:log
104
+ ```ruby
105
+ Ahoy.server_side_visits = :when_needed
120
106
  ```
121
107
 
122
- This logs visits to `log/visits.log` and events to `log/events.log`.
108
+ ### Events
123
109
 
124
- ### Custom
110
+ Each event has a `name` and `properties`. There are several ways to track events.
125
111
 
126
- ```sh
127
- rails generate ahoy:stores:custom
112
+ #### Ruby
113
+
114
+ ```ruby
115
+ ahoy.track "Viewed book", title: "Hot, Flat, and Crowded"
128
116
  ```
129
117
 
130
- This creates a class for you to fill out.
118
+ Track actions automatically with:
131
119
 
132
120
  ```ruby
133
- class Ahoy::Store < Ahoy::Stores::BaseStore
134
- def track_visit(options)
135
- end
121
+ class ApplicationController < ActionController::Base
122
+ after_action :track_action
136
123
 
137
- def track_event(name, properties, options)
124
+ protected
125
+
126
+ def track_action
127
+ ahoy.track "Ran action", request.path_parameters
138
128
  end
139
129
  end
140
130
  ```
141
131
 
142
- See the [ActiveRecordTokenStore](https://github.com/ankane/ahoy/blob/master/lib/ahoy/stores/active_record_token_store.rb) for an example.
143
-
144
- ## How It Works
145
-
146
- ### Visits
147
-
148
- When someone visits your website, Ahoy creates a visit with lots of useful information.
149
-
150
- - **traffic source** - referrer, referring domain, landing page, search keyword
151
- - **location** - country, region, and city
152
- - **technology** - browser, OS, and device type
153
- - **utm parameters** - source, medium, term, content, campaign
154
-
155
- Use the `current_visit` method to access it.
156
-
157
- ### Events
158
-
159
- Each event has a `name` and `properties`.
160
-
161
- There are three ways to track events.
162
-
163
132
  #### JavaScript
164
133
 
165
134
  ```javascript
166
135
  ahoy.track("Viewed book", {title: "The World is Flat"});
167
136
  ```
168
137
 
169
- or track events automatically with:
138
+ Track events automatically with:
170
139
 
171
140
  ```javascript
172
141
  ahoy.trackAll();
@@ -174,140 +143,155 @@ ahoy.trackAll();
174
143
 
175
144
  See [Ahoy.js](https://github.com/ankane/ahoy.js) for a complete list of features.
176
145
 
177
- #### Ruby
178
-
179
- ```ruby
180
- ahoy.track "Viewed book", title: "Hot, Flat, and Crowded"
181
- ```
182
-
183
146
  #### Native Apps
184
147
 
185
- See the [HTTP spec](#native-apps-1) until libraries are built.
148
+ For Android, check out [Ahoy Android](https://github.com/instacart/ahoy-android). For other platforms, see the [API spec](#api-spec).
186
149
 
187
- ### Users
150
+ #### AMP
188
151
 
189
- Ahoy automatically attaches the `current_user` to the visit.
152
+ ```erb
153
+ <head>
154
+ <script async custom-element="amp-analytics" src="https://cdn.ampproject.org/v0/amp-analytics-0.1.js"></script>
155
+ </head>
156
+ <body>
157
+ <%= amp_event "Viewed article", title: "Analytics with Rails" %>
158
+ </body>
159
+ ```
190
160
 
191
- With [Devise](https://github.com/plataformatec/devise), it will attach the user even if he or she signs in after the visit starts.
161
+ ### Associated Models
192
162
 
193
- With other authentication frameworks, add this to the end of your sign in method:
163
+ Say we want to associate orders with visits. Just add `visitable` to the model.
194
164
 
195
165
  ```ruby
196
- ahoy.authenticate(user)
166
+ class Order < ApplicationRecord
167
+ visitable :ahoy_visit
168
+ end
197
169
  ```
198
170
 
199
- ## Customize the Store
171
+ When a visitor places an order, the `ahoy_visit_id` column is automatically set :tada:
200
172
 
201
- Stores are built to be highly customizable.
173
+ See where orders are coming from with simple joins:
202
174
 
203
175
  ```ruby
204
- class Ahoy::Store < Ahoy::Stores::ActiveRecordTokenStore
205
- # add methods here
206
- end
176
+ Order.joins(:ahoy_visit).group("referring_domain").count
177
+ Order.joins(:ahoy_visit).group("city").count
178
+ Order.joins(:ahoy_visit).group("device_type").count
207
179
  ```
208
180
 
209
- ### Exclude Bots and More
210
-
211
- Exclude visits and events from being tracked with:
181
+ Here’s what the migration to add the `ahoy_visit_id` column should look like:
212
182
 
213
183
  ```ruby
214
- class Ahoy::Store < Ahoy::Stores::ActiveRecordTokenStore
215
- def exclude?
216
- bot? || request.ip == "192.168.1.1"
184
+ class AddVisitIdToOrders < ActiveRecord::Migration[6.0]
185
+ def change
186
+ add_column :orders, :ahoy_visit_id, :bigint
217
187
  end
218
188
  end
219
189
  ```
220
190
 
221
- Bots are excluded by default.
222
-
223
- ### Track Additional Values
191
+ Customize the column with:
224
192
 
225
193
  ```ruby
226
- class Ahoy::Store < Ahoy::Stores::ActiveRecordTokenStore
227
- def track_visit(options)
228
- super do |visit|
229
- visit.gclid = visit_properties.landing_params["gclid"]
230
- end
231
- end
232
-
233
- def track_event(name, properties, options)
234
- super do |event|
235
- event.ip = request.ip
236
- end
237
- end
238
- end
194
+ visitable :sign_up_visit
239
195
  ```
240
196
 
241
- Some methods you can use are `request`, `controller`, `visit_properties`, and `ahoy`.
197
+ ### Users
242
198
 
243
- ### Customize User
199
+ Ahoy automatically attaches the `current_user` to the visit. With [Devise](https://github.com/plataformatec/devise), it attaches the user even if he or she signs in after the visit starts.
244
200
 
245
- If you use a method other than `current_user`, set it here:
201
+ With other authentication frameworks, add this to the end of your sign in method:
246
202
 
247
203
  ```ruby
248
- class Ahoy::Store < Ahoy::Stores::ActiveRecordTokenStore
249
- def user
250
- controller.true_user
251
- end
204
+ ahoy.authenticate(user)
205
+ ```
206
+
207
+ To see the visits for a given user, create an association:
208
+
209
+ ```ruby
210
+ class User < ApplicationRecord
211
+ has_many :visits, class_name: "Ahoy::Visit"
252
212
  end
253
213
  ```
254
214
 
255
- ### Report Exceptions
215
+ And use:
216
+
217
+ ```ruby
218
+ User.find(123).visits
219
+ ```
256
220
 
257
- Exceptions are rescued so analytics do not break your app. Ahoy uses [Safely](https://github.com/ankane/safely) to try to report them to a service by default.
221
+ #### Custom User Method
258
222
 
259
- To customize this, use:
223
+ Use a method besides `current_user`
260
224
 
261
225
  ```ruby
262
- Safely.report_exception_method = proc { |e| Rollbar.error(e) }
226
+ Ahoy.user_method = :true_user
263
227
  ```
264
228
 
265
- ### Use Different Models
229
+ or use a proc
230
+
231
+ ```ruby
232
+ Ahoy.user_method = ->(controller) { controller.true_user }
233
+ ```
266
234
 
267
- For ActiveRecord and Mongoid stores
235
+ #### Doorkeeper
236
+
237
+ To attach the user with [Doorkeeper](https://github.com/doorkeeper-gem/doorkeeper), be sure you have a `current_resource_owner` method in `ApplicationController`.
268
238
 
269
239
  ```ruby
270
- class Ahoy::Store < Ahoy::Stores::ActiveRecordTokenStore
271
- def visit_model
272
- CustomVisit
273
- end
240
+ class ApplicationController < ActionController::Base
241
+ private
274
242
 
275
- def event_model
276
- CustomEvent
243
+ def current_resource_owner
244
+ User.find(doorkeeper_token.resource_owner_id) if doorkeeper_token
277
245
  end
278
246
  end
279
247
  ```
280
248
 
281
- ## More Features
249
+ #### Knock
282
250
 
283
- ### Automatic Tracking
251
+ To attach the user with [Knock](https://github.com/nsarno/knock), either include `Knock::Authenticable`in `ApplicationController`:
284
252
 
285
- Page views
253
+ ```ruby
254
+ class ApplicationController < ActionController::API
255
+ include Knock::Authenticable
256
+ end
257
+ ```
286
258
 
287
- ```javascript
288
- ahoy.trackView();
259
+ Or include it in Ahoy:
260
+
261
+ ```ruby
262
+ Ahoy::BaseController.include Knock::Authenticable
289
263
  ```
290
264
 
291
- Clicks
265
+ And use:
292
266
 
293
- ```javascript
294
- ahoy.trackClicks();
267
+ ```ruby
268
+ Ahoy.user_method = ->(controller) { controller.send(:authenticate_entity, "user") }
295
269
  ```
296
270
 
297
- Rails actions
271
+ ### Exclusions
272
+
273
+ Bots are excluded from tracking by default. To include them, use:
298
274
 
299
275
  ```ruby
300
- class ApplicationController < ActionController::Base
301
- after_action :track_action
276
+ Ahoy.track_bots = true
277
+ ```
302
278
 
303
- protected
279
+ Add your own rules with:
304
280
 
305
- def track_action
306
- ahoy.track "Processed #{controller_name}##{action_name}", request.filtered_parameters
307
- end
281
+ ```ruby
282
+ Ahoy.exclude_method = lambda do |controller, request|
283
+ request.ip == "192.168.1.1"
308
284
  end
309
285
  ```
310
286
 
287
+ ### Visit Duration
288
+
289
+ By default, a new visit is created after 4 hours of inactivity. Change this with:
290
+
291
+ ```ruby
292
+ Ahoy.visit_duration = 30.minutes
293
+ ```
294
+
311
295
  ### Multiple Subdomains
312
296
 
313
297
  To track visits across multiple subdomains, use:
@@ -316,103 +300,136 @@ To track visits across multiple subdomains, use:
316
300
  Ahoy.cookie_domain = :all
317
301
  ```
318
302
 
319
- ### Visit Duration
303
+ ### Geocoding
304
+
305
+ Disable geocoding with:
320
306
 
321
- By default, a new visit is created after 4 hours of inactivity.
307
+ ```ruby
308
+ Ahoy.geocode = false
309
+ ```
322
310
 
323
- Change this with:
311
+ Change the job queue with:
324
312
 
325
313
  ```ruby
326
- Ahoy.visit_duration = 30.minutes
314
+ Ahoy.job_queue = :low_priority
327
315
  ```
328
316
 
329
- ### ActiveRecord
317
+ #### Geocoding Performance
330
318
 
331
- Let’s associate orders with visits.
319
+ To avoid calls to a remote API, download the [GeoLite2 City database](https://dev.maxmind.com/geoip/geoip2/geolite2/) and configure Geocoder to use it.
332
320
 
333
- First, generate a migration and add a `visit_id` column.
321
+ Add this line to your application’s Gemfile:
334
322
 
335
323
  ```ruby
336
- class AddVisitIdToOrders < ActiveRecord::Migration
337
- def change
338
- add_column :orders, :visit_id, :integer
339
- end
340
- end
324
+ gem 'maxminddb'
341
325
  ```
342
326
 
343
- **Note**: Use the `uuid` column type if the `id` column on `visits` is a `uuid`.
344
-
345
- Then, add `visitable` to the model.
327
+ And create an initializer at `config/initializers/geocoder.rb` with:
346
328
 
347
329
  ```ruby
348
- class Order < ActiveRecord::Base
349
- visitable
350
- end
330
+ Geocoder.configure(
331
+ ip_lookup: :geoip2,
332
+ geoip2: {
333
+ file: Rails.root.join("lib", "GeoLite2-City.mmdb")
334
+ }
335
+ )
351
336
  ```
352
337
 
353
- When a visitor places an order, the `visit_id` column is automatically set. :tada:
338
+ If you use Heroku, you can use an unofficial buildpack like [this one](https://github.com/temedica/heroku-buildpack-maxmind-geolite2) to avoid including the database in your repo.
354
339
 
355
- Customize the column and class name with:
340
+ ### Token Generation
341
+
342
+ 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).
356
343
 
357
344
  ```ruby
358
- visitable :sign_up_visit, class_name: "Visit"
345
+ Ahoy.token_generator = -> { Druuid.gen }
359
346
  ```
360
347
 
361
- ### Doorkeeper
348
+ ### Throttling
362
349
 
363
- To attach the user with [Doorkeeper](https://github.com/doorkeeper-gem/doorkeeper), be sure you have a `current_resource_owner` method in `ApplicationController`.
350
+ You can use [Rack::Attack](https://github.com/kickstarter/rack-attack) to throttle requests to the API.
364
351
 
365
352
  ```ruby
366
- class ApplicationController < ActionController::Base
367
- private
368
-
369
- def current_resource_owner
370
- User.find(doorkeeper_token.resource_owner_id) if doorkeeper_token
353
+ class Rack::Attack
354
+ throttle("ahoy/ip", limit: 20, period: 1.minute) do |req|
355
+ if req.path.start_with?("/ahoy/")
356
+ req.ip
357
+ end
371
358
  end
372
359
  end
373
360
  ```
374
361
 
375
- ### Geocoding
362
+ ### Exceptions
376
363
 
377
- By default, geocoding is performed inline. For performance, move it to the background with:
364
+ Exceptions are rescued so analytics do not break your app. Ahoy uses [Safely](https://github.com/ankane/safely) to try to report them to a service by default. To customize this, use:
378
365
 
379
366
  ```ruby
380
- Ahoy.geocode = :async
367
+ Safely.report_exception_method = ->(e) { Rollbar.error(e) }
381
368
  ```
382
369
 
383
- For Rails 4.0 and 4.1, you’ll need to add [activejob_backport](https://github.com/ankane/activejob_backport).
370
+ ## GDPR Compliance
371
+
372
+ Ahoy provides a number of options to help with [GDPR compliance](https://en.wikipedia.org/wiki/General_Data_Protection_Regulation).
384
373
 
385
- To change the queue name (`ahoy` by default), use:
374
+ Update `config/initializers/ahoy.rb` with:
386
375
 
387
376
  ```ruby
388
- Ahoy.job_queue = :low_priority
377
+ class Ahoy::Store < Ahoy::DatabaseStore
378
+ def authenticate(data)
379
+ # disables automatic linking of visits and users
380
+ end
381
+ end
382
+
383
+ Ahoy.mask_ips = true
384
+ Ahoy.cookies = false
389
385
  ```
390
386
 
391
- Or disable geocoding with:
387
+ This:
388
+
389
+ - Masks IP addresses
390
+ - Switches from cookies to anonymity sets
391
+ - Disables automatic linking of visits and users
392
+
393
+ If you use JavaScript tracking, also set:
394
+
395
+ ```javascript
396
+ ahoy.configure({cookies: false});
397
+ ```
398
+
399
+ ### IP Masking
400
+
401
+ Ahoy can mask IPs with the same approach [Google Analytics uses for IP anonymization](https://support.google.com/analytics/answer/2763052). This means:
402
+
403
+ - For IPv4, the last octet is set to 0 (`8.8.4.4` becomes `8.8.4.0`)
404
+ - For IPv6, the last 80 bits are set to zeros (`2001:4860:4860:0:0:0:0:8844` becomes `2001:4860:4860::`)
392
405
 
393
406
  ```ruby
394
- Ahoy.geocode = false
407
+ Ahoy.mask_ips = true
395
408
  ```
396
409
 
397
- ### Track Visits Immediately
410
+ IPs are masked before geolocation is performed.
398
411
 
399
- Visitor and visit ids are generated on the first request (so you can use them immediately), but the `track_visit` method isn’t called until the JavaScript library posts to the server. This prevents browsers with cookies disabled from creating multiple visits and ensures visits are not created for API endpoints. Change this with:
412
+ To mask previously collected IPs, use:
400
413
 
401
414
  ```ruby
402
- Ahoy.track_visits_immediately = true
415
+ Ahoy::Visit.find_each do |visit|
416
+ visit.update_column :ip, Ahoy.mask_ip(visit.ip)
417
+ end
403
418
  ```
404
419
 
405
- **Note:** It’s highly recommended to perform geocoding in the background with this option.
420
+ ### Anonymity Sets & Cookies
406
421
 
407
- You can exclude API endpoints and other actions with:
422
+ 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.
408
423
 
409
424
  ```ruby
410
- skip_before_action :track_ahoy_visit
425
+ Ahoy.cookies = false
411
426
  ```
412
427
 
428
+ Previously set cookies are automatically deleted.
429
+
413
430
  ## Development
414
431
 
415
- Ahoy is built with developers in mind. You can run the following code in your browser’s console.
432
+ Ahoy is built with developers in mind. You can run the following code in your browser’s console.
416
433
 
417
434
  Force a new visit
418
435
 
@@ -432,266 +449,238 @@ Turn off logging
432
449
  ahoy.debug(false);
433
450
  ```
434
451
 
435
- Debug endpoint requests in Ruby
452
+ Debug API requests in Ruby
436
453
 
437
454
  ```ruby
438
455
  Ahoy.quiet = false
439
456
  ```
440
457
 
441
- ## Explore the Data
442
-
443
- How you explore the data depends on the data store used.
444
-
445
- For SQL databases, you can use [Blazer](https://github.com/ankane/blazer) to easily generate charts and dashboards.
458
+ ## Data Stores
446
459
 
447
- With ActiveRecord, you can do:
460
+ Data tracked by Ahoy is sent to your data store. Ahoy ships with a data store that uses your Rails database by default. You can find it in `config/initializers/ahoy.rb`:
448
461
 
449
462
  ```ruby
450
- Visit.group(:search_keyword).count
451
- Visit.group(:country).count
452
- Visit.group(:referring_domain).count
453
- ```
454
-
455
- [Chartkick](http://chartkick.com/) and [Groupdate](https://github.com/ankane/groupdate) make it easy to visualize the data.
456
-
457
- ```erb
458
- <%= line_chart Visit.group_by_day(:started_at).count %>
463
+ class Ahoy::Store < Ahoy::DatabaseStore
464
+ end
459
465
  ```
460
466
 
461
- See where orders are coming from with simple joins:
467
+ There are four events data stores can subscribe to:
462
468
 
463
469
  ```ruby
464
- Order.joins(:visit).group("referring_domain").count
465
- Order.joins(:visit).group("city").count
466
- Order.joins(:visit).group("device_type").count
467
- ```
470
+ class Ahoy::Store < Ahoy::BaseStore
471
+ def track_visit(data)
472
+ # new visit
473
+ end
468
474
 
469
- To see the visits for a given user, create an association:
475
+ def track_event(data)
476
+ # new event
477
+ end
470
478
 
471
- ```ruby
472
- class User < ActiveRecord::Base
473
- has_many :visits
479
+ def geocode(data)
480
+ # visit geocoded
481
+ end
482
+
483
+ def authenticate(data)
484
+ # user authenticates
485
+ end
474
486
  end
475
487
  ```
476
488
 
477
- And use:
489
+ Data stores are designed to be highly customizable so you can scale as you grow. Check out [examples](docs/Data-Store-Examples.md) for Kafka, RabbitMQ, Fluentd, NATS, NSQ, and Amazon Kinesis Firehose.
478
490
 
479
- ```ruby
480
- user = User.first
481
- user.visits
482
- ```
483
-
484
- ### Create Funnels
491
+ ### Track Additional Data
485
492
 
486
493
  ```ruby
487
- viewed_store_ids = Ahoy::Event.where(name: "Viewed store").uniq.pluck(:user_id)
488
- added_item_ids = Ahoy::Event.where(user_id: viewed_store_ids, name: "Added item to cart").uniq.pluck(:user_id)
489
- viewed_checkout_ids = Ahoy::Event.where(user_id: added_item_ids, name: "Viewed checkout").uniq.pluck(:user_id)
494
+ class Ahoy::Store < Ahoy::DatabaseStore
495
+ def track_visit(data)
496
+ data[:accept_language] = request.headers["Accept-Language"]
497
+ super(data)
498
+ end
499
+ end
490
500
  ```
491
501
 
492
- The same approach also works with visitor tokens.
502
+ Two useful methods you can use are `request` and `controller`.
493
503
 
494
- ### Querying Properties
504
+ You can pass additional visit data from JavaScript with:
495
505
 
496
- With ActiveRecord, use:
497
-
498
- ```ruby
499
- Ahoy::Event.where(name: "Viewed product").where_properties(product_id: 123).count
506
+ ```javascript
507
+ ahoy.configure({visitParams: {referral_code: 123}});
500
508
  ```
501
509
 
502
- **Note:** If you get a `NoMethodError`, upgrade Ahoy and add `include Ahoy::Properties` to the Ahoy::Event class:
510
+ And use:
503
511
 
504
512
  ```ruby
505
- module Ahoy
506
- class Event < ActiveRecord::Base
507
- include Ahoy::Properties
508
- ...
513
+ class Ahoy::Store < Ahoy::DatabaseStore
514
+ def track_visit(data)
515
+ data[:referral_code] = request.parameters[:referral_code]
516
+ super(data)
509
517
  end
510
518
  end
511
519
  ```
512
520
 
513
- ## Native Apps
514
-
515
- ### Visits
516
-
517
- When a user launches the app, create a visit.
518
-
519
- Generate a `visit_token` and `visitor_token` as [UUIDs](http://en.wikipedia.org/wiki/Universally_unique_identifier).
520
-
521
- Send these values in the `Ahoy-Visit` and `Ahoy-Visitor` headers with all requests.
522
-
523
- Send a `POST` request to `/ahoy/visits` with:
524
-
525
- - platform - `iOS`, `Android`, etc.
526
- - app_version - `1.0.0`
527
- - os_version - `7.0.6`
528
-
529
- After 4 hours of inactivity, create another visit and use the updated visit id.
530
-
531
- ### Events
521
+ ### Use Different Models
532
522
 
533
- Send a `POST` request as `Content-Type: application/json` to `/ahoy/events` with:
523
+ ```ruby
524
+ class Ahoy::Store < Ahoy::DatabaseStore
525
+ def visit_model
526
+ MyVisit
527
+ end
534
528
 
535
- - id - `5aea7b70-182d-4070-b062-b0a09699ad5e` - UUID
536
- - name - `Viewed item`
537
- - properties - `{"item_id": 123}`
538
- - time - `2014-06-17T00:00:00-07:00` - [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601)
539
- - `Ahoy-Visit` and `Ahoy-Visitor` headers
540
- - user token (depends on your authentication framework)
529
+ def event_model
530
+ MyEvent
531
+ end
532
+ end
533
+ ```
541
534
 
542
- Use an array to pass multiple events at once.
535
+ ## Explore the Data
543
536
 
544
- ## Reference
537
+ [Blazer](https://github.com/ankane/blazer) is a great tool for exploring your data.
545
538
 
546
- By default, Ahoy create endpoints at `/ahoy/visits` and `/ahoy/events`. To disable, use:
539
+ With ActiveRecord, you can do:
547
540
 
548
541
  ```ruby
549
- Ahoy.mount = false
542
+ Ahoy::Visit.group(:search_keyword).count
543
+ Ahoy::Visit.group(:country).count
544
+ Ahoy::Visit.group(:referring_domain).count
550
545
  ```
551
546
 
552
- ## Upgrading
547
+ [Chartkick](https://www.chartkick.com/) and [Groupdate](https://github.com/ankane/groupdate) make it easy to visualize the data.
553
548
 
554
- ### 1.4.0
549
+ ```erb
550
+ <%= line_chart Ahoy::Visit.group_by_day(:started_at).count %>
551
+ ```
555
552
 
556
- There’s nothing to do, but it’s worth noting the default store was changed from `ActiveRecordStore` to `ActiveRecordTokenStore` for new installations. `ActiveRecordStore` will continue to be supported.
553
+ ### Querying Events
557
554
 
558
- ### json -> jsonb
555
+ Ahoy provides two methods on the event model to make querying easier.
559
556
 
560
- Create a migration to add a new `jsonb` column.
557
+ To query on both name and properties, you can use:
561
558
 
562
559
  ```ruby
563
- rename_column :ahoy_events, :properties, :properties_json
564
- add_column :ahoy_events, :properties, :jsonb
560
+ Ahoy::Event.where_event("Viewed product", product_id: 123).count
565
561
  ```
566
562
 
567
- Restart your web server immediately afterwards, as Ahoy will rescue and report errors until then.
568
-
569
- Sync the new column.
563
+ Or just query properties with:
570
564
 
571
565
  ```ruby
572
- Ahoy::Event.where(properties: nil).select(:id).find_in_batches do |events|
573
- Ahoy::Event.where(id: events.map(&:id)).update_all("properties = properties_json::jsonb")
574
- end
566
+ Ahoy::Event.where_props(product_id: 123).count
575
567
  ```
576
568
 
577
- Then create a migration to drop the old column.
569
+ ### Funnels
570
+
571
+ It’s easy to create funnels.
578
572
 
579
573
  ```ruby
580
- remove_column :ahoy_events, :properties_json
574
+ viewed_store_ids = Ahoy::Event.where(name: "Viewed store").distinct.pluck(:user_id)
575
+ added_item_ids = Ahoy::Event.where(user_id: viewed_store_ids, name: "Added item to cart").distinct.pluck(:user_id)
576
+ viewed_checkout_ids = Ahoy::Event.where(user_id: added_item_ids, name: "Viewed checkout").distinct.pluck(:user_id)
581
577
  ```
582
578
 
583
- ### 1.0.0
579
+ The same approach also works with visitor tokens.
584
580
 
585
- Add the following code to the end of `config/intializers/ahoy.rb`.
581
+ ## Tutorials
586
582
 
587
- ```ruby
588
- class Ahoy::Store < Ahoy::Stores::ActiveRecordTokenStore
589
- uses_deprecated_subscribers
590
- end
591
- ```
583
+ - [Tracking Metrics with Ahoy and Blazer](https://gorails.com/episodes/internal-metrics-with-ahoy-and-blazer)
592
584
 
593
- If you use `Ahoy::Event` to track events, copy it into your project.
585
+ ## API Spec
594
586
 
595
- ```ruby
596
- module Ahoy
597
- class Event < ActiveRecord::Base
598
- self.table_name = "ahoy_events"
587
+ ### Visits
599
588
 
600
- belongs_to :visit
601
- belongs_to :user, polymorphic: true
589
+ Generate visit and visitor tokens as [UUIDs](https://en.wikipedia.org/wiki/Universally_unique_identifier), and include these values in the `Ahoy-Visit` and `Ahoy-Visitor` headers with all requests.
602
590
 
603
- serialize :properties, JSON
604
- end
605
- end
606
- ```
591
+ Send a `POST` request to `/ahoy/visits` with `Content-Type: application/json` and a body like:
607
592
 
608
- That’s it! To fix deprecations, keep reading.
593
+ ```json
594
+ {
595
+ "visit_token": "<visit-token>",
596
+ "visitor_token": "<visitor-token>",
597
+ "platform": "iOS",
598
+ "app_version": "1.0.0",
599
+ "os_version": "11.2.6"
600
+ }
601
+ ```
609
602
 
610
- #### Visits
603
+ After 4 hours of inactivity, create another visit (use the same visitor token).
611
604
 
612
- Remove `ahoy_visit` from your visit model and replace it with:
605
+ ### Events
613
606
 
614
- ```ruby
615
- class Visit < ActiveRecord::Base
616
- belongs_to :user, polymorphic: true
617
- end
607
+ Send a `POST` request to `/ahoy/events` with `Content-Type: application/json` and a body like:
608
+
609
+ ```json
610
+ {
611
+ "visit_token": "<visit-token>",
612
+ "visitor_token": "<visitor-token>",
613
+ "events": [
614
+ {
615
+ "id": "<optional-random-id>",
616
+ "name": "Viewed item",
617
+ "properties": {
618
+ "item_id": 123
619
+ },
620
+ "time": "2018-01-01T00:00:00-07:00"
621
+ }
622
+ ]
623
+ }
618
624
  ```
619
625
 
620
- #### Subscribers
626
+ ## Upgrading
621
627
 
622
- Remove `uses_deprecated_subscribers` from `Ahoy::Store`.
628
+ ### 3.0
623
629
 
624
- If you have a custom subscriber, copy the `track` method to `track_event` in `Ahoy::Store`.
630
+ If you installed Ahoy before 2.1 and want to keep legacy user agent parsing and bot detection, add to your Gemfile:
625
631
 
626
632
  ```ruby
627
- class Ahoy::Store < Ahoy::Stores::ActiveRecordTokenStore
628
- def track_event(name, properties, options)
629
- # code copied from the track method in your subscriber
630
- end
631
- end
633
+ gem "browser", "~> 2.0"
634
+ gem "user_agent_parser"
632
635
  ```
633
636
 
634
- #### Authentication
635
-
636
- Ahoy no longer tracks the `$authenticate` event automatically.
637
-
638
- To restore this behavior, use:
637
+ And add to `config/initializers/ahoy.rb`:
639
638
 
640
639
  ```ruby
641
- class Ahoy::Store < Ahoy::Stores::ActiveRecordTokenStore
642
- def authenticate(user)
643
- super
644
- ahoy.track "$authenticate"
645
- end
646
- end
640
+ Ahoy.user_agent_parser = :legacy
647
641
  ```
648
642
 
649
- #### Global Options
643
+ ### 2.2
650
644
 
651
- Replace the `Ahoy.user_method` with `user` method, and replace `Ahoy.track_bots` and `Ahoy.exclude_method` with `exclude?` method.
652
-
653
- Skip this step if you do not use these options.
645
+ Ahoy now ships with better bot detection if you use Device Detector. This should be more accurate but can significantly reduce the number of visits recorded. For existing installs, it’s opt-in to start. To use it, add to `config/initializers/ahoy.rb`:
654
646
 
655
647
  ```ruby
656
- class Ahoy::Store < Ahoy::Stores::ActiveRecordTokenStore
657
- def user
658
- # logic from Ahoy.user_method goes here
659
- controller.true_user
660
- end
661
-
662
- def exclude?
663
- # logic from Ahoy.track_bots and Ahoy.exclude_method goes here
664
- bot? || request.ip == "192.168.1.1"
665
- end
666
- end
648
+ Ahoy.bot_detection_version = 2
667
649
  ```
668
650
 
669
- You made it! Now, take advantage of Ahoy’s awesome new features, like easy customization and exception reporting.
670
-
671
- ### 0.3.0
651
+ ### 2.1
672
652
 
673
- Starting with `0.3.0`, visit and visitor tokens are now UUIDs.
653
+ Ahoy recommends [Device Detector](https://github.com/podigee/device_detector) for user agent parsing and makes it the default for new installations. To switch, add to `config/initializers/ahoy.rb`:
674
654
 
675
- ### 0.1.6
655
+ ```ruby
656
+ Ahoy.user_agent_parser = :device_detector
657
+ ```
676
658
 
677
- In `0.1.6`, a big improvement was made to `browser` and `os`. Update existing visits with:
659
+ Backfill existing records with:
678
660
 
679
661
  ```ruby
680
- Visit.find_each do |visit|
681
- visit.set_technology
682
- visit.save! if visit.changed?
662
+ Ahoy::Visit.find_each do |visit|
663
+ client = DeviceDetector.new(visit.user_agent)
664
+ device_type =
665
+ case client.device_type
666
+ when "smartphone"
667
+ "Mobile"
668
+ when "tv"
669
+ "TV"
670
+ else
671
+ client.device_type.try(:titleize)
672
+ end
673
+
674
+ visit.browser = client.name
675
+ visit.os = client.os_name
676
+ visit.device_type = device_type
677
+ visit.save(validate: false) if visit.changed?
683
678
  end
684
679
  ```
685
680
 
686
- ## TODO
687
-
688
- - real-time dashboard of visits and events
689
- - more events for append only stores
690
- - turn off modules
691
-
692
- ## No Ruby?
681
+ ### 2.0
693
682
 
694
- Check out [Ahoy.js](https://github.com/ankane/ahoy.js).
683
+ See the [upgrade guide](docs/Ahoy-2-Upgrade.md)
695
684
 
696
685
  ## History
697
686