ahoy_matey 3.0.5 → 4.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|