roda-cj 0.9.6 → 1.0.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.
data/CHANGELOG CHANGED
@@ -1,5 +1,21 @@
1
1
  = HEAD
2
2
 
3
+ * Support adding middleware after the route block has been added (jeremyevans)
4
+
5
+ * Allow Roda subclasses to use route block from superclass (jeremyevans)
6
+
7
+ * Have r.multi_route ignore non-String named routes (jeremyevans)
8
+
9
+ * Pick up newly added named routes while running in the multi_route plugin, useful for development (jeremyevans)
10
+
11
+ * Add path plugin, for named path support (jeremyevans) (#4)
12
+
13
+ * Add error_email plugin, for easily emailing an error notification for an exception (jeremyevans)
14
+
15
+ = 1.0.0 (2014-08-19)
16
+
17
+ * Don't have :extension hash matcher force a terminal match (jeremyevans)
18
+
3
19
  * Add :content option to view method in render plugin to use given content instead of rendering a template (jeremyevans)
4
20
 
5
21
  * Add :escape option to render plugin for using erb templates where <%= %> escapes and <%== %> does not (jeremyevans)
@@ -35,25 +35,30 @@ Here's a simple application, showing how the routing tree works:
35
35
  use Rack::Session::Cookie, :secret => ENV['SECRET']
36
36
 
37
37
  route do |r|
38
- # matches any GET request
39
- r.get do
38
+ # GET / request
39
+ r.root do
40
+ r.redirect "/hello"
41
+ end
40
42
 
41
- # matches GET /
42
- r.root do
43
- r.redirect "/hello"
44
- end
43
+ # /hello branch
44
+ r.on "hello" do
45
45
 
46
- # matches GET /hello or GET /hello/.*
47
- r.on "hello" do
46
+ # GET /hello/world request
47
+ r.get "world" do
48
+ "Hello world!"
49
+ end
48
50
 
49
- # matches GET /hello/world
50
- r.is "world" do
51
- "Hello world!"
51
+ # /hello request
52
+ r.is do
53
+ # GET /hello request
54
+ r.get do
55
+ "Hello!"
52
56
  end
53
57
 
54
- # matches GET /hello
55
- r.is do
56
- "Hello!"
58
+ # POST /hello request
59
+ r.post do
60
+ puts "Someone said hello!"
61
+ r.redirect
57
62
  end
58
63
  end
59
64
  end
@@ -62,8 +67,6 @@ Here's a simple application, showing how the routing tree works:
62
67
 
63
68
  run App.app
64
69
 
65
- You can now run +rackup+ and enjoy what you have just created.
66
-
67
70
  Here's a breakdown of what is going on in the above block:
68
71
 
69
72
  After requiring the library and subclassing Roda, the +use+ method
@@ -76,34 +79,120 @@ with some additional methods for matching routes. By
76
79
  convention, this argument should be named +r+.
77
80
 
78
81
  The primary way routes are matched in Roda is by calling
79
- +r.on+, or a method like +r.get+ or +r.is+ which calls +r.on+.
80
- +r.on+ takes each of the arguments given and tries to match them to
81
- the current request. If it is able to successfully match
82
- all of the arguments, it yields to the +r.on+ block, otherwise
83
- it returns immediately.
84
-
85
- +r.get+ is a shortcut that matches any GET request, and
86
- +r.is+ is a shortcut that ensures the the exact route is
87
- matched and there are no further entries in the path.
88
-
89
- If +r.on+ matches and control is yielded to the block, whenever
90
- the block returns, the response will be returned. If the block
91
- returns a string and the response body hasn't already been
92
- written to, the block return value will interpreted as the body
93
- for the response. If none of the +r.on+ blocks match and the
94
- route block returns a string, it will be interpreted as the body
95
- for the response.
82
+ +r.on+, +r.is+, +r.root+, +r.get+, or +r.post+. These methods are
83
+ calling the routing methods, and each of them takes a block. The
84
+ block is referred to as a match block.
85
+
86
+ Each routing method takes each of the arguments (called matchers)
87
+ given and tries to match them to the current request. If it is
88
+ able to match all of the arguments, it yields to the match block,
89
+ otherwise the block is skipped and execution continues.
90
+
91
+ +r.on+ matches if all of the arguments match.
92
+ +r.is+ matches if all of the arguments match, and there are no
93
+ further entries in the path after matching.
94
+ +r.get+ when called without arguments matches any +GET+ request.
95
+ +r.get+ when called with any arguments matches only if the
96
+ current request is a +GET+ request and there are no further entries
97
+ in the path after matching.
98
+ +r.root+ only matches a +GET+ request where the current path is +/+.
99
+
100
+ If a routing method matches and control is yielded to the match
101
+ block, whenever the match block returns, Roda will return the
102
+ rack response array of status, headers, and body, to the caller.
103
+
104
+ If the match block returns a string and the response body hasn't
105
+ already been written to, the block return value will interpreted
106
+ as the body for the response. If none of the routing methods match
107
+ and the route block returns a string, it will be interpreted as the
108
+ body for the response.
96
109
 
97
110
  +r.redirect+ immediately returns the response, allowing for
98
- code such as <tt>r.redirect(path) if some_condition</tt>.
111
+ code such as <tt>r.redirect(path) if some_condition</tt>. If
112
+ called without arguments, it redirects to the current path if
113
+ the current request method is not +GET+.
99
114
 
100
115
  The +.app+ at the end is an optimization, which you can leave
101
116
  off, but which saves a few methods call for every response.
102
117
 
118
+ == The Routing Tree
119
+
120
+ Roda is called a routing tree web framework because the way most
121
+ sites are structured, routing takes the form of a tree based on the
122
+ URL structure of the site. In general, +r.on+ is used to split the
123
+ tree into different branches, and +r.is+ is finalizes the routing,
124
+ where the request is actually handled.
125
+
126
+ So a simple routing tree may look something like this:
127
+
128
+ r.on "a" do # /a branch
129
+ r.on "b" do # /a/b branch
130
+ r.is "c" do # /a/b/c request
131
+ r.get do end # GET /a/b/c request
132
+ r.post do end # POST /a/b/c request
133
+ end
134
+ r.get "d" do end # GET /a/b/d request
135
+ r.post "e" do end # POST /a/b/e request
136
+ end
137
+ end
138
+
139
+ It's also possible to handle the same requests, but structure the
140
+ routing tree by first branching on the request method:
141
+
142
+ r.get do # GET
143
+ r.on "a" do # GET /a branch
144
+ r.on "b" do # GET /a/b branch
145
+ r.is "c" do end # GET /a/b/c request
146
+ r.is "d" do end # GET /a/b/d request
147
+ end
148
+ end
149
+ end
150
+
151
+ r.post do # POST
152
+ r.on "a" do # POST /a branch
153
+ r.on "b" do # POST /a/b branch
154
+ r.is "c" do end # POST /a/b/c request
155
+ r.is "e" do end # POST /a/b/e request
156
+ end
157
+ end
158
+ end
159
+
160
+ This allows you to easily separate your +GET+ request handling from
161
+ your +POST+ request handling. If you only have a small number of
162
+ +POST+ request URLs and a large number of +GET+ request URLs, this
163
+ may make things easier.
164
+
165
+ However, in general routing first by the path and last by the
166
+ request method is likely to lead to simpler and DRYer code. This
167
+ is because at any point during the routing, you can act on the
168
+ request. For example, if all requests in the +/a+ branch need
169
+ need access permission +A+ and all requests in the +/a/b+ branch
170
+ need access permission +B+, you can easily handle this in the
171
+ routing tree:
172
+
173
+ r.on "a" do # /a branch
174
+ check_perm(:A)
175
+ r.on "b" do # /a/b branch
176
+ check_perm(:B)
177
+ r.is "c" do # /a/b/c request
178
+ r.get do end # GET /a/b/c request
179
+ r.post do end # POST /a/b/c request
180
+ end
181
+ r.get "d" do end # GET /a/b/d request
182
+ r.post "e" do end # POST /a/b/e request
183
+ end
184
+ end
185
+
186
+ Being able to operate on the request at any point during the
187
+ the routing is one of the major advantages of Roda compared
188
+ to other web frameworks that do not use a routing tree.
189
+
103
190
  == Matchers
104
191
 
105
- Here's an example showcasing how different matchers work. Matchers
106
- are arguments passed to +r.on+.
192
+ Other than +r.root+, the routing methods all take arguments called
193
+ matchers. If all of the matchers match, the routing method yields to
194
+ the match block. Here's an example showcasing how different
195
+ matchers work:
107
196
 
108
197
  class App < Roda
109
198
  route do |r|
@@ -136,7 +225,6 @@ are arguments passed to +r.on+.
136
225
 
137
226
  # /username/foobar/posts
138
227
  r.is "posts" do
139
-
140
228
  # You can access user here, because the blocks are closures.
141
229
  "Total Posts: #{user.posts.size}" #=> "Total Posts: 6"
142
230
  end
@@ -158,7 +246,7 @@ are arguments passed to +r.on+.
158
246
  r.is "login" do
159
247
 
160
248
  # POST /login, user: foo, pass: baz
161
- r.on {:param=>"user"}, {:param=>"pass"} do |user, pass|
249
+ r.on({:param=>"user"}, {:param=>"pass"}) do |user, pass|
162
250
  "#{user}:#{pass}" #=> "foo:baz"
163
251
  end
164
252
 
@@ -180,75 +268,75 @@ The +/+ here is considered the empty segment.
180
268
  If it does not contain a colon or slash, it matches single segment
181
269
  with the text of the string, preceeded by a slash.
182
270
 
183
- "" matches "/"
184
- "foo" matches "/foo"
185
- "foo" does not match "/food"
271
+ "" # matches "/"
272
+ "foo" # matches "/foo"
273
+ "foo" # does not match "/food"
186
274
 
187
275
  If it contains any slashes, it matches one additional segment for
188
276
  each slash:
189
277
 
190
- "foo/bar" matches "/foo/bar"
191
- "foo/bar" does not match "/foo/bard"
278
+ "foo/bar" # matches "/foo/bar"
279
+ "foo/bar" # does not match "/foo/bard"
192
280
 
193
281
  If it contains a colon followed by any <tt>\\w</tt> characters, the colon and
194
282
  remaing <tt>\\w</tt> characters matches any nonempty segment that contains at
195
283
  least one character:
196
284
 
197
- "foo/:id" matches "/foo/bar", "/foo/baz", etc.
198
- "foo/:id" does not match "/fo/bar"
285
+ "foo/:id" # matches "/foo/bar", "/foo/baz", etc.
286
+ "foo/:id" # does not match "/fo/bar"
199
287
 
200
288
  You can use multiple colons in a string:
201
289
 
202
- ":x/:y" matches "/foo/bar", "/bar/foo" etc.
203
- ":x/:y" does not match "/foo", "/bar/"
290
+ ":x/:y" # matches "/foo/bar", "/bar/foo" etc.
291
+ ":x/:y" # does not match "/foo", "/bar/"
204
292
 
205
293
  You can prefix colons:
206
294
 
207
- "foo:x/bar:y" matches "/food/bard", "/fool/bart", etc.
208
- "foo:x/bar:y" does not match "/foo/bart", "/fool/bar", etc.
295
+ "foo:x/bar:y" # matches "/food/bard", "/fool/bart", etc.
296
+ "foo:x/bar:y" # does not match "/foo/bart", "/fool/bar", etc.
209
297
 
210
298
  If any colons are used, the block will yield one argument for
211
299
  each segment matched containing the matched text. So:
212
300
 
213
- "foo:x/:y" matching "/fool/bar" yields "l", "bar"
301
+ "foo:x/:y" # matching "/fool/bar" yields "l", "bar"
214
302
 
215
303
  Colons that are not followed by a <tt>\\w</tt> character are matched literally:
216
304
 
217
- ":/a" matches "/:/a"
305
+ ":/a" # matches "/:/a"
218
306
 
219
307
  Note that strings are regexp escaped before being used in a regular
220
308
  expression, so:
221
309
 
222
- "\\d+(/\\w+)?" matches "\d+(/\w+)?"
223
- "\\d+/\\w+" does not match "123/abc"
310
+ "\\d+(/\\w+)?" # matches "/\d+(/\w+)?"
311
+ "\\d+(/\\w+)?" # does not match "/123/abc"
224
312
 
225
313
  === Regexp
226
314
 
227
315
  Regexps match one or more segments by looking for the pattern preceeded by a
228
316
  slash:
229
317
 
230
- /foo\w+/ matches "/foobar"
231
- /foo\w+/ does not match "/foo/bar"
318
+ /foo\w+/ # matches "/foobar"
319
+ /foo\w+/ # does not match "/foo/bar"
232
320
 
233
321
  If any patterns are captured by the regexp, they are yielded:
234
322
 
235
- /foo\w+/ matches "/foobar", yields nothing
236
- /foo(\w+)/ matches "/foobar", yields "bar"
323
+ /foo\w+/ # matches "/foobar", yields nothing
324
+ /foo(\w+)/ # matches "/foobar", yields "bar"
237
325
 
238
326
  === Symbol
239
327
 
240
328
  Symbols match any nonempty segment, yielding the segment except for the
241
329
  preceeding slash:
242
330
 
243
- :id matches "/foo" yields "foo"
244
- :id does not match "/"
331
+ :id # matches "/foo" yields "foo"
332
+ :id # does not match "/"
245
333
 
246
334
  === Proc
247
335
 
248
336
  Procs match unless they return false or nil:
249
337
 
250
- proc{true} matches anything
251
- proc{false} does not match anything
338
+ proc{true} # matches anything
339
+ proc{false} # does not match anything
252
340
 
253
341
  Procs don't capture anything by default, but they can if you add
254
342
  the captured text to +r.captures+.
@@ -263,8 +351,8 @@ condition). Evaluation stops at the first matcher that matches.
263
351
  Additionally, if the matched object is a String, the string is yielded.
264
352
  This makes it easy to handle multiple strings without a Regexp:
265
353
 
266
- %w'page1 page2' matches "/page1", "/page2"
267
- [] does not match anything
354
+ ['page1', 'page2'] # matches "/page1", "/page2"
355
+ [] # does not match anything
268
356
 
269
357
  === Hash
270
358
 
@@ -276,12 +364,12 @@ block will be called with the value of the hash.
276
364
 
277
365
  class App < Roda
278
366
  hash_matcher(:foo) do |v|
279
- ...
367
+ # ...
280
368
  end
281
369
 
282
370
  route do |r|
283
371
  r.on :foo=>'bar' do
284
- ...
372
+ # ...
285
373
  end
286
374
  end
287
375
  end
@@ -291,13 +379,13 @@ block will be called with the value of the hash.
291
379
  The :all matcher matches if all of the entries in the given array matches. So
292
380
 
293
381
  r.on :all=>[:a, :b] do
294
- ...
382
+ # ...
295
383
  end
296
384
 
297
385
  is the same as:
298
386
 
299
387
  r.on :a, :b do
300
- ...
388
+ # ...
301
389
  end
302
390
 
303
391
  The reason it also exists as a separate hash matcher is so you can use it inside
@@ -312,34 +400,32 @@ Would match +/foo+ and +/foos/10+, but not +/foos+.
312
400
 
313
401
  The :extension matcher matches any nonempty path ending with the given extension:
314
402
 
315
- :extension => "css" matches "/foo.css", "/bar.css"
316
- :extension => "css" does not match "/foo.css/x", "/foo.bar", "/.css"
403
+ {:extension => "css"} # matches "/foo.css", "/bar.css"
404
+ {:extension => "css"} # does not match "/foo.css/x", "/foo.bar", "/.css"
317
405
 
318
- This matcher yields the part before the extension. Note that unlike other
319
- matchers, this matcher assumes terminal behavior, it doesn't match if there
320
- are additional segments.
406
+ This matcher yields the part before the extension.
321
407
 
322
408
  ==== :method
323
409
 
324
410
  This matches the method of the request. You can provide an array to specify multiple
325
411
  request methods and match on any of them:
326
412
 
327
- :method => :post matches POST
328
- :method => %w'post patch' matches POST and PATCH
413
+ {:method => :post} # matches POST
414
+ {:method => ['post', 'patch']} # matches POST and PATCH
329
415
 
330
416
  ==== :param
331
417
 
332
418
  The :param matcher matches if the given parameter is present, even if empty.
333
419
 
334
- :param => "user" matches "/foo?user=bar", "/foo?user="
335
- :param => "user" does not matches "/foo"
420
+ {:param => "user"} # matches "/foo?user=bar", "/foo?user="
421
+ {:param => "user"} # does not matches "/foo"
336
422
 
337
423
  ==== :param!
338
424
 
339
425
  The :param! matcher matches if the given parameter is present and not empty.
340
426
 
341
- :param! => "user" matches "/foo?user=bar"
342
- :param! => "user" does not matches "/foo", "/foo?user="
427
+ {:param! => "user"} # matches "/foo?user=bar"
428
+ {:param! => "user"} # does not matches "/foo", "/foo?user="
343
429
 
344
430
  === false, nil
345
431
 
@@ -361,10 +447,8 @@ You can always set the status code manually via the status attribute
361
447
  for the response.
362
448
 
363
449
  route do |r|
364
- r.get do
365
- r.is "hello" do
366
- response.status = 200
367
- end
450
+ r.get "hello" do
451
+ response.status = 200
368
452
  end
369
453
  end
370
454
 
@@ -372,22 +456,27 @@ for the response.
372
456
 
373
457
  The main match method is +r.on+, but as displayed above, you can also
374
458
  use +r.get+ or +r.post+. When called without any arguments, these
375
- call +r.on+ as long as the request has the appropriate method, so:
459
+ match as long as the request has the appropriate method, so:
460
+
461
+ r.get do end
376
462
 
377
- r.get{}
463
+ matches any +GET+ request, and
378
464
 
379
- is syntax sugar for:
465
+ r.post do end
380
466
 
381
- r.on{} if r.get?
467
+ matches any +POST+ request
382
468
 
383
- If any arguments are given to the method, these call +r.is+ as long as
384
- the request has the appropriate method, so:
469
+ If any arguments are given to the method, these match only
470
+ if the request method matches, all arguments match, and
471
+ only the path has been fully matched by the arguments. So:
385
472
 
386
- r.post(""){}
473
+ r.post "" do end
387
474
 
388
- is syntax sugar for:
475
+ matches only +POST+ requests where the current path is +/+.
389
476
 
390
- r.is(""){} if r.post?
477
+ r.get "a/b" do end
478
+
479
+ matches only +GET+ requests where the current path is +/a/b+.
391
480
 
392
481
  The reason for this difference in behavior is that if you are not
393
482
  providing any arguments, you probably don't want to to also test
@@ -400,17 +489,17 @@ you do want, you can provide true as an argument:
400
489
  end
401
490
 
402
491
  If you want to match the request method and do a partial match
403
- on the request path, you need to use +r.on+ with the <tt>:method</tt>
404
- hash matcher:
492
+ on the request path instead of a full match, you need to use
493
+ +r.on+ with the <tt>:method</tt> hash matcher:
405
494
 
406
495
  r.on "foo", :method=>:get do # Matches GET /foo(/.*)?
407
- edn
496
+ end
408
497
 
409
498
  == Root Method
410
499
 
411
500
  As displayed above, you can also use +r.root+ as a match method. This
412
- method matches <tt>GET /</tt> requests. +r.root+ is similar to <tt>r.get ""</tt>,
413
- except that it does not consume the +/+ from the path.
501
+ method matches +GET+ requests where the current path +/+. +r.root+ is
502
+ similar to <tt>r.get ""</tt>, except that it does not consume the +/+ from the path.
414
503
 
415
504
  Unlike the other matching methods, +r.root+ takes no arguments.
416
505
 
@@ -438,8 +527,8 @@ methods, or via plugins.
438
527
 
439
528
  == Pollution
440
529
 
441
- Roda tries very hard to avoid polluting the scope in which the +route+
442
- block operates. The only instance variables defined by default in the scope of
530
+ Roda tries very hard to avoid polluting the scope of the +route+
531
+ block. The only instance variables defined by default in the scope of
443
532
  the +route+ block are <tt>@_request</tt> and <tt>@_response</tt>. The only methods defined
444
533
  (beyond the default methods for +Object+) are: +env+, +opts+, +request+,
445
534
  +response+, +call+, +session+, and +_route+ (private). Constants inside the
@@ -476,8 +565,8 @@ in the request (via POST or QUERY_STRING) and it pushes the value as a capture.
476
565
 
477
566
  == Composition
478
567
 
479
- You can mount a Roda app, along with middlewares, inside another Roda app,
480
- via +r.run+:
568
+ You can mount any Rack app (including another Roda app), with its own middlewares,
569
+ inside a Roda app, using +r.run+:
481
570
 
482
571
  class API < Roda
483
572
  use SomeMiddleware
@@ -499,13 +588,24 @@ via +r.run+:
499
588
 
500
589
  run App.app
501
590
 
502
- You can also use the +multi_route+ plugin, which keeps the current scope of
591
+ This will take any path starting with +/api+ and send it to +API+. In this
592
+ example, +API+ is a Roda app, but it could easily be a Sinatra, Rails, or
593
+ other Rack app.
594
+
595
+ When you use +r.run+, Roda calls the given Rack app (+API+ in this
596
+ case), and whatever the Rack app returns will be returned as the response
597
+ for the current application.
598
+
599
+ === multi_route plugin
600
+
601
+ If you are just looking to split up the main route block up by branches, you
602
+ should use the +multi_route+ plugin, which keeps the current scope of
503
603
  the route block:
504
604
 
505
605
  class App < Roda
506
606
  plugin :multi_route
507
607
 
508
- route :api do |r|
608
+ route "api" do |r|
509
609
  r.is do
510
610
  # ...
511
611
  end
@@ -513,13 +613,16 @@ the route block:
513
613
 
514
614
  route do |r|
515
615
  r.on "api" do
516
- route :api
616
+ r.route "api"
517
617
  end
518
618
  end
519
619
  end
520
620
 
521
621
  run App.app
522
622
 
623
+ This allows you to set instance variables in the main route block, and still
624
+ have access to them inside the +api+ route block.
625
+
523
626
  == Testing
524
627
 
525
628
  It is very easy to test Roda with {Rack::Test}[https://github.com/brynary/rack-test]
@@ -656,6 +759,8 @@ Note that unlike most other render options, the :escape option
656
759
  must be passed to the <tt>plugin :render</tt> call, it won't be
657
760
  respected if added later.
658
761
 
762
+ This support requires {Erubis}[http://www.kuwata-lab.com/erubis/].
763
+
659
764
  === Other
660
765
 
661
766
  For prevention of some other vulnerabilities, such as click-jacking,
@@ -687,6 +792,8 @@ content_for :: Allows storage of content in one template and retrieval of
687
792
  csrf :: Adds CSRF protection and helper methods using
688
793
  {rack_csrf}[https://github.com/baldowl/rack_csrf].
689
794
  default_headers :: Override the default response headers used.
795
+ error_email :: Adds an +error_email+ method that can be used to email when
796
+ an exception is raised.
690
797
  error_handler :: Adds a +error+ block that is called for all responses that
691
798
  raise exceptions.
692
799
  flash :: Adds a flash handler.
@@ -710,6 +817,7 @@ not_found :: Adds a +not_found+ block that is called for all 404 responses
710
817
  without bodies.
711
818
  pass :: Adds a pass method allowing you to skip the current +r.on+ block as if
712
819
  it did not match.
820
+ path :: Adds support for named paths.
713
821
  per_thread_caching :: Switches the thread-safe cache from a shared cache to a
714
822
  per-thread cache.
715
823
  render :: Adds support for rendering templates via tilt, as described above.