signpost 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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