nano-quick-box 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.

Potentially problematic release.


This version of nano-quick-box might be problematic. Click here for more details.

Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/ahoy_matey-5.5.0/CHANGELOG.md +403 -0
  3. data/ahoy_matey-5.5.0/CONTRIBUTING.md +42 -0
  4. data/ahoy_matey-5.5.0/LICENSE.txt +22 -0
  5. data/ahoy_matey-5.5.0/README.md +802 -0
  6. data/ahoy_matey-5.5.0/app/controllers/ahoy/base_controller.rb +44 -0
  7. data/ahoy_matey-5.5.0/app/controllers/ahoy/events_controller.rb +51 -0
  8. data/ahoy_matey-5.5.0/app/controllers/ahoy/visits_controller.rb +15 -0
  9. data/ahoy_matey-5.5.0/config/routes.rb +10 -0
  10. data/ahoy_matey-5.5.0/lib/ahoy/base_store.rb +104 -0
  11. data/ahoy_matey-5.5.0/lib/ahoy/controller.rb +56 -0
  12. data/ahoy_matey-5.5.0/lib/ahoy/database_store.rb +96 -0
  13. data/ahoy_matey-5.5.0/lib/ahoy/engine.rb +37 -0
  14. data/ahoy_matey-5.5.0/lib/ahoy/geocode_v2_job.rb +31 -0
  15. data/ahoy_matey-5.5.0/lib/ahoy/helper.rb +40 -0
  16. data/ahoy_matey-5.5.0/lib/ahoy/model.rb +15 -0
  17. data/ahoy_matey-5.5.0/lib/ahoy/query_methods.rb +88 -0
  18. data/ahoy_matey-5.5.0/lib/ahoy/tracker.rb +287 -0
  19. data/ahoy_matey-5.5.0/lib/ahoy/utils.rb +7 -0
  20. data/ahoy_matey-5.5.0/lib/ahoy/version.rb +3 -0
  21. data/ahoy_matey-5.5.0/lib/ahoy/visit_properties.rb +122 -0
  22. data/ahoy_matey-5.5.0/lib/ahoy/warden.rb +5 -0
  23. data/ahoy_matey-5.5.0/lib/ahoy.rb +315 -0
  24. data/ahoy_matey-5.5.0/lib/ahoy_matey.rb +1 -0
  25. data/ahoy_matey-5.5.0/lib/generators/ahoy/activerecord_generator.rb +59 -0
  26. data/ahoy_matey-5.5.0/lib/generators/ahoy/base_generator.rb +13 -0
  27. data/ahoy_matey-5.5.0/lib/generators/ahoy/install_generator.rb +44 -0
  28. data/ahoy_matey-5.5.0/lib/generators/ahoy/mongoid_generator.rb +16 -0
  29. data/ahoy_matey-5.5.0/lib/generators/ahoy/templates/active_record_event_model.rb.tt +10 -0
  30. data/ahoy_matey-5.5.0/lib/generators/ahoy/templates/active_record_migration.rb.tt +62 -0
  31. data/ahoy_matey-5.5.0/lib/generators/ahoy/templates/active_record_visit_model.rb.tt +6 -0
  32. data/ahoy_matey-5.5.0/lib/generators/ahoy/templates/base_store_initializer.rb.tt +25 -0
  33. data/ahoy_matey-5.5.0/lib/generators/ahoy/templates/database_store_initializer.rb.tt +10 -0
  34. data/ahoy_matey-5.5.0/lib/generators/ahoy/templates/mongoid_event_model.rb.tt +14 -0
  35. data/ahoy_matey-5.5.0/lib/generators/ahoy/templates/mongoid_visit_model.rb.tt +50 -0
  36. data/ahoy_matey-5.5.0/vendor/assets/javascripts/ahoy.js +544 -0
  37. data/nano-quick-box.gemspec +12 -0
  38. 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,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
@@ -0,0 +1,3 @@
1
+ module Ahoy
2
+ VERSION = "5.5.0"
3
+ 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,5 @@
1
+ Warden::Manager.after_set_user except: :fetch do |user, auth, _|
2
+ request = ActionDispatch::Request.new(auth.env)
3
+ ahoy = Ahoy::Tracker.new(request: request)
4
+ ahoy.authenticate(user)
5
+ end