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.
@@ -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
@@ -2,6 +2,7 @@
2
2
 
3
3
  class Rackr
4
4
  class Router
5
+ # A Route for Rackr is an object that has endpoint and callbacks (before and after)
5
6
  class Route
6
7
  attr_reader :endpoint,
7
8
  :befores,
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, :not_found_instances, :error_instances
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
- @default_error = Route.new(proc { |_req, e| raise e })
40
- @default_not_found = Route.new(proc { [404, {}, ['Not found']] })
41
- @splitted_request_path_info = []
42
- @current_request_path_info = nil
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
- @splitted_request_path_info = path_info.split('/')
49
- @current_request_path_info =
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
- request_builder = BuildRequest.new(env, @splitted_request_path_info)
53
- env['REQUEST_METHOD'] = 'GET' if env['REQUEST_METHOD'] == 'HEAD'
54
-
55
- route_instance, found_scopes = match_path_route(env['REQUEST_METHOD'])
56
- rack_request = request_builder.call(route_instance)
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
- return before_result unless before_result.is_a?(Rack::Request)
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
- rack_request = before_result
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
- if !@dev_mode || ENV['RACKR_ERROR_DEV']
78
- return error_fallback(found_scopes, route_instance, before_result || rack_request, e)
79
- end
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
- return Endpoint.call(Errors::DevHtml, env.merge({ 'error' => e }))
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.sub(%r{\A/}, '')
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
- return push_to_scope(method.to_s.upcase, route_instance) if @scopes.size >= 1
176
+ path_segments = path_with_scopes.split('/').reject(&:empty?)
111
177
 
112
- @path_routes_instances[method.to_s.upcase][:__instances].push(route_instance)
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
- %i[error not_found].each do |v|
116
- define_method("add_#{v}") do |endpoint|
117
- Errors.check_endpoint(endpoint, v)
185
+ def add_not_found(endpoint)
186
+ Errors.check_endpoint(endpoint, 'not found')
118
187
 
119
- route_instance =
120
- Route.new(
121
- endpoint,
122
- befores: @befores,
123
- afters: @afters
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
- return set_to_scope(send("#{v}_instances"), route_instance) if @scopes.size >= 1
196
+ return set_to_scope(not_found_tree, route_instance) if @scopes.size >= 1
127
197
 
128
- instance_variable_set("@default_#{v}", route_instance)
129
- end
198
+ @default_not_found = route_instance
199
+ end
130
200
 
131
- define_method("#{v}_fallback") do |found_scopes, route_instance, request, error = nil|
132
- args = [
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
- endpoint_result = Endpoint.call(*args)
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
- call_afters(route_instance, endpoint_result)
212
+ if error_class
213
+ return set_to_scope(specific_error_tree[error_class] ||= {}, route_instance) if @scopes.size >= 1
145
214
 
146
- endpoint_result
147
- end
148
- end
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
- def call_afters(route_instance, endpoint_result)
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('/', '').gsub(':', '').gsub('/', '_')
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, (not_empty_scopes + %i[__instance]), route_instance)
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 not_empty_scopes
213
- @scopes.reject { |v| (v == '') }
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
- path_routes_instances = @path_routes_instances[request_method]
227
-
228
- while last_tail && !last_tail.empty?
229
- segment = last_tail.shift
230
- found_route = nil
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
- path_routes_instances.each_key do |scope|
233
- next if scope == :__instances
311
+ route_instance = path_routes_tree[:__instances]&.detect do |route|
312
+ route.match?(current_request_path_info)
313
+ end
234
314
 
235
- if segment == scope
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
- found_scopes << scope
244
- path_routes_instances = @path_routes_instances[request_method].dig(*found_scopes)
245
- break
246
- end
247
- end
248
- end
317
+ [route_instance, found_scopes]
318
+ end
249
319
 
250
- result_route = find_instance_in_scope.call(request_method, found_scopes)
320
+ def find_not_found_route(found_scopes)
321
+ not_found_route = nil
251
322
 
252
- if result_route.nil? && !found_scopes.empty?
253
- result_route = find_instance_in_scope.call(request_method, found_scopes[..-2])
254
- end
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
- if result_route.nil?
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
- [result_route, found_scopes]
330
+ not_found_route || @default_not_found
265
331
  end
266
332
 
267
333
  def match_route(found_scopes, instances, default_instance)
@@ -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 Action
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(&block)
20
- instance_eval(&block)
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.dig(:deps, :db)
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 = -> {}, &block)
81
+ def error(error_class_or_endpoint = nil, endpoint = nil, &block)
66
82
  if block_given?
67
- @router.add_error(block)
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(endpoint)
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
- @resource_namespace = (@resource_namespace || []).push([name.to_s.capitalize])
95
+ def resources(name, id: :id, path: nil, paths: {}, callbacks: [], before: [], after: [], &block)
96
+ @nested_resources ||= []
97
+ @nested_resources.push(name)
75
98
 
76
- get_const = ->(type, action) do
77
- if Object.const_defined?("#{type}::#{@resource_namespace.join('::')}::#{action}")
78
- Object.const_get("#{type}::#{@resource_namespace.join('::')}::#{action}")
79
- end
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: get_const.call('Actions', 'Index') },
84
- new: { method: :get, path: 'new', action: get_const.call('Actions', 'New') },
85
- create: { method: :post, path: nil, action: get_const.call('Actions', 'Create') },
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: get_const.call('Actions', 'Show') },
90
- edit: { method: :get, path: "edit", action: get_const.call('Actions', 'Edit') },
91
- update: { method: :put, path: nil, action: get_const.call('Actions', 'Update') },
92
- delete: { method: :delete, path: nil, action: get_const.call('Actions', 'Delete') }
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 |_, definition|
97
- send(definition[:method], definition[:path], definition[:action]) if definition[:action]
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
- scope(name.to_s, before:, after:) do
104
- actions.each do |_, definition|
105
- send(definition[:method], definition[:path], definition[:action]) if definition[:action]
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
- @resource_namespace = @resource_namespace.first(@resource_namespace.size - 1)
187
+ @nested_resources.pop
117
188
  end
118
189
 
119
190
  HTTP_METHODS.each do |http_method|