nano-pure-pkg 0.0.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 +7 -0
- data/ahoy_matey-5.5.0/CHANGELOG.md +403 -0
- data/ahoy_matey-5.5.0/CONTRIBUTING.md +42 -0
- data/ahoy_matey-5.5.0/LICENSE.txt +22 -0
- data/ahoy_matey-5.5.0/README.md +802 -0
- data/ahoy_matey-5.5.0/app/controllers/ahoy/base_controller.rb +44 -0
- data/ahoy_matey-5.5.0/app/controllers/ahoy/events_controller.rb +51 -0
- data/ahoy_matey-5.5.0/app/controllers/ahoy/visits_controller.rb +15 -0
- data/ahoy_matey-5.5.0/config/routes.rb +10 -0
- data/ahoy_matey-5.5.0/lib/ahoy/base_store.rb +104 -0
- data/ahoy_matey-5.5.0/lib/ahoy/controller.rb +56 -0
- data/ahoy_matey-5.5.0/lib/ahoy/database_store.rb +96 -0
- data/ahoy_matey-5.5.0/lib/ahoy/engine.rb +37 -0
- data/ahoy_matey-5.5.0/lib/ahoy/geocode_v2_job.rb +31 -0
- data/ahoy_matey-5.5.0/lib/ahoy/helper.rb +40 -0
- data/ahoy_matey-5.5.0/lib/ahoy/model.rb +15 -0
- data/ahoy_matey-5.5.0/lib/ahoy/query_methods.rb +88 -0
- data/ahoy_matey-5.5.0/lib/ahoy/tracker.rb +287 -0
- data/ahoy_matey-5.5.0/lib/ahoy/utils.rb +7 -0
- data/ahoy_matey-5.5.0/lib/ahoy/version.rb +3 -0
- data/ahoy_matey-5.5.0/lib/ahoy/visit_properties.rb +122 -0
- data/ahoy_matey-5.5.0/lib/ahoy/warden.rb +5 -0
- data/ahoy_matey-5.5.0/lib/ahoy.rb +167 -0
- data/ahoy_matey-5.5.0/lib/ahoy_matey.rb +1 -0
- data/ahoy_matey-5.5.0/lib/generators/ahoy/activerecord_generator.rb +59 -0
- data/ahoy_matey-5.5.0/lib/generators/ahoy/base_generator.rb +13 -0
- data/ahoy_matey-5.5.0/lib/generators/ahoy/install_generator.rb +44 -0
- data/ahoy_matey-5.5.0/lib/generators/ahoy/mongoid_generator.rb +16 -0
- data/ahoy_matey-5.5.0/lib/generators/ahoy/templates/active_record_event_model.rb.tt +10 -0
- data/ahoy_matey-5.5.0/lib/generators/ahoy/templates/active_record_migration.rb.tt +62 -0
- data/ahoy_matey-5.5.0/lib/generators/ahoy/templates/active_record_visit_model.rb.tt +6 -0
- data/ahoy_matey-5.5.0/lib/generators/ahoy/templates/base_store_initializer.rb.tt +25 -0
- data/ahoy_matey-5.5.0/lib/generators/ahoy/templates/database_store_initializer.rb.tt +10 -0
- data/ahoy_matey-5.5.0/lib/generators/ahoy/templates/mongoid_event_model.rb.tt +14 -0
- data/ahoy_matey-5.5.0/lib/generators/ahoy/templates/mongoid_visit_model.rb.tt +50 -0
- data/ahoy_matey-5.5.0/vendor/assets/javascripts/ahoy.js +544 -0
- data/nano-pure-pkg.gemspec +12 -0
- metadata +77 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
require "active_support/core_ext/digest/uuid"
|
|
2
|
+
|
|
3
|
+
module Ahoy
|
|
4
|
+
class Tracker
|
|
5
|
+
UUID_NAMESPACE = "a82ae811-5011-45ab-a728-569df7499c5f"
|
|
6
|
+
|
|
7
|
+
attr_reader :request, :controller
|
|
8
|
+
|
|
9
|
+
def initialize(**options)
|
|
10
|
+
@store = Ahoy::Store.new(options.merge(ahoy: self))
|
|
11
|
+
@controller = options[:controller]
|
|
12
|
+
@request = options[:request] || @controller.try(:request)
|
|
13
|
+
@visit_token = options[:visit_token]
|
|
14
|
+
@user = options[:user]
|
|
15
|
+
@options = options
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# can't use keyword arguments here
|
|
19
|
+
def track(name, properties = {}, options = {})
|
|
20
|
+
if exclude?
|
|
21
|
+
debug "Event excluded"
|
|
22
|
+
else
|
|
23
|
+
data = {
|
|
24
|
+
visit_token: visit_token,
|
|
25
|
+
user_id: user.try(:id),
|
|
26
|
+
name: name.to_s,
|
|
27
|
+
properties: properties,
|
|
28
|
+
time: trusted_time(options[:time]),
|
|
29
|
+
event_id: options[:id] || generate_id
|
|
30
|
+
}.select { |_, v| v }
|
|
31
|
+
|
|
32
|
+
@store.track_event(data)
|
|
33
|
+
end
|
|
34
|
+
true
|
|
35
|
+
rescue => e
|
|
36
|
+
report_exception(e)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def track_visit(defer: false, started_at: nil)
|
|
40
|
+
if exclude?
|
|
41
|
+
debug "Visit excluded"
|
|
42
|
+
else
|
|
43
|
+
if defer
|
|
44
|
+
set_cookie("ahoy_track", true, nil, false)
|
|
45
|
+
else
|
|
46
|
+
delete_cookie("ahoy_track", false)
|
|
47
|
+
|
|
48
|
+
data = {
|
|
49
|
+
visit_token: visit_token,
|
|
50
|
+
visitor_token: visitor_token,
|
|
51
|
+
user_id: user.try(:id),
|
|
52
|
+
started_at: trusted_time(started_at)
|
|
53
|
+
}.merge(visit_properties).select { |_, v| v }
|
|
54
|
+
|
|
55
|
+
@store.track_visit(data)
|
|
56
|
+
|
|
57
|
+
Ahoy::GeocodeV2Job.perform_later(visit_token, data[:ip]) if Ahoy.geocode && data[:ip]
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
true
|
|
61
|
+
rescue => e
|
|
62
|
+
report_exception(e)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def geocode(data)
|
|
66
|
+
data = {
|
|
67
|
+
visit_token: visit_token
|
|
68
|
+
}.merge(data).select { |_, v| v }
|
|
69
|
+
|
|
70
|
+
@store.geocode(data)
|
|
71
|
+
true
|
|
72
|
+
rescue => e
|
|
73
|
+
report_exception(e)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def authenticate(user)
|
|
77
|
+
if exclude?
|
|
78
|
+
debug "Authentication excluded"
|
|
79
|
+
else
|
|
80
|
+
@store.user = user
|
|
81
|
+
|
|
82
|
+
data = {
|
|
83
|
+
visit_token: visit_token,
|
|
84
|
+
user_id: user.try(:id)
|
|
85
|
+
}
|
|
86
|
+
@store.authenticate(data)
|
|
87
|
+
end
|
|
88
|
+
true
|
|
89
|
+
rescue => e
|
|
90
|
+
report_exception(e)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def visit
|
|
94
|
+
@visit ||= @store.visit
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def visit_or_create
|
|
98
|
+
@visit ||= @store.visit_or_create
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def new_visit?
|
|
102
|
+
Ahoy.cookies? ? !existing_visit_token : visit.nil?
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def new_visitor?
|
|
106
|
+
!existing_visitor_token
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def set_visit_cookie
|
|
110
|
+
set_cookie("ahoy_visit", visit_token, Ahoy.visit_duration)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def set_visitor_cookie
|
|
114
|
+
if new_visitor?
|
|
115
|
+
set_cookie("ahoy_visitor", visitor_token, Ahoy.visitor_duration)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def user
|
|
120
|
+
@user ||= @store.user
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def visit_properties
|
|
124
|
+
@visit_properties ||= request ? Ahoy::VisitProperties.new(request, api: api?).generate : {}
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def visit_token
|
|
128
|
+
@visit_token ||= ensure_token(visit_token_helper)
|
|
129
|
+
end
|
|
130
|
+
alias_method :visit_id, :visit_token
|
|
131
|
+
|
|
132
|
+
def visitor_token
|
|
133
|
+
@visitor_token ||= ensure_token(visitor_token_helper)
|
|
134
|
+
end
|
|
135
|
+
alias_method :visitor_id, :visitor_token
|
|
136
|
+
|
|
137
|
+
def reset
|
|
138
|
+
reset_visit
|
|
139
|
+
delete_cookie("ahoy_visitor")
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def reset_visit
|
|
143
|
+
delete_cookie("ahoy_visit")
|
|
144
|
+
delete_cookie("ahoy_events")
|
|
145
|
+
delete_cookie("ahoy_track", false)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def exclude?
|
|
149
|
+
unless defined?(@exclude)
|
|
150
|
+
@exclude = @store.exclude?
|
|
151
|
+
end
|
|
152
|
+
@exclude
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
protected
|
|
156
|
+
|
|
157
|
+
def api?
|
|
158
|
+
@options[:api]
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# private, but used by API
|
|
162
|
+
def missing_params?
|
|
163
|
+
if Ahoy.cookies? && api? && Ahoy.protect_from_forgery
|
|
164
|
+
!(existing_visit_token && existing_visitor_token)
|
|
165
|
+
else
|
|
166
|
+
false
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def set_cookie(name, value, duration = nil, use_domain = true)
|
|
171
|
+
# safety net
|
|
172
|
+
return unless Ahoy.cookies? && request
|
|
173
|
+
|
|
174
|
+
cookie = Ahoy.cookie_options.merge(value: value)
|
|
175
|
+
cookie[:expires] = duration.from_now if duration
|
|
176
|
+
# prefer cookie_options[:domain] over cookie_domain
|
|
177
|
+
cookie[:domain] ||= Ahoy.cookie_domain if Ahoy.cookie_domain
|
|
178
|
+
cookie.delete(:domain) unless use_domain
|
|
179
|
+
request.cookie_jar[name] = cookie
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def delete_cookie(name, use_domain = true)
|
|
183
|
+
if request && request.cookie_jar[name]
|
|
184
|
+
options = Ahoy.cookie_options.slice(:path)
|
|
185
|
+
if use_domain
|
|
186
|
+
domain = Ahoy.cookie_options[:domain] || Ahoy.cookie_domain
|
|
187
|
+
options[:domain] = domain if domain
|
|
188
|
+
end
|
|
189
|
+
request.cookie_jar.delete(name, **options)
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def trusted_time(time = nil)
|
|
194
|
+
if !time || (api? && !(1.minute.ago..Time.now).cover?(time))
|
|
195
|
+
Time.current
|
|
196
|
+
else
|
|
197
|
+
time
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def report_exception(e)
|
|
202
|
+
if defined?(ActionDispatch::RemoteIp::IpSpoofAttackError) && e.is_a?(ActionDispatch::RemoteIp::IpSpoofAttackError)
|
|
203
|
+
debug "Tracking excluded due to IP spoofing"
|
|
204
|
+
else
|
|
205
|
+
raise e if !defined?(Rails) || Rails.env.development? || Rails.env.test?
|
|
206
|
+
Safely.report_exception(e)
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def generate_id
|
|
211
|
+
@store.generate_id
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def visit_token_helper
|
|
215
|
+
@visit_token_helper ||= begin
|
|
216
|
+
token = existing_visit_token
|
|
217
|
+
token ||= visit&.visit_token unless Ahoy.cookies?
|
|
218
|
+
token ||= generate_id unless Ahoy.api_only
|
|
219
|
+
token
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def visitor_token_helper
|
|
224
|
+
@visitor_token_helper ||= begin
|
|
225
|
+
token = existing_visitor_token
|
|
226
|
+
token ||= visitor_anonymity_set unless Ahoy.cookies?
|
|
227
|
+
token ||= generate_id unless Ahoy.api_only
|
|
228
|
+
token
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def existing_visit_token
|
|
233
|
+
@existing_visit_token ||= begin
|
|
234
|
+
token = visit_header
|
|
235
|
+
token ||= visit_cookie if Ahoy.cookies? && !(api? && Ahoy.protect_from_forgery)
|
|
236
|
+
token ||= visit_param if api?
|
|
237
|
+
token
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def existing_visitor_token
|
|
242
|
+
@existing_visitor_token ||= begin
|
|
243
|
+
token = visitor_header
|
|
244
|
+
token ||= visitor_cookie if Ahoy.cookies? && !(api? && Ahoy.protect_from_forgery)
|
|
245
|
+
token ||= visitor_param if api?
|
|
246
|
+
token
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def visitor_anonymity_set
|
|
251
|
+
@visitor_anonymity_set ||= request && Digest::UUID.uuid_v5(UUID_NAMESPACE, ["visitor", Ahoy.mask_ip(request.remote_ip), request.user_agent].join("/"))
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def visit_cookie
|
|
255
|
+
@visit_cookie ||= request && request.cookies["ahoy_visit"]
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def visitor_cookie
|
|
259
|
+
@visitor_cookie ||= request && request.cookies["ahoy_visitor"]
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def visit_header
|
|
263
|
+
@visit_header ||= request && request.headers["Ahoy-Visit"]
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def visitor_header
|
|
267
|
+
@visitor_header ||= request && request.headers["Ahoy-Visitor"]
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def visit_param
|
|
271
|
+
@visit_param ||= request && request.params["visit_token"]
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def visitor_param
|
|
275
|
+
@visitor_param ||= request && request.params["visitor_token"]
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def ensure_token(token)
|
|
279
|
+
token = Ahoy::Utils.ensure_utf8(token)
|
|
280
|
+
token.to_s.gsub(/[^a-z0-9\-]/i, "").first(64) if token
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def debug(message)
|
|
284
|
+
Ahoy.log message
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
end
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
require "cgi"
|
|
2
|
+
require "device_detector"
|
|
3
|
+
require "uri"
|
|
4
|
+
|
|
5
|
+
module Ahoy
|
|
6
|
+
class VisitProperties
|
|
7
|
+
attr_reader :request, :params, :referrer, :landing_page
|
|
8
|
+
|
|
9
|
+
def initialize(request, api:)
|
|
10
|
+
@request = request
|
|
11
|
+
@params = request.params
|
|
12
|
+
@referrer = api ? params["referrer"] : request.referer
|
|
13
|
+
@landing_page = api ? params["landing_page"] : request.original_url
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def generate
|
|
17
|
+
@generate ||= request_properties.merge(tech_properties).merge(traffic_properties).merge(utm_properties)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def utm_properties
|
|
23
|
+
landing_params = {}
|
|
24
|
+
begin
|
|
25
|
+
landing_uri = URI.parse(landing_page)
|
|
26
|
+
# could also use Rack::Utils.parse_nested_query
|
|
27
|
+
landing_params = CGI.parse(landing_uri.query) if landing_uri
|
|
28
|
+
rescue
|
|
29
|
+
# do nothing
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
props = {}
|
|
33
|
+
%w(utm_source utm_medium utm_term utm_content utm_campaign).each do |name|
|
|
34
|
+
props[name.to_sym] = params[name] || landing_params[name].try(:first)
|
|
35
|
+
end
|
|
36
|
+
props
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def traffic_properties
|
|
40
|
+
uri = URI.parse(referrer) rescue nil
|
|
41
|
+
{
|
|
42
|
+
referring_domain: uri.try(:host).try(:first, 255)
|
|
43
|
+
}
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def tech_properties
|
|
47
|
+
if Ahoy.user_agent_parser == :device_detector
|
|
48
|
+
client = DeviceDetector.new(request.user_agent)
|
|
49
|
+
device_type =
|
|
50
|
+
case client.device_type
|
|
51
|
+
when "smartphone"
|
|
52
|
+
"Mobile"
|
|
53
|
+
when "tv"
|
|
54
|
+
"TV"
|
|
55
|
+
else
|
|
56
|
+
client.device_type.try(:titleize)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
{
|
|
60
|
+
browser: client.name,
|
|
61
|
+
os: client.os_name,
|
|
62
|
+
device_type: device_type
|
|
63
|
+
}
|
|
64
|
+
else
|
|
65
|
+
raise "Add browser to your Gemfile to use legacy user agent parsing" unless defined?(Browser)
|
|
66
|
+
raise "Add user_agent_parser to your Gemfile to use legacy user agent parsing" unless defined?(UserAgentParser)
|
|
67
|
+
|
|
68
|
+
# cache for performance
|
|
69
|
+
@@user_agent_parser ||= UserAgentParser::Parser.new
|
|
70
|
+
|
|
71
|
+
user_agent = request.user_agent
|
|
72
|
+
agent = @@user_agent_parser.parse(user_agent)
|
|
73
|
+
browser = Browser.new(user_agent)
|
|
74
|
+
device_type =
|
|
75
|
+
if browser.bot?
|
|
76
|
+
"Bot"
|
|
77
|
+
elsif browser.device.tv?
|
|
78
|
+
"TV"
|
|
79
|
+
elsif browser.device.console?
|
|
80
|
+
"Console"
|
|
81
|
+
elsif browser.device.tablet?
|
|
82
|
+
"Tablet"
|
|
83
|
+
elsif browser.device.mobile?
|
|
84
|
+
"Mobile"
|
|
85
|
+
else
|
|
86
|
+
"Desktop"
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
{
|
|
90
|
+
browser: agent.name,
|
|
91
|
+
os: agent.os.name,
|
|
92
|
+
device_type: device_type
|
|
93
|
+
}
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# masking based on Google Analytics anonymization
|
|
98
|
+
# https://support.google.com/analytics/answer/2763052
|
|
99
|
+
def ip
|
|
100
|
+
ip = request.remote_ip
|
|
101
|
+
if ip && Ahoy.mask_ips
|
|
102
|
+
Ahoy.mask_ip(ip)
|
|
103
|
+
else
|
|
104
|
+
ip
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def request_properties
|
|
109
|
+
{
|
|
110
|
+
ip: ip,
|
|
111
|
+
user_agent: Ahoy::Utils.ensure_utf8(request.user_agent),
|
|
112
|
+
referrer: referrer,
|
|
113
|
+
landing_page: landing_page,
|
|
114
|
+
platform: params["platform"],
|
|
115
|
+
app_version: params["app_version"],
|
|
116
|
+
os_version: params["os_version"],
|
|
117
|
+
screen_height: params["screen_height"],
|
|
118
|
+
screen_width: params["screen_width"]
|
|
119
|
+
}
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# stdlib
|
|
2
|
+
require "ipaddr"
|
|
3
|
+
|
|
4
|
+
# dependencies
|
|
5
|
+
require "active_support"
|
|
6
|
+
require "active_support/core_ext"
|
|
7
|
+
require "safely/core"
|
|
8
|
+
|
|
9
|
+
# modules
|
|
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
|
+
|
|
23
|
+
module Ahoy
|
|
24
|
+
# activejob optional
|
|
25
|
+
autoload :GeocodeV2Job, "ahoy/geocode_v2_job"
|
|
26
|
+
|
|
27
|
+
mattr_accessor :visit_duration
|
|
28
|
+
self.visit_duration = 4.hours
|
|
29
|
+
|
|
30
|
+
mattr_accessor :visitor_duration
|
|
31
|
+
self.visitor_duration = 2.years
|
|
32
|
+
|
|
33
|
+
def self.cookies=(value)
|
|
34
|
+
if value == false
|
|
35
|
+
if defined?(Mongoid::Document) && defined?(Ahoy::Visit) && Ahoy::Visit < Mongoid::Document
|
|
36
|
+
raise <<~EOS
|
|
37
|
+
This feature requires a new index in Ahoy 5. Set:
|
|
38
|
+
|
|
39
|
+
class Ahoy::Visit
|
|
40
|
+
index({visitor_token: 1, started_at: 1})
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
Create the index before upgrading, and set:
|
|
44
|
+
|
|
45
|
+
Ahoy.cookies = :none
|
|
46
|
+
EOS
|
|
47
|
+
else
|
|
48
|
+
raise <<~EOS
|
|
49
|
+
This feature requires a new index in Ahoy 5. Create a migration with:
|
|
50
|
+
|
|
51
|
+
add_index :ahoy_visits, [:visitor_token, :started_at]
|
|
52
|
+
|
|
53
|
+
Run it before upgrading, and set:
|
|
54
|
+
|
|
55
|
+
Ahoy.cookies = :none
|
|
56
|
+
EOS
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
@@cookies = value
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
mattr_reader :cookies
|
|
63
|
+
self.cookies = true
|
|
64
|
+
|
|
65
|
+
# TODO deprecate in favor of cookie_options
|
|
66
|
+
mattr_accessor :cookie_domain
|
|
67
|
+
|
|
68
|
+
mattr_accessor :cookie_options
|
|
69
|
+
self.cookie_options = {}
|
|
70
|
+
|
|
71
|
+
mattr_accessor :server_side_visits
|
|
72
|
+
self.server_side_visits = true
|
|
73
|
+
|
|
74
|
+
mattr_accessor :quiet
|
|
75
|
+
self.quiet = true
|
|
76
|
+
|
|
77
|
+
mattr_accessor :geocode
|
|
78
|
+
self.geocode = false
|
|
79
|
+
|
|
80
|
+
mattr_accessor :max_content_length
|
|
81
|
+
self.max_content_length = 8192
|
|
82
|
+
|
|
83
|
+
mattr_accessor :max_events_per_request
|
|
84
|
+
self.max_events_per_request = 10
|
|
85
|
+
|
|
86
|
+
mattr_accessor :job_queue
|
|
87
|
+
self.job_queue = :ahoy
|
|
88
|
+
|
|
89
|
+
mattr_accessor :api
|
|
90
|
+
self.api = false
|
|
91
|
+
|
|
92
|
+
mattr_accessor :api_only
|
|
93
|
+
self.api_only = false
|
|
94
|
+
|
|
95
|
+
mattr_accessor :protect_from_forgery
|
|
96
|
+
self.protect_from_forgery = true
|
|
97
|
+
|
|
98
|
+
mattr_accessor :preserve_callbacks
|
|
99
|
+
self.preserve_callbacks = [:load_authlogic, :activate_authlogic]
|
|
100
|
+
|
|
101
|
+
mattr_accessor :user_method
|
|
102
|
+
self.user_method = lambda do |controller|
|
|
103
|
+
(controller.respond_to?(:current_user, true) && controller.send(:current_user)) || (controller.respond_to?(:current_resource_owner, true) && controller.send(:current_resource_owner)) || nil
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
mattr_accessor :exclude_method
|
|
107
|
+
|
|
108
|
+
mattr_accessor :track_bots
|
|
109
|
+
self.track_bots = false
|
|
110
|
+
|
|
111
|
+
mattr_accessor :bot_detection_version
|
|
112
|
+
self.bot_detection_version = 2
|
|
113
|
+
|
|
114
|
+
mattr_accessor :token_generator
|
|
115
|
+
self.token_generator = -> { SecureRandom.uuid }
|
|
116
|
+
|
|
117
|
+
mattr_accessor :mask_ips
|
|
118
|
+
self.mask_ips = false
|
|
119
|
+
|
|
120
|
+
mattr_accessor :user_agent_parser
|
|
121
|
+
self.user_agent_parser = :device_detector
|
|
122
|
+
|
|
123
|
+
mattr_accessor :logger
|
|
124
|
+
|
|
125
|
+
def self.log(message)
|
|
126
|
+
logger.info { "[ahoy] #{message}" } if logger
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def self.cookies?
|
|
130
|
+
cookies && cookies != :none
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def self.mask_ip(ip)
|
|
134
|
+
addr = IPAddr.new(ip)
|
|
135
|
+
if addr.ipv4?
|
|
136
|
+
# set last octet to 0
|
|
137
|
+
addr.mask(24).to_s
|
|
138
|
+
else
|
|
139
|
+
# set last 80 bits to zeros
|
|
140
|
+
addr.mask(48).to_s
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def self.instance
|
|
145
|
+
Thread.current[:ahoy]
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def self.instance=(value)
|
|
149
|
+
Thread.current[:ahoy] = value
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
ActiveSupport.on_load(:action_controller) do
|
|
154
|
+
include Ahoy::Controller
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
ActiveSupport.on_load(:active_record) do
|
|
158
|
+
extend Ahoy::Model
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
ActiveSupport.on_load(:action_view) do
|
|
162
|
+
include Ahoy::Helper
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
ActiveSupport.on_load(:mongoid) do
|
|
166
|
+
Mongoid::Document::ClassMethods.include(Ahoy::Model)
|
|
167
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require_relative "ahoy"
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
require "rails/generators"
|
|
2
|
+
require "rails/generators/active_record"
|
|
3
|
+
|
|
4
|
+
module Ahoy
|
|
5
|
+
module Generators
|
|
6
|
+
class ActiverecordGenerator < Rails::Generators::Base
|
|
7
|
+
include ActiveRecord::Generators::Migration
|
|
8
|
+
source_root File.join(__dir__, "templates")
|
|
9
|
+
|
|
10
|
+
class_option :database, type: :string, aliases: "-d"
|
|
11
|
+
|
|
12
|
+
def copy_templates
|
|
13
|
+
template "database_store_initializer.rb", "config/initializers/ahoy.rb"
|
|
14
|
+
template "active_record_visit_model.rb", "app/models/ahoy/visit.rb"
|
|
15
|
+
template "active_record_event_model.rb", "app/models/ahoy/event.rb"
|
|
16
|
+
migration_template "active_record_migration.rb", "db/migrate/create_ahoy_visits_and_events.rb", migration_version: migration_version
|
|
17
|
+
puts "\nAlmost set! Last, run:\n\n rails db:migrate"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def properties_type
|
|
21
|
+
case adapter
|
|
22
|
+
when /postg/i # postgres, postgis
|
|
23
|
+
"jsonb"
|
|
24
|
+
when /mysql|trilogy/i
|
|
25
|
+
"json"
|
|
26
|
+
else
|
|
27
|
+
"text"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
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_db_config instead of connection.adapter
|
|
37
|
+
# so database connection isn't needed
|
|
38
|
+
def adapter
|
|
39
|
+
ActiveRecord::Base.connection_db_config.adapter.to_s
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def migration_version
|
|
43
|
+
"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def primary_key_type
|
|
47
|
+
", id: :#{key_type}" if key_type
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def foreign_key_type
|
|
51
|
+
", type: :#{key_type}" if key_type
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def key_type
|
|
55
|
+
Rails.configuration.generators.options.dig(:active_record, :primary_key_type)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require "rails/generators"
|
|
2
|
+
|
|
3
|
+
module Ahoy
|
|
4
|
+
module Generators
|
|
5
|
+
class BaseGenerator < Rails::Generators::Base
|
|
6
|
+
source_root File.join(__dir__, "templates")
|
|
7
|
+
|
|
8
|
+
def copy_templates
|
|
9
|
+
template "base_store_initializer.rb", "config/initializers/ahoy.rb"
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|