ahoy_matey 1.6.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE.md +7 -0
  3. data/CHANGELOG.md +22 -0
  4. data/CONTRIBUTING.md +40 -0
  5. data/LICENSE.txt +1 -1
  6. data/README.md +210 -489
  7. data/Rakefile +1 -0
  8. data/ahoy_matey.gemspec +6 -8
  9. data/app/controllers/ahoy/base_controller.rb +2 -6
  10. data/app/controllers/ahoy/events_controller.rb +7 -1
  11. data/app/controllers/ahoy/visits_controller.rb +7 -1
  12. data/app/jobs/ahoy/geocode_job.rb +10 -0
  13. data/app/jobs/ahoy/geocode_v2_job.rb +29 -0
  14. data/config/routes.rb +1 -1
  15. data/docs/Ahoy-2-Upgrade.md +147 -0
  16. data/docs/Data-Store-Examples.md +240 -0
  17. data/lib/ahoy.rb +30 -88
  18. data/lib/ahoy/base_store.rb +72 -0
  19. data/lib/ahoy/controller.rb +4 -10
  20. data/lib/ahoy/database_store.rb +72 -0
  21. data/lib/ahoy/engine.rb +5 -7
  22. data/lib/ahoy/model.rb +4 -26
  23. data/lib/ahoy/{properties.rb → query_methods.rb} +18 -4
  24. data/lib/ahoy/tracker.rb +60 -38
  25. data/lib/ahoy/version.rb +1 -1
  26. data/lib/ahoy/visit_properties.rb +65 -39
  27. data/lib/generators/ahoy/activerecord_generator.rb +58 -0
  28. data/lib/generators/ahoy/base_generator.rb +13 -0
  29. data/lib/generators/ahoy/install_generator.rb +44 -0
  30. data/lib/generators/ahoy/mongoid_generator.rb +20 -0
  31. data/lib/generators/ahoy/templates/active_record_event_model.rb +10 -0
  32. data/lib/generators/ahoy/{stores/templates/active_record_visits_migration.rb → templates/active_record_migration.rb} +19 -21
  33. data/lib/generators/ahoy/templates/active_record_visit_model.rb +6 -0
  34. data/lib/generators/ahoy/templates/base_store_initializer.rb +17 -0
  35. data/lib/generators/ahoy/templates/database_store_initializer.rb +5 -0
  36. data/lib/generators/ahoy/{stores/templates → templates}/mongoid_event_model.rb +4 -2
  37. data/lib/generators/ahoy/{stores/templates → templates}/mongoid_visit_model.rb +7 -3
  38. data/test/query_methods/mongoid_test.rb +23 -0
  39. data/test/{properties → query_methods}/mysql_json_test.rb +1 -1
  40. data/test/{properties → query_methods}/mysql_text_test.rb +1 -1
  41. data/test/{properties → query_methods}/postgresql_hstore_test.rb +1 -1
  42. data/test/{properties → query_methods}/postgresql_json_test.rb +1 -1
  43. data/test/{properties → query_methods}/postgresql_jsonb_test.rb +1 -1
  44. data/test/{properties → query_methods}/postgresql_text_test.rb +1 -1
  45. data/test/test_helper.rb +4 -3
  46. data/vendor/assets/javascripts/ahoy.js +551 -325
  47. metadata +67 -112
  48. data/lib/ahoy/deckhands/location_deckhand.rb +0 -49
  49. data/lib/ahoy/deckhands/request_deckhand.rb +0 -52
  50. data/lib/ahoy/deckhands/technology_deckhand.rb +0 -47
  51. data/lib/ahoy/deckhands/traffic_source_deckhand.rb +0 -22
  52. data/lib/ahoy/deckhands/utm_parameter_deckhand.rb +0 -23
  53. data/lib/ahoy/geocode_job.rb +0 -13
  54. data/lib/ahoy/logger_silencer.rb +0 -75
  55. data/lib/ahoy/stores/active_record_store.rb +0 -61
  56. data/lib/ahoy/stores/active_record_token_store.rb +0 -114
  57. data/lib/ahoy/stores/base_store.rb +0 -88
  58. data/lib/ahoy/stores/bunny_store.rb +0 -33
  59. data/lib/ahoy/stores/fluentd_store.rb +0 -17
  60. data/lib/ahoy/stores/kafka_store.rb +0 -42
  61. data/lib/ahoy/stores/kinesis_firehose_store.rb +0 -42
  62. data/lib/ahoy/stores/log_store.rb +0 -53
  63. data/lib/ahoy/stores/mongoid_store.rb +0 -63
  64. data/lib/ahoy/stores/nats_store.rb +0 -34
  65. data/lib/ahoy/stores/nsq_store.rb +0 -36
  66. data/lib/ahoy/subscribers/active_record.rb +0 -19
  67. data/lib/ahoy/throttle.rb +0 -17
  68. data/lib/generators/ahoy/stores/active_record_events_generator.rb +0 -59
  69. data/lib/generators/ahoy/stores/active_record_generator.rb +0 -16
  70. data/lib/generators/ahoy/stores/active_record_visits_generator.rb +0 -49
  71. data/lib/generators/ahoy/stores/bunny_generator.rb +0 -15
  72. data/lib/generators/ahoy/stores/custom_generator.rb +0 -15
  73. data/lib/generators/ahoy/stores/fluentd_generator.rb +0 -15
  74. data/lib/generators/ahoy/stores/kafka_generator.rb +0 -15
  75. data/lib/generators/ahoy/stores/kinesis_firehose_generator.rb +0 -15
  76. data/lib/generators/ahoy/stores/log_generator.rb +0 -15
  77. data/lib/generators/ahoy/stores/mongoid_events_generator.rb +0 -19
  78. data/lib/generators/ahoy/stores/mongoid_generator.rb +0 -14
  79. data/lib/generators/ahoy/stores/mongoid_visits_generator.rb +0 -27
  80. data/lib/generators/ahoy/stores/nats_generator.rb +0 -15
  81. data/lib/generators/ahoy/stores/nsq_generator.rb +0 -15
  82. data/lib/generators/ahoy/stores/templates/active_record_event_model.rb +0 -12
  83. data/lib/generators/ahoy/stores/templates/active_record_events_migration.rb +0 -20
  84. data/lib/generators/ahoy/stores/templates/active_record_initializer.rb +0 -3
  85. data/lib/generators/ahoy/stores/templates/active_record_visit_model.rb +0 -4
  86. data/lib/generators/ahoy/stores/templates/bunny_initializer.rb +0 -9
  87. data/lib/generators/ahoy/stores/templates/custom_initializer.rb +0 -10
  88. data/lib/generators/ahoy/stores/templates/fluentd_initializer.rb +0 -3
  89. data/lib/generators/ahoy/stores/templates/kafka_initializer.rb +0 -9
  90. data/lib/generators/ahoy/stores/templates/kinesis_firehose_initializer.rb +0 -17
  91. data/lib/generators/ahoy/stores/templates/log_initializer.rb +0 -3
  92. data/lib/generators/ahoy/stores/templates/mongoid_initializer.rb +0 -3
  93. data/lib/generators/ahoy/stores/templates/nats_initializer.rb +0 -9
  94. data/lib/generators/ahoy/stores/templates/nsq_initializer.rb +0 -9
  95. data/test/visit_properties_test.rb +0 -44
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cb076e85628b3c486e6d80013bf0729f8d3ab142
4
- data.tar.gz: 3413580422d7567e36a2acb5bf678637fdadf189
3
+ metadata.gz: 47cc04adfac18b2f14547b376a3465f601d59307
4
+ data.tar.gz: 6e1638296155ceab46f9d105e52d7422259ebc63
5
5
  SHA512:
6
- metadata.gz: ff07d818af43a63fa773cd891f08c2be04359c9c095e25bdd12e5c94e23b6164637573f82b4c7ebd265de918a7134d495f949f09be956492eb094fabcf648154
7
- data.tar.gz: 87347e995d4145626112aa2ea8277ef3c45432c7f6f89435af8ed068dd85c5849df58740d3ffa5f6da5c583917a1894ac9bccd8c97cb142e641a7d0f81744a2a
6
+ metadata.gz: 1f0a217bd790fff2b55e1e6395ea940079b2a457efc1328c61a054d6a3835f0d6fcb9c154eae7f3496f8a79559282cc3d9b1eefb2db4ce651d74d6c14dce536a
7
+ data.tar.gz: 6789de07f914f49b05f72b4b5517935571368be2e43987421e170dd8da838d78a1fa48e1d878424a9800f53ea779b5dcd8b3523432977f7aec4a29e296941a41
@@ -0,0 +1,7 @@
1
+ Hi,
2
+
3
+ Before creating an issue, please check out the Contributing Guide:
4
+
5
+ https://github.com/ankane/ahoy/blob/master/CONTRIBUTING.md
6
+
7
+ Thanks!
data/CHANGELOG.md CHANGED
@@ -1,3 +1,25 @@
1
+ ## 2.0.0
2
+
3
+ - Removed dependency on jQuery
4
+ - Use `navigator.sendBeacon` by default in supported browsers
5
+ - Added `geocode` event
6
+ - Added `where_event` method for querying events
7
+ - Added support for `visitable` and `where_props` to Mongoid
8
+ - Added `preserve_callbacks` option
9
+ - Use `json` for MySQL by default
10
+ - Fixed log silencing
11
+
12
+ Breaking changes
13
+
14
+ - Simpler interface for data stores
15
+ - Renamed `track_visits_immediately` to `server_side_visits` and enabled by default
16
+ - Renamed `mount` option to `api` and disabled by default
17
+ - Enabled `protect_from_forgery` by default
18
+ - Removed deprecated options
19
+ - Removed throttling
20
+ - Removed most built-in stores
21
+ - Removed support for Rails < 4.2
22
+
1
23
  ## 1.6.1
2
24
 
3
25
  - Added `gin` index on properties for events
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,40 @@
1
+ # Contributing
2
+
3
+ First, thanks for wanting to contribute. You’re awesome! :heart:
4
+
5
+ ## Questions
6
+
7
+ Use [Stack Overflow](https://stackoverflow.com/) with the tag `ahoy`.
8
+
9
+ ## Feature Requests
10
+
11
+ Create an issue. Start the title with `[Idea]`.
12
+
13
+ ## Issues
14
+
15
+ Think you’ve discovered an issue?
16
+
17
+ 1. Search existing issues to see if it’s been reported.
18
+ 2. Try the `master` branch to make sure it hasn’t been fixed.
19
+
20
+ ```rb
21
+ gem "ahoy_matey", github: "ankane/ahoy"
22
+ ```
23
+
24
+ If the above steps don’t help, create an issue. Include:
25
+
26
+ - Detailed steps to reproduce
27
+ - Complete backtraces for exceptions
28
+
29
+ ## Pull Requests
30
+
31
+ Fork the project and create a pull request. A few tips:
32
+
33
+ - Keep changes to a minimum. If you have multiple features or fixes, submit multiple pull requests.
34
+ - Follow the existing style. The code should read like it’s written by a single person.
35
+
36
+ Feel free to open an issue to get feedback on your idea before spending too much time on it.
37
+
38
+ ---
39
+
40
+ 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-2018 Andrew Kane
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -1,12 +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
4
 
5
- :tangerine: Battle-tested at [Instacart](https://www.instacart.com/opensource)
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
+ **Ahoy 2.0 was recently released!** See [how to upgrade](docs/Ahoy-2-Upgrade.md)
6
8
 
7
- :postbox: To track emails, check out [Ahoy Email](https://github.com/ankane/ahoy_email).
9
+ :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)
8
10
 
9
- :maple_leaf: For A/B testing, check out [Field Test](https://github.com/ankane/field_test).
11
+ :tangerine: Battle-tested at [Instacart](https://www.instacart.com/opensource)
10
12
 
11
13
  ## Installation
12
14
 
@@ -16,177 +18,73 @@ Add this line to your application’s Gemfile:
16
18
  gem 'ahoy_matey'
17
19
  ```
18
20
 
19
- And add the javascript file in `app/assets/javascripts/application.js` after jQuery.
20
-
21
- ```javascript
22
- //= require jquery
23
- //= require ahoy
24
- ```
25
-
26
- ## Choose a Data Store
27
-
28
- 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.
29
-
30
- - [PostgreSQL, MySQL, or SQLite](#postgresql-mysql-or-sqlite)
31
- - [MongoDB](#mongodb)
32
- - [Kafka](#kafka), [Fluentd](#fluentd), [RabbitMQ](#rabbitmq), [NATS](#nats), [NSQ](#nsq), or [Amazon Kinesis Firehose](#amazon-kinesis-firehose)
33
- - [Logs](#logs)
34
- - [Custom](#custom)
35
-
36
- ### PostgreSQL, MySQL, or SQLite
37
-
38
- Run:
39
-
40
- ```sh
41
- rails generate ahoy:stores:active_record
42
- rake db:migrate
43
- ```
44
-
45
- ### MongoDB
46
-
47
- Run:
48
-
49
- ```sh
50
- rails generate ahoy:stores:mongoid
51
- ```
52
-
53
- ### Kafka
54
-
55
- Add [ruby-kafka](https://github.com/zendesk/ruby-kafka) to your Gemfile.
56
-
57
- ```ruby
58
- gem 'ruby-kafka'
59
- ```
60
-
61
21
  And run:
62
22
 
63
23
  ```sh
64
- rails generate ahoy:stores:kafka
24
+ bundle install
25
+ rails generate ahoy:install
26
+ rails db:migrate
65
27
  ```
66
28
 
67
- Use `ENV["KAFKA_URL"]` to configure.
29
+ Restart your web server, open a page in your browser, and a visit will be created :tada:
68
30
 
69
- ### Fluentd
70
-
71
- Add [fluent-logger](https://github.com/fluent/fluent-logger-ruby) to your Gemfile.
31
+ Track your first event from a controller with:
72
32
 
73
33
  ```ruby
74
- gem 'fluent-logger'
75
- ```
76
-
77
- And run:
78
-
79
- ```sh
80
- rails generate ahoy:stores:fluentd
34
+ ahoy.track "My first event", {language: "Ruby"}
81
35
  ```
82
36
 
83
- Use `ENV["FLUENTD_HOST"]` and `ENV["FLUENTD_PORT"]` to configure.
84
-
85
- ### RabbitMQ
37
+ ### JavaScript & Native Apps
86
38
 
87
- Add [bunny](https://github.com/ruby-amqp/bunny) to your Gemfile.
39
+ First, enable the API in `config/initializers/ahoy.rb`:
88
40
 
89
41
  ```ruby
90
- gem 'bunny'
91
- ```
92
-
93
- And run:
94
-
95
- ```sh
96
- rails generate ahoy:stores:bunny
42
+ Ahoy.api = true
97
43
  ```
98
44
 
99
- Use `ENV["RABBITMQ_URL"]` to configure.
100
-
101
- ### NATS
45
+ And restart your web server.
102
46
 
103
- Add [nats-pure](https://github.com/nats-io/pure-ruby-nats) to your Gemfile.
47
+ For JavaScript, add to `app/assets/javascripts/application.js`:
104
48
 
105
- ```ruby
106
- gem 'nats-pure'
49
+ ```javascript
50
+ //= require ahoy
107
51
  ```
108
52
 
109
- And run:
53
+ And track an event with:
110
54
 
111
- ```sh
112
- rails generate ahoy:stores:nats
55
+ ```javascript
56
+ ahoy.track("My second event", {language: "JavaScript"});
113
57
  ```
114
58
 
115
- Use `ENV["NATS_URL"]` to configure.
116
-
117
- ### NSQ
118
-
119
- Add [nsq-ruby](https://github.com/wistia/nsq-ruby) to your Gemfile.
59
+ For native apps, see the [API spec](#api-spec).
120
60
 
121
- ```ruby
122
- gem 'nsq-ruby'
123
- ```
61
+ ## How It Works
124
62
 
125
- And run:
63
+ ### Visits
126
64
 
127
- ```sh
128
- rails generate ahoy:stores:nsq
129
- ```
65
+ When someone visits your website, Ahoy creates a visit with lots of useful information.
130
66
 
131
- Use `ENV["NSQ_URL"]` to configure.
67
+ - **traffic source** - referrer, referring domain, landing page, search keyword
68
+ - **location** - country, region, and city
69
+ - **technology** - browser, OS, and device type
70
+ - **utm parameters** - source, medium, term, content, campaign
132
71
 
133
- ### Amazon Kinesis Firehose
72
+ Use the `current_visit` method to access it.
134
73
 
135
- Add [aws-sdk](https://github.com/aws/aws-sdk-ruby) to your Gemfile.
74
+ Prevent certain Rails actions from creating visits with:
136
75
 
137
76
  ```ruby
138
- gem 'aws-sdk', '>= 2.0.0'
139
- ```
140
-
141
- And run:
142
-
143
- ```sh
144
- rails generate ahoy:stores:kinesis_firehose
145
- ```
146
-
147
- Configure delivery streams and credentials in the initializer.
148
-
149
- ### Logs
150
-
151
- ```sh
152
- rails generate ahoy:stores:log
77
+ skip_before_action :track_ahoy_visit
153
78
  ```
154
79
 
155
- This logs visits to `log/visits.log` and events to `log/events.log`.
156
-
157
- ### Custom
80
+ This is typically useful for APIs.
158
81
 
159
- ```sh
160
- rails generate ahoy:stores:custom
161
- ```
162
-
163
- This creates a class for you to fill out.
82
+ You can also defer visit tracking to JavaScript (Ahoy 1.0 behavior) with:
164
83
 
165
84
  ```ruby
166
- class Ahoy::Store < Ahoy::Stores::BaseStore
167
- def track_visit(options)
168
- end
169
-
170
- def track_event(name, properties, options)
171
- end
172
- end
85
+ Ahoy.server_side_visits = false
173
86
  ```
174
87
 
175
- See the [ActiveRecordTokenStore](https://github.com/ankane/ahoy/blob/master/lib/ahoy/stores/active_record_token_store.rb) for an example.
176
-
177
- ## How It Works
178
-
179
- ### Visits
180
-
181
- When someone visits your website, Ahoy creates a visit with lots of useful information.
182
-
183
- - **traffic source** - referrer, referring domain, landing page, search keyword
184
- - **location** - country, region, and city
185
- - **technology** - browser, OS, and device type
186
- - **utm parameters** - source, medium, term, content, campaign
187
-
188
- Use the `current_visit` method to access it.
189
-
190
88
  ### Events
191
89
 
192
90
  Each event has a `name` and `properties`.
@@ -213,239 +111,193 @@ See [Ahoy.js](https://github.com/ankane/ahoy.js) for a complete list of features
213
111
  ahoy.track "Viewed book", title: "Hot, Flat, and Crowded"
214
112
  ```
215
113
 
216
- #### Native Apps
217
-
218
- See the [HTTP spec](#native-apps-1) until libraries are built.
219
-
220
- ### Users
221
-
222
- Ahoy automatically attaches the `current_user` to the visit.
223
-
224
- With [Devise](https://github.com/plataformatec/devise), it will attach the user even if he or she signs in after the visit starts.
225
-
226
- With other authentication frameworks, add this to the end of your sign in method:
114
+ or track actions automatically with:
227
115
 
228
116
  ```ruby
229
- ahoy.authenticate(user)
230
- ```
231
-
232
- ## Customize the Store
117
+ class ApplicationController < ActionController::Base
118
+ after_action :track_action
233
119
 
234
- Stores are built to be highly customizable.
120
+ protected
235
121
 
236
- ```ruby
237
- class Ahoy::Store < Ahoy::Stores::ActiveRecordTokenStore
238
- # add methods here
122
+ def track_action
123
+ ahoy.track "Viewed action", request.path_parameters
124
+ end
239
125
  end
240
126
  ```
241
127
 
242
- ### Exclude Bots and More
128
+ #### Native Apps
243
129
 
244
- Exclude visits and events from being tracked with:
130
+ See the [API spec](#api-spec).
245
131
 
246
- ```ruby
247
- class Ahoy::Store < Ahoy::Stores::ActiveRecordTokenStore
248
- def exclude?
249
- bot? || request.ip == "192.168.1.1"
250
- end
251
- end
252
- ```
132
+ ### Associated Models
253
133
 
254
- Bots are excluded by default.
134
+ Say we want to associate orders with visits. Ahoy can do this automatically.
255
135
 
256
- ### Track Additional Values
136
+ First, generate a migration and add a `visit_id` column (not needed for Mongoid).
257
137
 
258
138
  ```ruby
259
- class Ahoy::Store < Ahoy::Stores::ActiveRecordTokenStore
260
- def track_visit(options)
261
- super do |visit|
262
- visit.gclid = visit_properties.landing_params["gclid"]
263
- end
264
- end
265
-
266
- def track_event(name, properties, options)
267
- super do |event|
268
- event.ip = request.ip
269
- end
139
+ class AddVisitIdToOrders < ActiveRecord::Migration[5.1]
140
+ def change
141
+ add_column :orders, :visit_id, :bigint
270
142
  end
271
143
  end
272
144
  ```
273
145
 
274
- Some methods you can use are `request`, `controller`, `visit_properties`, and `ahoy`.
275
-
276
- ### Customize User
277
-
278
- If you use a method other than `current_user`, set it here:
146
+ Then, add `visitable` to the model.
279
147
 
280
148
  ```ruby
281
- class Ahoy::Store < Ahoy::Stores::ActiveRecordTokenStore
282
- def user
283
- controller.true_user
284
- end
149
+ class Order < ApplicationRecord
150
+ visitable
285
151
  end
286
152
  ```
287
153
 
288
- ### Report Exceptions
289
-
290
- 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.
154
+ When a visitor places an order, the `visit_id` column is automatically set :tada:
291
155
 
292
- To customize this, use:
156
+ See where orders are coming from with simple joins:
293
157
 
294
158
  ```ruby
295
- Safely.report_exception_method = -> (e) { Rollbar.error(e) }
159
+ Order.joins(:visit).group("referring_domain").count
160
+ Order.joins(:visit).group("city").count
161
+ Order.joins(:visit).group("device_type").count
296
162
  ```
297
163
 
298
- ### Use Different Models
299
-
300
- For ActiveRecord and Mongoid stores
164
+ Customize the column and class name with:
301
165
 
302
166
  ```ruby
303
- class Ahoy::Store < Ahoy::Stores::ActiveRecordTokenStore
304
- def visit_model
305
- CustomVisit
306
- end
307
-
308
- def event_model
309
- CustomEvent
310
- end
311
- end
167
+ visitable :sign_up_visit, class_name: "Visit"
312
168
  ```
313
169
 
314
- ## More Features
170
+ ### Users
315
171
 
316
- ### Automatic Tracking
172
+ 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.
317
173
 
318
- Page views
174
+ With other authentication frameworks, add this to the end of your sign in method:
319
175
 
320
- ```javascript
321
- ahoy.trackView();
176
+ ```ruby
177
+ ahoy.authenticate(user)
322
178
  ```
323
179
 
324
- Clicks
180
+ To see the visits for a given user, create an association:
325
181
 
326
- ```javascript
327
- ahoy.trackClicks();
182
+ ```ruby
183
+ class User < ApplicationRecord
184
+ has_many :visits, class_name: "Ahoy::Visit"
185
+ end
328
186
  ```
329
187
 
330
- Rails actions
188
+ And use:
331
189
 
332
190
  ```ruby
333
- class ApplicationController < ActionController::Base
334
- after_action :track_action
335
-
336
- protected
337
-
338
- def track_action
339
- ahoy.track "Viewed #{controller_path}##{action_name}", params: request.path_parameters
340
- end
341
- end
191
+ User.find(123).visits
342
192
  ```
343
193
 
344
- ### Multiple Subdomains
194
+ #### Custom User Method
345
195
 
346
- To track visits across multiple subdomains, use:
196
+ Use a method besides `current_user`
347
197
 
348
198
  ```ruby
349
- Ahoy.cookie_domain = :all
199
+ Ahoy.user_method = :true_user
350
200
  ```
351
201
 
352
- ### Visit Duration
353
-
354
- By default, a new visit is created after 4 hours of inactivity.
355
-
356
- Change this with:
202
+ or use a proc
357
203
 
358
204
  ```ruby
359
- Ahoy.visit_duration = 30.minutes
205
+ Ahoy.user_method = ->(controller) { controller.true_user }
360
206
  ```
361
207
 
362
- ### ActiveRecord
363
-
364
- Let’s associate orders with visits.
208
+ ### Doorkeeper
365
209
 
366
- First, generate a migration and add a `visit_id` column.
210
+ To attach the user with [Doorkeeper](https://github.com/doorkeeper-gem/doorkeeper), be sure you have a `current_resource_owner` method in `ApplicationController`.
367
211
 
368
212
  ```ruby
369
- class AddVisitIdToOrders < ActiveRecord::Migration
370
- def change
371
- add_column :orders, :visit_id, :integer
213
+ class ApplicationController < ActionController::Base
214
+ private
215
+
216
+ def current_resource_owner
217
+ User.find(doorkeeper_token.resource_owner_id) if doorkeeper_token
372
218
  end
373
219
  end
374
220
  ```
375
221
 
376
- **Note**: Use the `uuid` column type if the `id` column on `visits` is a `uuid`.
222
+ ### Exclusions
377
223
 
378
- Then, add `visitable` to the model.
224
+ Bots are excluded from tracking by default. To enable, use:
379
225
 
380
226
  ```ruby
381
- class Order < ActiveRecord::Base
382
- visitable
227
+ Ahoy.track_bots = true
228
+ ```
229
+
230
+ Add your own rules with:
231
+
232
+ ```ruby
233
+ Ahoy.exclude_method = lambda do |controller, request|
234
+ request.ip == "192.168.1.1"
383
235
  end
384
236
  ```
385
237
 
386
- When a visitor places an order, the `visit_id` column is automatically set. :tada:
238
+ ### Visit Duration
387
239
 
388
- Customize the column and class name with:
240
+ By default, a new visit is created after 4 hours of inactivity. Change this with:
389
241
 
390
242
  ```ruby
391
- visitable :sign_up_visit, class_name: "Visit"
243
+ Ahoy.visit_duration = 30.minutes
392
244
  ```
393
245
 
394
- ### Doorkeeper
246
+ ### Multiple Subdomains
395
247
 
396
- To attach the user with [Doorkeeper](https://github.com/doorkeeper-gem/doorkeeper), be sure you have a `current_resource_owner` method in `ApplicationController`.
248
+ To track visits across multiple subdomains, use:
397
249
 
398
250
  ```ruby
399
- class ApplicationController < ActionController::Base
400
- private
401
-
402
- def current_resource_owner
403
- User.find(doorkeeper_token.resource_owner_id) if doorkeeper_token
404
- end
405
- end
251
+ Ahoy.cookie_domain = :all
406
252
  ```
407
253
 
408
254
  ### Geocoding
409
255
 
410
- By default, geocoding is performed inline. For performance, move it to the background with:
256
+ Disable geocoding with:
411
257
 
412
258
  ```ruby
413
- Ahoy.geocode = :async
259
+ Ahoy.geocode = false
414
260
  ```
415
261
 
416
- For Rails 4.0 and 4.1, you’ll need to add [activejob_backport](https://github.com/ankane/activejob_backport).
417
-
418
- To change the queue name (`ahoy` by default), use:
262
+ Change the job queue with:
419
263
 
420
264
  ```ruby
421
265
  Ahoy.job_queue = :low_priority
422
266
  ```
423
267
 
424
- Or disable geocoding with:
268
+ ### Token Generation
269
+
270
+ 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).
425
271
 
426
272
  ```ruby
427
- Ahoy.geocode = false
273
+ Ahoy.token_generator = -> { Druuid.gen }
428
274
  ```
429
275
 
430
- ### Track Visits Immediately
276
+ ### Throttling
431
277
 
432
- 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:
278
+ You can use [Rack::Attack](https://github.com/kickstarter/rack-attack) to throttle requests to the API.
433
279
 
434
280
  ```ruby
435
- Ahoy.track_visits_immediately = true
281
+ class Rack::Attack
282
+ throttle("ahoy/ip", limit: 20, period: 1.minute) do |req|
283
+ if req.path.start_with?("/ahoy/")
284
+ req.ip
285
+ end
286
+ end
287
+ end
436
288
  ```
437
289
 
438
- **Note:** It’s highly recommended to perform geocoding in the background with this option.
290
+ ### Exceptions
439
291
 
440
- You can exclude API endpoints and other actions with:
292
+ 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:
441
293
 
442
294
  ```ruby
443
- skip_before_action :track_ahoy_visit
295
+ Safely.report_exception_method = ->(e) { Rollbar.error(e) }
444
296
  ```
445
297
 
446
298
  ## Development
447
299
 
448
- Ahoy is built with developers in mind. You can run the following code in your browser’s console.
300
+ Ahoy is built with developers in mind. You can run the following code in your browser’s console.
449
301
 
450
302
  Force a new visit
451
303
 
@@ -465,293 +317,162 @@ Turn off logging
465
317
  ahoy.debug(false);
466
318
  ```
467
319
 
468
- Debug endpoint requests in Ruby
320
+ Debug API requests in Ruby
469
321
 
470
322
  ```ruby
471
323
  Ahoy.quiet = false
472
324
  ```
473
325
 
474
- ## Explore the Data
475
-
476
- How you explore the data depends on the data store used.
477
-
478
- For SQL databases, you can use [Blazer](https://github.com/ankane/blazer) to easily generate charts and dashboards.
326
+ ## Data Stores
479
327
 
480
- With ActiveRecord, you can do:
328
+ 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`:
481
329
 
482
330
  ```ruby
483
- Visit.group(:search_keyword).count
484
- Visit.group(:country).count
485
- Visit.group(:referring_domain).count
486
- ```
487
-
488
- [Chartkick](http://chartkick.com/) and [Groupdate](https://github.com/ankane/groupdate) make it easy to visualize the data.
489
-
490
- ```erb
491
- <%= line_chart Visit.group_by_day(:started_at).count %>
492
- ```
493
-
494
- See where orders are coming from with simple joins:
495
-
496
- ```ruby
497
- Order.joins(:visit).group("referring_domain").count
498
- Order.joins(:visit).group("city").count
499
- Order.joins(:visit).group("device_type").count
500
- ```
501
-
502
- To see the visits for a given user, create an association:
503
-
504
- ```ruby
505
- class User < ActiveRecord::Base
506
- has_many :visits
331
+ class Ahoy::Store < Ahoy::DatabaseStore
507
332
  end
508
333
  ```
509
334
 
510
- And use:
511
-
512
- ```ruby
513
- user = User.first
514
- user.visits
515
- ```
516
-
517
- ### Create Funnels
335
+ There are four events data stores can subscribe to:
518
336
 
519
337
  ```ruby
520
- viewed_store_ids = Ahoy::Event.where(name: "Viewed store").uniq.pluck(:user_id)
521
- added_item_ids = Ahoy::Event.where(user_id: viewed_store_ids, name: "Added item to cart").uniq.pluck(:user_id)
522
- viewed_checkout_ids = Ahoy::Event.where(user_id: added_item_ids, name: "Viewed checkout").uniq.pluck(:user_id)
523
- ```
524
-
525
- The same approach also works with visitor tokens.
526
-
527
- ### Querying Properties
528
-
529
- With ActiveRecord, use:
338
+ class Ahoy::Store < Ahoy::BaseStore
339
+ def track_visit(data)
340
+ # new visit
341
+ end
530
342
 
531
- ```ruby
532
- Ahoy::Event.where(name: "Viewed product").where_properties(product_id: 123).count
533
- ```
343
+ def track_event(data)
344
+ # new event
345
+ end
534
346
 
535
- **Note:** If you get a `NoMethodError`, upgrade Ahoy and add `include Ahoy::Properties` to the Ahoy::Event class:
347
+ def geocode(data)
348
+ # visit geocoded
349
+ end
536
350
 
537
- ```ruby
538
- module Ahoy
539
- class Event < ActiveRecord::Base
540
- include Ahoy::Properties
541
- ...
351
+ def authenticate(data)
352
+ # user authenticates
542
353
  end
543
354
  end
544
355
  ```
545
356
 
546
- ### Throttling
357
+ 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.
547
358
 
548
- By default, Ahoy uses [rack-attack](https://github.com/kickstarter/rack-attack) to throttle requests to Ahoy endpoints. Turn this off with:
359
+ ### Track Additional Data
549
360
 
550
361
  ```ruby
551
- Ahoy.throttle = false
552
- ```
553
-
554
- The default limit is 20 requests per minute. This can be overridden with:
555
-
556
- ```ruby
557
- # limit number of requests to 100 requests every 5 minutes
558
- Ahoy.throttle_limit = 100
559
- Ahoy.throttle_period = 5.minutes
560
- ```
561
-
562
- ## Tutorials
563
-
564
- - [Tracking Metrics with Ahoy and Blazer](https://gorails.com/episodes/internal-metrics-with-ahoy-and-blazer)
565
-
566
- ## Native Apps
567
-
568
- ### Visits
569
-
570
- When a user launches the app, create a visit.
571
-
572
- Generate a `visit_token` and `visitor_token` as [UUIDs](http://en.wikipedia.org/wiki/Universally_unique_identifier).
573
-
574
- Send these values in the `Ahoy-Visit` and `Ahoy-Visitor` headers with all requests.
575
-
576
- Send a `POST` request to `/ahoy/visits` with:
577
-
578
- - platform - `iOS`, `Android`, etc.
579
- - app_version - `1.0.0`
580
- - os_version - `7.0.6`
581
-
582
- After 4 hours of inactivity, create another visit and use the updated visit id.
583
-
584
- ### Events
585
-
586
- Send a `POST` request as `Content-Type: application/json` to `/ahoy/events` with:
587
-
588
- - id - `5aea7b70-182d-4070-b062-b0a09699ad5e` - UUID
589
- - name - `Viewed item`
590
- - properties - `{"item_id": 123}`
591
- - time - `2014-06-17T00:00:00-07:00` - [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601)
592
- - `Ahoy-Visit` and `Ahoy-Visitor` headers
593
- - user token (depends on your authentication framework)
594
-
595
- Use an array to pass multiple events at once.
596
-
597
- ## Reference
598
-
599
- By default, Ahoy create endpoints at `/ahoy/visits` and `/ahoy/events`. To disable, use:
600
-
601
- ```ruby
602
- Ahoy.mount = false
603
- ```
604
-
605
- ## Upgrading
606
-
607
- ### 1.4.0
608
-
609
- There’s nothing mandatory 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.
610
-
611
- **Optional** Consider migrating `ahoy_events` table to have the following multi-column index as this *may* benefit
612
- query performance depending on your usage:
613
-
614
- ```ruby
615
- add_index :ahoy_events, [:name, :time]
362
+ class Ahoy::Store < Ahoy::DatabaseStore
363
+ def track_visit(data)
364
+ data[:accept_language] = request.headers["Accept-Language"]
365
+ super(data)
366
+ end
367
+ end
616
368
  ```
617
369
 
618
- ### json -> jsonb
370
+ Two useful methods you can use are `request` and `controller`.
619
371
 
620
- Create a migration to add a new `jsonb` column.
372
+ ### Use Different Models
621
373
 
622
374
  ```ruby
623
- rename_column :ahoy_events, :properties, :properties_json
624
- add_column :ahoy_events, :properties, :jsonb
625
- ```
626
-
627
- Restart your web server immediately afterwards, as Ahoy will rescue and report errors until then.
628
-
629
- Sync the new column.
375
+ class Ahoy::Store < Ahoy::DatabaseStore
376
+ def visit_model
377
+ MyVisit
378
+ end
630
379
 
631
- ```ruby
632
- Ahoy::Event.where(properties: nil).select(:id).find_in_batches do |events|
633
- Ahoy::Event.where(id: events.map(&:id)).update_all("properties = properties_json::jsonb")
380
+ def event_model
381
+ MyEvent
382
+ end
634
383
  end
635
384
  ```
636
385
 
637
- Then create a migration to drop the old column.
638
-
639
- ```ruby
640
- remove_column :ahoy_events, :properties_json
641
- ```
386
+ ## Explore the Data
642
387
 
643
- ### 1.0.0
388
+ [Blazer](https://github.com/ankane/blazer) is a great tool for exploring your data.
644
389
 
645
- Add the following code to the end of `config/initializers/ahoy.rb`.
390
+ With ActiveRecord, you can do:
646
391
 
647
392
  ```ruby
648
- class Ahoy::Store < Ahoy::Stores::ActiveRecordTokenStore
649
- uses_deprecated_subscribers
650
- end
393
+ Ahoy::Visit.group(:search_keyword).count
394
+ Ahoy::Visit.group(:country).count
395
+ Ahoy::Visit.group(:referring_domain).count
651
396
  ```
652
397
 
653
- If you use `Ahoy::Event` to track events, copy it into your project.
654
-
655
- ```ruby
656
- module Ahoy
657
- class Event < ActiveRecord::Base
658
- self.table_name = "ahoy_events"
659
-
660
- belongs_to :visit
661
- belongs_to :user, polymorphic: true
398
+ [Chartkick](http://chartkick.com/) and [Groupdate](https://github.com/ankane/groupdate) make it easy to visualize the data.
662
399
 
663
- serialize :properties, JSON
664
- end
665
- end
400
+ ```erb
401
+ <%= line_chart Ahoy::Visit.group_by_day(:started_at).count %>
666
402
  ```
667
403
 
668
- That’s it! To fix deprecations, keep reading.
404
+ ### Querying Events
669
405
 
670
- #### Visits
406
+ Ahoy provides two methods on the event model to make querying easier.
671
407
 
672
- Remove `ahoy_visit` from your visit model and replace it with:
408
+ To query on both name and properties, you can use:
673
409
 
674
410
  ```ruby
675
- class Visit < ActiveRecord::Base
676
- belongs_to :user, polymorphic: true
677
- end
411
+ Ahoy::Event.where_event("Viewed product", product_id: 123).count
678
412
  ```
679
413
 
680
- #### Subscribers
681
-
682
- Remove `uses_deprecated_subscribers` from `Ahoy::Store`.
683
-
684
- If you have a custom subscriber, copy the `track` method to `track_event` in `Ahoy::Store`.
414
+ Or just query properties with:
685
415
 
686
416
  ```ruby
687
- class Ahoy::Store < Ahoy::Stores::ActiveRecordTokenStore
688
- def track_event(name, properties, options)
689
- # code copied from the track method in your subscriber
690
- end
691
- end
417
+ Ahoy::Event.where_props(product_id: 123).count
692
418
  ```
693
419
 
694
- #### Authentication
420
+ ### Funnels
695
421
 
696
- Ahoy no longer tracks the `$authenticate` event automatically.
697
-
698
- To restore this behavior, use:
422
+ It’s easy to create funnels.
699
423
 
700
424
  ```ruby
701
- class Ahoy::Store < Ahoy::Stores::ActiveRecordTokenStore
702
- def authenticate(user)
703
- super
704
- ahoy.track "$authenticate"
705
- end
706
- end
425
+ viewed_store_ids = Ahoy::Event.where(name: "Viewed store").distinct.pluck(:user_id)
426
+ added_item_ids = Ahoy::Event.where(user_id: viewed_store_ids, name: "Added item to cart").distinct.pluck(:user_id)
427
+ viewed_checkout_ids = Ahoy::Event.where(user_id: added_item_ids, name: "Viewed checkout").distinct.pluck(:user_id)
707
428
  ```
708
429
 
709
- #### Global Options
710
-
711
- Replace the `Ahoy.user_method` with `user` method, and replace `Ahoy.track_bots` and `Ahoy.exclude_method` with `exclude?` method.
712
-
713
- Skip this step if you do not use these options.
714
-
715
- ```ruby
716
- class Ahoy::Store < Ahoy::Stores::ActiveRecordTokenStore
717
- def user
718
- # logic from Ahoy.user_method goes here
719
- controller.true_user
720
- end
430
+ The same approach also works with visitor tokens.
721
431
 
722
- def exclude?
723
- # logic from Ahoy.track_bots and Ahoy.exclude_method goes here
724
- bot? || request.ip == "192.168.1.1"
725
- end
726
- end
727
- ```
432
+ ## Tutorials
728
433
 
729
- You made it! Now, take advantage of Ahoy’s awesome new features, like easy customization and exception reporting.
434
+ - [Tracking Metrics with Ahoy and Blazer](https://gorails.com/episodes/internal-metrics-with-ahoy-and-blazer)
730
435
 
731
- ### 0.3.0
436
+ ## API Spec
732
437
 
733
- Starting with `0.3.0`, visit and visitor tokens are now UUIDs.
438
+ ### Visits
734
439
 
735
- ### 0.1.6
440
+ Generate visit and visitor tokens as [UUIDs](http://en.wikipedia.org/wiki/Universally_unique_identifier), and include these values in the `Ahoy-Visit` and `Ahoy-Visitor` headers with all requests.
736
441
 
737
- In `0.1.6`, a big improvement was made to `browser` and `os`. Update existing visits with:
442
+ Send a `POST` request to `/ahoy/visits` with `Content-Type: application/json` and a body like:
738
443
 
739
- ```ruby
740
- Visit.find_each do |visit|
741
- visit.set_technology
742
- visit.save! if visit.changed?
743
- end
444
+ ```json
445
+ {
446
+ "visit_token": "<visit-token>",
447
+ "visitor_token": "<visitor-token>",
448
+ "platform": "iOS",
449
+ "app_version": "1.0.0",
450
+ "os_version": "11.2.6"
451
+ }
744
452
  ```
745
453
 
746
- ## TODO
747
-
748
- - real-time dashboard of visits and events
749
- - more events for append only stores
750
- - turn off modules
454
+ After 4 hours of inactivity, create another visit (use the same visitor token).
751
455
 
752
- ## No Ruby?
456
+ ### Events
753
457
 
754
- Check out [Ahoy.js](https://github.com/ankane/ahoy.js).
458
+ Send a `POST` request to `/ahoy/events` with `Content-Type: application/json` and a body like:
459
+
460
+ ```json
461
+ {
462
+ "visit_token": "<visit-token>",
463
+ "visitor_token": "<visitor-token>",
464
+ "events": [
465
+ {
466
+ "id": "<optional-random-id>",
467
+ "name": "Viewed item",
468
+ "properties": {
469
+ "item_id": 123
470
+ },
471
+ "time": "2018-01-01T00:00:00-07:00"
472
+ }
473
+ ]
474
+ }
475
+ ```
755
476
 
756
477
  ## History
757
478