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.
@@ -8,64 +8,40 @@ module Ahoy
8
8
  end
9
9
 
10
10
  def where_props(properties)
11
- relation = self
12
- if respond_to?(:columns_hash)
13
- column_type = columns_hash["properties"].type
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
- relation = where(Hash[properties.map { |k, v| ["properties.#{k}", v] }])
16
+ where(properties.to_h { |k, v| ["properties.#{k}", v] })
21
17
  when /mysql/
22
- if column_type == :json
23
- properties.each do |k, v|
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
- v = "null"
26
- elsif v == true
27
- v = "true"
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.each do |k, v|
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 /postgres|postgis/
39
- if column_type == :jsonb
40
- relation = relation.where("properties @> ?", properties.to_json)
41
- elsif column_type == :json
42
- properties.each do |k, v|
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 = self
77
- if respond_to?(:columns_hash)
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
- if connection.try(:mariadb?)
88
- props.each do |prop|
89
- quoted_prop = connection.quote("$.#{prop}")
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
- if exclude?
71
- debug "Geocode excluded"
72
- else
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
- @store.geocode(data)
78
- true
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
- @store.exclude?
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
@@ -1,3 +1,3 @@
1
1
  module Ahoy
2
- VERSION = "3.0.5"
2
+ VERSION = "4.2.1"
3
3
  end
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
- require "ahoy/utils"
11
- require "ahoy/base_store"
12
- require "ahoy/controller"
13
- require "ahoy/database_store"
14
- require "ahoy/helper"
15
- require "ahoy/model"
16
- require "ahoy/query_methods"
17
- require "ahoy/tracker"
18
- require "ahoy/version"
19
- require "ahoy/visit_properties"
20
-
21
- require "ahoy/engine" if defined?(Rails)
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 = true
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
- # Mongoid
122
- # TODO use
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
- require "ahoy"
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
- # use connection_config instead of connection.adapter
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 >= "5.2"
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 properties_type == "text" %>
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
@@ -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 (and add the geocoder gem to your Gemfile)
8
+ # we recommend configuring local geocoding as well
9
+ # see https://github.com/ankane/ahoy#geocoding
10
+ Ahoy.geocode = false
@@ -2,7 +2,7 @@ class Ahoy::Event
2
2
  include Mongoid::Document
3
3
 
4
4
  # associations
5
- belongs_to :visit, index: true
5
+ belongs_to :visit, class_name: "Ahoy::Visit", index: true
6
6
  belongs_to :user, index: true, optional: true
7
7
 
8
8
  # fields