ahoy_matey 3.2.0 → 3.3.0

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: 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