ahoy_matey 3.0.5 → 4.2.1
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 +4 -4
- data/CHANGELOG.md +56 -0
- data/LICENSE.txt +1 -1
- data/README.md +143 -101
- data/app/controllers/ahoy/base_controller.rb +12 -4
- data/app/jobs/ahoy/geocode_job.rb +1 -0
- data/app/jobs/ahoy/geocode_v2_job.rb +3 -0
- data/lib/ahoy/base_store.rb +5 -1
- data/lib/ahoy/controller.rb +3 -3
- data/lib/ahoy/database_store.rb +6 -1
- data/lib/ahoy/engine.rb +7 -0
- data/lib/ahoy/model.rb +1 -1
- data/lib/ahoy/query_methods.rb +32 -63
- data/lib/ahoy/tracker.rb +10 -14
- data/lib/ahoy/version.rb +1 -1
- data/lib/ahoy.rb +24 -21
- data/lib/ahoy_matey.rb +1 -1
- data/lib/generators/ahoy/activerecord_generator.rb +30 -4
- data/lib/generators/ahoy/templates/active_record_event_model.rb.tt +1 -1
- data/lib/generators/ahoy/templates/active_record_migration.rb.tt +5 -5
- data/lib/generators/ahoy/templates/base_store_initializer.rb.tt +5 -0
- data/lib/generators/ahoy/templates/database_store_initializer.rb.tt +5 -0
- data/lib/generators/ahoy/templates/mongoid_event_model.rb.tt +1 -1
- data/vendor/assets/javascripts/ahoy.js +78 -60
- metadata +11 -179
data/lib/ahoy/query_methods.rb
CHANGED
@@ -8,64 +8,40 @@ module Ahoy
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def where_props(properties)
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
adapter_name = connection.adapter_name.downcase
|
15
|
-
else
|
16
|
-
adapter_name = "mongoid"
|
17
|
-
end
|
11
|
+
return all if properties.empty?
|
12
|
+
|
13
|
+
adapter_name = respond_to?(:connection) ? connection.adapter_name.downcase : "mongoid"
|
18
14
|
case adapter_name
|
19
15
|
when "mongoid"
|
20
|
-
|
16
|
+
where(properties.to_h { |k, v| ["properties.#{k}", v] })
|
21
17
|
when /mysql/
|
22
|
-
|
23
|
-
|
18
|
+
where("JSON_CONTAINS(properties, ?, '$') = 1", properties.to_json)
|
19
|
+
when /postgres|postgis/
|
20
|
+
case columns_hash["properties"].type
|
21
|
+
when :hstore
|
22
|
+
properties.inject(all) do |relation, (k, v)|
|
24
23
|
if v.nil?
|
25
|
-
|
26
|
-
|
27
|
-
|
24
|
+
relation.where("properties -> ? IS NULL", k.to_s)
|
25
|
+
else
|
26
|
+
relation.where("properties -> ? = ?", k.to_s, v.to_s)
|
28
27
|
end
|
29
|
-
|
30
|
-
relation = relation.where("JSON_UNQUOTE(properties -> ?) = ?", "$.#{k}", v.as_json)
|
31
28
|
end
|
29
|
+
when :jsonb
|
30
|
+
where("properties @> ?", properties.to_json)
|
32
31
|
else
|
33
|
-
properties
|
34
|
-
# TODO cast to json instead
|
35
|
-
relation = relation.where("properties REGEXP ?", "[{,]#{{k.to_s => v}.to_json.sub(/\A\{/, "").sub(/\}\z/, "").gsub("+", "\\\\+")}[,}]")
|
36
|
-
end
|
32
|
+
where("properties::jsonb @> ?", properties.to_json)
|
37
33
|
end
|
38
|
-
when /
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
relation =
|
44
|
-
if v.nil?
|
45
|
-
relation.where("properties ->> ? IS NULL", k.to_s)
|
46
|
-
else
|
47
|
-
relation.where("properties ->> ? = ?", k.to_s, v.as_json.to_s)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
elsif column_type == :hstore
|
51
|
-
properties.each do |k, v|
|
52
|
-
relation =
|
53
|
-
if v.nil?
|
54
|
-
relation.where("properties -> ? IS NULL", k.to_s)
|
55
|
-
else
|
56
|
-
relation.where("properties -> ? = ?", k.to_s, v.to_s)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
else
|
60
|
-
properties.each do |k, v|
|
61
|
-
# TODO cast to jsonb instead
|
62
|
-
relation = relation.where("properties SIMILAR TO ?", "%[{,]#{{k.to_s => v}.to_json.sub(/\A\{/, "").sub(/\}\z/, "").gsub("+", "\\\\+")}[,}]%")
|
34
|
+
when /sqlite/
|
35
|
+
properties.inject(all) do |relation, (k, v)|
|
36
|
+
if v.nil?
|
37
|
+
relation.where("JSON_EXTRACT(properties, ?) IS NULL", "$.#{k}")
|
38
|
+
else
|
39
|
+
relation.where("JSON_EXTRACT(properties, ?) = ?", "$.#{k}", v.as_json)
|
63
40
|
end
|
64
41
|
end
|
65
42
|
else
|
66
43
|
raise "Adapter not supported: #{adapter_name}"
|
67
44
|
end
|
68
|
-
relation
|
69
45
|
end
|
70
46
|
alias_method :where_properties, :where_props
|
71
47
|
|
@@ -73,39 +49,32 @@ module Ahoy
|
|
73
49
|
# like with group
|
74
50
|
props.flatten!
|
75
51
|
|
76
|
-
relation =
|
77
|
-
|
78
|
-
column_type = columns_hash["properties"].type
|
79
|
-
adapter_name = connection.adapter_name.downcase
|
80
|
-
else
|
81
|
-
adapter_name = "mongoid"
|
82
|
-
end
|
52
|
+
relation = all
|
53
|
+
adapter_name = respond_to?(:connection) ? connection.adapter_name.downcase : "mongoid"
|
83
54
|
case adapter_name
|
84
55
|
when "mongoid"
|
85
56
|
raise "Adapter not supported: #{adapter_name}"
|
86
57
|
when /mysql/
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
relation = relation.group("JSON_UNQUOTE(JSON_EXTRACT(properties, #{quoted_prop}))")
|
91
|
-
end
|
92
|
-
else
|
93
|
-
column = column_type == :json ? "properties" : "CAST(properties AS JSON)"
|
94
|
-
props.each do |prop|
|
95
|
-
quoted_prop = connection.quote("$.#{prop}")
|
96
|
-
relation = relation.group("JSON_UNQUOTE(JSON_EXTRACT(#{column}, #{quoted_prop}))")
|
97
|
-
end
|
58
|
+
props.each do |prop|
|
59
|
+
quoted_prop = connection.quote("$.#{prop}")
|
60
|
+
relation = relation.group("JSON_UNQUOTE(JSON_EXTRACT(properties, #{quoted_prop}))")
|
98
61
|
end
|
99
62
|
when /postgres|postgis/
|
100
63
|
# convert to jsonb to fix
|
101
64
|
# could not identify an equality operator for type json
|
102
65
|
# and for text columns
|
66
|
+
column_type = columns_hash["properties"].type
|
103
67
|
cast = [:jsonb, :hstore].include?(column_type) ? "" : "::jsonb"
|
104
68
|
|
105
69
|
props.each do |prop|
|
106
70
|
quoted_prop = connection.quote(prop)
|
107
71
|
relation = relation.group("properties#{cast} -> #{quoted_prop}")
|
108
72
|
end
|
73
|
+
when /sqlite/
|
74
|
+
props.each do |prop|
|
75
|
+
quoted_prop = connection.quote("$.#{prop}")
|
76
|
+
relation = relation.group("JSON_EXTRACT(properties, #{quoted_prop})")
|
77
|
+
end
|
109
78
|
else
|
110
79
|
raise "Adapter not supported: #{adapter_name}"
|
111
80
|
end
|
data/lib/ahoy/tracker.rb
CHANGED
@@ -19,8 +19,6 @@ module Ahoy
|
|
19
19
|
def track(name, properties = {}, options = {})
|
20
20
|
if exclude?
|
21
21
|
debug "Event excluded"
|
22
|
-
elsif missing_params?
|
23
|
-
debug "Missing required parameters"
|
24
22
|
else
|
25
23
|
data = {
|
26
24
|
visit_token: visit_token,
|
@@ -41,8 +39,6 @@ module Ahoy
|
|
41
39
|
def track_visit(defer: false, started_at: nil)
|
42
40
|
if exclude?
|
43
41
|
debug "Visit excluded"
|
44
|
-
elsif missing_params?
|
45
|
-
debug "Missing required parameters"
|
46
42
|
else
|
47
43
|
if defer
|
48
44
|
set_cookie("ahoy_track", true, nil, false)
|
@@ -67,16 +63,12 @@ module Ahoy
|
|
67
63
|
end
|
68
64
|
|
69
65
|
def geocode(data)
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
data = {
|
74
|
-
visit_token: visit_token
|
75
|
-
}.merge(data).select { |_, v| v }
|
66
|
+
data = {
|
67
|
+
visit_token: visit_token
|
68
|
+
}.merge(data).select { |_, v| v }
|
76
69
|
|
77
|
-
|
78
|
-
|
79
|
-
end
|
70
|
+
@store.geocode(data)
|
71
|
+
true
|
80
72
|
rescue => e
|
81
73
|
report_exception(e)
|
82
74
|
end
|
@@ -159,6 +151,7 @@ module Ahoy
|
|
159
151
|
@options[:api]
|
160
152
|
end
|
161
153
|
|
154
|
+
# private, but used by API
|
162
155
|
def missing_params?
|
163
156
|
if Ahoy.cookies && api? && Ahoy.protect_from_forgery
|
164
157
|
!(existing_visit_token && existing_visitor_token)
|
@@ -192,7 +185,10 @@ module Ahoy
|
|
192
185
|
end
|
193
186
|
|
194
187
|
def exclude?
|
195
|
-
@
|
188
|
+
unless defined?(@exclude)
|
189
|
+
@exclude = @store.exclude?
|
190
|
+
end
|
191
|
+
@exclude
|
196
192
|
end
|
197
193
|
|
198
194
|
def report_exception(e)
|
data/lib/ahoy/version.rb
CHANGED
data/lib/ahoy.rb
CHANGED
@@ -1,24 +1,24 @@
|
|
1
|
+
# stdlib
|
1
2
|
require "ipaddr"
|
2
3
|
|
3
4
|
# dependencies
|
4
5
|
require "active_support"
|
5
6
|
require "active_support/core_ext"
|
6
|
-
require "geocoder"
|
7
7
|
require "safely/core"
|
8
8
|
|
9
9
|
# modules
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
10
|
+
require_relative "ahoy/utils"
|
11
|
+
require_relative "ahoy/base_store"
|
12
|
+
require_relative "ahoy/controller"
|
13
|
+
require_relative "ahoy/database_store"
|
14
|
+
require_relative "ahoy/helper"
|
15
|
+
require_relative "ahoy/model"
|
16
|
+
require_relative "ahoy/query_methods"
|
17
|
+
require_relative "ahoy/tracker"
|
18
|
+
require_relative "ahoy/version"
|
19
|
+
require_relative "ahoy/visit_properties"
|
20
|
+
|
21
|
+
require_relative "ahoy/engine" if defined?(Rails)
|
22
22
|
|
23
23
|
module Ahoy
|
24
24
|
mattr_accessor :visit_duration
|
@@ -43,7 +43,7 @@ module Ahoy
|
|
43
43
|
self.quiet = true
|
44
44
|
|
45
45
|
mattr_accessor :geocode
|
46
|
-
self.geocode =
|
46
|
+
self.geocode = false
|
47
47
|
|
48
48
|
mattr_accessor :max_content_length
|
49
49
|
self.max_content_length = 8192
|
@@ -104,6 +104,14 @@ module Ahoy
|
|
104
104
|
addr.mask(48).to_s
|
105
105
|
end
|
106
106
|
end
|
107
|
+
|
108
|
+
def self.instance
|
109
|
+
Thread.current[:ahoy]
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.instance=(value)
|
113
|
+
Thread.current[:ahoy] = value
|
114
|
+
end
|
107
115
|
end
|
108
116
|
|
109
117
|
ActiveSupport.on_load(:action_controller) do
|
@@ -118,11 +126,6 @@ ActiveSupport.on_load(:action_view) do
|
|
118
126
|
include Ahoy::Helper
|
119
127
|
end
|
120
128
|
|
121
|
-
|
122
|
-
|
123
|
-
# ActiveSupport.on_load(:mongoid) do
|
124
|
-
# Mongoid::Document::ClassMethods.include(Ahoy::Model)
|
125
|
-
# end
|
126
|
-
if defined?(ActiveModel)
|
127
|
-
ActiveModel::Callbacks.include(Ahoy::Model)
|
129
|
+
ActiveSupport.on_load(:mongoid) do
|
130
|
+
Mongoid::Document::ClassMethods.include(Ahoy::Model)
|
128
131
|
end
|
data/lib/ahoy_matey.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
require_relative "ahoy"
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require "rails/generators"
|
1
2
|
require "rails/generators/active_record"
|
2
3
|
|
3
4
|
module Ahoy
|
@@ -17,9 +18,7 @@ module Ahoy
|
|
17
18
|
end
|
18
19
|
|
19
20
|
def properties_type
|
20
|
-
|
21
|
-
# so database connection isn't needed
|
22
|
-
case ActiveRecord::Base.connection_config[:adapter].to_s
|
21
|
+
case adapter
|
23
22
|
when /postg/i # postgres, postgis
|
24
23
|
"jsonb"
|
25
24
|
when /mysql/i
|
@@ -29,13 +28,40 @@ module Ahoy
|
|
29
28
|
end
|
30
29
|
end
|
31
30
|
|
31
|
+
# requires database connection to check for MariaDB
|
32
|
+
def serialize_properties?
|
33
|
+
properties_type == "text" || (properties_type == "json" && ActiveRecord::Base.connection.try(:mariadb?))
|
34
|
+
end
|
35
|
+
|
36
|
+
# use connection_config instead of connection.adapter
|
37
|
+
# so database connection isn't needed
|
38
|
+
def adapter
|
39
|
+
if ActiveRecord::VERSION::STRING.to_f >= 6.1
|
40
|
+
ActiveRecord::Base.connection_db_config.adapter.to_s
|
41
|
+
else
|
42
|
+
ActiveRecord::Base.connection_config[:adapter].to_s
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
32
46
|
def rails52?
|
33
|
-
ActiveRecord::VERSION::STRING >=
|
47
|
+
ActiveRecord::VERSION::STRING.to_f >= 5.2
|
34
48
|
end
|
35
49
|
|
36
50
|
def migration_version
|
37
51
|
"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
|
38
52
|
end
|
53
|
+
|
54
|
+
def primary_key_type
|
55
|
+
", id: :#{key_type}" if key_type
|
56
|
+
end
|
57
|
+
|
58
|
+
def foreign_key_type
|
59
|
+
", type: :#{key_type}" if key_type
|
60
|
+
end
|
61
|
+
|
62
|
+
def key_type
|
63
|
+
Rails.configuration.generators.options.dig(:active_record, :primary_key_type)
|
64
|
+
end
|
39
65
|
end
|
40
66
|
end
|
41
67
|
end
|
@@ -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
|
7
|
+
belongs_to :user, optional: true<% if serialize_properties? %>
|
8
8
|
|
9
9
|
serialize :properties, JSON<% end %>
|
10
10
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version %>
|
2
2
|
def change
|
3
|
-
create_table :ahoy_visits do |t|
|
3
|
+
create_table :ahoy_visits<%= primary_key_type %> do |t|
|
4
4
|
t.string :visit_token
|
5
5
|
t.string :visitor_token
|
6
6
|
|
@@ -8,7 +8,7 @@ class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version
|
|
8
8
|
# simply remove any you don't want
|
9
9
|
|
10
10
|
# user
|
11
|
-
t.references :user
|
11
|
+
t.references :user<%= foreign_key_type %>
|
12
12
|
|
13
13
|
# standard
|
14
14
|
t.string :ip
|
@@ -46,9 +46,9 @@ class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version
|
|
46
46
|
|
47
47
|
add_index :ahoy_visits, :visit_token, unique: true
|
48
48
|
|
49
|
-
create_table :ahoy_events do |t|
|
50
|
-
t.references :visit
|
51
|
-
t.references :user
|
49
|
+
create_table :ahoy_events<%= primary_key_type %> do |t|
|
50
|
+
t.references :visit<%= foreign_key_type %>
|
51
|
+
t.references :user<%= foreign_key_type %>
|
52
52
|
|
53
53
|
t.string :name
|
54
54
|
t.<%= properties_type %> :properties
|
@@ -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 (and add the geocoder gem to your Gemfile)
|
23
|
+
# we recommend configuring local geocoding as well
|
24
|
+
# see https://github.com/ankane/ahoy#geocoding
|
25
|
+
Ahoy.geocode = false
|