hanami-router 2.0.0.alpha1 → 2.0.0.alpha2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,427 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "hanami/utils/string"
4
- require "hanami/utils/path_prefix"
5
- require "hanami/utils/class_attribute"
6
- require "hanami/routing/resource/nested"
7
-
8
- module Hanami
9
- module Routing
10
- class Resource
11
- # Action for RESTful resource
12
- #
13
- # @since 0.1.0
14
- #
15
- # @api private
16
- #
17
- # @see Hanami::Router#resource
18
- class Action
19
- include Utils::ClassAttribute
20
-
21
- # Nested routes separator
22
- #
23
- # @api private
24
- # @since 0.4.0
25
- NESTED_ROUTES_SEPARATOR = "/"
26
-
27
- # Ruby namespace where lookup for default subclasses.
28
- #
29
- # @api private
30
- # @since 0.1.0
31
- class_attribute :namespace
32
- self.namespace = Resource
33
-
34
- # Accepted HTTP verb
35
- #
36
- # @api private
37
- # @since 0.1.0
38
- class_attribute :verb
39
- self.verb = :get
40
-
41
- # Separator for named routes
42
- #
43
- # @api private
44
- # @since 0.2.0
45
- class_attribute :named_route_separator
46
- self.named_route_separator = "_"
47
-
48
- # Generate an action for the given router
49
- #
50
- # @param router [Hanami::Router]
51
- # @param action [Hanami::Routing::Resource::Action]
52
- # @param options [Hash]
53
- # @param resource [Hanami::Routing::Resource, Hanami::Routing::Resources]
54
- #
55
- # @api private
56
- #
57
- # @since 0.1.0
58
- def self.generate(router, action, options = {}, resource = nil)
59
- class_for(action).new(router, options, resource)
60
- end
61
-
62
- # Initialize an action
63
- #
64
- # @param router [Hanami::Router]
65
- # @param options [Hash]
66
- # @param resource [Hanami::Routing::Resource, Hanami::Routing::Resources]
67
- # @param blk [Proc]
68
- #
69
- # @api private
70
- #
71
- # @since 0.1.0
72
- def initialize(router, options = {}, resource = nil, &blk)
73
- @router = router
74
- @options = options
75
- @resource = resource
76
- generate(&blk)
77
- end
78
-
79
- # Generate an action for the given router
80
- #
81
- # @param blk [Proc]
82
- #
83
- # @api private
84
- #
85
- # @since 0.1.0
86
- def generate(&blk)
87
- @router.send verb, path, to: endpoint, as: as, namespace: namespace, configuration: configuration
88
- instance_eval(&blk) if block_given?
89
- end
90
-
91
- # Resource name
92
- #
93
- # @return [String]
94
- #
95
- # @api private
96
- # @since 0.1.0
97
- #
98
- # @example
99
- # require 'hanami/router'
100
- #
101
- # Hanami::Router.new do
102
- # resource 'identity'
103
- # end
104
- #
105
- # # 'identity' is the name passed in the @options
106
- def resource_name
107
- @resource_name ||= @options[:name].to_s
108
- end
109
-
110
- # Prefix
111
- #
112
- # @api private
113
- # @since 2.0.0
114
- def prefix
115
- @prefix ||= Utils::PathPrefix.new(@options[:prefix])
116
- end
117
-
118
- # Namespace
119
- #
120
- # @api private
121
- # @since 2.0.0
122
- def namespace
123
- @namespace ||= @options[:namespace]
124
- end
125
-
126
- # Configuration
127
- #
128
- # @api private
129
- # @since 2.0.0
130
- def configuration
131
- @configuration ||= @options[:configuration]
132
- end
133
-
134
- # Load a subclass, according to the given action name
135
- #
136
- # @param action [String] the action name
137
- #
138
- # @example
139
- # Hanami::Routing::Resource::Action.send(:class_for, 'New') # =>
140
- # Hanami::Routing::Resource::New
141
- #
142
- # @api private
143
- # @since 0.1.0
144
- def self.class_for(action)
145
- Utils::Class.load!(Utils::String.classify(action), namespace)
146
- end
147
-
148
- private_class_method :class_for
149
-
150
- # Accepted HTTP verb
151
- #
152
- # @see Hanami::Routing::Resource::Action.verb
153
- #
154
- # @api private
155
- # @since 0.1.0
156
- def verb
157
- self.class.verb
158
- end
159
-
160
- # The prefixed relative URL
161
- #
162
- # @example
163
- # require "hanami/router"
164
- #
165
- # Hanami::Router.new do
166
- # resources "flowers"
167
- #
168
- # prefix "animals" do
169
- # resources "mammals"
170
- # end
171
- # end
172
- #
173
- # # It will generate paths like "/flowers", "/flowers/:id" ..
174
- # # It will generate paths like "/animals/mammals", "/animals/mammals/:id" ..
175
- #
176
- # @api private
177
- # @since 0.1.0
178
- def path
179
- rest_path
180
- end
181
-
182
- # The URL relative path
183
- #
184
- # @example
185
- # '/flowers'
186
- # '/flowers/new'
187
- # '/flowers/:id'
188
- #
189
- # @api private
190
- # @since 0.1.0
191
- def rest_path
192
- prefix.join(_nested_rest_path || resource_name.to_s)
193
- end
194
-
195
- # The prefixed name of the action within the whole context of the router.
196
- #
197
- # @example
198
- # require "hanami/router"
199
- #
200
- # Hanami::Router.new do
201
- # resources "flowers"
202
- #
203
- # prefix "animals" do
204
- # resources "mammals"
205
- # end
206
- # end
207
- #
208
- # # It will generate named routes like :flowers, :new_flowers ..
209
- # # It will generate named routes like :animals_mammals, :animals_new_mammals ..
210
- #
211
- # @api private
212
- # @since 0.1.0
213
- def as
214
- prefix.relative_join(_singularized_as, self.class.named_route_separator).to_sym
215
- end
216
-
217
- # The name of the RESTful action.
218
- #
219
- # @example
220
- # 'index'
221
- # 'new'
222
- # 'create'
223
- #
224
- # @api private
225
- # @since 0.1.0
226
- def action_name
227
- Utils::String.transform(self.class.name, :demodulize, :downcase)
228
- end
229
-
230
- # A string that represents the endpoint to be loaded.
231
- # It is composed by controller and action name.
232
- #
233
- # @see Hanami::Routing::Resource::Action#separator
234
- #
235
- # @example
236
- # 'flowers#index'
237
- #
238
- # @api private
239
- # @since 0.1.0
240
- def endpoint
241
- [controller_name, action_name].join separator
242
- end
243
-
244
- # Separator between controller and action name
245
- #
246
- # @see Hanami::Routing::EndpointResolver#separator
247
- #
248
- # @example
249
- # '#' # default
250
- #
251
- # @api private
252
- # @since 0.1.0
253
- def separator
254
- @options[:separator]
255
- end
256
-
257
- # Resource controller name
258
- #
259
- # @example
260
- # Hanami::Router.new do
261
- # resources 'flowers', controller: 'rocks'
262
- # end
263
- #
264
- # # It will mount path 'flowers/new' to Rocks::New instead of Flowers::New
265
- # # Same for other action names
266
- #
267
- # @api private
268
- # @since 0.4.0
269
- def controller_name
270
- @options[:controller] || resource_name
271
- end
272
-
273
- private
274
-
275
- # Singularize as (helper route)
276
- #
277
- # @api private
278
- # @since 0.4.0
279
- def _singularized_as
280
- name = @options[:as] ? @options[:as].to_s : resource_name
281
- name.split(NESTED_ROUTES_SEPARATOR).map do |n|
282
- @router.inflector.singularize(n)
283
- end
284
- end
285
-
286
- # Create nested rest path
287
- #
288
- # @api private
289
- # @since 0.4.0
290
- def _nested_rest_path
291
- Nested.new(resource_name, @resource).to_path
292
- end
293
- end
294
-
295
- # Collection action
296
- # It implements #collection within a #resource block.
297
- #
298
- # @api private
299
- # @since 0.1.0
300
- # @see Hanami::Router#resource
301
- class CollectionAction < Action
302
- # @since 0.1.0
303
- # @api private
304
- def generate(&blk)
305
- instance_eval(&blk) if block_given?
306
- end
307
-
308
- # @since 0.1.0
309
- # @api private
310
- def method_missing(method_name, *args) # rubocop:disable Style/MethodMissingSuper
311
- verb = method_name
312
- action_name = Utils::PathPrefix.new(args.first).relative_join(nil)
313
-
314
- @router.__send__ verb, path(action_name),
315
- to: endpoint(action_name), as: as(action_name),
316
- namespace: namespace, configuration: configuration
317
- end
318
-
319
- def respond_to_missing?(method_name, include_all)
320
- @router.respond_to?(method_name, include_all)
321
- end
322
-
323
- private
324
-
325
- # @since 0.1.0
326
- # @api private
327
- def path(action_name)
328
- rest_path.join(action_name)
329
- end
330
-
331
- # @since 0.1.0
332
- # @api private
333
- def endpoint(action_name)
334
- [controller_name, action_name].join separator
335
- end
336
-
337
- # @since 0.1.0
338
- # @api private
339
- def as(action_name)
340
- [action_name, super()].join(self.class.named_route_separator).to_sym
341
- end
342
- end
343
-
344
- # Collection action
345
- # It implements #member within a #resource block.
346
- #
347
- # @api private
348
- # @since 0.1.0
349
- # @see Hanami::Router#resource
350
- class MemberAction < CollectionAction
351
- end
352
-
353
- # Implementation of common methods for concrete member actions
354
- #
355
- # @api private
356
- # @since 0.1.0
357
- module DefaultMemberAction
358
- private
359
-
360
- # @since 0.1.0
361
- # @api private
362
- def path
363
- rest_path.join(action_name)
364
- end
365
-
366
- # @since 0.1.0
367
- # @api private
368
- def as
369
- [action_name, super].join(self.class.named_route_separator).to_sym
370
- end
371
- end
372
-
373
- # New action
374
- #
375
- # @api private
376
- # @since 0.1.0
377
- # @see Hanami::Router#resource
378
- class New < Action
379
- include DefaultMemberAction
380
- end
381
-
382
- # Create action
383
- #
384
- # @api private
385
- # @since 0.1.0
386
- # @see Hanami::Router#resource
387
- class Create < Action
388
- self.verb = :post
389
- end
390
-
391
- # Show action
392
- #
393
- # @api private
394
- # @since 0.1.0
395
- # @see Hanami::Router#resource
396
- class Show < Action
397
- end
398
-
399
- # Edit action
400
- #
401
- # @api private
402
- # @since 0.1.0
403
- # @see Hanami::Router#resource
404
- class Edit < Action
405
- include DefaultMemberAction
406
- end
407
-
408
- # Update action
409
- #
410
- # @api private
411
- # @since 0.1.0
412
- # @see Hanami::Router#resource
413
- class Update < Action
414
- self.verb = :patch
415
- end
416
-
417
- # Destroy action
418
- #
419
- # @api private
420
- # @since 0.1.0
421
- # @see Hanami::Router#resource
422
- class Destroy < Action
423
- self.verb = :delete
424
- end
425
- end
426
- end
427
- end
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Hanami
4
- module Routing
5
- class Resource
6
- # Helper class to calculate nested path
7
- #
8
- # @api private
9
- # @since 0.4.0
10
- class Nested
11
- # @api private
12
- # @since 0.4.0
13
- SEPARATOR = "/"
14
-
15
- # @api private
16
- # @since 0.4.0
17
- def initialize(resource_name, resource)
18
- @resource_name = resource_name.to_s.split(SEPARATOR)
19
- @resource = resource
20
- @path = []
21
- _calculate(@resource_name.dup, @resource)
22
- end
23
-
24
- # @api private
25
- # @since 0.4.0
26
- def to_path
27
- @path.reverse!.pop
28
- @resource_name.zip(@path).flatten.join
29
- end
30
-
31
- private
32
-
33
- # @api private
34
- # @since 0.4.0
35
- def _calculate(param_wildcard, resource = nil)
36
- return if resource.nil?
37
-
38
- @path << resource.wildcard_param(param_wildcard.pop)
39
- _calculate(param_wildcard, resource.parent)
40
- end
41
- end
42
- end
43
- end
44
- end