rackr 0.0.65 → 0.0.67
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/lib/rackr/action.rb +234 -85
- data/lib/rackr/callback.rb +1 -0
- data/lib/rackr/router/build_request.rb +1 -0
- data/lib/rackr/router/dev_html/dump.rb +105 -0
- data/lib/rackr/router/{errors/dev_html.rb → dev_html/errors.rb} +11 -8
- data/lib/rackr/router/endpoint.rb +23 -0
- data/lib/rackr/router/errors.rb +10 -4
- data/lib/rackr/router/path_route.rb +35 -0
- data/lib/rackr/router/route.rb +1 -0
- data/lib/rackr/router.rb +182 -116
- data/lib/rackr/utils.rb +28 -0
- data/lib/rackr.rb +104 -33
- metadata +8 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a2c53919b2365343fd55636d5d3a29b41e93975a60ef3d2af74416e12808f27a
|
|
4
|
+
data.tar.gz: 32f5c5da65e14bf318c07427fd5bb0f9c978c04d75e94877ddab0793da4a8005
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8e0decd638c32afe394950c7b45bf44539704b136bde9d9e9dfc2a698e37c427317e16fb310bb6515d0f4a339fd224228d9d5ee424ddbaca7f191ec8f40f4b27
|
|
7
|
+
data.tar.gz: 04117f475a16165a813cac337f72880e6d461ffe41e3ce085ab06690fbc4b21a85f1f22d343fa4a4b070f4a7d9898f26eec29d231df7c8b9469b4d2d60f34848
|
data/lib/rackr/action.rb
CHANGED
|
@@ -3,95 +3,259 @@
|
|
|
3
3
|
require 'erubi'
|
|
4
4
|
require 'oj'
|
|
5
5
|
require 'rack'
|
|
6
|
+
require_relative 'action/callbacks'
|
|
6
7
|
|
|
7
8
|
class Rackr
|
|
9
|
+
# This module provides the action functions available inside the routes context or
|
|
10
|
+
# specific action class that included the Rackr::Action.
|
|
8
11
|
module Action
|
|
9
|
-
|
|
12
|
+
MIME_TYPES = {
|
|
13
|
+
text: 'text/plain',
|
|
14
|
+
html: 'text/html',
|
|
15
|
+
json: 'application/json',
|
|
16
|
+
manifest: 'text/cache-manifest',
|
|
17
|
+
atom: 'application/atom+xml',
|
|
18
|
+
avi: 'video/x-msvideo',
|
|
19
|
+
bmp: 'image/bmp',
|
|
20
|
+
bz: 'application/x-bzip',
|
|
21
|
+
bz2: 'application/x-bzip2',
|
|
22
|
+
chm: 'application/vnd.ms-htmlhelp',
|
|
23
|
+
css: 'text/css',
|
|
24
|
+
csv: 'text/csv',
|
|
25
|
+
flv: 'video/x-flv',
|
|
26
|
+
gif: 'image/gif',
|
|
27
|
+
gz: 'application/x-gzip',
|
|
28
|
+
h264: 'video/h264',
|
|
29
|
+
ico: 'image/vnd.microsoft.icon',
|
|
30
|
+
ics: 'text/calendar',
|
|
31
|
+
jpg: 'image/jpeg',
|
|
32
|
+
js: 'application/javascript',
|
|
33
|
+
mp4: 'video/mp4',
|
|
34
|
+
mov: 'video/quicktime',
|
|
35
|
+
mp3: 'audio/mpeg',
|
|
36
|
+
mp4a: 'audio/mp4',
|
|
37
|
+
mpg: 'video/mpeg',
|
|
38
|
+
oga: 'audio/ogg',
|
|
39
|
+
ogg: 'application/ogg',
|
|
40
|
+
ogv: 'video/ogg',
|
|
41
|
+
pdf: 'application/pdf',
|
|
42
|
+
pgp: 'application/pgp-encrypted',
|
|
43
|
+
png: 'image/png',
|
|
44
|
+
psd: 'image/vnd.adobe.photoshop',
|
|
45
|
+
rss: 'application/rss+xml',
|
|
46
|
+
rtf: 'application/rtf',
|
|
47
|
+
sh: 'application/x-sh',
|
|
48
|
+
svg: 'image/svg+xml',
|
|
49
|
+
swf: 'application/x-shockwave-flash',
|
|
50
|
+
tar: 'application/x-tar',
|
|
51
|
+
torrent: 'application/x-bittorrent',
|
|
52
|
+
tsv: 'text/tab-separated-values',
|
|
53
|
+
uri: 'text/uri-list',
|
|
54
|
+
vcs: 'text/x-vcalendar',
|
|
55
|
+
wav: 'audio/x-wav',
|
|
56
|
+
webm: 'video/webm',
|
|
57
|
+
wmv: 'video/x-ms-wmv',
|
|
58
|
+
woff: 'application/font-woff',
|
|
59
|
+
woff2: 'application/font-woff2',
|
|
60
|
+
wsdl: 'application/wsdl+xml',
|
|
61
|
+
xhtml: 'application/xhtml+xml',
|
|
62
|
+
xml: 'application/xml',
|
|
63
|
+
xslt: 'application/xslt+xml',
|
|
64
|
+
yml: 'text/yaml',
|
|
65
|
+
zip: 'application/zip'
|
|
66
|
+
}.freeze
|
|
67
|
+
|
|
68
|
+
DEFAULT_CSP_HEADERS = {
|
|
69
|
+
base_uri: "'self'",
|
|
70
|
+
child_src: "'self'",
|
|
71
|
+
connect_src: "'self'",
|
|
72
|
+
default_src: "'none'",
|
|
73
|
+
font_src: "'self'",
|
|
74
|
+
form_action: "'self'",
|
|
75
|
+
frame_ancestors: "'self'",
|
|
76
|
+
frame_src: "'self'",
|
|
77
|
+
img_src: "'self' https: data:",
|
|
78
|
+
media_src: "'self'",
|
|
79
|
+
object_src: "'none'",
|
|
80
|
+
script_src: "'self'",
|
|
81
|
+
style_src: "'self' 'unsafe-inline' https:"
|
|
82
|
+
}.freeze
|
|
83
|
+
|
|
84
|
+
# These are constant (not methods) for better performance
|
|
85
|
+
|
|
86
|
+
DEFAULT_HEADERS = (proc do |content_type, headers, content|
|
|
10
87
|
{
|
|
11
88
|
'content-type' => content_type,
|
|
12
89
|
'content-length' => content.bytesize.to_s
|
|
13
90
|
}.merge(headers)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
@@default_headers_for.call('text/html; charset=utf-8', headers, val),
|
|
21
|
-
[val]
|
|
22
|
-
]
|
|
91
|
+
end).freeze
|
|
92
|
+
|
|
93
|
+
RENDER = {
|
|
94
|
+
json: proc do |content, status, headers, charset|
|
|
95
|
+
content = Oj.dump(content, mode: :compat) unless content.is_a?(String)
|
|
96
|
+
[status || 200, DEFAULT_HEADERS.call("application/json; charset=#{charset}", headers, content), [content]]
|
|
23
97
|
end,
|
|
24
|
-
|
|
25
|
-
[
|
|
26
|
-
|
|
27
|
-
@@default_headers_for.call('text/plain', headers, val),
|
|
28
|
-
[val]
|
|
29
|
-
]
|
|
98
|
+
html: proc do |content, status, headers, charset, content_security_policy|
|
|
99
|
+
headers['content-security-policy'] = content_security_policy
|
|
100
|
+
[status || 200, DEFAULT_HEADERS.call("text/html; charset=#{charset}", headers, content), [content]]
|
|
30
101
|
end,
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
]
|
|
102
|
+
res: proc do |content, status, _headers, charset|
|
|
103
|
+
content.status = status if status
|
|
104
|
+
if charset
|
|
105
|
+
content.content_type =
|
|
106
|
+
(content.content_type || 'charset=utf-8').sub(/charset=\S+/, "charset=#{charset}")
|
|
107
|
+
end
|
|
108
|
+
content.headers['content-length'] ||= content.body.join.bytesize.to_s
|
|
109
|
+
content.finish
|
|
38
110
|
end,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
111
|
+
response: proc do |content, status, _headers, charset|
|
|
112
|
+
content.status = status if status
|
|
113
|
+
if charset
|
|
114
|
+
content.content_type =
|
|
115
|
+
(content.content_type || 'charset=utf-8').sub(/charset=\S+/, "charset=#{charset}")
|
|
116
|
+
end
|
|
117
|
+
content.headers['content-length'] ||= content.body.join.bytesize.to_s
|
|
118
|
+
content.finish
|
|
119
|
+
end
|
|
120
|
+
}.freeze
|
|
121
|
+
|
|
122
|
+
BUILD_RESPONSE = {
|
|
123
|
+
json: proc do |content, status, headers, charset|
|
|
124
|
+
content = Oj.dump(content, mode: :compat) unless content.is_a?(String)
|
|
125
|
+
Rack::Response.new(content, status,
|
|
126
|
+
DEFAULT_HEADERS.call("application/json; charset=#{charset}", headers, content))
|
|
127
|
+
end,
|
|
128
|
+
html: proc do |content, status, headers, charset, content_security_policy|
|
|
129
|
+
headers['content-security-policy'] = content_security_policy
|
|
130
|
+
Rack::Response.new(content, status, DEFAULT_HEADERS.call("text/html; charset=#{charset}", headers, content))
|
|
43
131
|
end,
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
132
|
+
head: proc do |status, _empty, headers|
|
|
133
|
+
Rack::Response.new(nil, status, headers)
|
|
134
|
+
end,
|
|
135
|
+
redirect_to: proc do |content, _status, headers|
|
|
136
|
+
Rack::Response.new(
|
|
137
|
+
nil,
|
|
138
|
+
302,
|
|
139
|
+
{ 'location' => content }.merge(headers)
|
|
140
|
+
)
|
|
48
141
|
end
|
|
49
142
|
}.freeze
|
|
50
143
|
|
|
51
144
|
def self.included(base)
|
|
52
145
|
base.class_eval do
|
|
53
|
-
|
|
146
|
+
if self != Rackr
|
|
147
|
+
attr_reader :routes, :config, :deps, :db, :log, :cache
|
|
148
|
+
|
|
149
|
+
include Callbacks unless included_modules.include?(Rackr::Callback)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
include HtmlSlice if Object.const_defined?('HtmlSlice')
|
|
153
|
+
include Stimulux if Object.const_defined?('Stimulux')
|
|
54
154
|
|
|
55
155
|
def initialize(routes: nil, config: nil)
|
|
56
156
|
@routes = routes
|
|
57
157
|
@config = config
|
|
58
|
-
@deps = config
|
|
59
|
-
@db = config
|
|
158
|
+
@deps = config&.dig(:deps)
|
|
159
|
+
@db = config&.dig(:deps, :db)
|
|
160
|
+
@log = config&.dig(:deps, :log)
|
|
161
|
+
@cache = config&.dig(:deps, :cache)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def url_for(method, name)
|
|
165
|
+
"#{config&.dig(:host)}#{path_for(method, name)}"
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def path_for(method, name)
|
|
169
|
+
routes.send(method)&.dig(name)
|
|
60
170
|
end
|
|
61
171
|
|
|
62
172
|
def render(**opts)
|
|
63
173
|
type = opts.keys.first
|
|
64
174
|
content = opts[type]
|
|
65
175
|
|
|
66
|
-
|
|
176
|
+
if (renderer = RENDER[type])
|
|
177
|
+
return renderer.call(
|
|
178
|
+
content,
|
|
179
|
+
opts[:status],
|
|
180
|
+
opts[:headers] || {},
|
|
181
|
+
opts[:charset] || 'utf-8',
|
|
182
|
+
content_security_policy
|
|
183
|
+
)
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
if (mime = MIME_TYPES[type])
|
|
187
|
+
return [
|
|
188
|
+
opts[:status] || 200,
|
|
189
|
+
DEFAULT_HEADERS.call(
|
|
190
|
+
"#{mime}; charset=#{opts[:charset] || 'utf-8'}",
|
|
191
|
+
opts[:headers] || {},
|
|
192
|
+
content
|
|
193
|
+
),
|
|
194
|
+
[content]
|
|
195
|
+
]
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
_render_view(
|
|
199
|
+
content,
|
|
200
|
+
status: opts[:status] || 200,
|
|
201
|
+
headers: opts[:headers] || {},
|
|
202
|
+
layout_path: opts[:layout_path] || 'layout',
|
|
203
|
+
view: opts[:view],
|
|
204
|
+
response_instance: opts[:response_instance] || false,
|
|
205
|
+
charset: opts[:charset] || 'utf-8'
|
|
206
|
+
)
|
|
67
207
|
end
|
|
68
208
|
|
|
69
|
-
def
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
209
|
+
def build_response(**opts)
|
|
210
|
+
type = opts.keys.first
|
|
211
|
+
content = opts[type]
|
|
212
|
+
|
|
213
|
+
if (builder = BUILD_RESPONSE[type])
|
|
214
|
+
return builder.call(
|
|
215
|
+
content,
|
|
216
|
+
opts[:status] || 200,
|
|
217
|
+
opts[:headers] || {},
|
|
218
|
+
opts[:charset] || 'utf-8',
|
|
219
|
+
content_security_policy
|
|
220
|
+
)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
if (mime = MIME_TYPES[type])
|
|
224
|
+
return Rack::Response.new(
|
|
225
|
+
content,
|
|
226
|
+
opts[:status] || 200,
|
|
227
|
+
DEFAULT_HEADERS.call(
|
|
228
|
+
"#{mime}; charset=#{opts[:charset] || 'utf-8'}",
|
|
229
|
+
opts[:headers] || {},
|
|
230
|
+
content
|
|
231
|
+
)
|
|
232
|
+
)
|
|
233
|
+
end
|
|
234
|
+
|
|
75
235
|
_render_view(
|
|
76
|
-
|
|
77
|
-
status: status,
|
|
78
|
-
headers: headers,
|
|
79
|
-
layout_path: layout_path,
|
|
80
|
-
|
|
236
|
+
content,
|
|
237
|
+
status: opts[:status] || 200,
|
|
238
|
+
headers: opts[:headers] || {},
|
|
239
|
+
layout_path: opts[:layout_path] || 'layout',
|
|
240
|
+
view: opts[:view],
|
|
241
|
+
response_instance: true,
|
|
242
|
+
charset: opts[:charset] || 'utf-8'
|
|
81
243
|
)
|
|
82
244
|
end
|
|
83
245
|
|
|
84
246
|
def _render_view(
|
|
85
247
|
paths,
|
|
86
|
-
status
|
|
87
|
-
headers
|
|
88
|
-
layout_path
|
|
89
|
-
response_instance
|
|
90
|
-
view
|
|
248
|
+
status:,
|
|
249
|
+
headers:,
|
|
250
|
+
layout_path:,
|
|
251
|
+
response_instance:,
|
|
252
|
+
view:,
|
|
253
|
+
charset:
|
|
91
254
|
)
|
|
92
255
|
base_path = config.dig(:views, :path) || 'views'
|
|
256
|
+
headers['content-security-policy'] ||= content_security_policy
|
|
93
257
|
|
|
94
|
-
file_or_nil =
|
|
258
|
+
file_or_nil = proc do |path|
|
|
95
259
|
::File.read(path)
|
|
96
260
|
rescue Errno::ENOENT
|
|
97
261
|
nil
|
|
@@ -118,56 +282,31 @@ class Rackr
|
|
|
118
282
|
return Rack::Response.new(
|
|
119
283
|
parsed_erb,
|
|
120
284
|
status,
|
|
121
|
-
|
|
285
|
+
DEFAULT_HEADERS.call("text/html; charset=#{charset}", headers, parsed_erb)
|
|
122
286
|
)
|
|
123
287
|
end
|
|
124
288
|
|
|
125
|
-
[status,
|
|
289
|
+
[status, DEFAULT_HEADERS.call("text/html; charset=#{charset}", headers, parsed_erb), [parsed_erb]]
|
|
126
290
|
end
|
|
127
291
|
|
|
128
|
-
def
|
|
129
|
-
raise Rackr::
|
|
292
|
+
def d(content)
|
|
293
|
+
raise Rackr::Dump, content
|
|
130
294
|
end
|
|
131
295
|
|
|
132
|
-
def
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
Oj.load(val)
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
def html_response(content = '', status: 200, headers: {})
|
|
139
|
-
Rack::Response.new(content, status, @@default_headers_for.call('text/html; charset=utf-8', headers, content))
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
def json_response(content = {}, status: 200, headers: {})
|
|
143
|
-
content = Oj.dump(content, mode: :compat) unless content.is_a?(String)
|
|
144
|
-
Rack::Response.new(content, status, @@default_headers_for.call('application/json', headers, content))
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
def text_response(content, status: 200, headers: {})
|
|
148
|
-
Rack::Response.new(content, status, @@default_headers_for.call('text/plain', headers, content))
|
|
296
|
+
def not_found!
|
|
297
|
+
raise Rackr::NotFound
|
|
149
298
|
end
|
|
150
299
|
|
|
300
|
+
# rubocop:disable Security/Eval
|
|
151
301
|
def load_erb(content, binding_context: nil)
|
|
152
302
|
eval(Erubi::Engine.new(content).src, binding_context)
|
|
153
303
|
end
|
|
304
|
+
# rubocop:enable Security/Eval
|
|
154
305
|
|
|
155
306
|
def head(status, headers: {})
|
|
156
307
|
[status, headers, []]
|
|
157
308
|
end
|
|
158
309
|
|
|
159
|
-
def head_response(status, headers: {})
|
|
160
|
-
Rack::Response.new(nil, status, headers)
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
def redirect_response(url, headers: {})
|
|
164
|
-
Rack::Response.new(
|
|
165
|
-
nil,
|
|
166
|
-
302,
|
|
167
|
-
{ 'location' => url }.merge(headers)
|
|
168
|
-
)
|
|
169
|
-
end
|
|
170
|
-
|
|
171
310
|
def redirect_to(url, headers: {})
|
|
172
311
|
[302, { 'location' => url }.merge(headers), []]
|
|
173
312
|
end
|
|
@@ -175,7 +314,17 @@ class Rackr
|
|
|
175
314
|
def response(body = nil, status = 200, headers = {})
|
|
176
315
|
Rack::Response.new(body, status, headers)
|
|
177
316
|
end
|
|
317
|
+
|
|
318
|
+
def content_security_policy
|
|
319
|
+
@content_security_policy ||=
|
|
320
|
+
DEFAULT_CSP_HEADERS
|
|
321
|
+
.merge(config&.dig(:csp_headers) || {})
|
|
322
|
+
.map { |k, v| "#{k.to_s.tr('_', '-')} #{v}" }
|
|
323
|
+
.join('; ')
|
|
324
|
+
end
|
|
178
325
|
end
|
|
179
326
|
end
|
|
180
327
|
end
|
|
328
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
|
329
|
+
# rubocop:enable Metrics/MethodLength
|
|
181
330
|
end
|
data/lib/rackr/callback.rb
CHANGED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rack/utils'
|
|
4
|
+
|
|
5
|
+
class Rackr
|
|
6
|
+
class Router
|
|
7
|
+
module DevHtml
|
|
8
|
+
# Pretty print the content of the dump
|
|
9
|
+
class PrettyPrint
|
|
10
|
+
def self.call(content, level = 0)
|
|
11
|
+
content = content.inspect if content.instance_of?(String)
|
|
12
|
+
content = 'nil' if content.instance_of?(nil)
|
|
13
|
+
content = 'false' if content.instance_of?(false)
|
|
14
|
+
|
|
15
|
+
return "<pre>#{Rack::Utils.escape_html(content)}</pre>" if level >= 2
|
|
16
|
+
|
|
17
|
+
case content
|
|
18
|
+
when String, Symbol, Numeric, TrueClass, FalseClass, NilClass
|
|
19
|
+
"<pre>#{Rack::Utils.escape_html(content)}</pre>"
|
|
20
|
+
when Array
|
|
21
|
+
pretty_print_array(content, level)
|
|
22
|
+
when Hash
|
|
23
|
+
pretty_print_hash(content, level)
|
|
24
|
+
else
|
|
25
|
+
pretty_print_object(content, level)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.pretty_print_array(array, level)
|
|
30
|
+
list_items = array.map do |item|
|
|
31
|
+
"<li>#{call(item, level + 1)}</li>"
|
|
32
|
+
end.join
|
|
33
|
+
|
|
34
|
+
"<ul>#{list_items}</ul>"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.pretty_print_hash(hash, level)
|
|
38
|
+
list_items = hash.map do |key, value|
|
|
39
|
+
"<li><strong>#{key.inspect} =></strong> #{call(value, level + 1)}</li>"
|
|
40
|
+
end.join
|
|
41
|
+
|
|
42
|
+
"<ul>#{list_items}</ul>"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def self.pretty_print_object(content, level)
|
|
46
|
+
instance_vars = content.instance_variables.map do |var|
|
|
47
|
+
value = content.instance_variable_get(var)
|
|
48
|
+
"<li><strong>#{var}:</strong> #{call(value, level + 1)}</li>"
|
|
49
|
+
end.join
|
|
50
|
+
|
|
51
|
+
"<h3>#{content.class}</h3><ul>#{instance_vars}</ul>"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# This is the action that is called when a dump is raised
|
|
56
|
+
class Dump
|
|
57
|
+
include Rackr::Action
|
|
58
|
+
|
|
59
|
+
def call(env)
|
|
60
|
+
res = response(<<-HTML
|
|
61
|
+
<!DOCTYPE html>
|
|
62
|
+
<html>
|
|
63
|
+
<head>
|
|
64
|
+
<title>Application dump</title>
|
|
65
|
+
<style>
|
|
66
|
+
body {
|
|
67
|
+
padding: 0px;
|
|
68
|
+
margin: 0px;
|
|
69
|
+
font:small sans-serif;
|
|
70
|
+
background-color: #2b2b2b;
|
|
71
|
+
color: white;
|
|
72
|
+
}
|
|
73
|
+
h2 {
|
|
74
|
+
margin: 0px;
|
|
75
|
+
padding: 0.2em;
|
|
76
|
+
background-color: #353535;
|
|
77
|
+
}
|
|
78
|
+
li {
|
|
79
|
+
padding: 0px;
|
|
80
|
+
}
|
|
81
|
+
div {
|
|
82
|
+
margin: 1em;
|
|
83
|
+
}
|
|
84
|
+
</style>
|
|
85
|
+
</head>
|
|
86
|
+
<body>
|
|
87
|
+
<h2>#{env['dump'].content.class}</h2>
|
|
88
|
+
<div>
|
|
89
|
+
<h3>Methods</h3>
|
|
90
|
+
#{env['dump'].content.methods}
|
|
91
|
+
<h3>Content</h3>
|
|
92
|
+
#{PrettyPrint.call(env['dump'].content)}
|
|
93
|
+
</div>
|
|
94
|
+
</body>
|
|
95
|
+
</html>
|
|
96
|
+
HTML
|
|
97
|
+
)
|
|
98
|
+
res.status = 200
|
|
99
|
+
res.headers['content-type'] = 'text/html'
|
|
100
|
+
render res:
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
class Rackr
|
|
4
4
|
class Router
|
|
5
|
-
module
|
|
6
|
-
|
|
5
|
+
module DevHtml
|
|
6
|
+
# This is the action that is called when an error is raised
|
|
7
|
+
class Errors
|
|
7
8
|
include Rackr::Action
|
|
8
9
|
|
|
9
10
|
def call(env)
|
|
10
|
-
|
|
11
|
+
res = build_response(html: <<-HTML
|
|
11
12
|
<!DOCTYPE html>
|
|
12
13
|
<html>
|
|
13
14
|
<head>
|
|
@@ -20,9 +21,9 @@ class Rackr
|
|
|
20
21
|
body>div { border-bottom:1px solid #ddd; }
|
|
21
22
|
h1 { font-weight:normal; }
|
|
22
23
|
h2 { margin-bottom:.8em; }
|
|
23
|
-
h2 span { font-size:80%; color:#
|
|
24
|
+
h2 span { font-size:80%; color:#555; font-weight:normal; }
|
|
24
25
|
#summary { background: #ffc; }
|
|
25
|
-
#summary h2 { font-weight: normal; color: #
|
|
26
|
+
#summary h2 { font-weight: normal; color: #555; }
|
|
26
27
|
#backtrace { background: #eee; }
|
|
27
28
|
pre {
|
|
28
29
|
background: #ddd;
|
|
@@ -45,12 +46,14 @@ class Rackr
|
|
|
45
46
|
</body>
|
|
46
47
|
</html>
|
|
47
48
|
HTML
|
|
48
|
-
|
|
49
|
+
)
|
|
50
|
+
res.status = 500
|
|
51
|
+
render res:
|
|
49
52
|
end
|
|
50
53
|
|
|
51
54
|
def backtrace(env)
|
|
52
55
|
first, *tail = env['error'].backtrace
|
|
53
|
-
traceback =
|
|
56
|
+
traceback = +'<h2>Traceback <span>(innermost first)</span></h2>'
|
|
54
57
|
traceback << "<p class=\"first-p\">#{first}</p><br/>"
|
|
55
58
|
|
|
56
59
|
line_number = extract_line_number(first)
|
|
@@ -58,7 +61,7 @@ class Rackr
|
|
|
58
61
|
file_path = (match ? match[1] : nil)
|
|
59
62
|
unless file_path.nil?
|
|
60
63
|
lines = File.readlines(file_path).map.with_index { |line, i| "#{i + 1}: #{line}" }
|
|
61
|
-
traceback << "<pre>#{slice_around_index(lines, line_number).join
|
|
64
|
+
traceback << "<pre>#{slice_around_index(lines, line_number).join}</pre>"
|
|
62
65
|
end
|
|
63
66
|
|
|
64
67
|
traceback << "<p>#{tail.join('<br>')}</p>"
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Rackr
|
|
4
|
+
class Router
|
|
5
|
+
# A endpoint in Rackr is all objects that respond do .call, or .new.call
|
|
6
|
+
module Endpoint
|
|
7
|
+
def self.call(endpoint, content, routes = nil, config = nil, error = nil)
|
|
8
|
+
instance =
|
|
9
|
+
if endpoint.respond_to?(:call)
|
|
10
|
+
endpoint
|
|
11
|
+
elsif endpoint < Rackr::Action || endpoint < Rackr::Callback
|
|
12
|
+
endpoint.new(routes:, config:)
|
|
13
|
+
else
|
|
14
|
+
endpoint.new
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
return instance.call(content, error) if error
|
|
18
|
+
|
|
19
|
+
instance.call(content)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
data/lib/rackr/router/errors.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
class Rackr
|
|
4
4
|
class Router
|
|
5
|
+
# Errors for the router
|
|
5
6
|
module Errors
|
|
6
7
|
class Error < StandardError; end
|
|
7
8
|
class InvalidNamedRouteError < Error; end
|
|
@@ -10,8 +11,15 @@ class Rackr
|
|
|
10
11
|
class InvalidCallbackError < Error; end
|
|
11
12
|
class InvalidPathError < Error; end
|
|
12
13
|
class InvalidBranchNameError < Error; end
|
|
14
|
+
class InvalidRackResponseError < StandardError; end
|
|
13
15
|
|
|
14
16
|
class << self
|
|
17
|
+
def check_rack_response(response, where)
|
|
18
|
+
return if response.is_a?(Array)
|
|
19
|
+
|
|
20
|
+
raise(InvalidRackResponseError, "Invalid Rack response in #{where}, received: #{response}")
|
|
21
|
+
end
|
|
22
|
+
|
|
15
23
|
def check_scope_name(path)
|
|
16
24
|
return if path.is_a?(String) || path.is_a?(Symbol) || path.nil?
|
|
17
25
|
|
|
@@ -39,7 +47,7 @@ class Rackr
|
|
|
39
47
|
|
|
40
48
|
def check_callbacks(callbacks, path)
|
|
41
49
|
check = lambda { |callback|
|
|
42
|
-
unless callback.nil? || callback.respond_to?(:call) || (callback.respond_to?(:new) && callback.
|
|
50
|
+
unless callback.nil? || callback.respond_to?(:call) || (callback.respond_to?(:new) && callback.method_defined?(:call))
|
|
43
51
|
raise(InvalidCallbackError,
|
|
44
52
|
"Callbacks must respond to a `call` method or be a class with a `call` instance method, got: '#{callback.inspect}' for '#{path}'")
|
|
45
53
|
end
|
|
@@ -49,9 +57,7 @@ class Rackr
|
|
|
49
57
|
end
|
|
50
58
|
|
|
51
59
|
def check_endpoint(endpoint, path)
|
|
52
|
-
if endpoint.respond_to?(:call) || (endpoint.respond_to?(:new) && endpoint.
|
|
53
|
-
return
|
|
54
|
-
end
|
|
60
|
+
return if endpoint.respond_to?(:call) || (endpoint.respond_to?(:new) && endpoint.method_defined?(:call))
|
|
55
61
|
|
|
56
62
|
raise(InvalidEndpointError,
|
|
57
63
|
"Endpoints must respond to a `call` method or be a class with a `call` instance method, got: '#{endpoint.inspect}' for '#{path}'")
|