ahoy_matey 3.0.5 → 3.2.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: 17572400fdaab440040c753cf23084f2a47868e5c1ed12229e85fd1904e9c937
4
- data.tar.gz: cc917af046906e308830fa1ffa9543db1bc1fe0f79488ccaddef090e6366433d
3
+ metadata.gz: 51e11f3086c12a1418ea7436e291ca9d2f0c7e0aa3091c48c904d05b4be6a210
4
+ data.tar.gz: 5b5c6afc94c0db23f2f1616f6e9edc0ae95ff334e33704c26417705d06d04670
5
5
  SHA512:
6
- metadata.gz: 325caa06934a20bf7a58f573fff7359b7058cf63e3e7576c48faf1bd5d08ac2564810a28e5a775c3ac324170754db4de35d46f43c0b421f8c4977760af19d153
7
- data.tar.gz: e7af674522e09e3fb472b52546f30d10b743e2b2814efd97162d05cb1d4f9b13615dce211cd83e291da16e370a86cf7d8d384b16ad60d8af07c8ced8fbd17f59
6
+ metadata.gz: fd75680d3d04b2383c30b19848f88fcb838f1498e8819d2d758aec6443e85bfb2f73a421da968cb54628097631c777ac1f95dd1ca4bae6d6b22d748a44dcb84d
7
+ data.tar.gz: 2d336bd12312cddf4b76f213b1f7f84b1fc2f0b67302e75096d1eb21013982f532064b31894ba217f7ccbafdbaf5eb4c691a3a86350b8abf1449374f66015909
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## 3.2.0 (2021-03-01)
2
+
3
+ - Disabled geocoding by default for new installations
4
+ - Fixed deprecation warning with Active Record 6.1
5
+
6
+ ## 3.1.0 (2020-12-04)
7
+
8
+ - Added `instance` method
9
+ - Added `request` argument to `user_method`
10
+ - Updated Ahoy.js to 0.3.8
11
+ - Removed `exclude_method` call when geocoding
12
+
1
13
  ## 3.0.5 (2020-09-09)
2
14
 
3
15
  - Added `group_prop` method
data/README.md CHANGED
@@ -8,7 +8,7 @@ Track visits and events in Ruby, JavaScript, and native apps. Data is stored in
8
8
 
9
9
  :tangerine: Battle-tested at [Instacart](https://www.instacart.com/opensource)
10
10
 
11
- [![Build Status](https://travis-ci.org/ankane/ahoy.svg?branch=master)](https://travis-ci.org/ankane/ahoy)
11
+ [![Build Status](https://github.com/ankane/ahoy/workflows/build/badge.svg?branch=master)](https://github.com/ankane/ahoy/actions)
12
12
 
13
13
  ## Installation
14
14
 
@@ -70,10 +70,18 @@ Track an event with:
70
70
  ahoy.track("My second event", {language: "JavaScript"});
71
71
  ```
72
72
 
73
+ ### Native Apps
74
+
75
+ Check out [Ahoy iOS](https://github.com/namolnad/ahoy-ios) and [Ahoy Android](https://github.com/instacart/ahoy-android).
76
+
73
77
  ### GDPR Compliance
74
78
 
75
79
  Ahoy provides a number of options to help with GDPR compliance. See the [GDPR section](#gdpr-compliance-1) for more info.
76
80
 
81
+ ### Geocoding Setup
82
+
83
+ To enable geocoding, see the [Geocoding section](#geocoding).
84
+
77
85
  ## How It Works
78
86
 
79
87
  ### Visits
@@ -145,7 +153,7 @@ See [Ahoy.js](https://github.com/ankane/ahoy.js) for a complete list of features
145
153
 
146
154
  #### Native Apps
147
155
 
148
- For Android, check out [Ahoy Android](https://github.com/instacart/ahoy-android). For other platforms, see the [API spec](#api-spec).
156
+ See the docs for [Ahoy iOS](https://github.com/namolnad/ahoy-ios) and [Ahoy Android](https://github.com/instacart/ahoy-android).
149
157
 
150
158
  #### AMP
151
159
 
@@ -308,40 +316,56 @@ Ahoy.cookie_options = {same_site: :lax}
308
316
 
309
317
  ### Geocoding
310
318
 
311
- Disable geocoding with:
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).
320
+
321
+ To enable geocoding, update `config/initializers/ahoy.rb`:
312
322
 
313
323
  ```ruby
314
- Ahoy.geocode = false
324
+ Ahoy.geocode = true
315
325
  ```
316
326
 
317
- The default job queue is `:ahoy`. Change this with:
327
+ Geocoding is performed in a background job so it doesn’t slow down web requests. The default job queue is `:ahoy`. Change this with:
318
328
 
319
329
  ```ruby
320
330
  Ahoy.job_queue = :low_priority
321
331
  ```
322
332
 
323
- #### Geocoding Performance
333
+ ### Local Geocoding
324
334
 
325
- To avoid calls to a remote API, download the [GeoLite2 City database](https://dev.maxmind.com/geoip/geoip2/geolite2/) and configure Geocoder to use it.
326
-
327
- Add this line to your application’s Gemfile:
335
+ For privacy and performance, we recommend geocoding locally. Add this line to your application’s Gemfile:
328
336
 
329
337
  ```ruby
330
338
  gem 'maxminddb'
331
339
  ```
332
340
 
333
- And create an initializer at `config/initializers/geocoder.rb` with:
341
+ For city-level geocoding, download the [GeoLite2 City database](https://dev.maxmind.com/geoip/geoip2/geolite2/) and create `config/initializers/geocoder.rb` with:
334
342
 
335
343
  ```ruby
336
344
  Geocoder.configure(
337
345
  ip_lookup: :geoip2,
338
346
  geoip2: {
339
- file: Rails.root.join("lib", "GeoLite2-City.mmdb")
347
+ file: "path/to/GeoLite2-City.mmdb"
340
348
  }
341
349
  )
342
350
  ```
343
351
 
344
- If you use Heroku, you can use an unofficial buildpack like [this one](https://github.com/temedica/heroku-buildpack-maxmind-geolite2) to avoid including the database in your repo.
352
+ For country-level geocoding, install the `geoip-database` package. It’s preinstalled on Heroku. For Ubuntu, use:
353
+
354
+ ```sh
355
+ sudo apt-get install geoip-database
356
+ ```
357
+
358
+ And create `config/initializers/geocoder.rb` with:
359
+
360
+ ```ruby
361
+ Geocoder.configure(
362
+ ip_lookup: :maxmind_local,
363
+ maxmind_local: {
364
+ file: "/usr/share/GeoIP/GeoIP.dat",
365
+ package: :country
366
+ }
367
+ )
368
+ ```
345
369
 
346
370
  ### Token Generation
347
371
 
data/lib/ahoy.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # stdlib
1
2
  require "ipaddr"
2
3
 
3
4
  # dependencies
@@ -104,6 +105,14 @@ module Ahoy
104
105
  addr.mask(48).to_s
105
106
  end
106
107
  end
108
+
109
+ def self.instance
110
+ Thread.current[:ahoy]
111
+ end
112
+
113
+ def self.instance=(value)
114
+ Thread.current[:ahoy] = value
115
+ end
107
116
  end
108
117
 
109
118
  ActiveSupport.on_load(:action_controller) do
@@ -24,7 +24,11 @@ module Ahoy
24
24
  def user
25
25
  @user ||= begin
26
26
  if Ahoy.user_method.respond_to?(:call)
27
- Ahoy.user_method.call(controller)
27
+ if Ahoy.user_method.arity == 1
28
+ Ahoy.user_method.call(controller)
29
+ else
30
+ Ahoy.user_method.call(controller, request)
31
+ end
28
32
  else
29
33
  controller.send(Ahoy.user_method) if controller.respond_to?(Ahoy.user_method, true)
30
34
  end
@@ -39,12 +39,12 @@ module Ahoy
39
39
  end
40
40
 
41
41
  def set_ahoy_request_store
42
- previous_value = Thread.current[:ahoy]
42
+ previous_value = Ahoy.instance
43
43
  begin
44
- Thread.current[:ahoy] = ahoy
44
+ Ahoy.instance = ahoy
45
45
  yield
46
46
  ensure
47
- Thread.current[:ahoy] = previous_value
47
+ Ahoy.instance = previous_value
48
48
  end
49
49
  end
50
50
  end
data/lib/ahoy/model.rb CHANGED
@@ -7,7 +7,7 @@ module Ahoy
7
7
  end
8
8
  class_eval %{
9
9
  def set_ahoy_visit
10
- self.#{name} ||= Thread.current[:ahoy].try(:visit_or_create)
10
+ self.#{name} ||= Ahoy.instance.try(:visit_or_create)
11
11
  end
12
12
  }
13
13
  end
data/lib/ahoy/tracker.rb CHANGED
@@ -67,16 +67,12 @@ module Ahoy
67
67
  end
68
68
 
69
69
  def geocode(data)
70
- if exclude?
71
- debug "Geocode excluded"
72
- else
73
- data = {
74
- visit_token: visit_token
75
- }.merge(data).select { |_, v| v }
70
+ data = {
71
+ visit_token: visit_token
72
+ }.merge(data).select { |_, v| v }
76
73
 
77
- @store.geocode(data)
78
- true
79
- end
74
+ @store.geocode(data)
75
+ true
80
76
  rescue => e
81
77
  report_exception(e)
82
78
  end
data/lib/ahoy/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Ahoy
2
- VERSION = "3.0.5"
2
+ VERSION = "3.2.0"
3
3
  end
@@ -17,9 +17,7 @@ module Ahoy
17
17
  end
18
18
 
19
19
  def properties_type
20
- # use connection_config instead of connection.adapter
21
- # so database connection isn't needed
22
- case ActiveRecord::Base.connection_config[:adapter].to_s
20
+ case adapter
23
21
  when /postg/i # postgres, postgis
24
22
  "jsonb"
25
23
  when /mysql/i
@@ -29,8 +27,18 @@ module Ahoy
29
27
  end
30
28
  end
31
29
 
30
+ # use connection_config instead of connection.adapter
31
+ # so database connection isn't needed
32
+ def adapter
33
+ if ActiveRecord::VERSION::STRING.to_f >= 6.1
34
+ ActiveRecord::Base.connection_db_config.adapter.to_s
35
+ else
36
+ ActiveRecord::Base.connection_config[:adapter].to_s
37
+ end
38
+ end
39
+
32
40
  def rails52?
33
- ActiveRecord::VERSION::STRING >= "5.2"
41
+ ActiveRecord::VERSION::STRING.to_f >= 5.2
34
42
  end
35
43
 
36
44
  def migration_version
@@ -18,3 +18,8 @@ end
18
18
 
19
19
  # set to true for JavaScript tracking
20
20
  Ahoy.api = false
21
+
22
+ # set to true for geocoding
23
+ # we recommend configuring local geocoding first
24
+ # see https://github.com/ankane/ahoy#geocoding
25
+ Ahoy.geocode = false
@@ -3,3 +3,8 @@ end
3
3
 
4
4
  # set to true for JavaScript tracking
5
5
  Ahoy.api = false
6
+
7
+ # set to true for geocoding
8
+ # we recommend configuring local geocoding first
9
+ # see https://github.com/ankane/ahoy#geocoding
10
+ Ahoy.geocode = false
@@ -2,7 +2,7 @@
2
2
  * Ahoy.js
3
3
  * Simple, powerful JavaScript analytics
4
4
  * https://github.com/ankane/ahoy.js
5
- * v0.3.6
5
+ * v0.3.8
6
6
  * MIT License
7
7
  */
8
8
 
@@ -12,7 +12,96 @@
12
12
  (global = global || self, global.ahoy = factory());
13
13
  }(this, (function () { 'use strict';
14
14
 
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)};
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;
16
105
 
17
106
  // https://www.quirksmode.org/js/cookies.html
18
107
 
@@ -146,17 +235,23 @@
146
235
  element.webkitMatchesSelector;
147
236
 
148
237
  if (matches) {
149
- return matches.apply(element, [selector]);
238
+ if (matches.apply(element, [selector])) {
239
+ return element;
240
+ } else if (element.parentElement) {
241
+ return matchesSelector(element.parentElement, selector)
242
+ }
243
+ return null;
150
244
  } else {
151
245
  log("Unable to match");
152
- return false;
246
+ return null;
153
247
  }
154
248
  }
155
249
 
156
250
  function onEvent(eventName, selector, callback) {
157
251
  document.addEventListener(eventName, function (e) {
158
- if (matchesSelector(e.target, selector)) {
159
- callback(e);
252
+ var matchedElement = matchesSelector(e.target, selector);
253
+ if (matchedElement) {
254
+ callback.call(matchedElement, e);
160
255
  }
161
256
  });
162
257
  }
@@ -275,7 +370,7 @@
275
370
  // stringify so we keep the type
276
371
  data.events_json = JSON.stringify(data.events);
277
372
  delete data.events;
278
- window.navigator.sendBeacon(eventsUrl(), s(data));
373
+ window.navigator.sendBeacon(eventsUrl(), index_module_1(data));
279
374
  });
280
375
  }
281
376
 
@@ -299,13 +394,12 @@
299
394
  }
300
395
 
301
396
  function eventProperties(e) {
302
- var target = e.target;
303
397
  return cleanObject({
304
- tag: target.tagName.toLowerCase(),
305
- id: presence(target.id),
306
- "class": presence(target.className),
398
+ tag: this.tagName.toLowerCase(),
399
+ id: presence(this.id),
400
+ "class": presence(this.className),
307
401
  page: page(),
308
- section: getClosestSection(target)
402
+ section: getClosestSection(this)
309
403
  });
310
404
  }
311
405
 
@@ -465,24 +559,23 @@
465
559
 
466
560
  ahoy.trackClicks = function () {
467
561
  onEvent("click", "a, button, input[type=submit]", function (e) {
468
- var target = e.target;
469
- var properties = eventProperties(e);
470
- properties.text = properties.tag == "input" ? target.value : (target.textContent || target.innerText || target.innerHTML).replace(/[\s\r\n]+/g, " ").trim();
471
- properties.href = target.href;
562
+ var properties = eventProperties.call(this, e);
563
+ properties.text = properties.tag == "input" ? this.value : (this.textContent || this.innerText || this.innerHTML).replace(/[\s\r\n]+/g, " ").trim();
564
+ properties.href = this.href;
472
565
  ahoy.track("$click", properties);
473
566
  });
474
567
  };
475
568
 
476
569
  ahoy.trackSubmits = function () {
477
570
  onEvent("submit", "form", function (e) {
478
- var properties = eventProperties(e);
571
+ var properties = eventProperties.call(this, e);
479
572
  ahoy.track("$submit", properties);
480
573
  });
481
574
  };
482
575
 
483
576
  ahoy.trackChanges = function () {
484
577
  onEvent("change", "input, textarea, select", function (e) {
485
- var properties = eventProperties(e);
578
+ var properties = eventProperties.call(this, e);
486
579
  ahoy.track("$change", properties);
487
580
  });
488
581
  };
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.5
4
+ version: 3.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-09-10 00:00:00.000000000 Z
11
+ date: 2021-03-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -66,162 +66,8 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
- - !ruby/object:Gem::Dependency
70
- name: bundler
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: '0'
83
- - !ruby/object:Gem::Dependency
84
- name: rake
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - ">="
88
- - !ruby/object:Gem::Version
89
- version: '0'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - ">="
95
- - !ruby/object:Gem::Version
96
- version: '0'
97
- - !ruby/object:Gem::Dependency
98
- name: minitest
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - ">="
102
- - !ruby/object:Gem::Version
103
- version: '0'
104
- type: :development
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - ">="
109
- - !ruby/object:Gem::Version
110
- version: '0'
111
- - !ruby/object:Gem::Dependency
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
141
- requirement: !ruby/object:Gem::Requirement
142
- requirements:
143
- - - ">="
144
- - !ruby/object:Gem::Version
145
- version: '0'
146
- type: :development
147
- prerelease: false
148
- version_requirements: !ruby/object:Gem::Requirement
149
- requirements:
150
- - - ">="
151
- - !ruby/object:Gem::Version
152
- version: '0'
153
- - !ruby/object:Gem::Dependency
154
- name: pg
155
- requirement: !ruby/object:Gem::Requirement
156
- requirements:
157
- - - ">="
158
- - !ruby/object:Gem::Version
159
- version: '0'
160
- type: :development
161
- prerelease: false
162
- version_requirements: !ruby/object:Gem::Requirement
163
- requirements:
164
- - - ">="
165
- - !ruby/object:Gem::Version
166
- version: '0'
167
- - !ruby/object:Gem::Dependency
168
- name: mysql2
169
- requirement: !ruby/object:Gem::Requirement
170
- requirements:
171
- - - ">="
172
- - !ruby/object:Gem::Version
173
- version: '0'
174
- type: :development
175
- prerelease: false
176
- version_requirements: !ruby/object:Gem::Requirement
177
- requirements:
178
- - - ">="
179
- - !ruby/object:Gem::Version
180
- version: '0'
181
- - !ruby/object:Gem::Dependency
182
- name: mongoid
183
- requirement: !ruby/object:Gem::Requirement
184
- requirements:
185
- - - ">="
186
- - !ruby/object:Gem::Version
187
- version: '0'
188
- type: :development
189
- prerelease: false
190
- version_requirements: !ruby/object:Gem::Requirement
191
- requirements:
192
- - - ">="
193
- - !ruby/object:Gem::Version
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'
223
- description:
224
- email: andrew@chartkick.com
69
+ description:
70
+ email: andrew@ankane.org
225
71
  executables: []
226
72
  extensions: []
227
73
  extra_rdoc_files: []
@@ -266,7 +112,7 @@ homepage: https://github.com/ankane/ahoy
266
112
  licenses:
267
113
  - MIT
268
114
  metadata: {}
269
- post_install_message:
115
+ post_install_message:
270
116
  rdoc_options: []
271
117
  require_paths:
272
118
  - lib
@@ -281,8 +127,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
281
127
  - !ruby/object:Gem::Version
282
128
  version: '0'
283
129
  requirements: []
284
- rubygems_version: 3.1.2
285
- signing_key:
130
+ rubygems_version: 3.2.3
131
+ signing_key:
286
132
  specification_version: 4
287
133
  summary: Simple, powerful, first-party analytics for Rails
288
134
  test_files: []