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
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Rackr
|
|
4
|
+
class Router
|
|
5
|
+
# Path route is a route that has a path value that can be matched, and may have path params
|
|
6
|
+
class PathRoute < Route
|
|
7
|
+
attr_reader :splitted_path,
|
|
8
|
+
:has_params
|
|
9
|
+
|
|
10
|
+
def initialize(path, endpoint, befores: [], afters: [], wildcard: false)
|
|
11
|
+
super(endpoint, befores:, afters:)
|
|
12
|
+
|
|
13
|
+
@path = path
|
|
14
|
+
@splitted_path = @path.split('/')
|
|
15
|
+
@params = fetch_params
|
|
16
|
+
@has_params = @params != []
|
|
17
|
+
@path_regex = /\A#{path.gsub(/(:\w+)/, '([^/]+)')}\z/
|
|
18
|
+
@wildcard = wildcard
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def match?(path_info)
|
|
22
|
+
return path_info.match?(@path_regex) if @has_params
|
|
23
|
+
return true if @wildcard
|
|
24
|
+
|
|
25
|
+
path_info == @path
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def fetch_params
|
|
31
|
+
@splitted_path.select { |value| value.start_with? ':' }
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
data/lib/rackr/router/route.rb
CHANGED
data/lib/rackr/router.rb
CHANGED
|
@@ -1,27 +1,23 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'router/utils'
|
|
4
3
|
require_relative 'router/errors'
|
|
5
4
|
require_relative 'router/endpoint'
|
|
6
5
|
require_relative 'router/route'
|
|
7
6
|
require_relative 'router/path_route'
|
|
8
7
|
require_relative 'router/build_request'
|
|
8
|
+
require_relative 'router/dev_html/errors'
|
|
9
|
+
require_relative 'router/dev_html/dump'
|
|
9
10
|
|
|
10
11
|
class Rackr
|
|
12
|
+
# This is the core class of Rackr. This class aggregate the route instance tree, callbacks (before and after) and scopes
|
|
13
|
+
# then, using the building blocks, match the request and call the endpoints
|
|
11
14
|
class Router
|
|
12
|
-
include Utils
|
|
15
|
+
include Rackr::Utils
|
|
13
16
|
|
|
14
17
|
attr_writer :default_not_found
|
|
15
|
-
attr_reader :routes, :config, :
|
|
18
|
+
attr_reader :routes, :config, :not_found_tree, :error_tree, :specific_error_tree
|
|
16
19
|
|
|
17
20
|
def initialize(config = {}, before: [], after: [])
|
|
18
|
-
@path_routes_instances = {}
|
|
19
|
-
%w[GET POST DELETE PUT TRACE OPTIONS PATCH].each do |method|
|
|
20
|
-
@path_routes_instances[method] = { __instances: [] }
|
|
21
|
-
end
|
|
22
|
-
@not_found_instances = {}
|
|
23
|
-
@error_instances = {}
|
|
24
|
-
|
|
25
21
|
http_methods = HTTP_METHODS.map { |m| m.downcase.to_sym }
|
|
26
22
|
@routes = Struct.new(*http_methods).new
|
|
27
23
|
http_methods.each do |method|
|
|
@@ -36,54 +32,123 @@ class Rackr
|
|
|
36
32
|
@scopes_befores = {}
|
|
37
33
|
@afters = ensure_array(after)
|
|
38
34
|
@scopes_afters = {}
|
|
39
|
-
@
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
35
|
+
@path_routes_tree = {}
|
|
36
|
+
%w[GET POST DELETE PUT TRACE OPTIONS PATCH].each do |method|
|
|
37
|
+
@path_routes_tree[method] = { __instances: [] }
|
|
38
|
+
end
|
|
39
|
+
@not_found_tree = {}
|
|
40
|
+
@default_not_found =
|
|
41
|
+
Route.new(
|
|
42
|
+
proc { [404, {}, ['Not found']] },
|
|
43
|
+
befores: @befores,
|
|
44
|
+
afters: @afters
|
|
45
|
+
)
|
|
46
|
+
@error_tree = {}
|
|
47
|
+
@default_error =
|
|
48
|
+
Route.new(
|
|
49
|
+
proc { |_req, _e| [500, {}, ['Internal server error']] },
|
|
50
|
+
befores: @befores,
|
|
51
|
+
afters: @afters
|
|
52
|
+
)
|
|
53
|
+
@specific_error_tree = {}
|
|
54
|
+
@specific_errors = {}
|
|
43
55
|
end
|
|
44
56
|
|
|
45
57
|
def call(env)
|
|
46
58
|
path_info = env['PATH_INFO']
|
|
59
|
+
request_method = env['REQUEST_METHOD'] == 'HEAD' ? 'GET' : env['REQUEST_METHOD']
|
|
47
60
|
|
|
48
|
-
|
|
49
|
-
|
|
61
|
+
splitted_request_path_info = path_info.split('/')
|
|
62
|
+
current_request_path_info =
|
|
50
63
|
path_info == '/' ? path_info : path_info.chomp('/') # remove trailing "/"
|
|
51
64
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
65
|
+
route_instance, found_scopes = match_path_route(
|
|
66
|
+
request_method,
|
|
67
|
+
splitted_request_path_info,
|
|
68
|
+
current_request_path_info
|
|
69
|
+
)
|
|
70
|
+
rack_request = BuildRequest.new(env, splitted_request_path_info).call(route_instance)
|
|
57
71
|
befores = route_instance.befores
|
|
58
72
|
before_result = nil
|
|
59
73
|
|
|
60
74
|
begin
|
|
61
75
|
i = 0
|
|
62
76
|
while i < befores.size
|
|
63
|
-
before_result = Endpoint.call(befores[i], rack_request)
|
|
64
|
-
|
|
77
|
+
before_result = Endpoint.call(befores[i], rack_request, @routes, @config)
|
|
78
|
+
unless before_result.is_a?(Rack::Request)
|
|
79
|
+
Errors.check_rack_response(before_result, 'before callback')
|
|
65
80
|
|
|
66
|
-
|
|
81
|
+
return before_result
|
|
82
|
+
end
|
|
67
83
|
|
|
68
84
|
i += 1
|
|
69
85
|
end
|
|
70
86
|
|
|
71
|
-
endpoint_result = Endpoint.call(route_instance.endpoint, before_result || rack_request)
|
|
87
|
+
endpoint_result = Endpoint.call(route_instance.endpoint, before_result || rack_request, @routes, @config)
|
|
72
88
|
|
|
73
89
|
call_afters(route_instance, endpoint_result)
|
|
74
90
|
rescue Rackr::NotFound
|
|
75
91
|
return not_found_fallback(found_scopes, route_instance, before_result || rack_request)
|
|
92
|
+
rescue Rackr::Dump => e
|
|
93
|
+
return Endpoint.call(DevHtml::Dump, env.merge({ 'dump' => e }))
|
|
94
|
+
# rubocop:disable Lint/RescueException
|
|
76
95
|
rescue Exception => e
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
96
|
+
return error_fallback(found_scopes, route_instance, before_result || rack_request, e, env)
|
|
97
|
+
end
|
|
98
|
+
# rubocop:enable Lint/RescueException
|
|
99
|
+
|
|
100
|
+
Errors.check_rack_response(endpoint_result, 'action')
|
|
101
|
+
endpoint_result
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def not_found_fallback(found_scopes, route_instance, request)
|
|
105
|
+
endpoint_result = Endpoint.call(
|
|
106
|
+
match_route(
|
|
107
|
+
found_scopes,
|
|
108
|
+
not_found_tree,
|
|
109
|
+
@default_not_found
|
|
110
|
+
).endpoint,
|
|
111
|
+
request,
|
|
112
|
+
@routes,
|
|
113
|
+
@config
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
call_afters(route_instance, endpoint_result)
|
|
117
|
+
|
|
118
|
+
endpoint_result
|
|
119
|
+
end
|
|
80
120
|
|
|
81
|
-
|
|
121
|
+
def error_fallback(found_scopes, route_instance, request, error, env)
|
|
122
|
+
error_route = match_route(
|
|
123
|
+
found_scopes,
|
|
124
|
+
specific_error_tree[error.class] || error_tree,
|
|
125
|
+
@specific_errors[error.class] || @default_error
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
return Endpoint.call(DevHtml::Errors, env.merge({ 'error' => error })) if @dev_mode && error_route == @default_error
|
|
129
|
+
|
|
130
|
+
endpoint_result = Endpoint.call(error_route.endpoint, request, @routes, @config, error)
|
|
131
|
+
|
|
132
|
+
if endpoint_result.nil?
|
|
133
|
+
return Endpoint.call(DevHtml::Errors, env.merge({ 'error' => error })) if @dev_mode
|
|
134
|
+
|
|
135
|
+
endpoint_result = Endpoint.call(@default_error.endpoint, request, @routes, @config, error)
|
|
82
136
|
end
|
|
83
137
|
|
|
138
|
+
call_afters(route_instance, endpoint_result)
|
|
139
|
+
|
|
84
140
|
endpoint_result
|
|
85
141
|
end
|
|
86
142
|
|
|
143
|
+
def call_afters(route_instance, endpoint_result)
|
|
144
|
+
afters = route_instance.afters
|
|
145
|
+
i = 0
|
|
146
|
+
while i < afters.size
|
|
147
|
+
Endpoint.call(afters[i], endpoint_result, @routes, @config)
|
|
148
|
+
i += 1
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
87
152
|
def add(method, path, endpoint, as: nil, route_befores: [], route_afters: [])
|
|
88
153
|
Errors.check_path(path)
|
|
89
154
|
Errors.check_endpoint(endpoint, path)
|
|
@@ -94,65 +159,64 @@ class Rackr
|
|
|
94
159
|
method = :get if method == :head
|
|
95
160
|
|
|
96
161
|
wildcard = path == '*'
|
|
97
|
-
path = path.is_a?(Symbol) ? path.inspect : path.
|
|
162
|
+
path = path.is_a?(Symbol) ? path.inspect : path.delete_prefix('/')
|
|
98
163
|
path_with_scopes = "/#{not_empty_scopes.join('/')}#{put_path_slash(path)}"
|
|
99
164
|
add_named_route(method, path_with_scopes, as)
|
|
165
|
+
action_befores, action_afters = fetch_endpoint_callbacks(endpoint)
|
|
100
166
|
|
|
101
167
|
route_instance =
|
|
102
168
|
PathRoute.new(
|
|
103
169
|
path_with_scopes,
|
|
104
170
|
endpoint,
|
|
105
|
-
befores: @befores + ensure_array(route_befores),
|
|
106
|
-
afters: @afters + ensure_array(route_afters),
|
|
171
|
+
befores: @befores + ensure_array(route_befores) + action_befores,
|
|
172
|
+
afters: @afters + ensure_array(route_afters) + action_afters,
|
|
107
173
|
wildcard: wildcard
|
|
108
174
|
)
|
|
109
175
|
|
|
110
|
-
|
|
176
|
+
path_segments = path_with_scopes.split('/').reject(&:empty?)
|
|
111
177
|
|
|
112
|
-
|
|
178
|
+
if path_segments.empty?
|
|
179
|
+
@path_routes_tree[method.to_s.upcase][:__instances].push(route_instance)
|
|
180
|
+
else
|
|
181
|
+
deep_hash_push(@path_routes_tree[method.to_s.upcase], *(path_segments + [:__instances]), route_instance)
|
|
182
|
+
end
|
|
113
183
|
end
|
|
114
184
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
Errors.check_endpoint(endpoint, v)
|
|
185
|
+
def add_not_found(endpoint)
|
|
186
|
+
Errors.check_endpoint(endpoint, 'not found')
|
|
118
187
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
188
|
+
action_befores, action_afters = fetch_endpoint_callbacks(endpoint)
|
|
189
|
+
route_instance =
|
|
190
|
+
Route.new(
|
|
191
|
+
endpoint,
|
|
192
|
+
befores: @befores + action_befores,
|
|
193
|
+
afters: @afters + action_afters
|
|
194
|
+
)
|
|
125
195
|
|
|
126
|
-
|
|
196
|
+
return set_to_scope(not_found_tree, route_instance) if @scopes.size >= 1
|
|
127
197
|
|
|
128
|
-
|
|
129
|
-
|
|
198
|
+
@default_not_found = route_instance
|
|
199
|
+
end
|
|
130
200
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
match_route(
|
|
134
|
-
found_scopes,
|
|
135
|
-
send("#{v}_instances"),
|
|
136
|
-
instance_variable_get("@default_#{v}")
|
|
137
|
-
).endpoint,
|
|
138
|
-
request
|
|
139
|
-
]
|
|
140
|
-
args << error if error
|
|
201
|
+
def add_error(endpoint, error_class = nil)
|
|
202
|
+
Errors.check_endpoint(endpoint, 'error')
|
|
141
203
|
|
|
142
|
-
|
|
204
|
+
action_befores, action_afters = fetch_endpoint_callbacks(endpoint)
|
|
205
|
+
route_instance =
|
|
206
|
+
Route.new(
|
|
207
|
+
endpoint,
|
|
208
|
+
befores: @befores + action_befores,
|
|
209
|
+
afters: @afters + action_afters
|
|
210
|
+
)
|
|
143
211
|
|
|
144
|
-
|
|
212
|
+
if error_class
|
|
213
|
+
return set_to_scope(specific_error_tree[error_class] ||= {}, route_instance) if @scopes.size >= 1
|
|
145
214
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
215
|
+
@specific_errors[error_class] = route_instance
|
|
216
|
+
else
|
|
217
|
+
return set_to_scope(error_tree, route_instance) if @scopes.size >= 1
|
|
149
218
|
|
|
150
|
-
|
|
151
|
-
afters = route_instance.afters
|
|
152
|
-
i = 0
|
|
153
|
-
while i < afters.size
|
|
154
|
-
Endpoint.call(afters[i], endpoint_result)
|
|
155
|
-
i += 1
|
|
219
|
+
@default_error = route_instance
|
|
156
220
|
end
|
|
157
221
|
end
|
|
158
222
|
|
|
@@ -181,22 +245,34 @@ class Rackr
|
|
|
181
245
|
@scopes = @scopes.first(@scopes.size - 1)
|
|
182
246
|
end
|
|
183
247
|
|
|
248
|
+
def not_empty_scopes
|
|
249
|
+
@scopes.reject { |v| (v == '') }
|
|
250
|
+
end
|
|
251
|
+
|
|
184
252
|
private
|
|
185
253
|
|
|
254
|
+
def fetch_endpoint_callbacks(endpoint)
|
|
255
|
+
action_befores = []
|
|
256
|
+
action_afters = []
|
|
257
|
+
if endpoint.is_a?(Class) && endpoint.ancestors.include?(Rackr::Action)
|
|
258
|
+
action_instance = endpoint.new
|
|
259
|
+
action_befores = action_instance.befores
|
|
260
|
+
action_afters = action_instance.afters
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
[action_befores, action_afters]
|
|
264
|
+
end
|
|
265
|
+
|
|
186
266
|
def add_named_route(method, path_with_scopes, as)
|
|
187
267
|
return @routes.send(method.downcase)[:root] = path_with_scopes if path_with_scopes == '/'
|
|
188
268
|
return @routes.send(method.downcase)[as] = path_with_scopes unless as.nil?
|
|
189
269
|
|
|
190
|
-
key = path_with_scopes.sub('/', '').
|
|
270
|
+
key = path_with_scopes.sub('/', '').delete(':').tr('/', '_')
|
|
191
271
|
@routes.send(method.downcase)[key.to_s.to_sym] = path_with_scopes
|
|
192
272
|
end
|
|
193
273
|
|
|
194
|
-
def push_to_scope(method, route_instance)
|
|
195
|
-
deep_hash_push(@path_routes_instances[method], *(not_empty_scopes + %i[__instances]), route_instance)
|
|
196
|
-
end
|
|
197
|
-
|
|
198
274
|
def set_to_scope(instances, route_instance)
|
|
199
|
-
deep_hash_set(instances,
|
|
275
|
+
deep_hash_set(instances, not_empty_scopes + %i[__instance], route_instance)
|
|
200
276
|
end
|
|
201
277
|
|
|
202
278
|
def put_path_slash(path)
|
|
@@ -209,59 +285,49 @@ class Rackr
|
|
|
209
285
|
path
|
|
210
286
|
end
|
|
211
287
|
|
|
212
|
-
def
|
|
213
|
-
|
|
214
|
-
end
|
|
215
|
-
|
|
216
|
-
def match_path_route(request_method)
|
|
217
|
-
find_instance_in_scope = proc do |request_method, found_scopes|
|
|
218
|
-
@path_routes_instances[request_method].dig(
|
|
219
|
-
*(found_scopes + [:__instances])
|
|
220
|
-
)&.detect { |route_instance| route_instance.match?(@current_request_path_info) }
|
|
221
|
-
end
|
|
222
|
-
|
|
223
|
-
last_tail = @splitted_request_path_info.drop(1)
|
|
288
|
+
def match_path_route(request_method, splitted_request_path_info, current_request_path_info)
|
|
289
|
+
path_routes_tree = @path_routes_tree[request_method]
|
|
224
290
|
found_scopes = []
|
|
225
291
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
292
|
+
i = 1
|
|
293
|
+
while i < splitted_request_path_info.size
|
|
294
|
+
segment = splitted_request_path_info[i]
|
|
295
|
+
|
|
296
|
+
if path_routes_tree.key?(segment)
|
|
297
|
+
found_scopes << segment
|
|
298
|
+
path_routes_tree = path_routes_tree[segment]
|
|
299
|
+
elsif (param_key = path_routes_tree.keys.find { |k| k.start_with?(':') })
|
|
300
|
+
found_scopes << param_key
|
|
301
|
+
path_routes_tree = path_routes_tree[param_key]
|
|
302
|
+
elsif path_routes_tree.key?('*')
|
|
303
|
+
path_routes_tree = path_routes_tree['*']
|
|
304
|
+
break
|
|
305
|
+
else
|
|
306
|
+
break
|
|
307
|
+
end
|
|
308
|
+
i += 1
|
|
309
|
+
end
|
|
231
310
|
|
|
232
|
-
|
|
233
|
-
|
|
311
|
+
route_instance = path_routes_tree[:__instances]&.detect do |route|
|
|
312
|
+
route.match?(current_request_path_info)
|
|
313
|
+
end
|
|
234
314
|
|
|
235
|
-
|
|
236
|
-
found_scopes << scope
|
|
237
|
-
path_routes_instances = @path_routes_instances[request_method].dig(*found_scopes)
|
|
238
|
-
break
|
|
239
|
-
elsif scope.start_with?(':')
|
|
240
|
-
found_route = find_instance_in_scope.call(request_method, found_scopes)
|
|
241
|
-
return found_route if found_route
|
|
315
|
+
route_instance = find_not_found_route(found_scopes) if route_instance.nil?
|
|
242
316
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
break
|
|
246
|
-
end
|
|
247
|
-
end
|
|
248
|
-
end
|
|
317
|
+
[route_instance, found_scopes]
|
|
318
|
+
end
|
|
249
319
|
|
|
250
|
-
|
|
320
|
+
def find_not_found_route(found_scopes)
|
|
321
|
+
not_found_route = nil
|
|
251
322
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
323
|
+
while not_found_route.nil? && !found_scopes.empty?
|
|
324
|
+
not_found_route = @not_found_tree.dig(*found_scopes, :__instance)
|
|
325
|
+
break if not_found_route
|
|
255
326
|
|
|
256
|
-
|
|
257
|
-
result_route = match_route(
|
|
258
|
-
found_scopes,
|
|
259
|
-
@not_found_instances,
|
|
260
|
-
@default_not_found
|
|
261
|
-
)
|
|
327
|
+
found_scopes.pop
|
|
262
328
|
end
|
|
263
329
|
|
|
264
|
-
|
|
330
|
+
not_found_route || @default_not_found
|
|
265
331
|
end
|
|
266
332
|
|
|
267
333
|
def match_route(found_scopes, instances, default_instance)
|
data/lib/rackr/utils.rb
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Rackr
|
|
4
|
+
# Utils methods for Rackr
|
|
5
|
+
module Utils
|
|
6
|
+
def deep_hash_push(hash, first_key, *rest_keys, val)
|
|
7
|
+
if rest_keys.empty?
|
|
8
|
+
(hash[first_key] ||= []) << val
|
|
9
|
+
else
|
|
10
|
+
hash[first_key] = deep_hash_push(hash[first_key] ||= {}, *rest_keys, val)
|
|
11
|
+
end
|
|
12
|
+
hash
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def ensure_array(list)
|
|
16
|
+
return [] if list.nil?
|
|
17
|
+
return list if list.is_a?(Array)
|
|
18
|
+
|
|
19
|
+
[list]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def deep_hash_set(hash, keys, value)
|
|
23
|
+
*path, last = keys
|
|
24
|
+
node = path.inject(hash) { |h, k| h[k] ||= {} }
|
|
25
|
+
node[last] = value
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
data/lib/rackr.rb
CHANGED
|
@@ -1,28 +1,36 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative 'rackr/utils'
|
|
3
4
|
require_relative 'rackr/action'
|
|
4
5
|
require_relative 'rackr/callback'
|
|
5
|
-
require_relative 'rackr/router/errors/dev_html'
|
|
6
6
|
require_relative 'rackr/router'
|
|
7
7
|
|
|
8
|
+
# Rackr is a simple router for Rack.
|
|
8
9
|
class Rackr
|
|
9
10
|
class NotFound < StandardError; end
|
|
10
11
|
|
|
12
|
+
# Dump is a special error that is used to dump the content of a request.
|
|
13
|
+
class Dump < StandardError
|
|
14
|
+
attr_reader :content
|
|
15
|
+
|
|
16
|
+
def initialize(content)
|
|
17
|
+
@content = content
|
|
18
|
+
|
|
19
|
+
super
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
11
23
|
HTTP_METHODS = %w[GET POST DELETE PUT TRACE OPTIONS PATCH].freeze
|
|
12
24
|
|
|
13
|
-
include
|
|
25
|
+
include Callback
|
|
26
|
+
include Utils
|
|
14
27
|
|
|
15
28
|
def initialize(config = {}, before: [], after: [])
|
|
16
29
|
@router = Router.new(config, before: before, after: after)
|
|
17
30
|
end
|
|
18
31
|
|
|
19
|
-
def call(&
|
|
20
|
-
instance_eval(&
|
|
21
|
-
#puts "\n= Routes =============="
|
|
22
|
-
#routes.each_pair { |v| p v }
|
|
23
|
-
#puts "\n= Config =============="
|
|
24
|
-
#puts config
|
|
25
|
-
#puts "\n"
|
|
32
|
+
def call(&)
|
|
33
|
+
instance_eval(&)
|
|
26
34
|
|
|
27
35
|
@router
|
|
28
36
|
end
|
|
@@ -40,7 +48,15 @@ class Rackr
|
|
|
40
48
|
end
|
|
41
49
|
|
|
42
50
|
def db
|
|
43
|
-
@router.config
|
|
51
|
+
@router.config&.dig(:deps, :db)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def log
|
|
55
|
+
@router.config&.dig(:deps, :log)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def cache
|
|
59
|
+
@router.config&.dig(:deps, :cache)
|
|
44
60
|
end
|
|
45
61
|
|
|
46
62
|
def scope(name = '', before: [], after: [], &block)
|
|
@@ -62,50 +78,105 @@ class Rackr
|
|
|
62
78
|
end
|
|
63
79
|
end
|
|
64
80
|
|
|
65
|
-
def error(endpoint =
|
|
81
|
+
def error(error_class_or_endpoint = nil, endpoint = nil, &block)
|
|
66
82
|
if block_given?
|
|
67
|
-
|
|
83
|
+
if error_class_or_endpoint
|
|
84
|
+
@router.add_error(block, error_class_or_endpoint)
|
|
85
|
+
else
|
|
86
|
+
@router.add_error(block)
|
|
87
|
+
end
|
|
88
|
+
elsif endpoint
|
|
89
|
+
@router.add_error(endpoint, error_class_or_endpoint)
|
|
68
90
|
else
|
|
69
|
-
@router.add_error(
|
|
91
|
+
@router.add_error(error_class_or_endpoint)
|
|
70
92
|
end
|
|
71
93
|
end
|
|
72
94
|
|
|
73
|
-
def resources(name, id: :id, before: [], after: [], &block)
|
|
74
|
-
@
|
|
95
|
+
def resources(name, id: :id, path: nil, paths: {}, callbacks: [], before: [], after: [], &block)
|
|
96
|
+
@nested_resources ||= []
|
|
97
|
+
@nested_resources.push(name)
|
|
75
98
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
99
|
+
infer_action_const = lambda do |action|
|
|
100
|
+
scope_parts = @router.not_empty_scopes
|
|
101
|
+
.map(&:to_s)
|
|
102
|
+
.reject { |s| s.start_with?(':') }
|
|
103
|
+
.map(&:capitalize)
|
|
104
|
+
|
|
105
|
+
parts = ['Actions'] + scope_parts + [name.to_s.capitalize, action]
|
|
106
|
+
const_path = parts.join('::')
|
|
107
|
+
|
|
108
|
+
Object.const_get(const_path) if Object.const_defined?(const_path)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
infer_assign_const = lambda do
|
|
112
|
+
parts = @nested_resources.map { |s| s.to_s.capitalize }
|
|
113
|
+
const_path = "Callbacks::#{parts.join('::')}::Assign"
|
|
114
|
+
Object.const_get(const_path) if Object.const_defined?(const_path)
|
|
80
115
|
end
|
|
81
116
|
|
|
82
117
|
actions = {
|
|
83
|
-
index: { method: :get, path: nil, action:
|
|
84
|
-
new: { method: :get, path: 'new', action:
|
|
85
|
-
create: { method: :post, path: nil, action:
|
|
118
|
+
index: { method: :get, path: nil, action: infer_action_const.call('Index') },
|
|
119
|
+
new: { method: :get, path: 'new', action: infer_action_const.call('New') },
|
|
120
|
+
create: { method: :post, path: nil, action: infer_action_const.call('Create') }
|
|
86
121
|
}
|
|
87
122
|
|
|
88
123
|
actions_for_id = {
|
|
89
|
-
show: { method: :get, path: nil, action:
|
|
90
|
-
edit: { method: :get, path:
|
|
91
|
-
update: { method: :put, path: nil, action:
|
|
92
|
-
delete: { method: :delete, path: nil, action:
|
|
124
|
+
show: { method: :get, path: nil, action: infer_action_const.call('Show') },
|
|
125
|
+
edit: { method: :get, path: 'edit', action: infer_action_const.call('Edit') },
|
|
126
|
+
update: { method: :put, path: nil, action: infer_action_const.call('Update') },
|
|
127
|
+
delete: { method: :delete, path: nil, action: infer_action_const.call('Delete') }
|
|
93
128
|
}
|
|
94
129
|
|
|
130
|
+
received_callbacks = Hash.new { |h, k| h[k] = { before: [], after: [] } }
|
|
131
|
+
(callbacks || []).each do |callback_config|
|
|
132
|
+
ensure_array(callback_config[:actions]).each do |action|
|
|
133
|
+
received_callbacks[action][:before].concat(ensure_array(callback_config[:before]))
|
|
134
|
+
received_callbacks[action][:after].concat(ensure_array(callback_config[:after]))
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
(paths || {}).each do |action, new_path|
|
|
139
|
+
if actions[action]
|
|
140
|
+
actions[action][:path] = new_path
|
|
141
|
+
elsif actions_for_id[action]
|
|
142
|
+
actions_for_id[action][:path] = new_path
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
95
146
|
block_for_id = proc do
|
|
96
|
-
actions_for_id.each do |
|
|
97
|
-
|
|
147
|
+
actions_for_id.each do |action_name, definition|
|
|
148
|
+
next unless definition[:action]
|
|
149
|
+
|
|
150
|
+
action_callbacks = received_callbacks[action_name]
|
|
151
|
+
send(
|
|
152
|
+
definition[:method],
|
|
153
|
+
definition[:path],
|
|
154
|
+
definition[:action],
|
|
155
|
+
before: action_callbacks[:before],
|
|
156
|
+
after: action_callbacks[:after]
|
|
157
|
+
)
|
|
98
158
|
end
|
|
99
159
|
|
|
100
160
|
instance_eval(&block) if block_given?
|
|
101
161
|
end
|
|
102
162
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
163
|
+
scope_name = path || name.to_s
|
|
164
|
+
assign_callback = infer_assign_const.call
|
|
165
|
+
|
|
166
|
+
scope(scope_name, before:, after:) do
|
|
167
|
+
actions.each do |action_name, definition|
|
|
168
|
+
next unless definition[:action]
|
|
169
|
+
|
|
170
|
+
action_callbacks = received_callbacks[action_name]
|
|
171
|
+
send(
|
|
172
|
+
definition[:method],
|
|
173
|
+
definition[:path],
|
|
174
|
+
definition[:action],
|
|
175
|
+
before: action_callbacks[:before],
|
|
176
|
+
after: action_callbacks[:after]
|
|
177
|
+
)
|
|
106
178
|
end
|
|
107
179
|
|
|
108
|
-
assign_callback = get_const.call('Callbacks', 'Assign')
|
|
109
180
|
if assign_callback
|
|
110
181
|
scope(id.to_sym, before: assign_callback, &block_for_id)
|
|
111
182
|
else
|
|
@@ -113,7 +184,7 @@ class Rackr
|
|
|
113
184
|
end
|
|
114
185
|
end
|
|
115
186
|
|
|
116
|
-
@
|
|
187
|
+
@nested_resources.pop
|
|
117
188
|
end
|
|
118
189
|
|
|
119
190
|
HTTP_METHODS.each do |http_method|
|