rackr 0.0.65 → 0.0.68

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,38 @@
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
10
+ VERSION = '0.0.68'
11
+
9
12
  class NotFound < StandardError; end
10
13
 
14
+ # Dump is a special error that is used to dump the content of a request.
15
+ class Dump < StandardError
16
+ attr_reader :content
17
+
18
+ def initialize(content)
19
+ @content = content
20
+
21
+ super
22
+ end
23
+ end
24
+
11
25
  HTTP_METHODS = %w[GET POST DELETE PUT TRACE OPTIONS PATCH].freeze
12
26
 
13
- include Action
27
+ include Callback
28
+ include Utils
14
29
 
15
30
  def initialize(config = {}, before: [], after: [])
16
31
  @router = Router.new(config, before: before, after: after)
17
32
  end
18
33
 
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"
34
+ def call(&)
35
+ instance_eval(&)
26
36
 
27
37
  @router
28
38
  end
@@ -40,7 +50,15 @@ class Rackr
40
50
  end
41
51
 
42
52
  def db
43
- @router.config.dig(:deps, :db)
53
+ @router.config&.dig(:deps, :db)
54
+ end
55
+
56
+ def log
57
+ @router.config&.dig(:deps, :log)
58
+ end
59
+
60
+ def cache
61
+ @router.config&.dig(:deps, :cache)
44
62
  end
45
63
 
46
64
  def scope(name = '', before: [], after: [], &block)
@@ -62,50 +80,105 @@ class Rackr
62
80
  end
63
81
  end
64
82
 
65
- def error(endpoint = -> {}, &block)
83
+ def error(error_class_or_endpoint = nil, endpoint = nil, &block)
66
84
  if block_given?
67
- @router.add_error(block)
85
+ if error_class_or_endpoint
86
+ @router.add_error(block, error_class_or_endpoint)
87
+ else
88
+ @router.add_error(block)
89
+ end
90
+ elsif endpoint
91
+ @router.add_error(endpoint, error_class_or_endpoint)
68
92
  else
69
- @router.add_error(endpoint)
93
+ @router.add_error(error_class_or_endpoint)
70
94
  end
71
95
  end
72
96
 
73
- def resources(name, id: :id, before: [], after: [], &block)
74
- @resource_namespace = (@resource_namespace || []).push([name.to_s.capitalize])
97
+ def resources(name, id: :id, path: nil, paths: {}, callbacks: [], before: [], after: [], &block)
98
+ @nested_resources ||= []
99
+ @nested_resources.push(name)
75
100
 
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
101
+ infer_action_const = lambda do |action|
102
+ scope_parts = @router.not_empty_scopes
103
+ .map(&:to_s)
104
+ .reject { |s| s.start_with?(':') }
105
+ .map(&:capitalize)
106
+
107
+ parts = ['Actions'] + scope_parts + [name.to_s.capitalize, action]
108
+ const_path = parts.join('::')
109
+
110
+ Object.const_get(const_path) if Object.const_defined?(const_path)
111
+ end
112
+
113
+ infer_assign_const = lambda do
114
+ parts = @nested_resources.map { |s| s.to_s.capitalize }
115
+ const_path = "Callbacks::#{parts.join('::')}::Assign"
116
+ Object.const_get(const_path) if Object.const_defined?(const_path)
80
117
  end
81
118
 
82
119
  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') },
120
+ index: { method: :get, path: nil, action: infer_action_const.call('Index') },
121
+ new: { method: :get, path: 'new', action: infer_action_const.call('New') },
122
+ create: { method: :post, path: nil, action: infer_action_const.call('Create') }
86
123
  }
87
124
 
88
125
  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') }
126
+ show: { method: :get, path: nil, action: infer_action_const.call('Show') },
127
+ edit: { method: :get, path: 'edit', action: infer_action_const.call('Edit') },
128
+ update: { method: :put, path: nil, action: infer_action_const.call('Update') },
129
+ delete: { method: :delete, path: nil, action: infer_action_const.call('Delete') }
93
130
  }
94
131
 
132
+ received_callbacks = Hash.new { |h, k| h[k] = { before: [], after: [] } }
133
+ (callbacks || []).each do |callback_config|
134
+ ensure_array(callback_config[:actions]).each do |action|
135
+ received_callbacks[action][:before].concat(ensure_array(callback_config[:before]))
136
+ received_callbacks[action][:after].concat(ensure_array(callback_config[:after]))
137
+ end
138
+ end
139
+
140
+ (paths || {}).each do |action, new_path|
141
+ if actions[action]
142
+ actions[action][:path] = new_path
143
+ elsif actions_for_id[action]
144
+ actions_for_id[action][:path] = new_path
145
+ end
146
+ end
147
+
95
148
  block_for_id = proc do
96
- actions_for_id.each do |_, definition|
97
- send(definition[:method], definition[:path], definition[:action]) if definition[:action]
149
+ actions_for_id.each do |action_name, definition|
150
+ next unless definition[:action]
151
+
152
+ action_callbacks = received_callbacks[action_name]
153
+ send(
154
+ definition[:method],
155
+ definition[:path],
156
+ definition[:action],
157
+ before: action_callbacks[:before],
158
+ after: action_callbacks[:after]
159
+ )
98
160
  end
99
161
 
100
162
  instance_eval(&block) if block_given?
101
163
  end
102
164
 
103
- scope(name.to_s, before:, after:) do
104
- actions.each do |_, definition|
105
- send(definition[:method], definition[:path], definition[:action]) if definition[:action]
165
+ scope_name = path || name.to_s
166
+ assign_callback = infer_assign_const.call
167
+
168
+ scope(scope_name, before:, after:) do
169
+ actions.each do |action_name, definition|
170
+ next unless definition[:action]
171
+
172
+ action_callbacks = received_callbacks[action_name]
173
+ send(
174
+ definition[:method],
175
+ definition[:path],
176
+ definition[:action],
177
+ before: action_callbacks[:before],
178
+ after: action_callbacks[:after]
179
+ )
106
180
  end
107
181
 
108
- assign_callback = get_const.call('Callbacks', 'Assign')
109
182
  if assign_callback
110
183
  scope(id.to_sym, before: assign_callback, &block_for_id)
111
184
  else
@@ -113,7 +186,7 @@ class Rackr
113
186
  end
114
187
  end
115
188
 
116
- @resource_namespace = @resource_namespace.first(@resource_namespace.size - 1)
189
+ @nested_resources.pop
117
190
  end
118
191
 
119
192
  HTTP_METHODS.each do |http_method|