ahoy_matey 3.2.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 51e11f3086c12a1418ea7436e291ca9d2f0c7e0aa3091c48c904d05b4be6a210
4
- data.tar.gz: 5b5c6afc94c0db23f2f1616f6e9edc0ae95ff334e33704c26417705d06d04670
3
+ metadata.gz: d09c2a1f7783c94ad857652adfbe8150deb702f9162daf28c2bf9c11454f4277
4
+ data.tar.gz: 0e0a9779762c9ad912ba6d8866f85783c00aa0a96f3c53a4b6b803a3cdf41af2
5
5
  SHA512:
6
- metadata.gz: fd75680d3d04b2383c30b19848f88fcb838f1498e8819d2d758aec6443e85bfb2f73a421da968cb54628097631c777ac1f95dd1ca4bae6d6b22d748a44dcb84d
7
- data.tar.gz: 2d336bd12312cddf4b76f213b1f7f84b1fc2f0b67302e75096d1eb21013982f532064b31894ba217f7ccbafdbaf5eb4c691a3a86350b8abf1449374f66015909
6
+ metadata.gz: e479ea5345820038c0a4fe4296504781614b53c6cfef5a79856c51073f768dc64082d494ba64d1e03ec41a346832c320565de63c5953f3bdf91e6e2697a1b7c9
7
+ data.tar.gz: 5fc7c4e73835b1ffc7be2d9c125894baccf204b7e7785784887574d80b8d7581d10e1ced1c0562cd7804ad66a22ce05df64ef6b302a981faa567569b618c8557
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## 3.3.0 (2021-08-13)
2
+
3
+ - Added `country_code` to geocoding
4
+ - Updated Ahoy.js to 0.3.9
5
+ - Fixed install generator for MariaDB
6
+
1
7
  ## 3.2.0 (2021-03-01)
2
8
 
3
9
  - Disabled geocoding by default for new installations
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2014-2020 Andrew Kane
1
+ Copyright (c) 2014-2021 Andrew Kane
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  :fire: Simple, powerful, first-party analytics for Rails
4
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.
5
+ Track visits and events in Ruby, JavaScript, and native apps. Data is stored in your database by default, and you can customize it for any data store as you grow.
6
6
 
7
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
 
@@ -74,14 +74,14 @@ ahoy.track("My second event", {language: "JavaScript"});
74
74
 
75
75
  Check out [Ahoy iOS](https://github.com/namolnad/ahoy-ios) and [Ahoy Android](https://github.com/instacart/ahoy-android).
76
76
 
77
- ### GDPR Compliance
78
-
79
- Ahoy provides a number of options to help with GDPR compliance. See the [GDPR section](#gdpr-compliance-1) for more info.
80
-
81
77
  ### Geocoding Setup
82
78
 
83
79
  To enable geocoding, see the [Geocoding section](#geocoding).
84
80
 
81
+ ### GDPR Compliance
82
+
83
+ Ahoy provides a number of options to help with GDPR compliance. See the [GDPR section](#gdpr-compliance-1) for more info.
84
+
85
85
  ## How It Works
86
86
 
87
87
  ### Visits
@@ -189,7 +189,7 @@ Order.joins(:ahoy_visit).group("device_type").count
189
189
  Here’s what the migration to add the `ahoy_visit_id` column should look like:
190
190
 
191
191
  ```ruby
192
- class AddVisitIdToOrders < ActiveRecord::Migration[6.0]
192
+ class AddVisitIdToOrders < ActiveRecord::Migration[6.1]
193
193
  def change
194
194
  add_column :orders, :ahoy_visit_id, :bigint
195
195
  end
@@ -204,7 +204,7 @@ visitable :sign_up_visit
204
204
 
205
205
  ### Users
206
206
 
207
- 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.
207
+ Ahoy automatically attaches the `current_user` to the visit. With [Devise](https://github.com/plataformatec/devise), it attaches the user even if they sign in after the visit starts.
208
208
 
209
209
  With other authentication frameworks, add this to the end of your sign in method:
210
210
 
@@ -314,9 +314,41 @@ Set other [cookie options](https://api.rubyonrails.org/classes/ActionDispatch/Co
314
314
  Ahoy.cookie_options = {same_site: :lax}
315
315
  ```
316
316
 
317
- ### Geocoding
317
+ You can also [disable cookies](#anonymity-sets--cookies)
318
+
319
+ ### Token Generation
318
320
 
319
- Ahoy uses [Geocoder](https://github.com/alexreisner/geocoder) for geocoding. We recommend configuring [local geocoding](#local-geocoding) so IP addresses are not sent to a 3rd party service. If you do use a 3rd party service, be sure to add it to your GDPR subprocessor list. If Ahoy is configured to [mask ips](#ip-masking), the masked IP is used (this increases privacy but can reduce accuracy).
321
+ 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).
322
+
323
+ ```ruby
324
+ Ahoy.token_generator = -> { Druuid.gen }
325
+ ```
326
+
327
+ ### Throttling
328
+
329
+ You can use [Rack::Attack](https://github.com/kickstarter/rack-attack) to throttle requests to the API.
330
+
331
+ ```ruby
332
+ class Rack::Attack
333
+ throttle("ahoy/ip", limit: 20, period: 1.minute) do |req|
334
+ if req.path.start_with?("/ahoy/")
335
+ req.ip
336
+ end
337
+ end
338
+ end
339
+ ```
340
+
341
+ ### Exceptions
342
+
343
+ 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:
344
+
345
+ ```ruby
346
+ Safely.report_exception_method = ->(e) { Rollbar.error(e) }
347
+ ```
348
+
349
+ ## Geocoding
350
+
351
+ Ahoy uses [Geocoder](https://github.com/alexreisner/geocoder) for geocoding. We recommend configuring [local geocoding](#local-geocoding) or [load balancer geocoding](#load-balancer-geocoding) so IP addresses are not sent to a 3rd party service. If you do use a 3rd party service and adhere to GDPR, be sure to add it to your subprocessor list. If Ahoy is configured to [mask IPs](#ip-masking), the masked IP is used (this can reduce accuracy but is better for privacy).
320
352
 
321
353
  To enable geocoding, update `config/initializers/ahoy.rb`:
322
354
 
@@ -367,36 +399,29 @@ Geocoder.configure(
367
399
  )
368
400
  ```
369
401
 
370
- ### Token Generation
402
+ ### Load Balancer Geocoding
371
403
 
372
- 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).
404
+ Some load balancers can add geocoding information to request headers.
373
405
 
374
- ```ruby
375
- Ahoy.token_generator = -> { Druuid.gen }
376
- ```
406
+ - [nginx](https://nginx.org/en/docs/http/ngx_http_geoip_module.html)
407
+ - [Google Cloud](https://cloud.google.com/load-balancing/docs/custom-headers)
408
+ - [Cloudflare](https://support.cloudflare.com/hc/en-us/articles/200168236-Configuring-Cloudflare-IP-Geolocation)
377
409
 
378
- ### Throttling
379
-
380
- You can use [Rack::Attack](https://github.com/kickstarter/rack-attack) to throttle requests to the API.
410
+ Update `config/initializers/ahoy.rb` with:
381
411
 
382
412
  ```ruby
383
- class Rack::Attack
384
- throttle("ahoy/ip", limit: 20, period: 1.minute) do |req|
385
- if req.path.start_with?("/ahoy/")
386
- req.ip
387
- end
413
+ Ahoy.geocode = false
414
+
415
+ class Ahoy::Store < Ahoy::DatabaseStore
416
+ def track_visit(data)
417
+ data[:country] = request.headers["<country-header>"]
418
+ data[:region] = request.headers["<region-header>"]
419
+ data[:city] = request.headers["<city-header>"]
420
+ super(data)
388
421
  end
389
422
  end
390
423
  ```
391
424
 
392
- ### Exceptions
393
-
394
- 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:
395
-
396
- ```ruby
397
- Safely.report_exception_method = ->(e) { Rollbar.error(e) }
398
- ```
399
-
400
425
  ## GDPR Compliance
401
426
 
402
427
  Ahoy provides a number of options to help with [GDPR compliance](https://en.wikipedia.org/wiki/General_Data_Protection_Regulation).
@@ -455,7 +480,11 @@ Ahoy can switch from cookies to [anonymity sets](https://privacypatterns.org/pat
455
480
  Ahoy.cookies = false
456
481
  ```
457
482
 
458
- Previously set cookies are automatically deleted.
483
+ Previously set cookies are automatically deleted. If you use JavaScript tracking, also set:
484
+
485
+ ```javascript
486
+ ahoy.configure({cookies: false});
487
+ ```
459
488
 
460
489
  ## Data Retention
461
490
 
@@ -14,6 +14,7 @@ module Ahoy
14
14
  if location && location.country.present?
15
15
  data = {
16
16
  country: location.country,
17
+ country_code: location.try(:country_code).presence,
17
18
  region: location.try(:state).presence,
18
19
  city: location.try(:city).presence,
19
20
  postal_code: location.try(:postal_code).presence,
@@ -53,7 +53,7 @@ module Ahoy
53
53
 
54
54
  def visit
55
55
  unless defined?(@visit)
56
- @visit = visit_model.where(visit_token: ahoy.visit_token).first if ahoy.visit_token
56
+ @visit = visit_model.find_by(visit_token: ahoy.visit_token) if ahoy.visit_token
57
57
  end
58
58
  @visit
59
59
  end
data/lib/ahoy/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Ahoy
2
- VERSION = "3.2.0"
2
+ VERSION = "3.3.0"
3
3
  end
@@ -27,6 +27,11 @@ module Ahoy
27
27
  end
28
28
  end
29
29
 
30
+ # requires database connection to check for MariaDB
31
+ def serialize_properties?
32
+ properties_type == "text" || (properties_type == "json" && ActiveRecord::Base.connection.try(:mariadb?))
33
+ end
34
+
30
35
  # use connection_config instead of connection.adapter
31
36
  # so database connection isn't needed
32
37
  def adapter
@@ -4,7 +4,7 @@ class Ahoy::Event < ApplicationRecord
4
4
  self.table_name = "ahoy_events"
5
5
 
6
6
  belongs_to :visit
7
- belongs_to :user, optional: true<% if properties_type == "text" %>
7
+ belongs_to :user, optional: true<% if serialize_properties? %>
8
8
 
9
9
  serialize :properties, JSON<% end %>
10
10
  end
@@ -1,8 +1,8 @@
1
- /*
1
+ /*!
2
2
  * Ahoy.js
3
3
  * Simple, powerful JavaScript analytics
4
4
  * https://github.com/ankane/ahoy.js
5
- * v0.3.8
5
+ * v0.3.9
6
6
  * MIT License
7
7
  */
8
8
 
@@ -12,97 +12,6 @@
12
12
  (global = global || self, global.ahoy = factory());
13
13
  }(this, (function () { 'use strict';
14
14
 
15
- var isUndefined = function (value) { return value === undefined; };
16
-
17
- var isNull = function (value) { return value === null; };
18
-
19
- var isBoolean = function (value) { return typeof value === 'boolean'; };
20
-
21
- var isObject = function (value) { return value === Object(value); };
22
-
23
- var isArray = function (value) { return Array.isArray(value); };
24
-
25
- var isDate = function (value) { return value instanceof Date; };
26
-
27
- var isBlob = function (value) { return value &&
28
- typeof value.size === 'number' &&
29
- typeof value.type === 'string' &&
30
- typeof value.slice === 'function'; };
31
-
32
- var isFile = function (value) { return isBlob(value) &&
33
- typeof value.name === 'string' &&
34
- (typeof value.lastModifiedDate === 'object' ||
35
- typeof value.lastModified === 'number'); };
36
-
37
- var serialize = function (obj, cfg, fd, pre) {
38
- cfg = cfg || {};
39
-
40
- cfg.indices = isUndefined(cfg.indices) ? false : cfg.indices;
41
-
42
- cfg.nullsAsUndefineds = isUndefined(cfg.nullsAsUndefineds)
43
- ? false
44
- : cfg.nullsAsUndefineds;
45
-
46
- cfg.booleansAsIntegers = isUndefined(cfg.booleansAsIntegers)
47
- ? false
48
- : cfg.booleansAsIntegers;
49
-
50
- cfg.allowEmptyArrays = isUndefined(cfg.allowEmptyArrays)
51
- ? false
52
- : cfg.allowEmptyArrays;
53
-
54
- fd = fd || new FormData();
55
-
56
- if (isUndefined(obj)) {
57
- return fd;
58
- } else if (isNull(obj)) {
59
- if (!cfg.nullsAsUndefineds) {
60
- fd.append(pre, '');
61
- }
62
- } else if (isBoolean(obj)) {
63
- if (cfg.booleansAsIntegers) {
64
- fd.append(pre, obj ? 1 : 0);
65
- } else {
66
- fd.append(pre, obj);
67
- }
68
- } else if (isArray(obj)) {
69
- if (obj.length) {
70
- obj.forEach(function (value, index) {
71
- var key = pre + '[' + (cfg.indices ? index : '') + ']';
72
-
73
- serialize(value, cfg, fd, key);
74
- });
75
- } else if (cfg.allowEmptyArrays) {
76
- fd.append(pre + '[]', '');
77
- }
78
- } else if (isDate(obj)) {
79
- fd.append(pre, obj.toISOString());
80
- } else if (isObject(obj) && !isFile(obj) && !isBlob(obj)) {
81
- Object.keys(obj).forEach(function (prop) {
82
- var value = obj[prop];
83
-
84
- if (isArray(value)) {
85
- while (prop.length > 2 && prop.lastIndexOf('[]') === prop.length - 2) {
86
- prop = prop.substring(0, prop.length - 2);
87
- }
88
- }
89
-
90
- var key = pre ? pre + '[' + prop + ']' : prop;
91
-
92
- serialize(value, cfg, fd, key);
93
- });
94
- } else {
95
- fd.append(pre, obj);
96
- }
97
-
98
- return fd;
99
- };
100
-
101
- var index_module = {
102
- serialize: serialize,
103
- };
104
- var index_module_1 = index_module.serialize;
105
-
106
15
  // https://www.quirksmode.org/js/cookies.html
107
16
 
108
17
  var Cookies = {
@@ -190,6 +99,16 @@
190
99
  return (config.useBeacon || config.trackNow) && isEmpty(config.headers) && canStringify && typeof(window.navigator.sendBeacon) !== "undefined" && !config.withCredentials;
191
100
  }
192
101
 
102
+ function serialize(object) {
103
+ var data = new FormData();
104
+ for (var key in object) {
105
+ if (object.hasOwnProperty(key)) {
106
+ data.append(key, object[key]);
107
+ }
108
+ }
109
+ return data;
110
+ }
111
+
193
112
  // cookies
194
113
 
195
114
  function setCookie(name, value, ttl) {
@@ -238,7 +157,7 @@
238
157
  if (matches.apply(element, [selector])) {
239
158
  return element;
240
159
  } else if (element.parentElement) {
241
- return matchesSelector(element.parentElement, selector)
160
+ return matchesSelector(element.parentElement, selector);
242
161
  }
243
162
  return null;
244
163
  } else {
@@ -370,7 +289,7 @@
370
289
  // stringify so we keep the type
371
290
  data.events_json = JSON.stringify(data.events);
372
291
  delete data.events;
373
- window.navigator.sendBeacon(eventsUrl(), index_module_1(data));
292
+ window.navigator.sendBeacon(eventsUrl(), serialize(data));
374
293
  });
375
294
  }
376
295
 
@@ -393,7 +312,7 @@
393
312
  return obj;
394
313
  }
395
314
 
396
- function eventProperties(e) {
315
+ function eventProperties() {
397
316
  return cleanObject({
398
317
  tag: this.tagName.toLowerCase(),
399
318
  id: presence(this.id),
@@ -557,8 +476,12 @@
557
476
  ahoy.track("$view", properties);
558
477
  };
559
478
 
560
- ahoy.trackClicks = function () {
561
- onEvent("click", "a, button, input[type=submit]", function (e) {
479
+ ahoy.trackClicks = function (selector) {
480
+ if (selector === undefined) {
481
+ log("trackClicks will require a selector in 0.4.0");
482
+ selector = "a, button, input[type=submit]";
483
+ }
484
+ onEvent("click", selector, function (e) {
562
485
  var properties = eventProperties.call(this, e);
563
486
  properties.text = properties.tag == "input" ? this.value : (this.textContent || this.innerText || this.innerHTML).replace(/[\s\r\n]+/g, " ").trim();
564
487
  properties.href = this.href;
@@ -566,25 +489,35 @@
566
489
  });
567
490
  };
568
491
 
569
- ahoy.trackSubmits = function () {
570
- onEvent("submit", "form", function (e) {
492
+ ahoy.trackSubmits = function (selector) {
493
+ if (selector === undefined) {
494
+ log("trackSubmits will require a selector in 0.4.0");
495
+ selector = "form";
496
+ }
497
+ onEvent("submit", selector, function (e) {
571
498
  var properties = eventProperties.call(this, e);
572
499
  ahoy.track("$submit", properties);
573
500
  });
574
501
  };
575
502
 
576
- ahoy.trackChanges = function () {
577
- onEvent("change", "input, textarea, select", function (e) {
503
+ ahoy.trackChanges = function (selector) {
504
+ if (selector === undefined) {
505
+ // put here instead of above to prevent message with trackAll
506
+ log("trackChanges is deprecated and will be removed in 0.4.0");
507
+ selector = "input, textarea, select";
508
+ }
509
+ onEvent("change", selector, function (e) {
578
510
  var properties = eventProperties.call(this, e);
579
511
  ahoy.track("$change", properties);
580
512
  });
581
513
  };
582
514
 
583
515
  ahoy.trackAll = function() {
516
+ log("trackAll is deprecated and will be removed in 0.4.0");
584
517
  ahoy.trackView();
585
- ahoy.trackClicks();
586
- ahoy.trackSubmits();
587
- ahoy.trackChanges();
518
+ ahoy.trackClicks("a, button, input[type=submit]");
519
+ ahoy.trackSubmits("form");
520
+ ahoy.trackChanges("input, textarea, select");
588
521
  };
589
522
 
590
523
  // push events from queue
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.2.0
4
+ version: 3.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-02 00:00:00.000000000 Z
11
+ date: 2021-08-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -127,7 +127,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
127
127
  - !ruby/object:Gem::Version
128
128
  version: '0'
129
129
  requirements: []
130
- rubygems_version: 3.2.3
130
+ rubygems_version: 3.2.22
131
131
  signing_key:
132
132
  specification_version: 4
133
133
  summary: Simple, powerful, first-party analytics for Rails