ahoy_matey 3.0.0 → 3.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1da649f5c85699714bff1777f3226b232f555e79ec2ece218280541bbb1264d5
4
- data.tar.gz: '0180556a4ad007b5d9df07faa327a6d53e9a2cfe1bf810cb78e9d24d6cdc060b'
3
+ metadata.gz: 17572400fdaab440040c753cf23084f2a47868e5c1ed12229e85fd1904e9c937
4
+ data.tar.gz: cc917af046906e308830fa1ffa9543db1bc1fe0f79488ccaddef090e6366433d
5
5
  SHA512:
6
- metadata.gz: e7cd93d376c86d34123c964fceb7f84ef33dc0513bfb66ac1e9da1b5654a7bed69041eb0d99477b50f87c77b5bf8e4c396e4dc8bf99b10db2699ef09fb11ecf8
7
- data.tar.gz: f11f02dbd35f36982d932d12a905d060770d9f01f4b0bdfe572002dd5a10feaa07b79b1d3eb0e2df3376f00c6fbe67bbcc29549498599bd9634e164ff04d4184
6
+ metadata.gz: 325caa06934a20bf7a58f573fff7359b7058cf63e3e7576c48faf1bd5d08ac2564810a28e5a775c3ac324170754db4de35d46f43c0b421f8c4977760af19d153
7
+ data.tar.gz: e7af674522e09e3fb472b52546f30d10b743e2b2814efd97162d05cb1d4f9b13615dce211cd83e291da16e370a86cf7d8d384b16ad60d8af07c8ced8fbd17f59
@@ -1,4 +1,28 @@
1
- ## 3.0.0
1
+ ## 3.0.5 (2020-09-09)
2
+
3
+ - Added `group_prop` method
4
+ - Use `datetime` type in migration
5
+
6
+ ## 3.0.4 (2020-06-07)
7
+
8
+ - Updated Ahoy.js to 0.3.6
9
+
10
+ ## 3.0.3 (2020-04-17)
11
+
12
+ - Updated Ahoy.js to 0.3.5
13
+
14
+ ## 3.0.2 (2020-04-03)
15
+
16
+ - Added `cookie_options`
17
+
18
+ ## 3.0.1 (2019-09-21)
19
+
20
+ - Made `Ahoy::Tracker` work outside of requests
21
+ - Fixed storage of `false` values with customized store
22
+ - Fixed error with `user_method` and `Rails::InfoController`
23
+ - Gracefully handle `ActionDispatch::RemoteIp::IpSpoofAttackError`
24
+
25
+ ## 3.0.0 (2019-05-29)
2
26
 
3
27
  - Made Device Detector the default user agent parser
4
28
  - Made v2 the default bot detection version
@@ -6,18 +30,18 @@
6
30
  - Removed search keyword detection (most search engines today prevent this)
7
31
  - Removed support for Rails < 5
8
32
 
9
- ## 2.2.1
33
+ ## 2.2.1 (2019-05-26)
10
34
 
11
35
  - Updated Ahoy.js to 0.3.4
12
36
  - Fixed v2 bot detection
13
37
  - Added latitude and longitude to installation
14
38
 
15
- ## 2.2.0
39
+ ## 2.2.0 (2019-01-04)
16
40
 
17
41
  - Added `amp_event` helper
18
42
  - Improved bot detection for Device Detector
19
43
 
20
- ## 2.1.0
44
+ ## 2.1.0 (2018-05-18)
21
45
 
22
46
  - Added option for IP masking
23
47
  - Added option to use anonymity sets instead of cookies
@@ -25,19 +49,19 @@
25
49
  - Fixed `visitable` for Rails 4.2
26
50
  - Removed `search_keyword` from new installs
27
51
 
28
- ## 2.0.2
52
+ ## 2.0.2 (2018-03-14)
29
53
 
30
54
  - Fixed error on duplicate records
31
55
  - Fixed message when visit not found for geocoding
32
56
  - Better compatibility with GeoLite2
33
57
  - Better browser compatibility for Ahoy.js
34
58
 
35
- ## 2.0.1
59
+ ## 2.0.1 (2018-02-26)
36
60
 
37
61
  - Added `Ahoy.server_side_visits = :when_needed` to automatically create visits server-side when needed for events and `visitable`
38
62
  - Better handling of visit duration and expiration in JavaScript
39
63
 
40
- ## 2.0.0
64
+ ## 2.0.0 (2018-02-25)
41
65
 
42
66
  - Removed dependency on jQuery
43
67
  - Use `navigator.sendBeacon` by default in supported browsers
@@ -59,58 +83,58 @@ Breaking changes
59
83
  - Removed most built-in stores
60
84
  - Removed support for Rails < 4.2
61
85
 
62
- ## 1.6.1
86
+ ## 1.6.1 (2018-02-02)
63
87
 
64
88
  - Added `gin` index on properties for events
65
89
  - Fixed `visitable` options when name not provided
66
90
 
67
- ## 1.6.0
91
+ ## 1.6.0 (2017-05-01)
68
92
 
69
93
  - Added support for Rails 5.1
70
94
 
71
- ## 1.5.5
95
+ ## 1.5.5 (2017-03-23)
72
96
 
73
97
  - Added support for Rails API
74
98
  - Added NATS and NSQ stores
75
99
 
76
- ## 1.5.4
100
+ ## 1.5.4 (2017-01-22)
77
101
 
78
102
  - Fixed issue with duplicate events
79
103
  - Added support for PostGIS for `where_properties`
80
104
 
81
- ## 1.5.3
105
+ ## 1.5.3 (2016-10-31)
82
106
 
83
107
  - Fixed error with Rails 5 and Mongoid 6
84
108
  - Fixed regression with server not generating visit and visitor tokens
85
109
  - Accept UTM parameters as request parameters (for native apps)
86
110
 
87
- ## 1.5.2
111
+ ## 1.5.2 (2016-08-26)
88
112
 
89
113
  - Better support for Rails 5
90
114
 
91
- ## 1.5.1
115
+ ## 1.5.1 (2016-08-19)
92
116
 
93
117
  - Restored throttling after removing side effects
94
118
 
95
- ## 1.5.0
119
+ ## 1.5.0 (2016-08-19)
96
120
 
97
121
  - Removed throttling due to unintended side effects with its implementation
98
122
  - Ensure basic token requirements
99
123
  - Fixed visit recreation on cookie expiration
100
124
  - Fixed issue where `/ahoy/visits` is called indefinitely when `Ahoy.cookie_domain = :all`
101
125
 
102
- ## 1.4.2
126
+ ## 1.4.2 (2016-06-21)
103
127
 
104
128
  - Fixed issues with `where_properties`
105
129
 
106
- ## 1.4.1
130
+ ## 1.4.1 (2016-06-20)
107
131
 
108
132
  - Added `where_properties` method
109
133
  - Added Kafka store
110
134
  - Added `mount` option
111
135
  - Use less intrusive version of `safely`
112
136
 
113
- ## 1.4.0
137
+ ## 1.4.0 (2016-03-23)
114
138
 
115
139
  - Use `ActiveRecordTokenStore` by default (integer instead of uuid for id)
116
140
  - Detect database for `rails g ahoy:stores:active_record` for easier installation
@@ -118,55 +142,55 @@ Breaking changes
118
142
  - Fixed issue with log silencer
119
143
  - Use multi-column indexes on `ahoy_events` table creation
120
144
 
121
- ## 1.3.1
145
+ ## 1.3.1 (2016-03-22)
122
146
 
123
147
  - Raise errors in test environment
124
148
 
125
- ## 1.3.0
149
+ ## 1.3.0 (2016-03-06)
126
150
 
127
151
  - Added throttling
128
152
  - Added `max_content_length` and `max_events_per_request`
129
153
 
130
- ## 1.2.2
154
+ ## 1.2.2 (2016-03-05)
131
155
 
132
156
  - Fixed issue with latest version of `browser` gem
133
157
  - Added support for RabbitMQ
134
158
  - Added support for Amazon Kinesis Firehose
135
159
  - Fixed deprecation warnings in Rails 5
136
160
 
137
- ## 1.2.1
161
+ ## 1.2.1 (2015-08-14)
138
162
 
139
163
  - Fixed `SystemStackError: stack level too deep` when used with `activerecord-session_store`
140
164
 
141
- ## 1.2.0
165
+ ## 1.2.0 (2015-06-07)
142
166
 
143
167
  - Added support for PostgreSQL `jsonb` column type
144
168
  - Added Fluentd store
145
169
  - Added latitude, longitude, and postal_code to visits
146
170
  - Log exclusions
147
171
 
148
- ## 1.1.1
172
+ ## 1.1.1 (2015-01-05)
149
173
 
150
174
  - Better support for Authlogic
151
175
  - Added `screen_height` and `screen_width`
152
176
 
153
- ## 1.1.0
177
+ ## 1.1.0 (2014-11-02)
154
178
 
155
179
  - Added `geocode` option
156
180
  - Report errors to service by default
157
181
  - Fixed association mismatch
158
182
 
159
- ## 1.0.2
183
+ ## 1.0.2 (2014-07-10)
160
184
 
161
185
  - Fixed BSON for Mongoid 3
162
186
  - Fixed Doorkeeper integration
163
187
  - Fixed user tracking in overridden authenticate method
164
188
 
165
- ## 1.0.1
189
+ ## 1.0.1 (2014-06-27)
166
190
 
167
191
  - Fixed `visitable` outside of requests
168
192
 
169
- ## 1.0.0
193
+ ## 1.0.0 (2014-06-18)
170
194
 
171
195
  - Added support for any data store, and Mongoid out of the box
172
196
  - Added `track_visits_immediately` option
@@ -174,17 +198,17 @@ Breaking changes
174
198
  - Visits expire after inactivity, not fixed interval
175
199
  - Added `visit_duration` and `visitor_duration` options
176
200
 
177
- ## 0.3.2
201
+ ## 0.3.2 (2014-06-15)
178
202
 
179
203
  - Fixed bot exclusion for visits
180
204
  - Fixed user method
181
205
 
182
- ## 0.3.1
206
+ ## 0.3.1 (2014-06-12)
183
207
 
184
208
  - Fixed visitor cookies when set on server
185
209
  - Added `domain` option for server cookies
186
210
 
187
- ## 0.3.0
211
+ ## 0.3.0 (2014-06-11)
188
212
 
189
213
  - Added `current_visit_token` and `current_visitor_token` method
190
214
  - Switched to UUIDs
@@ -192,47 +216,47 @@ Breaking changes
192
216
  - Skip server-side bot events
193
217
  - Added `request` argument to `exclude_method`
194
218
 
195
- ## 0.2.2
219
+ ## 0.2.2 (2014-05-26)
196
220
 
197
221
  - Added `exclude_method` option
198
222
  - Added support for batch events
199
223
  - Fixed cookie encoding
200
224
  - Fixed `options` variable from being modified
201
225
 
202
- ## 0.2.1
226
+ ## 0.2.1 (2014-05-16)
203
227
 
204
228
  - Fixed IE 8 error
205
229
  - Added `track_bots` option
206
230
  - Added `$authenticate` event
207
231
 
208
- ## 0.2.0
232
+ ## 0.2.0 (2014-05-13)
209
233
 
210
234
  - Added event tracking (merged ahoy_events)
211
235
  - Added ahoy.js
212
236
 
213
- ## 0.1.8
237
+ ## 0.1.8 (2014-05-11)
214
238
 
215
239
  - Fixed bug with `user_type` set to `false` instead of `nil`
216
240
 
217
- ## 0.1.7
241
+ ## 0.1.7 (2014-05-11)
218
242
 
219
243
  - Made cookie functions public for ahoy_events
220
244
 
221
- ## 0.1.6
245
+ ## 0.1.6 (2014-05-07)
222
246
 
223
247
  - Better user agent parser
224
248
 
225
- ## 0.1.5
249
+ ## 0.1.5 (2014-05-01)
226
250
 
227
251
  - Added support for Doorkeeper
228
252
  - Added options to `visitable`
229
253
  - Added `landing_params` method
230
254
 
231
- ## 0.1.4
255
+ ## 0.1.4 (2014-04-27)
232
256
 
233
257
  - Added `ahoy.ready()` and `ahoy.log()` for events
234
258
 
235
- ## 0.1.3
259
+ ## 0.1.3 (2014-04-24)
236
260
 
237
261
  - Supports `current_user` from `ApplicationController`
238
262
  - Added `ahoy.reset()`
@@ -240,16 +264,16 @@ Breaking changes
240
264
  - Added experimental support for native apps
241
265
  - Prefer `ahoy` over `Ahoy`
242
266
 
243
- ## 0.1.2
267
+ ## 0.1.2 (2014-04-15)
244
268
 
245
269
  - Attach user on Devise sign up
246
270
  - Ability to specify visit model
247
271
 
248
- ## 0.1.1
272
+ ## 0.1.1 (2014-03-20)
249
273
 
250
274
  - Made most database columns optional
251
275
  - Performance hack for referer-parser
252
276
 
253
- ## 0.1.0
277
+ ## 0.1.0 (2014-03-19)
254
278
 
255
279
  - First major release
@@ -1,4 +1,4 @@
1
- Copyright (c) 2014-2019 Andrew Kane
1
+ Copyright (c) 2014-2020 Andrew Kane
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # Ahoy
2
2
 
3
- :fire: Simple, powerful analytics for Rails
3
+ :fire: Simple, powerful, first-party analytics for Rails
4
4
 
5
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
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)
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
8
8
 
9
9
  :tangerine: Battle-tested at [Instacart](https://www.instacart.com/opensource)
10
10
 
@@ -181,7 +181,7 @@ Order.joins(:ahoy_visit).group("device_type").count
181
181
  Here’s what the migration to add the `ahoy_visit_id` column should look like:
182
182
 
183
183
  ```ruby
184
- class AddVisitIdToOrders < ActiveRecord::Migration[5.2]
184
+ class AddVisitIdToOrders < ActiveRecord::Migration[6.0]
185
185
  def change
186
186
  add_column :orders, :ahoy_visit_id, :bigint
187
187
  end
@@ -292,7 +292,7 @@ By default, a new visit is created after 4 hours of inactivity. Change this with
292
292
  Ahoy.visit_duration = 30.minutes
293
293
  ```
294
294
 
295
- ### Multiple Subdomains
295
+ ### Cookies
296
296
 
297
297
  To track visits across multiple subdomains, use:
298
298
 
@@ -300,6 +300,12 @@ To track visits across multiple subdomains, use:
300
300
  Ahoy.cookie_domain = :all
301
301
  ```
302
302
 
303
+ Set other [cookie options](https://api.rubyonrails.org/classes/ActionDispatch/Cookies.html) with:
304
+
305
+ ```ruby
306
+ Ahoy.cookie_options = {same_site: :lax}
307
+ ```
308
+
303
309
  ### Geocoding
304
310
 
305
311
  Disable geocoding with:
@@ -308,7 +314,7 @@ Disable geocoding with:
308
314
  Ahoy.geocode = false
309
315
  ```
310
316
 
311
- Change the job queue with:
317
+ The default job queue is `:ahoy`. Change this with:
312
318
 
313
319
  ```ruby
314
320
  Ahoy.job_queue = :low_priority
@@ -427,6 +433,34 @@ Ahoy.cookies = false
427
433
 
428
434
  Previously set cookies are automatically deleted.
429
435
 
436
+ ## Data Retention
437
+
438
+ Data should only be retained for as long as it’s needed. Delete older data with:
439
+
440
+ ```ruby
441
+ Ahoy::Visit.where("started_at < ?", 2.years.ago).find_in_batches do |visits|
442
+ visit_ids = visits.map(&:id)
443
+ Ahoy::Event.where(visit_id: visit_ids).delete_all
444
+ Ahoy::Visit.where(id: visit_ids).delete_all
445
+ end
446
+ ```
447
+
448
+ You can use [Rollup](https://github.com/ankane/rollup) to aggregate important data before you do.
449
+
450
+ ```ruby
451
+ Ahoy::Visit.rollup("Visits", interval: "hour")
452
+ ```
453
+
454
+ Delete data for a specific user with:
455
+
456
+ ```ruby
457
+ user_id = 123
458
+ visit_ids = Ahoy::Visit.where(user_id: user_id).pluck(:id)
459
+ Ahoy::Event.where(visit_id: visit_ids).delete_all
460
+ Ahoy::Visit.where(id: visit_ids).delete_all
461
+ Ahoy::Event.where(user_id: user_id).delete_all
462
+ ```
463
+
430
464
  ## Development
431
465
 
432
466
  Ahoy is built with developers in mind. You can run the following code in your browser’s console.
@@ -552,7 +586,7 @@ Ahoy::Visit.group(:referring_domain).count
552
586
 
553
587
  ### Querying Events
554
588
 
555
- Ahoy provides two methods on the event model to make querying easier.
589
+ Ahoy provides a few methods on the event model to make querying easier.
556
590
 
557
591
  To query on both name and properties, you can use:
558
592
 
@@ -563,9 +597,17 @@ Ahoy::Event.where_event("Viewed product", product_id: 123).count
563
597
  Or just query properties with:
564
598
 
565
599
  ```ruby
566
- Ahoy::Event.where_props(product_id: 123).count
600
+ Ahoy::Event.where_props(product_id: 123, category: "Books").count
567
601
  ```
568
602
 
603
+ Group by properties with:
604
+
605
+ ```ruby
606
+ Ahoy::Event.group_prop(:product_id, :category).count
607
+ ```
608
+
609
+ Note: MySQL and MariaDB always return string keys (include `"null"` for `nil`) for `group_prop`.
610
+
569
611
  ### Funnels
570
612
 
571
613
  It’s easy to create funnels.
@@ -578,6 +620,29 @@ viewed_checkout_ids = Ahoy::Event.where(user_id: added_item_ids, name: "Viewed c
578
620
 
579
621
  The same approach also works with visitor tokens.
580
622
 
623
+ ### Rollups
624
+
625
+ Improve query performance by pre-aggregating data with [Rollup](https://github.com/ankane/rollup).
626
+
627
+ ```ruby
628
+ Ahoy::Event.where(name: "Viewed store").rollup("Store views")
629
+ ```
630
+
631
+ This is only needed if you have a lot of data.
632
+
633
+ ### Forecasting
634
+
635
+ To forecast future visits and events, check out [Prophet](https://github.com/ankane/prophet).
636
+
637
+ ```ruby
638
+ daily_visits = Ahoy::Visit.group_by_day(:started_at).count # uses Groupdate
639
+ Prophet.forecast(daily_visits)
640
+ ```
641
+
642
+ ### Recommendations
643
+
644
+ To make recommendations based on events, check out [Disco](https://github.com/ankane/disco#ahoy).
645
+
581
646
  ## Tutorials
582
647
 
583
648
  - [Tracking Metrics with Ahoy and Blazer](https://gorails.com/episodes/internal-metrics-with-ahoy-and-blazer)
@@ -694,3 +759,20 @@ Everyone is encouraged to help improve this project. Here are a few ways you can
694
759
  - Fix bugs and [submit pull requests](https://github.com/ankane/ahoy/pulls)
695
760
  - Write, clarify, or fix documentation
696
761
  - Suggest or add new features
762
+
763
+ To get started with development:
764
+
765
+ ```sh
766
+ git clone https://github.com/ankane/ahoy.git
767
+ cd ahoy
768
+ bundle install
769
+ bundle exec rake test
770
+ ```
771
+
772
+ To test query methods, start PostgreSQL, MySQL, and MongoDB and use:
773
+
774
+ ```sh
775
+ createdb ahoy_test
776
+ mysqladmin create ahoy_test
777
+ bundle exec rake test:query_methods
778
+ ```
@@ -30,8 +30,12 @@ module Ahoy
30
30
  mattr_accessor :cookies
31
31
  self.cookies = true
32
32
 
33
+ # TODO deprecate in favor of cookie_options
33
34
  mattr_accessor :cookie_domain
34
35
 
36
+ mattr_accessor :cookie_options
37
+ self.cookie_options = {}
38
+
35
39
  mattr_accessor :server_side_visits
36
40
  self.server_side_visits = true
37
41
 
@@ -115,6 +119,10 @@ ActiveSupport.on_load(:action_view) do
115
119
  end
116
120
 
117
121
  # Mongoid
122
+ # TODO use
123
+ # ActiveSupport.on_load(:mongoid) do
124
+ # Mongoid::Document::ClassMethods.include(Ahoy::Model)
125
+ # end
118
126
  if defined?(ActiveModel)
119
127
  ActiveModel::Callbacks.include(Ahoy::Model)
120
128
  end
@@ -26,7 +26,7 @@ module Ahoy
26
26
  if Ahoy.user_method.respond_to?(:call)
27
27
  Ahoy.user_method.call(controller)
28
28
  else
29
- controller.send(Ahoy.user_method)
29
+ controller.send(Ahoy.user_method) if controller.respond_to?(Ahoy.user_method, true)
30
30
  end
31
31
  end
32
32
  end
@@ -76,7 +76,7 @@ module Ahoy
76
76
 
77
77
  def slice_data(model, data)
78
78
  column_names = model.try(:column_names) || model.attribute_names
79
- data.slice(*column_names.map(&:to_sym)).select { |_, v| v }
79
+ data.slice(*column_names.map(&:to_sym)).select { |_, v| !v.nil? }
80
80
  end
81
81
 
82
82
  def unique_exception?(e)
@@ -31,6 +31,7 @@ module Ahoy
31
31
  end
32
32
  else
33
33
  properties.each do |k, v|
34
+ # TODO cast to json instead
34
35
  relation = relation.where("properties REGEXP ?", "[{,]#{{k.to_s => v}.to_json.sub(/\A\{/, "").sub(/\}\z/, "").gsub("+", "\\\\+")}[,}]")
35
36
  end
36
37
  end
@@ -57,6 +58,7 @@ module Ahoy
57
58
  end
58
59
  else
59
60
  properties.each do |k, v|
61
+ # TODO cast to jsonb instead
60
62
  relation = relation.where("properties SIMILAR TO ?", "%[{,]#{{k.to_s => v}.to_json.sub(/\A\{/, "").sub(/\}\z/, "").gsub("+", "\\\\+")}[,}]%")
61
63
  end
62
64
  end
@@ -66,6 +68,49 @@ module Ahoy
66
68
  relation
67
69
  end
68
70
  alias_method :where_properties, :where_props
71
+
72
+ def group_prop(*props)
73
+ # like with group
74
+ props.flatten!
75
+
76
+ relation = self
77
+ if respond_to?(:columns_hash)
78
+ column_type = columns_hash["properties"].type
79
+ adapter_name = connection.adapter_name.downcase
80
+ else
81
+ adapter_name = "mongoid"
82
+ end
83
+ case adapter_name
84
+ when "mongoid"
85
+ raise "Adapter not supported: #{adapter_name}"
86
+ when /mysql/
87
+ if connection.try(:mariadb?)
88
+ props.each do |prop|
89
+ quoted_prop = connection.quote("$.#{prop}")
90
+ relation = relation.group("JSON_UNQUOTE(JSON_EXTRACT(properties, #{quoted_prop}))")
91
+ end
92
+ else
93
+ column = column_type == :json ? "properties" : "CAST(properties AS JSON)"
94
+ props.each do |prop|
95
+ quoted_prop = connection.quote("$.#{prop}")
96
+ relation = relation.group("JSON_UNQUOTE(JSON_EXTRACT(#{column}, #{quoted_prop}))")
97
+ end
98
+ end
99
+ when /postgres|postgis/
100
+ # convert to jsonb to fix
101
+ # could not identify an equality operator for type json
102
+ # and for text columns
103
+ cast = [:jsonb, :hstore].include?(column_type) ? "" : "::jsonb"
104
+
105
+ props.each do |prop|
106
+ quoted_prop = connection.quote(prop)
107
+ relation = relation.group("properties#{cast} -> #{quoted_prop}")
108
+ end
109
+ else
110
+ raise "Adapter not supported: #{adapter_name}"
111
+ end
112
+ relation
113
+ end
69
114
  end
70
115
  end
71
116
  end
@@ -11,6 +11,7 @@ module Ahoy
11
11
  @controller = options[:controller]
12
12
  @request = options[:request] || @controller.try(:request)
13
13
  @visit_token = options[:visit_token]
14
+ @user = options[:user]
14
15
  @options = options
15
16
  end
16
17
 
@@ -57,7 +58,7 @@ module Ahoy
57
58
 
58
59
  @store.track_visit(data)
59
60
 
60
- Ahoy::GeocodeV2Job.perform_later(visit_token, data[:ip]) if Ahoy.geocode
61
+ Ahoy::GeocodeV2Job.perform_later(visit_token, data[:ip]) if Ahoy.geocode && data[:ip]
61
62
  end
62
63
  end
63
64
  true
@@ -128,7 +129,7 @@ module Ahoy
128
129
  end
129
130
 
130
131
  def visit_properties
131
- @visit_properties ||= Ahoy::VisitProperties.new(request, api: api?).generate
132
+ @visit_properties ||= request ? Ahoy::VisitProperties.new(request, api: api?).generate : {}
132
133
  end
133
134
 
134
135
  def visit_token
@@ -168,19 +169,18 @@ module Ahoy
168
169
 
169
170
  def set_cookie(name, value, duration = nil, use_domain = true)
170
171
  # safety net
171
- return unless Ahoy.cookies
172
+ return unless Ahoy.cookies && request
172
173
 
173
- cookie = {
174
- value: value
175
- }
174
+ cookie = Ahoy.cookie_options.merge(value: value)
176
175
  cookie[:expires] = duration.from_now if duration
177
- domain = Ahoy.cookie_domain
178
- cookie[:domain] = domain if domain && use_domain
176
+ # prefer cookie_options[:domain] over cookie_domain
177
+ cookie[:domain] ||= Ahoy.cookie_domain if Ahoy.cookie_domain
178
+ cookie.delete(:domain) unless use_domain
179
179
  request.cookie_jar[name] = cookie
180
180
  end
181
181
 
182
182
  def delete_cookie(name)
183
- request.cookie_jar.delete(name) if request.cookie_jar[name]
183
+ request.cookie_jar.delete(name) if request && request.cookie_jar[name]
184
184
  end
185
185
 
186
186
  def trusted_time(time = nil)
@@ -196,8 +196,12 @@ module Ahoy
196
196
  end
197
197
 
198
198
  def report_exception(e)
199
- raise e if !defined?(Rails) || Rails.env.development? || Rails.env.test?
200
- Safely.report_exception(e)
199
+ if defined?(ActionDispatch::RemoteIp::IpSpoofAttackError) && e.is_a?(ActionDispatch::RemoteIp::IpSpoofAttackError)
200
+ debug "Tracking excluded due to IP spoofing"
201
+ else
202
+ raise e if !defined?(Rails) || Rails.env.development? || Rails.env.test?
203
+ Safely.report_exception(e)
204
+ end
201
205
  end
202
206
 
203
207
  def generate_id
@@ -1,3 +1,3 @@
1
1
  module Ahoy
2
- VERSION = "3.0.0"
2
+ VERSION = "3.0.5"
3
3
  end
@@ -1,27 +1,13 @@
1
- # taken from https://github.com/collectiveidea/audited/blob/master/lib/generators/audited/install_generator.rb
2
- require "rails/generators"
3
- require "rails/generators/migration"
4
- require "active_record"
5
1
  require "rails/generators/active_record"
6
2
 
7
3
  module Ahoy
8
4
  module Generators
9
5
  class ActiverecordGenerator < Rails::Generators::Base
10
- include Rails::Generators::Migration
11
- source_root File.expand_path("../templates", __FILE__)
6
+ include ActiveRecord::Generators::Migration
7
+ source_root File.join(__dir__, "templates")
12
8
 
13
9
  class_option :database, type: :string, aliases: "-d"
14
10
 
15
- # Implement the required interface for Rails::Generators::Migration.
16
- def self.next_migration_number(dirname) #:nodoc:
17
- next_migration_number = current_migration_number(dirname) + 1
18
- if ::ActiveRecord::Base.timestamped_migrations
19
- [Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % next_migration_number].max
20
- else
21
- "%.3d" % next_migration_number
22
- end
23
- end
24
-
25
11
  def copy_templates
26
12
  template "database_store_initializer.rb", "config/initializers/ahoy.rb"
27
13
  template "active_record_visit_model.rb", "app/models/ahoy/visit.rb"
@@ -3,7 +3,7 @@ require "rails/generators"
3
3
  module Ahoy
4
4
  module Generators
5
5
  class BaseGenerator < Rails::Generators::Base
6
- source_root File.expand_path("../templates", __FILE__)
6
+ source_root File.join(__dir__, "templates")
7
7
 
8
8
  def copy_templates
9
9
  template "base_store_initializer.rb", "config/initializers/ahoy.rb"
@@ -3,7 +3,7 @@ require "rails/generators"
3
3
  module Ahoy
4
4
  module Generators
5
5
  class InstallGenerator < Rails::Generators::Base
6
- source_root File.expand_path("../templates", __FILE__)
6
+ source_root File.join(__dir__, "templates")
7
7
 
8
8
  def copy_templates
9
9
  activerecord = defined?(ActiveRecord)
@@ -3,7 +3,7 @@ require "rails/generators"
3
3
  module Ahoy
4
4
  module Generators
5
5
  class MongoidGenerator < Rails::Generators::Base
6
- source_root File.expand_path("../templates", __FILE__)
6
+ source_root File.join(__dir__, "templates")
7
7
 
8
8
  def copy_templates
9
9
  template "database_store_initializer.rb", "config/initializers/ahoy.rb"
@@ -41,10 +41,10 @@ class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version
41
41
  t.string :os_version
42
42
  t.string :platform
43
43
 
44
- t.timestamp :started_at
44
+ t.datetime :started_at
45
45
  end
46
46
 
47
- add_index :ahoy_visits, [:visit_token], unique: true
47
+ add_index :ahoy_visits, :visit_token, unique: true
48
48
 
49
49
  create_table :ahoy_events do |t|
50
50
  t.references :visit
@@ -52,7 +52,7 @@ class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version
52
52
 
53
53
  t.string :name
54
54
  t.<%= properties_type %> :properties
55
- t.timestamp :time
55
+ t.datetime :time
56
56
  end
57
57
 
58
58
  add_index :ahoy_events, [:name, :time]<% if properties_type == "jsonb" %><% if rails52? %>
@@ -2,112 +2,17 @@
2
2
  * Ahoy.js
3
3
  * Simple, powerful JavaScript analytics
4
4
  * https://github.com/ankane/ahoy.js
5
- * v0.3.4
5
+ * v0.3.6
6
6
  * MIT License
7
7
  */
8
8
 
9
9
  (function (global, factory) {
10
10
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
11
11
  typeof define === 'function' && define.amd ? define(factory) :
12
- (global.ahoy = factory());
12
+ (global = global || self, global.ahoy = factory());
13
13
  }(this, (function () { 'use strict';
14
14
 
15
- function isUndefined(value) {
16
- return value === undefined;
17
- }
18
-
19
- function isNull(value) {
20
- return value === null;
21
- }
22
-
23
- function isObject(value) {
24
- return value === Object(value);
25
- }
26
-
27
- function isArray(value) {
28
- return Array.isArray(value);
29
- }
30
-
31
- function isDate(value) {
32
- return value instanceof Date;
33
- }
34
-
35
- function isBlob(value) {
36
- return (
37
- value &&
38
- typeof value.size === 'number' &&
39
- typeof value.type === 'string' &&
40
- typeof value.slice === 'function'
41
- );
42
- }
43
-
44
- function isFile(value) {
45
- return (
46
- isBlob(value) &&
47
- (typeof value.lastModifiedDate === 'object' ||
48
- typeof value.lastModified === 'number') &&
49
- typeof value.name === 'string'
50
- );
51
- }
52
-
53
- function isFormData(value) {
54
- return value instanceof FormData;
55
- }
56
-
57
- function objectToFormData(obj, cfg, fd, pre) {
58
- if (isFormData(cfg)) {
59
- pre = fd;
60
- fd = cfg;
61
- cfg = null;
62
- }
63
-
64
- cfg = cfg || {};
65
- cfg.indices = isUndefined(cfg.indices) ? false : cfg.indices;
66
- cfg.nulls = isUndefined(cfg.nulls) ? true : cfg.nulls;
67
- fd = fd || new FormData();
68
-
69
- if (isUndefined(obj)) {
70
- return fd;
71
- } else if (isNull(obj)) {
72
- if (cfg.nulls) {
73
- fd.append(pre, '');
74
- }
75
- } else if (isArray(obj)) {
76
- if (!obj.length) {
77
- var key = pre + '[]';
78
-
79
- fd.append(key, '');
80
- } else {
81
- obj.forEach(function(value, index) {
82
- var key = pre + '[' + (cfg.indices ? index : '') + ']';
83
-
84
- objectToFormData(value, cfg, fd, key);
85
- });
86
- }
87
- } else if (isDate(obj)) {
88
- fd.append(pre, obj.toISOString());
89
- } else if (isObject(obj) && !isFile(obj) && !isBlob(obj)) {
90
- Object.keys(obj).forEach(function(prop) {
91
- var value = obj[prop];
92
-
93
- if (isArray(value)) {
94
- while (prop.length > 2 && prop.lastIndexOf('[]') === prop.length - 2) {
95
- prop = prop.substring(0, prop.length - 2);
96
- }
97
- }
98
-
99
- var key = pre ? pre + '[' + prop + ']' : prop;
100
-
101
- objectToFormData(value, cfg, fd, key);
102
- });
103
- } else {
104
- fd.append(pre, obj);
105
- }
106
-
107
- return fd;
108
- }
109
-
110
- var objectToFormdata = objectToFormData;
15
+ var n=function(n){return void 0===n},e=function(n){return Array.isArray(n)},t=function(n){return n&&"number"==typeof n.size&&"string"==typeof n.type&&"function"==typeof n.slice},s=function(o,i,r,f){return (i=i||{}).indices=!n(i.indices)&&i.indices,i.nullsAsUndefineds=!n(i.nullsAsUndefineds)&&i.nullsAsUndefineds,i.booleansAsIntegers=!n(i.booleansAsIntegers)&&i.booleansAsIntegers,r=r||new FormData,n(o)?r:(null===o?i.nullsAsUndefineds||r.append(f,""):"boolean"!=typeof o?e(o)?o.length&&o.forEach(function(n,e){s(n,i,r,f+"["+(i.indices?e:"")+"]");}):o instanceof Date?r.append(f,o.toISOString()):o!==Object(o)||function(n){return t(n)&&"string"==typeof n.name&&("object"==typeof n.lastModifiedDate||"number"==typeof n.lastModified)}(o)||t(o)?r.append(f,o):Object.keys(o).forEach(function(n){var t=o[n];if(e(t)){ for(;n.length>2&&n.lastIndexOf("[]")===n.length-2;){ n=n.substring(0,n.length-2); } }s(t,i,r,f?f+"["+n+"]":n);}):r.append(f,i.booleansAsIntegers?o?1:0:o),r)};
111
16
 
112
17
  // https://www.quirksmode.org/js/cookies.html
113
18
 
@@ -155,7 +60,9 @@
155
60
  cookieDomain: null,
156
61
  headers: {},
157
62
  visitParams: {},
158
- withCredentials: false
63
+ withCredentials: false,
64
+ visitDuration: 4 * 60, // default 4 hours
65
+ visitorDuration: 2 * 365 * 24 * 60 // default 2 years
159
66
  };
160
67
 
161
68
  var ahoy = window.ahoy || window.Ahoy || {};
@@ -173,8 +80,6 @@
173
80
 
174
81
  var $ = window.jQuery || window.Zepto || window.$;
175
82
  var visitId, visitorId, track;
176
- var visitTtl = 4 * 60; // 4 hours
177
- var visitorTtl = 2 * 365 * 24 * 60; // 2 years
178
83
  var isReady = false;
179
84
  var queue = [];
180
85
  var canStringify = typeof(JSON) !== "undefined" && typeof(JSON.stringify) !== "undefined";
@@ -224,13 +129,13 @@
224
129
  isReady = true;
225
130
  }
226
131
 
227
- function ready(callback) {
132
+ ahoy.ready = function (callback) {
228
133
  if (isReady) {
229
134
  callback();
230
135
  } else {
231
136
  queue.push(callback);
232
137
  }
233
- }
138
+ };
234
139
 
235
140
  function matchesSelector(element, selector) {
236
141
  var matches = element.matches ||
@@ -258,7 +163,11 @@
258
163
 
259
164
  // http://beeker.io/jquery-document-ready-equivalent-vanilla-javascript
260
165
  function documentReady(callback) {
261
- document.readyState === "interactive" || document.readyState === "complete" ? callback() : document.addEventListener("DOMContentLoaded", callback);
166
+ if (document.readyState === "interactive" || document.readyState === "complete") {
167
+ setTimeout(callback, 0);
168
+ } else {
169
+ document.addEventListener("DOMContentLoaded", callback);
170
+ }
262
171
  }
263
172
 
264
173
  // https://stackoverflow.com/a/2117523/1177228
@@ -294,7 +203,7 @@
294
203
 
295
204
  function sendRequest(url, data, success) {
296
205
  if (canStringify) {
297
- if ($) {
206
+ if ($ && $.ajax) {
298
207
  $.ajax({
299
208
  type: "POST",
300
209
  url: url,
@@ -343,7 +252,7 @@
343
252
  }
344
253
 
345
254
  function trackEvent(event) {
346
- ready( function () {
255
+ ahoy.ready( function () {
347
256
  sendRequest(eventsUrl(), eventData(event), function() {
348
257
  // remove from queue
349
258
  for (var i = 0; i < eventQueue.length; i++) {
@@ -358,7 +267,7 @@
358
267
  }
359
268
 
360
269
  function trackEventNow(event) {
361
- ready( function () {
270
+ ahoy.ready( function () {
362
271
  var data = eventData(event);
363
272
  var param = csrfParam();
364
273
  var token = csrfToken();
@@ -366,7 +275,7 @@
366
275
  // stringify so we keep the type
367
276
  data.events_json = JSON.stringify(data.events);
368
277
  delete data.events;
369
- window.navigator.sendBeacon(eventsUrl(), objectToFormdata(data));
278
+ window.navigator.sendBeacon(eventsUrl(), s(data));
370
279
  });
371
280
  }
372
281
 
@@ -427,7 +336,7 @@
427
336
  } else {
428
337
  if (!visitId) {
429
338
  visitId = generateId();
430
- setCookie("ahoy_visit", visitId, visitTtl);
339
+ setCookie("ahoy_visit", visitId, config.visitDuration);
431
340
  }
432
341
 
433
342
  // make sure cookies are enabled
@@ -436,7 +345,7 @@
436
345
 
437
346
  if (!visitorId) {
438
347
  visitorId = generateId();
439
- setCookie("ahoy_visitor", visitorId, visitorTtl);
348
+ setCookie("ahoy_visitor", visitorId, config.visitorDuration);
440
349
  }
441
350
 
442
351
  var data = {
@@ -509,12 +418,12 @@
509
418
  js: true
510
419
  };
511
420
 
512
- ready( function () {
421
+ ahoy.ready( function () {
513
422
  if (config.cookies && !ahoy.getVisitId()) {
514
423
  createVisit();
515
424
  }
516
425
 
517
- ready( function () {
426
+ ahoy.ready( function () {
518
427
  log(event);
519
428
 
520
429
  event.visit_token = ahoy.getVisitId();
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ahoy_matey
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 3.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-05-29 00:00:00.000000000 Z
11
+ date: 2020-09-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -109,7 +109,35 @@ dependencies:
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
111
  - !ruby/object:Gem::Dependency
112
- name: activerecord
112
+ name: combustion
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rails
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: sqlite3
113
141
  requirement: !ruby/object:Gem::Requirement
114
142
  requirements:
115
143
  - - ">="
@@ -164,6 +192,34 @@ dependencies:
164
192
  - - ">="
165
193
  - !ruby/object:Gem::Version
166
194
  version: '0'
195
+ - !ruby/object:Gem::Dependency
196
+ name: browser
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - "~>"
200
+ - !ruby/object:Gem::Version
201
+ version: '2.0'
202
+ type: :development
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - "~>"
207
+ - !ruby/object:Gem::Version
208
+ version: '2.0'
209
+ - !ruby/object:Gem::Dependency
210
+ name: user_agent_parser
211
+ requirement: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - ">="
214
+ - !ruby/object:Gem::Version
215
+ version: '0'
216
+ type: :development
217
+ prerelease: false
218
+ version_requirements: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - ">="
221
+ - !ruby/object:Gem::Version
222
+ version: '0'
167
223
  description:
168
224
  email: andrew@chartkick.com
169
225
  executables: []
@@ -225,8 +281,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
225
281
  - !ruby/object:Gem::Version
226
282
  version: '0'
227
283
  requirements: []
228
- rubygems_version: 3.0.3
284
+ rubygems_version: 3.1.2
229
285
  signing_key:
230
286
  specification_version: 4
231
- summary: Simple, powerful analytics for Rails
287
+ summary: Simple, powerful, first-party analytics for Rails
232
288
  test_files: []