signpost 0.1.0
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 +7 -0
- data/lib/signpost.rb +29 -0
- data/lib/signpost/builder.rb +416 -0
- data/lib/signpost/builder/namespace.rb +13 -0
- data/lib/signpost/builder/nested.rb +34 -0
- data/lib/signpost/endpoint/action.rb +54 -0
- data/lib/signpost/endpoint/builder.rb +22 -0
- data/lib/signpost/endpoint/dynamic.rb +24 -0
- data/lib/signpost/endpoint/redirect.rb +65 -0
- data/lib/signpost/endpoint/resolver.rb +144 -0
- data/lib/signpost/middleware.rb +17 -0
- data/lib/signpost/route/nested.rb +20 -0
- data/lib/signpost/route/simple.rb +89 -0
- data/lib/signpost/router.rb +59 -0
- data/lib/signpost/sign/flat.rb +79 -0
- data/lib/signpost/sign/flat/path.rb +114 -0
- data/lib/signpost/sign/flat/redirect.rb +60 -0
- data/lib/signpost/sign/namespace.rb +11 -0
- data/lib/signpost/sign/nested.rb +39 -0
- data/lib/signpost/version.rb +13 -0
- metadata +92 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a59506bcf00a7ee073cb30b7a122231f85e513ca
|
4
|
+
data.tar.gz: 623c980473e5f4a33faa69f7b646faa58a068c8e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5f4e3d1381b106061d28ec8e67d94d6d3625a758051cdcb9c1e11a7cd3fed1c20b5276f26c14c5a93116e29b389f15b522381bc0e5ee5e90b190ef968c8eebc0
|
7
|
+
data.tar.gz: 5d2cd384d8462b7ab734f14e3789cc6a92c9fbc78cd624e3bbf1ad6ee967359476bda4ba6e378f276d2ba90b18789db3a1f7c1cc130db228c744fc88af213f73
|
data/lib/signpost.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'mustermann'
|
3
|
+
require 'inflecto'
|
4
|
+
|
5
|
+
class Signpost
|
6
|
+
SUPPORTED_METHODS = %w(GET POST PUT PATCH OPTIONS DELETE).map(&:freeze).to_set.freeze
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'signpost/version'
|
10
|
+
require 'signpost/route/simple'
|
11
|
+
require 'signpost/route/nested'
|
12
|
+
require 'signpost/router'
|
13
|
+
|
14
|
+
require 'signpost/middleware'
|
15
|
+
|
16
|
+
require 'signpost/endpoint/resolver'
|
17
|
+
require 'signpost/endpoint/builder'
|
18
|
+
require 'signpost/endpoint/dynamic'
|
19
|
+
require 'signpost/endpoint/redirect'
|
20
|
+
require 'signpost/endpoint/action'
|
21
|
+
|
22
|
+
require 'signpost/sign/flat'
|
23
|
+
require 'signpost/sign/flat/path'
|
24
|
+
require 'signpost/sign/flat/redirect'
|
25
|
+
require 'signpost/sign/nested'
|
26
|
+
require 'signpost/sign/namespace'
|
27
|
+
require 'signpost/builder/nested'
|
28
|
+
require 'signpost/builder/namespace'
|
29
|
+
require 'signpost/builder'
|
@@ -0,0 +1,416 @@
|
|
1
|
+
class Signpost
|
2
|
+
class Builder
|
3
|
+
DEFAULT_OPTIONS = {
|
4
|
+
params_key: 'router.params',
|
5
|
+
default_redirect_status: 303,
|
6
|
+
default_redirect_additional_values: :ignore,
|
7
|
+
style: :sinatra,
|
8
|
+
middlewares: [],
|
9
|
+
rack_params: false
|
10
|
+
}.freeze
|
11
|
+
SUBPATH_REG = /^\//.freeze
|
12
|
+
|
13
|
+
##
|
14
|
+
# Define root route
|
15
|
+
#
|
16
|
+
# Root route will always be in top of routes
|
17
|
+
# Also this is named route with name `root`
|
18
|
+
#
|
19
|
+
# Example:
|
20
|
+
#
|
21
|
+
# root.to(HomeController)
|
22
|
+
#
|
23
|
+
def root(&block)
|
24
|
+
builder = Sign::Flat::Path::GET.new(absolute('/'), @options, &block)
|
25
|
+
@builders.unshift(builder)
|
26
|
+
builder.as(root_name)
|
27
|
+
end
|
28
|
+
|
29
|
+
##
|
30
|
+
# Define route which accepts GET request for the given pattern
|
31
|
+
#
|
32
|
+
# Params:
|
33
|
+
# - path {String|RegExp} pattern of the request path which should be matched
|
34
|
+
#
|
35
|
+
# Yields: the Rack compatible block
|
36
|
+
#
|
37
|
+
# Example:
|
38
|
+
#
|
39
|
+
# # Most usual case: with controller and action. Will resolve Controller#action or Controller::Action
|
40
|
+
# get('/users').to(controller: 'Users', action: 'index')
|
41
|
+
# get('/users').to(controller: UsersController, action: 'index')
|
42
|
+
# get('/users').to('users#index')
|
43
|
+
#
|
44
|
+
# # Single action controller (responds to .call)
|
45
|
+
# get('/').to(HomeController)
|
46
|
+
#
|
47
|
+
# # Sinatra-style block action
|
48
|
+
# get('/echo').to do |env|
|
49
|
+
# [200, {}, [env['SERVER_NAME']]]
|
50
|
+
# end
|
51
|
+
# get('/echo') do |env|
|
52
|
+
# [200, {}, [env['SERVER_NAME']]]
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# # Path params
|
56
|
+
# get('/users/:id').to('users#show')
|
57
|
+
#
|
58
|
+
# # Params constraints
|
59
|
+
# get('/users/:id').to('users#show').capture(:digit)
|
60
|
+
# get('/users/:id.:ext').to('pages#show').capture({ id: /\d+/, ext: ['json', 'html'] })
|
61
|
+
#
|
62
|
+
# # Exclude pattern
|
63
|
+
# get('/pages/*slug/edit').to('pages#edit').except('/pages/system/*/edit')
|
64
|
+
#
|
65
|
+
# # Default params
|
66
|
+
# get('/:controller/:action/:id').params(id: 1)
|
67
|
+
#
|
68
|
+
def get(path, &block)
|
69
|
+
builder = Sign::Flat::Path::GET.new(absolute(path), @options, &block)
|
70
|
+
@builders << builder
|
71
|
+
builder
|
72
|
+
end
|
73
|
+
|
74
|
+
##
|
75
|
+
# Define route which accepts POST request for the given pattern
|
76
|
+
#
|
77
|
+
# Params:
|
78
|
+
# - path {String|RegExp} pattern of the request path which should be matched
|
79
|
+
#
|
80
|
+
# Yields: the Rack compatible block
|
81
|
+
#
|
82
|
+
# Example:
|
83
|
+
#
|
84
|
+
# # Most usual case: with controller and action. Will resolve Controller#action or Controller::Action
|
85
|
+
# post('/users').to(controller: 'Users', action: 'create')
|
86
|
+
# post('/users').to(controller: UsersController, action: 'create')
|
87
|
+
# post('/users').to('users#create')
|
88
|
+
#
|
89
|
+
# # Single action controller (responds to .call)
|
90
|
+
# post('/search').to(SearchController)
|
91
|
+
#
|
92
|
+
# # Sinatra-style block action
|
93
|
+
# post('/echo').to do |env|
|
94
|
+
# [201, {}, [env['SERVER_NAME']]]
|
95
|
+
# end
|
96
|
+
# post('/echo') do |env|
|
97
|
+
# [201, {}, [env['SERVER_NAME']]]
|
98
|
+
# end
|
99
|
+
#
|
100
|
+
# # Path params
|
101
|
+
# post('/users/:id').to('users#update')
|
102
|
+
#
|
103
|
+
# # Params constraints
|
104
|
+
# post('/users/:id').to('users#update').capture(:digit)
|
105
|
+
# post('/users/:id.:ext').to('pages#show').capture({ id: /\d+/, ext: ['json', 'html'] })
|
106
|
+
#
|
107
|
+
# # Exclude pattern
|
108
|
+
# post('/pages/*slug/update').to('pages#update').except('/pages/system/*/update')
|
109
|
+
#
|
110
|
+
# # Default params
|
111
|
+
# post('/:controller/:action/:id').params(id: 1)
|
112
|
+
#
|
113
|
+
def post(path, &block)
|
114
|
+
builder = Sign::Flat::Path::POST.new(absolute(path), @options, &block)
|
115
|
+
@builders << builder
|
116
|
+
builder
|
117
|
+
end
|
118
|
+
|
119
|
+
##
|
120
|
+
# Define route which accepts PUT request for the given pattern
|
121
|
+
#
|
122
|
+
# Params:
|
123
|
+
# - path {String|RegExp} pattern of the request path which should be matched
|
124
|
+
#
|
125
|
+
# Yields: the Rack compatible block
|
126
|
+
#
|
127
|
+
# Example:
|
128
|
+
#
|
129
|
+
# # Most usual case: with controller and action. Will resolve Controller#action or Controller::Action
|
130
|
+
# put('/users').to(controller: 'Users', action: 'create')
|
131
|
+
# put('/users').to(controller: UsersController, action: 'create')
|
132
|
+
# put('/users').to('users#create')
|
133
|
+
#
|
134
|
+
# # Single action controller (responds to .call)
|
135
|
+
# put('/search').to(SearchController)
|
136
|
+
#
|
137
|
+
# # Sinatra-style block action
|
138
|
+
# put('/echo').to do |env|
|
139
|
+
# [201, {}, [env['SERVER_NAME']]]
|
140
|
+
# end
|
141
|
+
# put('/echo') do |env|
|
142
|
+
# [201, {}, [env['SERVER_NAME']]]
|
143
|
+
# end
|
144
|
+
#
|
145
|
+
# # Path params
|
146
|
+
# put('/users/:id').to('users#update')
|
147
|
+
#
|
148
|
+
# # Params constraints
|
149
|
+
# put('/users/:id').to('users#update').capture(:digit)
|
150
|
+
# put('/users/:id.:ext').to('pages#show').capture({ id: /\d+/, ext: ['json', 'html'] })
|
151
|
+
#
|
152
|
+
# # Exclude pattern
|
153
|
+
# put('/pages/*slug/update').to('pages#update').except('/pages/system/*/update')
|
154
|
+
#
|
155
|
+
# # Default params
|
156
|
+
# put('/:controller/:action/:id').params(id: 1)
|
157
|
+
#
|
158
|
+
def put(path, &block)
|
159
|
+
builder = Sign::Flat::Path::PUT.new(absolute(path), @options, &block)
|
160
|
+
@builders << builder
|
161
|
+
builder
|
162
|
+
end
|
163
|
+
|
164
|
+
##
|
165
|
+
# Define route which accepts PATCH request for the given pattern
|
166
|
+
#
|
167
|
+
# Params:
|
168
|
+
# - path {String|RegExp} pattern of the request path which should be matched
|
169
|
+
#
|
170
|
+
# Yields: the Rack compatible block
|
171
|
+
#
|
172
|
+
# Example:
|
173
|
+
#
|
174
|
+
# # Most usual case: with controller and action. Will resolve Controller#action or Controller::Action
|
175
|
+
# patch('/users/:id').to(controller: 'Users', action: 'update')
|
176
|
+
# patch('/users/:id').to(controller: UsersController, action: 'update')
|
177
|
+
# patch('/users/:id').to('users#update')
|
178
|
+
#
|
179
|
+
# # Single action controller (responds to .call)
|
180
|
+
# patch('/users/:id').to(UsersUpdateController)
|
181
|
+
#
|
182
|
+
# # Sinatra-style block action
|
183
|
+
# patch('/echo').to do |env|
|
184
|
+
# [201, {}, [env['SERVER_NAME']]]
|
185
|
+
# end
|
186
|
+
# patch('/echo') do |env|
|
187
|
+
# [201, {}, [env['SERVER_NAME']]]
|
188
|
+
# end
|
189
|
+
#
|
190
|
+
# # Params constraints
|
191
|
+
# patch('/users/:id').to('users#update').capture(:digit)
|
192
|
+
# patch('/users/:id.:ext').to('pages#show').capture({ id: /\d+/, ext: ['json', 'html'] })
|
193
|
+
#
|
194
|
+
# # Exclude pattern
|
195
|
+
# patch('/pages/*slug/update').to('pages#update').except('/pages/system/*/update')
|
196
|
+
#
|
197
|
+
# # Default params
|
198
|
+
# patch('/:controller/:action/:id').params(id: 1)
|
199
|
+
#
|
200
|
+
def patch(path, &block)
|
201
|
+
builder = Sign::Flat::Path::PATCH.new(absolute(path), @options, &block)
|
202
|
+
@builders << builder
|
203
|
+
builder
|
204
|
+
end
|
205
|
+
|
206
|
+
##
|
207
|
+
# Define route which accepts OPTIONS request for the given pattern
|
208
|
+
#
|
209
|
+
# Params:
|
210
|
+
# - path {String|RegExp} pattern of the request path which should be matched
|
211
|
+
#
|
212
|
+
# Yields: the Rack compatible block
|
213
|
+
#
|
214
|
+
# Example:
|
215
|
+
#
|
216
|
+
# # Most usual case: with controller and action. Will resolve Controller#action or Controller::Action
|
217
|
+
# options('/users').to(controller: 'Users', action: 'usage')
|
218
|
+
# options('/users').to(controller: UsersController, action: 'usage')
|
219
|
+
# options('/users').to('users#usage')
|
220
|
+
#
|
221
|
+
# # Single action controller (responds to .call)
|
222
|
+
# options('/').to(HomeController)
|
223
|
+
#
|
224
|
+
# # Sinatra-style block action
|
225
|
+
# options('/echo').to do |env|
|
226
|
+
# [200, {}, [env['SERVER_NAME']]]
|
227
|
+
# end
|
228
|
+
# options('/echo') do |env|
|
229
|
+
# [200, {}, [env['SERVER_NAME']]]
|
230
|
+
# end
|
231
|
+
#
|
232
|
+
# # Path params
|
233
|
+
# options('/users/:id').to('users#show')
|
234
|
+
#
|
235
|
+
# # Params constraints
|
236
|
+
# options('/users/:id').to('users#usage').capture(:digit)
|
237
|
+
# options('/users/:id.:ext').to('pages#usage').capture({ id: /\d+/, ext: ['json', 'html'] })
|
238
|
+
#
|
239
|
+
# # Exclude pattern
|
240
|
+
# options('/pages/*slug/usage').to('pages#usage').except('/pages/private/*/usage')
|
241
|
+
#
|
242
|
+
# # Default params
|
243
|
+
# options('/:controller/:action/:id').params(id: 1)
|
244
|
+
#
|
245
|
+
def options(path, &block)
|
246
|
+
builder = Sign::Flat::Path::OPTIONS.new(absolute(path), @options, &block)
|
247
|
+
@builders << builder
|
248
|
+
builder
|
249
|
+
end
|
250
|
+
|
251
|
+
##
|
252
|
+
# Define route which accepts PATCH request for the given pattern
|
253
|
+
#
|
254
|
+
# Params:
|
255
|
+
# - path {String|RegExp} pattern of the request path which should be matched
|
256
|
+
#
|
257
|
+
# Yields: the Rack compatible block
|
258
|
+
#
|
259
|
+
# Example:
|
260
|
+
#
|
261
|
+
# # Most usual case: with controller and action. Will resolve Controller#action or Controller::Action
|
262
|
+
# delete('/users/:id').to(controller: 'Users', action: 'destroy')
|
263
|
+
# delete('/users/:id').to(controller: UsersController, action: 'destroy')
|
264
|
+
# delete('/users/:id').to('users#destroy')
|
265
|
+
#
|
266
|
+
# # Single action controller (responds to .call)
|
267
|
+
# delete('/users/:id').to(UsersDestroyController)
|
268
|
+
#
|
269
|
+
# # Sinatra-style block action
|
270
|
+
# delete('/echo').to do |env|
|
271
|
+
# [201, {}, [env['SERVER_NAME']]]
|
272
|
+
# end
|
273
|
+
# delete('/echo') do |env|
|
274
|
+
# [201, {}, [env['SERVER_NAME']]]
|
275
|
+
# end
|
276
|
+
#
|
277
|
+
# # Params constraints
|
278
|
+
# delete('/users/:id').to('users#destroy').capture(:digit)
|
279
|
+
# delete('/users/:id.:ext').to('pages#destroy').capture({ id: /\d+/, ext: ['json', 'html'] })
|
280
|
+
#
|
281
|
+
# # Exclude pattern
|
282
|
+
# delete('/pages/*slug/destroy').to('pages#destroy').except('/pages/system/*/destroy')
|
283
|
+
#
|
284
|
+
# # Default params
|
285
|
+
# delete('/:controller/:action/:id').params(id: 1)
|
286
|
+
#
|
287
|
+
def delete(path, &block)
|
288
|
+
builder = Sign::Flat::Path::DELETE.new(absolute(path), @options, &block)
|
289
|
+
@builders << builder
|
290
|
+
builder
|
291
|
+
end
|
292
|
+
|
293
|
+
##
|
294
|
+
# Define route which accepts any type (or specified list) of request for the given pattern
|
295
|
+
#
|
296
|
+
# Params:
|
297
|
+
# - path {String|RegExp} pattern of the request path which should be matched
|
298
|
+
#
|
299
|
+
# Yields: the Rack compatible block
|
300
|
+
#
|
301
|
+
# Example:
|
302
|
+
#
|
303
|
+
# # Most usual case: with controller and action. Will resolve Controller#action or Controller::Action
|
304
|
+
# match('/users').to(controller: 'Users', action: 'index')
|
305
|
+
# match('/users').to(controller: UsersController, action: 'index')
|
306
|
+
# match('/users').to('users#index')
|
307
|
+
#
|
308
|
+
# # Specify types of requests
|
309
|
+
# match('/users').to('users#create').via(:post, :put)
|
310
|
+
#
|
311
|
+
# # Single action controller (responds to .call)
|
312
|
+
# match('/').to(HomeController)
|
313
|
+
#
|
314
|
+
# # Sinatra-style block action
|
315
|
+
# match('/echo').to do |env|
|
316
|
+
# [200, {}, [env['SERVER_NAME']]]
|
317
|
+
# end
|
318
|
+
# match('/echo') do |env|
|
319
|
+
# [200, {}, [env['SERVER_NAME']]]
|
320
|
+
# end
|
321
|
+
#
|
322
|
+
# # Path params
|
323
|
+
# match('/users/:id').to('users#show')
|
324
|
+
#
|
325
|
+
# # Params constraints
|
326
|
+
# match('/users/:id').to('users#show').capture(:digit)
|
327
|
+
# match('/users/:id.:ext').to('pages#show').capture({ id: /\d+/, ext: ['json', 'html'] })
|
328
|
+
#
|
329
|
+
# # Exclude pattern
|
330
|
+
# match('/pages/*slug/edit').to('pages#edit').except('/pages/system/*/edit')
|
331
|
+
#
|
332
|
+
# # Default params
|
333
|
+
# match('/:controller/:action/:id').params(id: 1)
|
334
|
+
#
|
335
|
+
def match(path, &block)
|
336
|
+
builder = Sign::Flat::Path::Any.new(absolute(path), @options, &block)
|
337
|
+
@builders << builder
|
338
|
+
builder
|
339
|
+
end
|
340
|
+
|
341
|
+
##
|
342
|
+
# Define nested routes
|
343
|
+
#
|
344
|
+
# Params:
|
345
|
+
# - path {String} subpath
|
346
|
+
#
|
347
|
+
# Yields: routes dsl
|
348
|
+
#
|
349
|
+
# Example:
|
350
|
+
#
|
351
|
+
# within('/admin') do
|
352
|
+
# get('/pages').to('Admin::Pages#index') # /admin/pages
|
353
|
+
# put('/users/:id').to('admin/users#create') # /admin/users/2
|
354
|
+
# end
|
355
|
+
#
|
356
|
+
# # Nested routes can has their own routes
|
357
|
+
# within('/admin') do
|
358
|
+
# get('/').to('admin#index')
|
359
|
+
#
|
360
|
+
# within('/pages') do
|
361
|
+
# get('/').to('admin/pages#index') # /admin/pages
|
362
|
+
# put('/:id').to('admin/pages#create') # /admin/pages/2
|
363
|
+
# end
|
364
|
+
# end
|
365
|
+
#
|
366
|
+
# # You can also build a middleware stack for subroutes
|
367
|
+
# within('/admin') do
|
368
|
+
# use AuthMiddleware
|
369
|
+
#
|
370
|
+
# get('/').to('admin#index')
|
371
|
+
# end
|
372
|
+
#
|
373
|
+
def within(path, &block)
|
374
|
+
@builders << Sign::Nested.new(absolute(path), @options, &block)
|
375
|
+
end
|
376
|
+
|
377
|
+
def namespace(name, &block)
|
378
|
+
options = @options.merge(namespace: [@options[:namespace], name].compact)
|
379
|
+
@builders << Sign::Namespace.new(absolute(name.to_s), options, &block)
|
380
|
+
end
|
381
|
+
|
382
|
+
def redirect(path, &block)
|
383
|
+
builder = Sign::Flat::Redirect.new(absolute(path), @options, &block)
|
384
|
+
@builders << builder
|
385
|
+
builder
|
386
|
+
end
|
387
|
+
|
388
|
+
##
|
389
|
+
# Build router
|
390
|
+
#
|
391
|
+
# Returns: {Signpost::Router}
|
392
|
+
#
|
393
|
+
def build
|
394
|
+
Router.new(@builders, @options, true)
|
395
|
+
end
|
396
|
+
|
397
|
+
private
|
398
|
+
|
399
|
+
def initialize(options={}, &block)
|
400
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
401
|
+
@subroute = options[:subroute] || '/'
|
402
|
+
@builders = []
|
403
|
+
|
404
|
+
instance_eval(&block) if block_given?
|
405
|
+
end
|
406
|
+
|
407
|
+
def absolute(path)
|
408
|
+
File.join(@subroute, path.gsub(SUBPATH_REG, ''))
|
409
|
+
end
|
410
|
+
|
411
|
+
def root_name
|
412
|
+
'root'
|
413
|
+
end
|
414
|
+
|
415
|
+
end
|
416
|
+
end
|