ahoy_matey 2.1.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/CONTRIBUTING.md +9 -7
  4. data/README.md +28 -5
  5. data/app/controllers/ahoy/base_controller.rb +1 -1
  6. data/app/controllers/ahoy/events_controller.rb +1 -1
  7. data/lib/ahoy.rb +9 -0
  8. data/lib/ahoy/base_store.rb +5 -1
  9. data/lib/ahoy/controller.rb +6 -2
  10. data/lib/ahoy/database_store.rb +4 -3
  11. data/lib/ahoy/engine.rb +1 -1
  12. data/lib/ahoy/helper.rb +40 -0
  13. data/lib/ahoy/query_methods.rb +1 -1
  14. data/lib/ahoy/tracker.rb +4 -2
  15. data/lib/ahoy/utils.rb +7 -0
  16. data/lib/ahoy/version.rb +1 -1
  17. data/lib/ahoy/visit_properties.rb +1 -7
  18. data/lib/generators/ahoy/templates/{active_record_event_model.rb → active_record_event_model.rb.tt} +0 -0
  19. data/lib/generators/ahoy/templates/{active_record_migration.rb → active_record_migration.rb.tt} +5 -0
  20. data/lib/generators/ahoy/templates/{active_record_visit_model.rb → active_record_visit_model.rb.tt} +0 -0
  21. data/lib/generators/ahoy/templates/{base_store_initializer.rb → base_store_initializer.rb.tt} +9 -0
  22. data/lib/generators/ahoy/templates/{database_store_initializer.rb → database_store_initializer.rb.tt} +3 -0
  23. data/lib/generators/ahoy/templates/{mongoid_event_model.rb → mongoid_event_model.rb.tt} +0 -0
  24. data/lib/generators/ahoy/templates/{mongoid_visit_model.rb → mongoid_visit_model.rb.tt} +5 -0
  25. metadata +15 -37
  26. data/.github/ISSUE_TEMPLATE.md +0 -7
  27. data/.gitignore +0 -17
  28. data/Gemfile +0 -6
  29. data/Rakefile +0 -9
  30. data/ahoy_matey.gemspec +0 -37
  31. data/docs/Ahoy-2-Upgrade.md +0 -149
  32. data/docs/Data-Store-Examples.md +0 -240
  33. data/test/query_methods/mongoid_test.rb +0 -23
  34. data/test/query_methods/mysql_json_test.rb +0 -18
  35. data/test/query_methods/mysql_text_test.rb +0 -19
  36. data/test/query_methods/postgresql_hstore_test.rb +0 -20
  37. data/test/query_methods/postgresql_json_test.rb +0 -18
  38. data/test/query_methods/postgresql_jsonb_test.rb +0 -19
  39. data/test/query_methods/postgresql_text_test.rb +0 -19
  40. data/test/test_helper.rb +0 -100
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f0b1c52401a07fcd017af2e52aca841090ee47a1330c9e2c941d19a82fd2b6d7
4
- data.tar.gz: 0d9ddec6c4bbe552672dc41cc444cffc2fa0ffda7b4a6a63200bd2de8d8e65e6
3
+ metadata.gz: 65d9ae15e5742baab80cca54630bd9c446dde514ab820c16163bf856c23839e6
4
+ data.tar.gz: ca4d9f657de0ef24e56334ae11045ccd615012dac032a0c8e42f1acfc2973da4
5
5
  SHA512:
6
- metadata.gz: a8c9083093116b8931861b9f180902038871d76e6a363833bd10d55bbd9e8ea89346f62a609ec3990da3232b24e151e075fa4fd3641a247850e18c40db30d912
7
- data.tar.gz: a5fe8df81cf9510e8472cda07ff31e2c85d7b2af2102655303623a6677937fb922706db9f2cabac78e49aaf132936c19834be09c3e8acda4ebea3eec7ef82063
6
+ metadata.gz: 931116ac80eb7774c70fdfeeb4168724fc1608cb1476e9fc7568686d1c523bd4fd3ab5f023249e26b654088136f7004a8d09537d628db8610b23268270b9363a
7
+ data.tar.gz: 85e15776ffb648e5385658def3525cf0dfc3c8a0d2c1060e5f14af7a50a806c17640bf6741ccc8ed34b8b98b1b3afd3894a534faa539825171b9602ba4279afa
@@ -1,3 +1,8 @@
1
+ ## 2.2.0
2
+
3
+ - Added `amp_event` helper
4
+ - Improved bot detection for Device Detector
5
+
1
6
  ## 2.1.0
2
7
 
3
8
  - Added option for IP masking
@@ -2,17 +2,15 @@
2
2
 
3
3
  First, thanks for wanting to contribute. You’re awesome! :heart:
4
4
 
5
- ## Questions
5
+ ## Help
6
6
 
7
- Use [Stack Overflow](https://stackoverflow.com/) with the tag `ahoy`.
7
+ We’re not able to provide support through GitHub Issues. If you’re looking for help with your code, try posting on [Stack Overflow](https://stackoverflow.com/).
8
8
 
9
- ## Feature Requests
9
+ All features should be documented. If you don’t see a feature in the docs, assume it doesn’t exist.
10
10
 
11
- Create an issue. Start the title with `[Idea]`.
11
+ ## Bugs
12
12
 
13
- ## Issues
14
-
15
- Think you’ve discovered an issue?
13
+ Think you’ve discovered a bug?
16
14
 
17
15
  1. Search existing issues to see if it’s been reported.
18
16
  2. Try the `master` branch to make sure it hasn’t been fixed.
@@ -26,6 +24,10 @@ If the above steps don’t help, create an issue. Include:
26
24
  - Detailed steps to reproduce
27
25
  - Complete backtraces for exceptions
28
26
 
27
+ ## New Features
28
+
29
+ If you’d like to discuss a new feature, create an issue and start the title with `[Idea]`.
30
+
29
31
  ## Pull Requests
30
32
 
31
33
  Fork the project and create a pull request. A few tips:
data/README.md CHANGED
@@ -4,8 +4,6 @@
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
- **Ahoy 2.0 was recently released!** See [how to upgrade](docs/Ahoy-2-Upgrade.md)
8
-
9
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)
10
8
 
11
9
  :tangerine: Battle-tested at [Instacart](https://www.instacart.com/opensource)
@@ -34,7 +32,7 @@ Track your first event from a controller with:
34
32
  ahoy.track "My first event", {language: "Ruby"}
35
33
  ```
36
34
 
37
- ### JavaScript & Native Apps
35
+ ### JavaScript, Native Apps, & AMP
38
36
 
39
37
  First, enable the API in `config/initializers/ahoy.rb`:
40
38
 
@@ -58,6 +56,12 @@ ahoy.track("My second event", {language: "JavaScript"});
58
56
 
59
57
  For Android, check out [Ahoy Android](https://github.com/instacart/ahoy-android). For other platforms, see the [API spec](#api-spec).
60
58
 
59
+ For AMP, track an event with:
60
+
61
+ ```erb
62
+ <%= amp_event "My third event", language: "AMP" %>
63
+ ```
64
+
61
65
  ### GDPR Compliance
62
66
 
63
67
  Ahoy provides a number of options to help with GDPR compliance. See the [GDPR section](#gdpr-compliance-1) for more info.
@@ -93,7 +97,7 @@ Ahoy.server_side_visits = :when_needed
93
97
 
94
98
  Each event has a `name` and `properties`.
95
99
 
96
- There are three ways to track events.
100
+ There are several ways to track events.
97
101
 
98
102
  #### JavaScript
99
103
 
@@ -133,6 +137,17 @@ end
133
137
 
134
138
  For Android, check out [Ahoy Android](https://github.com/instacart/ahoy-android). For other platforms, see the [API spec](#api-spec).
135
139
 
140
+ #### AMP
141
+
142
+ ```erb
143
+ <head>
144
+ <script async custom-element="amp-analytics" src="https://cdn.ampproject.org/v0/amp-analytics-0.1.js"></script>
145
+ </head>
146
+ <body>
147
+ <%= amp_event "Viewed article", title: "Analytics with Rails" %>
148
+ </body>
149
+ ```
150
+
136
151
  ### Associated Models
137
152
 
138
153
  Say we want to associate orders with visits. Just add `visitable` to the model.
@@ -166,7 +181,7 @@ end
166
181
  Customize the column and class name with:
167
182
 
168
183
  ```ruby
169
- visitable :sign_up_visit, class_name: "Visit"
184
+ visitable :sign_up_visit, class_name: "Ahoy::Visit"
170
185
  ```
171
186
 
172
187
  ### Users
@@ -571,6 +586,14 @@ import ahoy from "ahoy.js";
571
586
 
572
587
  ## Upgrading
573
588
 
589
+ ### 2.2
590
+
591
+ Ahoy now ships with better bot detection if you use Device Detector. This should be more accurate but can significantly reduce the number of visits recorded. For existing installs, it’s opt-in to start. To use it, add to `config/initializers/ahoy.rb`:
592
+
593
+ ```ruby
594
+ Ahoy.bot_detection_version = 2
595
+ ```
596
+
574
597
  ### 2.1
575
598
 
576
599
  Ahoy recommends [Device Detector](https://github.com/podigee/device_detector) for user agent parsing and makes it the default for new installations. To switch, add to `config/initializers/ahoy.rb`:
@@ -6,7 +6,7 @@ module Ahoy
6
6
  skip_after_action(*filters, raise: false)
7
7
  skip_around_action(*filters, raise: false)
8
8
  else
9
- skip_action_callback *filters
9
+ skip_action_callback(*filters)
10
10
  end
11
11
  before_action :verify_request_size
12
12
  before_action :renew_cookies
@@ -3,7 +3,7 @@ module Ahoy
3
3
  def create
4
4
  events =
5
5
  if params[:name]
6
- # legacy API
6
+ # legacy API and AMP
7
7
  [request.params]
8
8
  elsif params[:events]
9
9
  request.params[:events]
@@ -6,9 +6,11 @@ require "addressable/uri"
6
6
  require "geocoder"
7
7
  require "safely/core"
8
8
 
9
+ require "ahoy/utils"
9
10
  require "ahoy/base_store"
10
11
  require "ahoy/controller"
11
12
  require "ahoy/database_store"
13
+ require "ahoy/helper"
12
14
  require "ahoy/model"
13
15
  require "ahoy/query_methods"
14
16
  require "ahoy/tracker"
@@ -69,6 +71,9 @@ module Ahoy
69
71
  mattr_accessor :track_bots
70
72
  self.track_bots = false
71
73
 
74
+ mattr_accessor :bot_detection_version
75
+ self.bot_detection_version = 1
76
+
72
77
  mattr_accessor :token_generator
73
78
  self.token_generator = -> { SecureRandom.uuid }
74
79
 
@@ -102,6 +107,10 @@ ActiveSupport.on_load(:active_record) do
102
107
  extend Ahoy::Model
103
108
  end
104
109
 
110
+ ActiveSupport.on_load(:action_view) do
111
+ include Ahoy::Helper
112
+ end
113
+
105
114
  # Mongoid
106
115
  if defined?(ActiveModel)
107
116
  ActiveModel::Callbacks.include(Ahoy::Model)
@@ -50,7 +50,11 @@ module Ahoy
50
50
  @bot = begin
51
51
  if request
52
52
  if Ahoy.user_agent_parser == :device_detector
53
- DeviceDetector.new(request.user_agent).bot?
53
+ if Ahoy.bot_detection_version == 2
54
+ DeviceDetector.new(request.user_agent).device_type.nil?
55
+ else
56
+ DeviceDetector.new(request.user_agent).bot?
57
+ end
54
58
  else
55
59
  Browser.new(request.user_agent).bot?
56
60
  end
@@ -31,8 +31,12 @@ module Ahoy
31
31
  end
32
32
 
33
33
  def track_ahoy_visit
34
- if ahoy.new_visit?
35
- ahoy.track_visit(defer: Ahoy.server_side_visits != true)
34
+ defer = Ahoy.server_side_visits != true
35
+
36
+ if defer && !Ahoy.cookies
37
+ # avoid calling new_visit?, which triggers a database call
38
+ elsif ahoy.new_visit?
39
+ ahoy.track_visit(defer: defer)
36
40
  end
37
41
  end
38
42
 
@@ -12,10 +12,11 @@ module Ahoy
12
12
  end
13
13
 
14
14
  def track_event(data)
15
- visit = visit_or_create
15
+ visit = visit_or_create(started_at: data[:time])
16
16
  if visit
17
17
  event = event_model.new(slice_data(event_model, data))
18
18
  event.visit = visit
19
+ event.time = visit.started_at if event.time < visit.started_at
19
20
  begin
20
21
  event.save!
21
22
  rescue => e
@@ -58,8 +59,8 @@ module Ahoy
58
59
  end
59
60
 
60
61
  # if we don't have a visit, let's try to create one first
61
- def visit_or_create
62
- ahoy.track_visit if !visit && Ahoy.server_side_visits
62
+ def visit_or_create(started_at: nil)
63
+ ahoy.track_visit(started_at: started_at) if !visit && Ahoy.server_side_visits
63
64
  visit
64
65
  end
65
66
 
@@ -1,6 +1,6 @@
1
1
  module Ahoy
2
2
  class Engine < ::Rails::Engine
3
- initializer "ahoy", after: "sprockets.environment" do |app|
3
+ initializer "ahoy", after: "sprockets.environment" do
4
4
  # allow Devise to be loaded after Ahoy
5
5
  require "ahoy/warden" if defined?(Warden)
6
6
 
@@ -0,0 +1,40 @@
1
+ module Ahoy
2
+ module Helper
3
+ def amp_event(name, properties = {})
4
+ url = Ahoy::Engine.routes.url_helpers.events_url(
5
+ url_options.slice(:host, :port, :protocol).merge(
6
+ name: name,
7
+ properties: properties,
8
+ screen_width: "SCREEN_WIDTH",
9
+ screen_height: "SCREEN_HEIGHT",
10
+ platform: "Web",
11
+ landing_page: "AMPDOC_URL",
12
+ referrer: "DOCUMENT_REFERRER",
13
+ random: "RANDOM"
14
+ )
15
+ )
16
+ url = "#{url}&visit_token=${clientId(ahoy_visit)}&visitor_token=${clientId(ahoy_visitor)}"
17
+
18
+ content_tag "amp-analytics" do
19
+ content_tag "script", type: "application/json" do
20
+ json_escape({
21
+ requests: {
22
+ pageview: url
23
+ },
24
+ triggers: {
25
+ trackPageview: {
26
+ on: "visible",
27
+ request: "pageview"
28
+ }
29
+ },
30
+ transport: {
31
+ beacon: true,
32
+ xhrpost: true,
33
+ image: false
34
+ }
35
+ }.to_json).html_safe
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -27,7 +27,7 @@ module Ahoy
27
27
  v = "true"
28
28
  end
29
29
 
30
- relation = relation.where("JSON_UNQUOTE(properties -> ?) = ?", "$.#{k.to_s}", v.as_json)
30
+ relation = relation.where("JSON_UNQUOTE(properties -> ?) = ?", "$.#{k}", v.as_json)
31
31
  end
32
32
  else
33
33
  properties.each do |k, v|
@@ -37,7 +37,7 @@ module Ahoy
37
37
  report_exception(e)
38
38
  end
39
39
 
40
- def track_visit(defer: false)
40
+ def track_visit(defer: false, started_at: nil)
41
41
  if exclude?
42
42
  debug "Visit excluded"
43
43
  elsif missing_params?
@@ -52,7 +52,7 @@ module Ahoy
52
52
  visit_token: visit_token,
53
53
  visitor_token: visitor_token,
54
54
  user_id: user.try(:id),
55
- started_at: trusted_time,
55
+ started_at: trusted_time(started_at),
56
56
  }.merge(visit_properties).select { |_, v| v }
57
57
 
58
58
  @store.track_visit(data)
@@ -197,6 +197,7 @@ module Ahoy
197
197
  end
198
198
 
199
199
  def report_exception(e)
200
+ raise e if Rails.env.development? || Rails.env.test?
200
201
  Safely.report_exception(e)
201
202
  end
202
203
 
@@ -273,6 +274,7 @@ module Ahoy
273
274
  end
274
275
 
275
276
  def ensure_token(token)
277
+ token = Ahoy::Utils.ensure_utf8(token)
276
278
  token.to_s.gsub(/[^a-z0-9\-]/i, "").first(64) if token
277
279
  end
278
280
 
@@ -0,0 +1,7 @@
1
+ module Ahoy
2
+ module Utils
3
+ def self.ensure_utf8(str)
4
+ str.encode("UTF-8", "binary", invalid: :replace, undef: :replace, replace: "") if str
5
+ end
6
+ end
7
+ end
@@ -1,3 +1,3 @@
1
1
  module Ahoy
2
- VERSION = "2.1.0"
2
+ VERSION = "2.2.0"
3
3
  end
@@ -103,7 +103,7 @@ module Ahoy
103
103
  def request_properties
104
104
  {
105
105
  ip: ip,
106
- user_agent: ensure_utf8(request.user_agent),
106
+ user_agent: Ahoy::Utils.ensure_utf8(request.user_agent),
107
107
  referrer: referrer,
108
108
  landing_page: landing_page,
109
109
  platform: params["platform"],
@@ -113,11 +113,5 @@ module Ahoy
113
113
  screen_width: params["screen_width"]
114
114
  }
115
115
  end
116
-
117
- def ensure_utf8(str)
118
- if str
119
- str.encode("UTF-8", "binary", invalid: :replace, undef: :replace, replace: "")
120
- end
121
- end
122
116
  end
123
117
  end
@@ -34,6 +34,11 @@ class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version
34
34
  t.string :utm_content
35
35
  t.string :utm_campaign
36
36
 
37
+ # native apps
38
+ t.string :app_version
39
+ t.string :os_version
40
+ t.string :platform
41
+
37
42
  t.timestamp :started_at
38
43
  end
39
44
 
@@ -15,3 +15,12 @@ class Ahoy::Store < Ahoy::BaseStore
15
15
  # !!!
16
16
  end
17
17
  end
18
+
19
+ # set to true for JavaScript tracking
20
+ Ahoy.api = false
21
+
22
+ # better user agent parsing
23
+ Ahoy.user_agent_parser = :device_detector
24
+
25
+ # better bot detection
26
+ Ahoy.bot_detection_version = 2
@@ -6,3 +6,6 @@ Ahoy.api = false
6
6
 
7
7
  # better user agent parsing
8
8
  Ahoy.user_agent_parser = :device_detector
9
+
10
+ # better bot detection
11
+ Ahoy.bot_detection_version = 2
@@ -41,6 +41,11 @@ class Ahoy::Visit
41
41
  field :utm_content, type: String
42
42
  field :utm_campaign, type: String
43
43
 
44
+ # native apps
45
+ field :app_version, type: String
46
+ field :os_version, type: String
47
+ field :platform, type: String
48
+
44
49
  field :started_at, type: Time
45
50
 
46
51
  index({visit_token: 1}, {unique: true})
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: 2.1.0
4
+ version: 2.2.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: 2018-05-18 00:00:00.000000000 Z
11
+ date: 2019-01-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -235,37 +235,31 @@ dependencies:
235
235
  - !ruby/object:Gem::Version
236
236
  version: '0'
237
237
  description:
238
- email:
239
- - andrew@chartkick.com
238
+ email: andrew@chartkick.com
240
239
  executables: []
241
240
  extensions: []
242
241
  extra_rdoc_files: []
243
242
  files:
244
- - ".github/ISSUE_TEMPLATE.md"
245
- - ".gitignore"
246
243
  - CHANGELOG.md
247
244
  - CONTRIBUTING.md
248
- - Gemfile
249
245
  - LICENSE.txt
250
246
  - README.md
251
- - Rakefile
252
- - ahoy_matey.gemspec
253
247
  - app/controllers/ahoy/base_controller.rb
254
248
  - app/controllers/ahoy/events_controller.rb
255
249
  - app/controllers/ahoy/visits_controller.rb
256
250
  - app/jobs/ahoy/geocode_job.rb
257
251
  - app/jobs/ahoy/geocode_v2_job.rb
258
252
  - config/routes.rb
259
- - docs/Ahoy-2-Upgrade.md
260
- - docs/Data-Store-Examples.md
261
253
  - lib/ahoy.rb
262
254
  - lib/ahoy/base_store.rb
263
255
  - lib/ahoy/controller.rb
264
256
  - lib/ahoy/database_store.rb
265
257
  - lib/ahoy/engine.rb
258
+ - lib/ahoy/helper.rb
266
259
  - lib/ahoy/model.rb
267
260
  - lib/ahoy/query_methods.rb
268
261
  - lib/ahoy/tracker.rb
262
+ - lib/ahoy/utils.rb
269
263
  - lib/ahoy/version.rb
270
264
  - lib/ahoy/visit_properties.rb
271
265
  - lib/ahoy/warden.rb
@@ -274,21 +268,13 @@ files:
274
268
  - lib/generators/ahoy/base_generator.rb
275
269
  - lib/generators/ahoy/install_generator.rb
276
270
  - lib/generators/ahoy/mongoid_generator.rb
277
- - lib/generators/ahoy/templates/active_record_event_model.rb
278
- - lib/generators/ahoy/templates/active_record_migration.rb
279
- - lib/generators/ahoy/templates/active_record_visit_model.rb
280
- - lib/generators/ahoy/templates/base_store_initializer.rb
281
- - lib/generators/ahoy/templates/database_store_initializer.rb
282
- - lib/generators/ahoy/templates/mongoid_event_model.rb
283
- - lib/generators/ahoy/templates/mongoid_visit_model.rb
284
- - test/query_methods/mongoid_test.rb
285
- - test/query_methods/mysql_json_test.rb
286
- - test/query_methods/mysql_text_test.rb
287
- - test/query_methods/postgresql_hstore_test.rb
288
- - test/query_methods/postgresql_json_test.rb
289
- - test/query_methods/postgresql_jsonb_test.rb
290
- - test/query_methods/postgresql_text_test.rb
291
- - test/test_helper.rb
271
+ - lib/generators/ahoy/templates/active_record_event_model.rb.tt
272
+ - lib/generators/ahoy/templates/active_record_migration.rb.tt
273
+ - lib/generators/ahoy/templates/active_record_visit_model.rb.tt
274
+ - lib/generators/ahoy/templates/base_store_initializer.rb.tt
275
+ - lib/generators/ahoy/templates/database_store_initializer.rb.tt
276
+ - lib/generators/ahoy/templates/mongoid_event_model.rb.tt
277
+ - lib/generators/ahoy/templates/mongoid_visit_model.rb.tt
292
278
  - vendor/assets/javascripts/ahoy.js
293
279
  homepage: https://github.com/ankane/ahoy
294
280
  licenses:
@@ -302,7 +288,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
302
288
  requirements:
303
289
  - - ">="
304
290
  - !ruby/object:Gem::Version
305
- version: '0'
291
+ version: '2.2'
306
292
  required_rubygems_version: !ruby/object:Gem::Requirement
307
293
  requirements:
308
294
  - - ">="
@@ -313,13 +299,5 @@ rubyforge_project:
313
299
  rubygems_version: 2.7.6
314
300
  signing_key:
315
301
  specification_version: 4
316
- summary: Simple, powerful visit tracking for Rails
317
- test_files:
318
- - test/query_methods/mongoid_test.rb
319
- - test/query_methods/mysql_json_test.rb
320
- - test/query_methods/mysql_text_test.rb
321
- - test/query_methods/postgresql_hstore_test.rb
322
- - test/query_methods/postgresql_json_test.rb
323
- - test/query_methods/postgresql_jsonb_test.rb
324
- - test/query_methods/postgresql_text_test.rb
325
- - test/test_helper.rb
302
+ summary: Simple, powerful analytics for Rails
303
+ test_files: []
@@ -1,7 +0,0 @@
1
- Hi,
2
-
3
- Before creating an issue, please check out the Contributing Guide:
4
-
5
- https://github.com/ankane/ahoy/blob/master/CONTRIBUTING.md
6
-
7
- Thanks!
data/.gitignore DELETED
@@ -1,17 +0,0 @@
1
- *.gem
2
- *.rbc
3
- .bundle
4
- .config
5
- .yardoc
6
- Gemfile.lock
7
- InstalledFiles
8
- _yardoc
9
- coverage
10
- doc/
11
- lib/bundler/man
12
- pkg
13
- rdoc
14
- spec/reports
15
- test/tmp
16
- test/version_tmp
17
- tmp
data/Gemfile DELETED
@@ -1,6 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- # Specify your gem's dependencies in ahoy.gemspec
4
- gemspec
5
-
6
- gem "rails", "~> 5.2.0"
data/Rakefile DELETED
@@ -1,9 +0,0 @@
1
- require "bundler/gem_tasks"
2
- require "rake/testtask"
3
-
4
- task default: :test
5
- Rake::TestTask.new do |t|
6
- t.libs << "test"
7
- t.pattern = "test/**/*_test.rb"
8
- t.warning = false
9
- end
@@ -1,37 +0,0 @@
1
- # coding: utf-8
2
- lib = File.expand_path("../lib", __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require "ahoy/version"
5
-
6
- Gem::Specification.new do |spec|
7
- spec.name = "ahoy_matey"
8
- spec.version = Ahoy::VERSION
9
- spec.authors = ["Andrew Kane"]
10
- spec.email = ["andrew@chartkick.com"]
11
- spec.summary = "Simple, powerful visit tracking for Rails"
12
- spec.homepage = "https://github.com/ankane/ahoy"
13
- spec.license = "MIT"
14
-
15
- spec.files = `git ls-files -z`.split("\x0")
16
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
- spec.require_paths = ["lib"]
19
-
20
- spec.add_dependency "railties", ">= 4.2"
21
- spec.add_dependency "addressable"
22
- spec.add_dependency "geocoder", ">= 1.4.5"
23
- spec.add_dependency "browser", "~> 2.0"
24
- spec.add_dependency "referer-parser", ">= 0.3"
25
- spec.add_dependency "user_agent_parser"
26
- spec.add_dependency "request_store"
27
- spec.add_dependency "safely_block", ">= 0.2.1"
28
- spec.add_dependency "device_detector"
29
-
30
- spec.add_development_dependency "bundler"
31
- spec.add_development_dependency "rake"
32
- spec.add_development_dependency "minitest"
33
- spec.add_development_dependency "activerecord"
34
- spec.add_development_dependency "pg"
35
- spec.add_development_dependency "mysql2"
36
- spec.add_development_dependency "mongoid"
37
- end
@@ -1,149 +0,0 @@
1
- # Ahoy 2 Upgrade
2
-
3
- Ahoy 2.0 brings a number of exciting changes:
4
-
5
- - jQuery is no longer required
6
- - Uses `navigator.sendBeacon` by default in supported browsers
7
- - Simpler interface for data stores
8
-
9
- ## How to Upgrade
10
-
11
- Update your Gemfile:
12
-
13
- ```ruby
14
- gem 'ahoy_matey', '~> 2'
15
- ```
16
-
17
- And run:
18
-
19
- ```sh
20
- bundle update ahoy_matey
21
- ```
22
-
23
- Add to `config/initializers/ahoy.rb`:
24
-
25
- ```ruby
26
- Ahoy.api = true
27
- Ahoy.server_side_visits = false
28
- ```
29
-
30
- You can also try the new `Ahoy.server_side_visits = :when_needed` to automatically create visits server-side when needed for events and `visitable`.
31
-
32
- If you use `visitable`, add `class_name` to each instance:
33
-
34
- ```ruby
35
- visitable class_name: "Visit"
36
- ```
37
-
38
- Then follow the instructions for your data store.
39
-
40
- - [ActiveRecordTokenStore](#activerecordtokenstore)
41
- - [ActiveRecordStore](#activerecordstore)
42
- - [MongoidStore](#mongoidstore)
43
- - [Others](#others)
44
-
45
- ## Data Stores
46
-
47
- ### ActiveRecordTokenStore
48
-
49
- In `config/initializers/ahoy.rb`, replace `Ahoy::Store` with:
50
-
51
- ```ruby
52
- class Ahoy::Store < Ahoy::DatabaseStore
53
- def visit_model
54
- Visit
55
- end
56
- end
57
- ```
58
-
59
- ### ActiveRecordStore
60
-
61
- Add [uuidtools](https://github.com/sporkmonger/uuidtools) to your Gemfile.
62
-
63
- In `config/initializers/ahoy.rb`, replace `Ahoy::Store` with:
64
-
65
- ```ruby
66
- class Ahoy::Store < Ahoy::DatabaseStore
67
- def track_visit(data)
68
- data[:id] = ensure_uuid(data.delete(:visit_token))
69
- data[:visitor_id] = ensure_uuid(data.delete(:visitor_token))
70
- super(data)
71
- end
72
-
73
- def track_event(data)
74
- data[:id] = ensure_uuid(data.delete(:event_id))
75
- super(data)
76
- end
77
-
78
- def visit
79
- @visit ||= visit_model.find_by(id: ensure_uuid(ahoy.visit_token)) if ahoy.visit_token
80
- end
81
-
82
- def visit_model
83
- Visit
84
- end
85
-
86
- UUID_NAMESPACE = UUIDTools::UUID.parse("a82ae811-5011-45ab-a728-569df7499c5f")
87
-
88
- def ensure_uuid(id)
89
- UUIDTools::UUID.parse(id).to_s
90
- rescue
91
- UUIDTools::UUID.sha1_create(UUID_NAMESPACE, id).to_s
92
- end
93
- end
94
- ```
95
-
96
- ### MongoidStore
97
-
98
- In `config/initializers/ahoy.rb`, replace `Ahoy::Store` with:
99
-
100
- ```ruby
101
- class Ahoy::Store < Ahoy::DatabaseStore
102
- def track_visit(data)
103
- data[:_id] = binary_uuid(data.delete(:visit_token))
104
- data[:visitor_id] = binary_uuid(data.delete(:visitor_token))
105
- super(data)
106
- end
107
-
108
- def track_event(data)
109
- data[:_id] = binary_uuid(data.delete(:event_id))
110
- super(data)
111
- end
112
-
113
- def geocode(data)
114
- visit_model.where(id: binary_uuid(ahoy.visit_token)).find_one_and_update({"$set": data}, {upsert: true})
115
- end
116
-
117
- def visit
118
- @visit ||= visit_model.where(id: binary_uuid(ahoy.visit_token)).first if ahoy.visit_token
119
- end
120
-
121
- def visit_model
122
- Visit
123
- end
124
-
125
- def binary_uuid(token)
126
- token = token.delete("-")
127
- if defined?(::BSON)
128
- ::BSON::Binary.new(token, :uuid)
129
- elsif defined?(::Moped::BSON)
130
- ::Moped::BSON::Binary.new(:uuid, token)
131
- else
132
- token
133
- end
134
- end
135
- end
136
- ```
137
-
138
- ### Others
139
-
140
- Check out the [data store examples](Data-Store-Examples.md).
141
-
142
- ## Throttling
143
-
144
- Throttling was removed due to limited practical usefulness. See [instructions for adding it back](../README.md#throttling) if you need it.
145
-
146
- ## Options
147
-
148
- - The `mount` option was renamed to `api`
149
- - The `track_visits_immediately` option was renamed to `server_side_visits`
@@ -1,240 +0,0 @@
1
- # Data Store Examples
2
-
3
- - [Kafka](#kafka)
4
- - [RabbitMQ](#rabbitmq)
5
- - [Fluentd](#fluentd)
6
- - [NATS](#nats)
7
- - [NSQ](#nsq)
8
- - [Amazon Kinesis Firehose](#amazon-kinesis-firehose)
9
-
10
- ### Kafka
11
-
12
- Add [ruby-kafka](https://github.com/zendesk/ruby-kafka) to your Gemfile.
13
-
14
- ```ruby
15
- class Ahoy::Store < Ahoy::BaseStore
16
- def track_visit(data)
17
- post("ahoy_visits", data)
18
- end
19
-
20
- def track_event(data)
21
- post("ahoy_events", data)
22
- end
23
-
24
- def geocode(data)
25
- post("ahoy_geocode", data)
26
- end
27
-
28
- def authenticate(data)
29
- post("ahoy_auth", data)
30
- end
31
-
32
- private
33
-
34
- def post(topic, data)
35
- producer.produce(data.to_json, topic: topic)
36
- end
37
-
38
- def producer
39
- @producer ||= begin
40
- client =
41
- Kafka.new(
42
- seed_brokers: ENV["KAFKA_URL"] || "localhost:9092",
43
- logger: Rails.logger
44
- )
45
- producer = client.async_producer(delivery_interval: 3)
46
- at_exit { producer.shutdown }
47
- producer
48
- end
49
- end
50
- end
51
- ```
52
-
53
- ### RabbitMQ
54
-
55
- Add [bunny](https://github.com/ruby-amqp/bunny) to your Gemfile.
56
-
57
- ```ruby
58
- class Ahoy::Store < Ahoy::BaseStore
59
- def track_visit(data)
60
- post("ahoy_visits", data)
61
- end
62
-
63
- def track_event(data)
64
- post("ahoy_events", data)
65
- end
66
-
67
- def geocode(data)
68
- post("ahoy_geocode", data)
69
- end
70
-
71
- def authenticate(data)
72
- post("ahoy_auth", data)
73
- end
74
-
75
- private
76
-
77
- def post(topic, message)
78
- channel.queue(topic, durable: true).publish(message.to_json)
79
- end
80
-
81
- def channel
82
- @channel ||= begin
83
- conn = Bunny.new
84
- conn.start
85
- conn.create_channel
86
- end
87
- end
88
- end
89
- ```
90
-
91
- ### Fluentd
92
-
93
- Add [fluent-logger](https://github.com/fluent/fluent-logger-ruby) to your Gemfile.
94
-
95
- ```ruby
96
- class Ahoy::Store < Ahoy::BaseStore
97
- def track_visit(data)
98
- post("ahoy_visits", data)
99
- end
100
-
101
- def track_event(data)
102
- post("ahoy_events", data)
103
- end
104
-
105
- def geocode(data)
106
- post("ahoy_geocode", data)
107
- end
108
-
109
- def authenticate(data)
110
- post("ahoy_auth", data)
111
- end
112
-
113
- private
114
-
115
- def post(topic, message)
116
- logger.post(topic, message)
117
- end
118
-
119
- def logger
120
- @logger ||= Fluent::Logger::FluentLogger.new("ahoy", host: "localhost", port: 24224)
121
- end
122
- end
123
- ```
124
-
125
- ### NATS
126
-
127
- Add [nats-pure](https://github.com/nats-io/pure-ruby-nats) to your Gemfile.
128
-
129
- ```ruby
130
- class Ahoy::Store < Ahoy::BaseStore
131
- def track_visit(data)
132
- post("ahoy_visits", data)
133
- end
134
-
135
- def track_event(data)
136
- post("ahoy_events", data)
137
- end
138
-
139
- def geocode(data)
140
- post("ahoy_geocode", data)
141
- end
142
-
143
- def authenticate(data)
144
- post("ahoy_auth", data)
145
- end
146
-
147
- private
148
-
149
- def post(topic, data)
150
- client.publish(topic, data.to_json)
151
- end
152
-
153
- def client
154
- @client ||= begin
155
- require "nats/io/client"
156
- client = NATS::IO::Client.new
157
- client.connect(servers: (ENV["NATS_URL"] || "nats://127.0.0.1:4222").split(","))
158
- client
159
- end
160
- end
161
- end
162
- ```
163
-
164
- ### NSQ
165
-
166
- Add [nsq-ruby](https://github.com/wistia/nsq-ruby) to your Gemfile.
167
-
168
- ```ruby
169
- class Ahoy::Store < Ahoy::BaseStore
170
- def track_visit(data)
171
- post("ahoy_visits", data)
172
- end
173
-
174
- def track_event(data)
175
- post("ahoy_events", data)
176
- end
177
-
178
- def geocode(data)
179
- post("ahoy_geocode", data)
180
- end
181
-
182
- def authenticate(data)
183
- post("ahoy_auth", data)
184
- end
185
-
186
- private
187
-
188
- def post(topic, data)
189
- client.write_to_topic(topic, data.to_json)
190
- end
191
-
192
- def client
193
- @client ||= begin
194
- require "nsq"
195
- client = Nsq::Producer.new(
196
- nsqd: ENV["NSQ_URL"] || "127.0.0.1:4150"
197
- )
198
- at_exit { client.terminate }
199
- client
200
- end
201
- end
202
- end
203
- ```
204
-
205
- ### Amazon Kinesis Firehose
206
-
207
- Add [aws-sdk-firehose](https://github.com/aws/aws-sdk-ruby) to your Gemfile.
208
-
209
- ```ruby
210
- class Ahoy::Store < Ahoy::BaseStore
211
- def track_visit(data)
212
- post("ahoy_visits", data)
213
- end
214
-
215
- def track_event(data)
216
- post("ahoy_events", data)
217
- end
218
-
219
- def geocode(data)
220
- post("ahoy_geocode", data)
221
- end
222
-
223
- def authenticate(data)
224
- post("ahoy_auth", data)
225
- end
226
-
227
- private
228
-
229
- def post(topic, data)
230
- client.put_record(
231
- delivery_stream_name: topic,
232
- record: {data: "#{data.to_json}\n"}
233
- )
234
- end
235
-
236
- def client
237
- @client ||= Aws::Firehose::Client.new
238
- end
239
- end
240
- ```
@@ -1,23 +0,0 @@
1
- require_relative "../test_helper"
2
-
3
- Mongoid.logger.level = Logger::WARN
4
- Mongo::Logger.logger.level = Logger::WARN
5
-
6
- Mongoid.configure do |config|
7
- config.connect_to("ahoy_test")
8
- end
9
-
10
- class MongoidEvent
11
- include Mongoid::Document
12
- include Ahoy::QueryMethods
13
-
14
- field :properties, type: Hash
15
- end
16
-
17
- class MongoidTest < Minitest::Test
18
- include QueryMethodsTest
19
-
20
- def model
21
- MongoidEvent
22
- end
23
- end
@@ -1,18 +0,0 @@
1
- require_relative "../test_helper"
2
-
3
- ActiveRecord::Base.establish_connection adapter: "mysql2", username: "root", database: "ahoy_test"
4
-
5
- ActiveRecord::Migration.create_table :mysql_json_events, force: true do |t|
6
- t.json :properties
7
- end
8
-
9
- class MysqlJsonEvent < MysqlBase
10
- end
11
-
12
- class MysqlJsonTest < Minitest::Test
13
- include QueryMethodsTest
14
-
15
- def model
16
- MysqlJsonEvent
17
- end
18
- end
@@ -1,19 +0,0 @@
1
- require_relative "../test_helper"
2
-
3
- ActiveRecord::Base.establish_connection adapter: "mysql2", username: "root", database: "ahoy_test"
4
-
5
- ActiveRecord::Migration.create_table :mysql_text_events, force: true do |t|
6
- t.text :properties
7
- end
8
-
9
- class MysqlTextEvent < MysqlBase
10
- serialize :properties, JSON
11
- end
12
-
13
- class MysqlTextTest < Minitest::Test
14
- include QueryMethodsTest
15
-
16
- def model
17
- MysqlTextEvent
18
- end
19
- end
@@ -1,20 +0,0 @@
1
- require_relative "../test_helper"
2
-
3
- ActiveRecord::Base.establish_connection adapter: "postgresql", database: "ahoy_test"
4
-
5
- ActiveRecord::Migration.enable_extension "hstore"
6
-
7
- ActiveRecord::Migration.create_table :postgresql_hstore_events, force: true do |t|
8
- t.hstore :properties
9
- end
10
-
11
- class PostgresqlHstoreEvent < PostgresqlBase
12
- end
13
-
14
- class PostgresqlHstoreTest < Minitest::Test
15
- include QueryMethodsTest
16
-
17
- def model
18
- PostgresqlHstoreEvent
19
- end
20
- end
@@ -1,18 +0,0 @@
1
- require_relative "../test_helper"
2
-
3
- ActiveRecord::Base.establish_connection adapter: "postgresql", database: "ahoy_test"
4
-
5
- ActiveRecord::Migration.create_table :postgresql_json_events, force: true do |t|
6
- t.json :properties
7
- end
8
-
9
- class PostgresqlJsonEvent < PostgresqlBase
10
- end
11
-
12
- class PostgresqlJsonTest < Minitest::Test
13
- include QueryMethodsTest
14
-
15
- def model
16
- PostgresqlJsonEvent
17
- end
18
- end
@@ -1,19 +0,0 @@
1
- require_relative "../test_helper"
2
-
3
- ActiveRecord::Base.establish_connection adapter: "postgresql", database: "ahoy_test"
4
-
5
- ActiveRecord::Migration.create_table :postgresql_jsonb_events, force: true do |t|
6
- t.jsonb :properties
7
- t.index :properties, using: 'gin'
8
- end
9
-
10
- class PostgresqlJsonbEvent < PostgresqlBase
11
- end
12
-
13
- class PostgresqlJsonbTest < Minitest::Test
14
- include QueryMethodsTest
15
-
16
- def model
17
- PostgresqlJsonbEvent
18
- end
19
- end
@@ -1,19 +0,0 @@
1
- require_relative "../test_helper"
2
-
3
- ActiveRecord::Base.establish_connection adapter: "postgresql", database: "ahoy_test"
4
-
5
- ActiveRecord::Migration.create_table :postgresql_text_events, force: true do |t|
6
- t.text :properties
7
- end
8
-
9
- class PostgresqlTextEvent < PostgresqlBase
10
- serialize :properties, JSON
11
- end
12
-
13
- class PostgresqlTextTest < Minitest::Test
14
- include QueryMethodsTest
15
-
16
- def model
17
- PostgresqlTextEvent
18
- end
19
- end
@@ -1,100 +0,0 @@
1
- require "bundler/setup"
2
- Bundler.require(:default)
3
- require "minitest/autorun"
4
- require "minitest/pride"
5
- require "active_record"
6
- require "mongoid"
7
-
8
- ActiveRecord::Base.logger = ActiveSupport::Logger.new(STDOUT) if ENV["VERBOSE"]
9
-
10
- class PostgresqlBase < ActiveRecord::Base
11
- include Ahoy::QueryMethods
12
- establish_connection adapter: "postgresql", database: "ahoy_test"
13
- self.abstract_class = true
14
- end
15
-
16
- class MysqlBase < ActiveRecord::Base
17
- include Ahoy::QueryMethods
18
- establish_connection adapter: "mysql2", username: "root", database: "ahoy_test"
19
- self.abstract_class = true
20
- end
21
-
22
- module QueryMethodsTest
23
- def setup
24
- model.delete_all
25
- end
26
-
27
- def test_empty
28
- assert_equal 0, count_events({})
29
- end
30
-
31
- def test_string
32
- create_event value: "world"
33
- assert_equal 1, count_events(value: "world")
34
- end
35
-
36
- def test_number
37
- create_event value: 1
38
- assert_equal 1, count_events(value: 1)
39
- end
40
-
41
- def test_date
42
- today = Date.today
43
- create_event value: today
44
- assert_equal 1, count_events(value: today)
45
- end
46
-
47
- def test_time
48
- now = Time.now
49
- create_event value: now
50
- assert_equal 1, count_events(value: now)
51
- end
52
-
53
- def test_true
54
- create_event value: true
55
- assert_equal 1, count_events(value: true)
56
- end
57
-
58
- def test_false
59
- create_event value: false
60
- assert_equal 1, count_events(value: false)
61
- end
62
-
63
- def test_nil
64
- create_event value: nil
65
- assert_equal 1, count_events(value: nil)
66
- end
67
-
68
- def test_any
69
- create_event hello: "world", prop2: "hi"
70
- assert_equal 1, count_events(hello: "world")
71
- end
72
-
73
- def test_multiple
74
- create_event prop1: "hi", prop2: "bye"
75
- assert_equal 1, count_events(prop1: "hi", prop2: "bye")
76
- end
77
-
78
- def test_multiple_order
79
- create_event prop2: "bye", prop1: "hi"
80
- assert_equal 1, count_events(prop1: "hi", prop2: "bye")
81
- end
82
-
83
- def test_partial
84
- create_event hello: "world"
85
- assert_equal 0, count_events(hello: "world", prop2: "hi")
86
- end
87
-
88
- def test_prefix
89
- create_event value: 123
90
- assert_equal 0, count_events(value: 1)
91
- end
92
-
93
- def create_event(properties)
94
- model.create(properties: properties)
95
- end
96
-
97
- def count_events(properties)
98
- model.where_properties(properties).count
99
- end
100
- end