roda 1.1.0 → 1.2.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 +4 -4
- data/CHANGELOG +70 -0
- data/README.rdoc +261 -302
- data/Rakefile +1 -1
- data/doc/release_notes/1.2.0.txt +406 -0
- data/lib/roda.rb +206 -124
- data/lib/roda/plugins/all_verbs.rb +11 -10
- data/lib/roda/plugins/assets.rb +5 -5
- data/lib/roda/plugins/backtracking_array.rb +12 -5
- data/lib/roda/plugins/caching.rb +10 -8
- data/lib/roda/plugins/class_level_routing.rb +94 -0
- data/lib/roda/plugins/content_for.rb +6 -0
- data/lib/roda/plugins/default_headers.rb +4 -11
- data/lib/roda/plugins/delay_build.rb +42 -0
- data/lib/roda/plugins/delegate.rb +64 -0
- data/lib/roda/plugins/drop_body.rb +33 -0
- data/lib/roda/plugins/empty_root.rb +48 -0
- data/lib/roda/plugins/environments.rb +68 -0
- data/lib/roda/plugins/error_email.rb +1 -2
- data/lib/roda/plugins/error_handler.rb +1 -1
- data/lib/roda/plugins/halt.rb +7 -5
- data/lib/roda/plugins/head.rb +4 -2
- data/lib/roda/plugins/header_matchers.rb +17 -9
- data/lib/roda/plugins/hooks.rb +16 -32
- data/lib/roda/plugins/json.rb +4 -10
- data/lib/roda/plugins/mailer.rb +233 -0
- data/lib/roda/plugins/match_affix.rb +48 -0
- data/lib/roda/plugins/multi_route.rb +9 -11
- data/lib/roda/plugins/multi_run.rb +81 -0
- data/lib/roda/plugins/named_templates.rb +93 -0
- data/lib/roda/plugins/not_allowed.rb +43 -48
- data/lib/roda/plugins/path.rb +63 -2
- data/lib/roda/plugins/render.rb +79 -48
- data/lib/roda/plugins/render_each.rb +6 -0
- data/lib/roda/plugins/sinatra_helpers.rb +523 -0
- data/lib/roda/plugins/slash_path_empty.rb +25 -0
- data/lib/roda/plugins/static_path_info.rb +64 -0
- data/lib/roda/plugins/streaming.rb +1 -1
- data/lib/roda/plugins/view_subdirs.rb +12 -8
- data/lib/roda/version.rb +1 -1
- data/spec/integration_spec.rb +33 -0
- data/spec/plugin/backtracking_array_spec.rb +24 -18
- data/spec/plugin/class_level_routing_spec.rb +138 -0
- data/spec/plugin/delay_build_spec.rb +23 -0
- data/spec/plugin/delegate_spec.rb +20 -0
- data/spec/plugin/drop_body_spec.rb +20 -0
- data/spec/plugin/empty_root_spec.rb +14 -0
- data/spec/plugin/environments_spec.rb +31 -0
- data/spec/plugin/h_spec.rb +1 -3
- data/spec/plugin/header_matchers_spec.rb +14 -0
- data/spec/plugin/hooks_spec.rb +3 -5
- data/spec/plugin/mailer_spec.rb +191 -0
- data/spec/plugin/match_affix_spec.rb +22 -0
- data/spec/plugin/multi_run_spec.rb +31 -0
- data/spec/plugin/named_templates_spec.rb +65 -0
- data/spec/plugin/path_spec.rb +66 -2
- data/spec/plugin/render_spec.rb +46 -1
- data/spec/plugin/sinatra_helpers_spec.rb +534 -0
- data/spec/plugin/slash_path_empty_spec.rb +22 -0
- data/spec/plugin/static_path_info_spec.rb +50 -0
- data/spec/request_spec.rb +23 -0
- data/spec/response_spec.rb +12 -1
- metadata +48 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3d246589728bb46404806737bb7e67819956f167
|
4
|
+
data.tar.gz: e57198e8df759bd70039ff42f94d86583c75c2f8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3127e9f7b7525780556555366e82b0e9f565216e62dacdd5e3e7a69e3440a47771ddad5472b94f7c09d15cf6acb49c7bcdb54fb1d469f68569eb5cf77e589dca
|
7
|
+
data.tar.gz: f6dd7cda1372931ce0bf92934d584e084f8b1b84d5a181ce1b28bcf951983adce7c477e6b4bdabdad9a8f0038cacafa49ba87948b46a38de0ad23b0b1330d86c
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,73 @@
|
|
1
|
+
= 1.2.0 (2014-12-17)
|
2
|
+
|
3
|
+
* Don't override explicit nil :default_encoding template option in the render plugin (jeremyevans)
|
4
|
+
|
5
|
+
* Add remaining_path and matched_path request methods (jeremyevans)
|
6
|
+
|
7
|
+
* Add slash_path_emty plugin, for considering a path of "/" as empty when doing a terminal match (jeremyevans)
|
8
|
+
|
9
|
+
* Remove def_verb_method request class method (jeremyevans)
|
10
|
+
|
11
|
+
* Support :add_script_name, :name, :url, and :url_only options when creating named paths in the path plugin (jeremyevans)
|
12
|
+
|
13
|
+
* Add match_affix plugin, for overriding default prefix/suffix used in match patterns (jeremyevans)
|
14
|
+
|
15
|
+
* Add empty_root plugin, for making root matcher also match empty string (jeremyevans)
|
16
|
+
|
17
|
+
* Add roda_class instance methods to RodaRequest and RodaResponse, to DRY up plugin code (jeremyevans)
|
18
|
+
|
19
|
+
* Add sinatra_helpers plugin, porting Sinatra::Helpers methods not covered by other plugins (jeremyevans)
|
20
|
+
|
21
|
+
* Don't set the default headers until the response is finished (jeremyevans)
|
22
|
+
|
23
|
+
* Add RodaRequest#default_redirect_status, so plugins can override the default status used for redirects (jeremyevans)
|
24
|
+
|
25
|
+
* Add drop_body plugin, for automatically dropping body and Content-{Length,Type} headers based on response status (jeremyevans)
|
26
|
+
|
27
|
+
* Add clear_middleware! class method, for clearing the current middleware (jeremyevans)
|
28
|
+
|
29
|
+
* Add inherit_middleware class accessor, allowing users to turn off middleware inheritance (jeremyevans)
|
30
|
+
|
31
|
+
* Add multi_run plugin, for dispatching to multiple rack applications based on the request path prefix (jeremyevans)
|
32
|
+
|
33
|
+
* Add environments plugin, for handling development/test/production environments (jeremyevans)
|
34
|
+
|
35
|
+
* Do not cache templates by default if RACK_ENV is development (jeremyevans)
|
36
|
+
|
37
|
+
* Add delay_build plugin, to delay building the rack app until Roda.app is called (jeremyevans)
|
38
|
+
|
39
|
+
* Add :user_agent hash matcher to the header_matchers plugin (jeremyevans)
|
40
|
+
|
41
|
+
* Fix caching of templates in the render plugin when :opts or :template_class is used (jeremyevans)
|
42
|
+
|
43
|
+
* Require loading the render plugin again if you want to change the default layout (jeremyevans)
|
44
|
+
|
45
|
+
* Pass :css_opts and :js_opts as template options (via :opts) instead of render options when rendering (jeremyevans)
|
46
|
+
|
47
|
+
* Only pass :opts hash to template class during rendering, instead of all render/view options (jeremyevans)
|
48
|
+
|
49
|
+
* Support :template_class option in the render plugin for overriding template class to use (jeremyevans)
|
50
|
+
|
51
|
+
* Automatically dup unfrozen Array/Hash opts values when subclassing (jeremyevans)
|
52
|
+
|
53
|
+
* Add named_templates plugin, for creating inline templates by name, instead of storing them in the file system (jeremyevans)
|
54
|
+
|
55
|
+
* Support :template option in for render/view to specify template to use, instead of requiring separate argument (jeremyevans)
|
56
|
+
|
57
|
+
* Add class_level_routing plugin, for a DSL similar to Sinatra (jeremyevans)
|
58
|
+
|
59
|
+
* Make RodaRequest.consume_pattern not capture pattern by default (jeremyevans)
|
60
|
+
|
61
|
+
* Add static_path_info plugin, making Roda not modify PATH_INFO or SCRIPT_NAME during routing (jeremyevans)
|
62
|
+
|
63
|
+
* Use local/instance variable lookups instead of method calls to improve performance (jeremyevans)
|
64
|
+
|
65
|
+
* Add RodaRequest#session, and have #session delegate to that (jeremyevans)
|
66
|
+
|
67
|
+
* Add delegate plugin, for easily creating methods that delegate to request or response (jeremyevans)
|
68
|
+
|
69
|
+
* Add mailer plugin, allowing use of a routing tree for email instead of web responses (jeremyevans)
|
70
|
+
|
1
71
|
= 1.1.0 (2014-11-11)
|
2
72
|
|
3
73
|
* Add assets plugin, for rendering assets on the fly, or compiling them to a single compressed file (cj, jeremyevans) (#5)
|
data/README.rdoc
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
= Roda
|
2
2
|
|
3
|
-
Roda is a routing tree web framework.
|
3
|
+
Roda is a routing tree web framework toolkit.
|
4
4
|
|
5
5
|
= Installation
|
6
6
|
|
@@ -17,10 +17,11 @@ IRC :: irc://chat.freenode.net/#roda
|
|
17
17
|
== Inspiration
|
18
18
|
|
19
19
|
Roda was inspired by {Sinatra}[http://www.sinatrarb.com] and {Cuba}[http://cuba.is],
|
20
|
-
two other Ruby web frameworks.
|
21
|
-
|
22
|
-
{Rum}[https://github.com/chneukirchen/rum]).
|
23
|
-
route blocks should return the request bodies
|
20
|
+
two other Ruby web frameworks.
|
21
|
+
It started out as a fork of Cuba, from which it borrows the idea of using a routing tree
|
22
|
+
(which Cuba in turn took from {Rum}[https://github.com/chneukirchen/rum]).
|
23
|
+
From Sinatra, it takes the ideas that route blocks should return the request bodies
|
24
|
+
and that routes should be canonical.
|
24
25
|
It pilfers the idea for an extensible plugin system from the Ruby database library
|
25
26
|
{Sequel}[http://sequel.jeremyevans.net].
|
26
27
|
|
@@ -67,77 +68,77 @@ Here's a simple application, showing how the routing tree works:
|
|
67
68
|
|
68
69
|
run App.app
|
69
70
|
|
70
|
-
Here's a breakdown of what is going on in the above
|
71
|
+
Here's a breakdown of what is going on in the block above:
|
71
72
|
|
72
|
-
After requiring the library and subclassing Roda, the +use+ method
|
73
|
-
|
74
|
-
application.
|
73
|
+
After requiring the library and subclassing Roda, the +use+ method is called.
|
74
|
+
This loads a Rack middleware into the current application.
|
75
75
|
|
76
|
-
The +route+ block is called whenever a new request comes in
|
77
|
-
|
78
|
-
with some additional methods for matching routes.
|
79
|
-
convention, this argument should be named +r+.
|
76
|
+
The +route+ block is called whenever a new request comes in.
|
77
|
+
It is yielded an instance of a subclass of <tt>Rack::Request</tt>
|
78
|
+
with some additional methods for matching routes.
|
79
|
+
By convention, this argument should be named +r+.
|
80
80
|
|
81
81
|
The primary way routes are matched in Roda is by calling
|
82
|
-
+r.on+, +r.is+, +r.root+, +r.get+, or +r.post+.
|
83
|
-
|
84
|
-
block is referred to as a match block.
|
82
|
+
+r.on+, +r.is+, +r.root+, +r.get+, or +r.post+.
|
83
|
+
Each of these "routing methods" takes a "match block".
|
85
84
|
|
86
85
|
Each routing method takes each of the arguments (called matchers)
|
87
|
-
given and tries to match
|
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
|
93
|
-
further entries in the path after matching.
|
94
|
-
+r.get+
|
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
|
-
|
102
|
-
|
103
|
-
|
104
|
-
If the match block returns a string
|
105
|
-
already been written to,
|
106
|
-
as the body for the response.
|
107
|
-
and the route block returns a string,
|
108
|
-
body for the response.
|
109
|
-
|
110
|
-
+r.redirect+ immediately returns the response,
|
111
|
-
code such as <tt>r.redirect(path) if some_condition</tt>.
|
112
|
-
called without arguments
|
113
|
-
the current request method is not +GET
|
114
|
-
|
115
|
-
The +.app+ at the end is an optimization
|
116
|
-
|
86
|
+
that is given and tries to match it to the current request.
|
87
|
+
If the method is able to match all of the arguments, it yields to the match block;
|
88
|
+
otherwise, the block is skipped and execution continues.
|
89
|
+
|
90
|
+
- +r.on+ matches if all of the arguments match.
|
91
|
+
- +r.is+ matches if all of the arguments match and there are no
|
92
|
+
further entries in the path after matching.
|
93
|
+
- +r.get+ matches any +GET+ request when called without arguments.
|
94
|
+
- +r.get+ (when called with any arguments) matches only if the
|
95
|
+
current request is a +GET+ request and there are no further entries
|
96
|
+
in the path after matching.
|
97
|
+
- +r.root+ only matches a +GET+ request where the current path is +/+.
|
98
|
+
|
99
|
+
If a routing method matches and control is yielded to the match block,
|
100
|
+
whenever the match block returns, Roda will return the Rack response array
|
101
|
+
(containing status, headers, and body) to the caller.
|
102
|
+
|
103
|
+
If the match block returns a string
|
104
|
+
and the response body hasn't already been written to,
|
105
|
+
the block return value will be interpreted as the body for the response.
|
106
|
+
If none of the routing methods match and the route block returns a string,
|
107
|
+
it will be interpreted as the body for the response.
|
108
|
+
|
109
|
+
+r.redirect+ immediately returns the response,
|
110
|
+
allowing for code such as <tt>r.redirect(path) if some_condition</tt>.
|
111
|
+
If +r.redirect+ is called without arguments
|
112
|
+
and the current request method is not +GET+, it redirects to the current path.
|
113
|
+
|
114
|
+
The +.app+ at the end is an (optional) optimization.
|
115
|
+
It saves a few method calls for every response.
|
117
116
|
|
118
117
|
== The Routing Tree
|
119
118
|
|
120
|
-
Roda is called a routing tree web framework because the way most
|
121
|
-
|
122
|
-
|
123
|
-
tree into different branches, and +r.is+ is finalizes the routing,
|
124
|
-
where the request is actually handled.
|
119
|
+
Roda is called a routing tree web framework because the way most sites are structured,
|
120
|
+
routing takes the form of a tree (based on the URL structure of the site).
|
121
|
+
In general:
|
125
122
|
|
126
|
-
|
123
|
+
- +r.on+ is used to split the tree into different branches.
|
124
|
+
- +r.is+ finalizes the routing path.
|
125
|
+
- +r.get+ and +r.post+ handle specific request methods.
|
126
|
+
|
127
|
+
So, a simple routing tree might look something like this:
|
127
128
|
|
128
129
|
r.on "a" do # /a branch
|
129
130
|
r.on "b" do # /a/b branch
|
130
131
|
r.is "c" do # /a/b/c request
|
131
|
-
r.get do end # GET
|
132
|
+
r.get do end # GET /a/b/c request
|
132
133
|
r.post do end # POST /a/b/c request
|
133
134
|
end
|
134
|
-
r.get "d" do end # GET
|
135
|
+
r.get "d" do end # GET /a/b/d request
|
135
136
|
r.post "e" do end # POST /a/b/e request
|
136
137
|
end
|
137
138
|
end
|
138
139
|
|
139
|
-
It's also possible to handle the same requests,
|
140
|
-
routing tree by first branching on the request method:
|
140
|
+
It's also possible to handle the same requests,
|
141
|
+
but structure the routing tree by first branching on the request method:
|
141
142
|
|
142
143
|
r.get do # GET
|
143
144
|
r.on "a" do # GET /a branch
|
@@ -157,42 +158,40 @@ routing tree by first branching on the request method:
|
|
157
158
|
end
|
158
159
|
end
|
159
160
|
|
160
|
-
This allows you to easily separate your +GET+ request handling
|
161
|
-
your +POST+ request handling.
|
162
|
-
|
163
|
-
may make things easier.
|
161
|
+
This allows you to easily separate your +GET+ request handling
|
162
|
+
from your +POST+ request handling.
|
163
|
+
If you only have a small number of +POST+ request URLs
|
164
|
+
and a large number of +GET+ request URLs, this may make things easier.
|
164
165
|
|
165
|
-
However,
|
166
|
-
|
167
|
-
is because
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
routing tree:
|
166
|
+
However, routing first by the path and last by the request method
|
167
|
+
is likely to lead to simpler and DRYer code.
|
168
|
+
This is because you can act on the request at any point during the routing.
|
169
|
+
For example, if all requests in the +/a+ branch need access permission +A+
|
170
|
+
and all requests in the +/a/b+ branch need access permission +B+,
|
171
|
+
you can easily handle this in the routing tree:
|
172
172
|
|
173
173
|
r.on "a" do # /a branch
|
174
174
|
check_perm(:A)
|
175
175
|
r.on "b" do # /a/b branch
|
176
176
|
check_perm(:B)
|
177
177
|
r.is "c" do # /a/b/c request
|
178
|
-
r.get do end # GET
|
178
|
+
r.get do end # GET /a/b/c request
|
179
179
|
r.post do end # POST /a/b/c request
|
180
180
|
end
|
181
|
-
r.get "d" do end # GET
|
181
|
+
r.get "d" do end # GET /a/b/d request
|
182
182
|
r.post "e" do end # POST /a/b/e request
|
183
183
|
end
|
184
184
|
end
|
185
185
|
|
186
|
-
Being able to operate on the request at any point during the
|
187
|
-
|
188
|
-
|
186
|
+
Being able to operate on the request at any point during the routing
|
187
|
+
is one of the major advantages of Roda, as compared to frameworks
|
188
|
+
that do not use a routing tree.
|
189
189
|
|
190
190
|
== Matchers
|
191
191
|
|
192
|
-
Other than +r.root+, the routing methods all take arguments called
|
193
|
-
|
194
|
-
|
195
|
-
matchers work:
|
192
|
+
Other than +r.root+, the routing methods all take arguments called matchers.
|
193
|
+
If all of the matchers match, the routing method yields to the match block.
|
194
|
+
Here's an example showcasing how different matchers work:
|
196
195
|
|
197
196
|
class App < Roda
|
198
197
|
route do |r|
|
@@ -258,29 +257,28 @@ matchers work:
|
|
258
257
|
end
|
259
258
|
end
|
260
259
|
|
261
|
-
Here's a description of the matchers.
|
262
|
-
here means one part of the path
|
263
|
-
as +/foo/bar//baz+ has
|
260
|
+
Here's a description of the matchers.
|
261
|
+
Note that "segment", as used here, means one part of the path preceded by a +/+.
|
262
|
+
So, a path such as +/foo/bar//baz+ has four segments: +/foo+, +/bar+, +/+, and +/baz+.
|
264
263
|
The +/+ here is considered the empty segment.
|
265
264
|
|
266
265
|
=== String
|
267
266
|
|
268
|
-
If
|
269
|
-
|
267
|
+
If a string does not contain a colon or slash, it matches a single segment
|
268
|
+
containing the text of the string, preceded by a slash.
|
270
269
|
|
271
270
|
"" # matches "/"
|
272
271
|
"foo" # matches "/foo"
|
273
272
|
"foo" # does not match "/food"
|
274
273
|
|
275
|
-
If
|
276
|
-
each slash:
|
274
|
+
If a string contains any slashes, it matches one additional segment for each slash:
|
277
275
|
|
278
276
|
"foo/bar" # matches "/foo/bar"
|
279
277
|
"foo/bar" # does not match "/foo/bard"
|
280
278
|
|
281
|
-
If
|
282
|
-
|
283
|
-
least one character:
|
279
|
+
If a string contains a colon followed by any <tt>\\w</tt> characters,
|
280
|
+
the colon and remaining <tt>\\w</tt> characters match any nonempty segment
|
281
|
+
that contains at least one character:
|
284
282
|
|
285
283
|
"foo/:id" # matches "/foo/bar", "/foo/baz", etc.
|
286
284
|
"foo/:id" # does not match "/fo/bar"
|
@@ -295,8 +293,8 @@ You can prefix colons:
|
|
295
293
|
"foo:x/bar:y" # matches "/food/bard", "/fool/bart", etc.
|
296
294
|
"foo:x/bar:y" # does not match "/foo/bart", "/fool/bar", etc.
|
297
295
|
|
298
|
-
If any colons are used, the block will yield one argument
|
299
|
-
each segment matched containing the matched text
|
296
|
+
If any colons are used, the block will yield one argument
|
297
|
+
for each segment matched containing the matched text, so:
|
300
298
|
|
301
299
|
"foo:x/:y" # matching "/fool/bar" yields "l", "bar"
|
302
300
|
|
@@ -304,29 +302,28 @@ Colons that are not followed by a <tt>\\w</tt> character are matched literally:
|
|
304
302
|
|
305
303
|
":/a" # matches "/:/a"
|
306
304
|
|
307
|
-
Note that strings
|
308
|
-
expression, so:
|
305
|
+
Note that strings must be escaped before being used in a regular expression, so:
|
309
306
|
|
310
307
|
"\\d+(/\\w+)?" # matches "/\d+(/\w+)?"
|
311
308
|
"\\d+(/\\w+)?" # does not match "/123/abc"
|
312
309
|
|
313
310
|
=== Regexp
|
314
311
|
|
315
|
-
Regexps match one or more segments by looking for the pattern
|
316
|
-
slash:
|
312
|
+
Regexps match one or more segments by looking for the pattern,
|
313
|
+
preceded by a slash:
|
317
314
|
|
318
315
|
/foo\w+/ # matches "/foobar"
|
319
316
|
/foo\w+/ # does not match "/foo/bar"
|
320
317
|
|
321
|
-
If any patterns are captured by the
|
318
|
+
If any patterns are captured by the Regexp, they are yielded:
|
322
319
|
|
323
320
|
/foo\w+/ # matches "/foobar", yields nothing
|
324
321
|
/foo(\w+)/ # matches "/foobar", yields "bar"
|
325
322
|
|
326
323
|
=== Symbol
|
327
324
|
|
328
|
-
Symbols match any nonempty segment,
|
329
|
-
|
325
|
+
Symbols match any nonempty segment,
|
326
|
+
yielding the segment except for the preceding slash:
|
330
327
|
|
331
328
|
:id # matches "/foo" yields "foo"
|
332
329
|
:id # does not match "/"
|
@@ -338,15 +335,15 @@ Procs match unless they return false or nil:
|
|
338
335
|
proc{true} # matches anything
|
339
336
|
proc{false} # does not match anything
|
340
337
|
|
341
|
-
Procs don't capture anything by default,
|
342
|
-
the captured text to +r.captures+.
|
338
|
+
Procs don't capture anything by default,
|
339
|
+
but they can do so if you add the captured text to +r.captures+.
|
343
340
|
|
344
341
|
=== Arrays
|
345
342
|
|
346
|
-
Arrays match when any of their elements
|
347
|
-
are given to +r.on+, they all must match (an AND condition)
|
348
|
-
|
349
|
-
|
343
|
+
Arrays match when any of their elements match.
|
344
|
+
If multiple matchers are given to +r.on+, they all must match (an AND condition).
|
345
|
+
If an array of matchers is given, only one needs to match (an OR condition).
|
346
|
+
Evaluation stops at the first matcher that matches.
|
350
347
|
|
351
348
|
Additionally, if the matched object is a String, the string is yielded.
|
352
349
|
This makes it easy to handle multiple strings without a Regexp:
|
@@ -376,7 +373,7 @@ block will be called with the value of the hash.
|
|
376
373
|
|
377
374
|
==== :all
|
378
375
|
|
379
|
-
The
|
376
|
+
The +:all+ matcher matches if all of the entries in the given array match, so
|
380
377
|
|
381
378
|
r.on :all=>[:a, :b] do
|
382
379
|
# ...
|
@@ -388,48 +385,48 @@ is the same as:
|
|
388
385
|
# ...
|
389
386
|
end
|
390
387
|
|
391
|
-
The reason it also exists as a separate hash matcher
|
392
|
-
an array matcher
|
388
|
+
The reason it also exists as a separate hash matcher
|
389
|
+
is so you can use it inside an array matcher, so:
|
393
390
|
|
394
391
|
r.on ['foo', {:all=>['foos', :id]}] do
|
395
392
|
end
|
396
393
|
|
397
|
-
|
394
|
+
would match +/foo+ and +/foos/10+, but not +/foos+.
|
398
395
|
|
399
396
|
==== :extension
|
400
397
|
|
401
|
-
The
|
398
|
+
The +:extension+ matcher matches any nonempty path ending with the given extension:
|
402
399
|
|
403
400
|
{:extension => "css"} # matches "/foo.css", "/bar.css"
|
404
401
|
{:extension => "css"} # does not match "/foo.css/x", "/foo.bar", "/.css"
|
405
402
|
|
406
|
-
This matcher yields the part before the extension.
|
403
|
+
This matcher yields the part found before the period and extension (e.g., +foo+).
|
407
404
|
|
408
405
|
==== :method
|
409
406
|
|
410
|
-
|
411
|
-
request methods and match on any of them:
|
407
|
+
The +:method+ matcher matches the method of the request.
|
408
|
+
You can provide an array to specify multiple request methods and match on any of them:
|
412
409
|
|
413
410
|
{:method => :post} # matches POST
|
414
411
|
{:method => ['post', 'patch']} # matches POST and PATCH
|
415
412
|
|
416
413
|
==== :param
|
417
414
|
|
418
|
-
The
|
415
|
+
The +:param+ matcher matches if the given parameter is present (even if it is empty).
|
419
416
|
|
420
417
|
{:param => "user"} # matches "/foo?user=bar", "/foo?user="
|
421
418
|
{:param => "user"} # does not matches "/foo"
|
422
419
|
|
423
420
|
==== :param!
|
424
421
|
|
425
|
-
The
|
422
|
+
The +:param!+ matcher matches if the given parameter is present and not empty.
|
426
423
|
|
427
424
|
{:param! => "user"} # matches "/foo?user=bar"
|
428
425
|
{:param! => "user"} # does not matches "/foo", "/foo?user="
|
429
426
|
|
430
427
|
=== false, nil
|
431
428
|
|
432
|
-
If false or nil is given directly as a matcher, it doesn't match anything.
|
429
|
+
If +false+ or +nil+ is given directly as a matcher, it doesn't match anything.
|
433
430
|
|
434
431
|
=== Everything else
|
435
432
|
|
@@ -437,14 +434,15 @@ Everything else matches anything.
|
|
437
434
|
|
438
435
|
== Status codes
|
439
436
|
|
440
|
-
When it comes time to finalize a response,
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
437
|
+
When it comes time to finalize a response,
|
438
|
+
if a status code has not been set manually and anything has been written to the response,
|
439
|
+
the response will use a 200 status code.
|
440
|
+
Otherwise, it will use a 404 status code.
|
441
|
+
This enables the principle of least surprise to work:
|
442
|
+
if you don't handle an action, a 404 response is assumed.
|
445
443
|
|
446
|
-
You can always set the status code manually
|
447
|
-
for the response.
|
444
|
+
You can always set the status code manually,
|
445
|
+
via the +status+ attribute for the response.
|
448
446
|
|
449
447
|
route do |r|
|
450
448
|
r.get "hello" do
|
@@ -454,9 +452,10 @@ for the response.
|
|
454
452
|
|
455
453
|
== Verb Methods
|
456
454
|
|
457
|
-
The main match method is +r.on+, but as displayed above,
|
458
|
-
use +r.get+ or +r.post+.
|
459
|
-
|
455
|
+
The main match method is +r.on+, but as displayed above,
|
456
|
+
you can also use +r.get+ or +r.post+.
|
457
|
+
When called without any arguments, these match as long
|
458
|
+
as the request has the appropriate method, so:
|
460
459
|
|
461
460
|
r.get do end
|
462
461
|
|
@@ -468,7 +467,7 @@ matches any +POST+ request
|
|
468
467
|
|
469
468
|
If any arguments are given to the method, these match only
|
470
469
|
if the request method matches, all arguments match, and
|
471
|
-
only the path has been fully matched by the arguments
|
470
|
+
only the path has been fully matched by the arguments, so:
|
472
471
|
|
473
472
|
r.post "" do end
|
474
473
|
|
@@ -478,89 +477,91 @@ matches only +POST+ requests where the current path is +/+.
|
|
478
477
|
|
479
478
|
matches only +GET+ requests where the current path is +/a/b+.
|
480
479
|
|
481
|
-
The reason for this difference in behavior is that
|
482
|
-
providing any arguments, you probably don't want
|
483
|
-
for an exact match with the current path.
|
484
|
-
you do want, you can provide true as an argument:
|
480
|
+
The reason for this difference in behavior is that
|
481
|
+
if you are not providing any arguments, you probably don't want
|
482
|
+
to also test for an exact match with the current path.
|
483
|
+
If that is something you do want, you can provide +true+ as an argument:
|
485
484
|
|
486
485
|
r.on "foo" do
|
487
486
|
r.get true do # Matches GET /foo, not GET /foo/.*
|
488
487
|
end
|
489
488
|
end
|
490
489
|
|
491
|
-
If you want to match the request method
|
492
|
-
|
493
|
-
+r.on+ with the <tt>:method</tt> hash matcher:
|
490
|
+
If you want to match the request method
|
491
|
+
and do only a partial match on the request path,
|
492
|
+
you need to use +r.on+ with the <tt>:method</tt> hash matcher:
|
494
493
|
|
495
494
|
r.on "foo", :method=>:get do # Matches GET /foo(/.*)?
|
496
495
|
end
|
497
496
|
|
498
497
|
== Root Method
|
499
498
|
|
500
|
-
As displayed above, you can also use +r.root+ as a match method.
|
501
|
-
method matches +GET+ requests where the current path +/+.
|
502
|
-
similar to <tt>r.get ""</tt>,
|
499
|
+
As displayed above, you can also use +r.root+ as a match method.
|
500
|
+
This method matches +GET+ requests where the current path is +/+.
|
501
|
+
+r.root+ is similar to <tt>r.get ""</tt>,
|
502
|
+
except that it does not consume the +/+ from the path.
|
503
503
|
|
504
504
|
Unlike the other matching methods, +r.root+ takes no arguments.
|
505
505
|
|
506
|
-
Note that +r.root+ does not match if the path is empty
|
507
|
-
<tt>r.get true</tt> for that.
|
508
|
-
|
506
|
+
Note that +r.root+ does not match if the path is empty;
|
507
|
+
you should use <tt>r.get true</tt> for that.
|
508
|
+
If you want to match either the the empty path or +/+,
|
509
|
+
you can use <tt>r.get ["", true]</tt>.
|
509
510
|
|
510
|
-
Note that +r.root+
|
511
|
-
<tt>POST /</tt> requests, use <tt>r.post ''</tt>.
|
511
|
+
Note that +r.root+ only matches +GET+ requests.
|
512
|
+
So, to handle <tt>POST /</tt> requests, use <tt>r.post ''</tt>.
|
512
513
|
|
513
514
|
== Request and Response
|
514
515
|
|
515
|
-
While the request object is yielded to the route block,
|
516
|
-
available via the +request+ method.
|
517
|
-
is available via the +response+ method.
|
516
|
+
While the request object is yielded to the +route+ block,
|
517
|
+
it is also available via the +request+ method.
|
518
|
+
Likewise, the response object is available via the +response+ method.
|
518
519
|
|
519
|
-
The request object is an instance of a subclass of <tt>Rack::Request</tt
|
520
|
-
with some additional methods
|
521
|
-
instance of a subclass of <tt>Rack::Response</tt
|
522
|
-
methods.
|
520
|
+
The request object is an instance of a subclass of <tt>Rack::Request</tt>,
|
521
|
+
with some additional methods.
|
522
|
+
The response object is an instance of a subclass of <tt>Rack::Response</tt>,
|
523
|
+
with some additional methods.
|
523
524
|
|
524
|
-
If you want to extend the request and response objects with additional
|
525
|
-
|
526
|
-
methods, or via plugins.
|
525
|
+
If you want to extend the request and response objects with additional modules,
|
526
|
+
you can do so via the +request_module+ or +response_module+ methods, or via plugins.
|
527
527
|
|
528
528
|
== Pollution
|
529
529
|
|
530
|
-
Roda tries very hard to avoid polluting the scope of the +route+
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
530
|
+
Roda tries very hard to avoid polluting the scope of the +route+ block.
|
531
|
+
This should make it unlikely that Roda will cause namespace issues
|
532
|
+
with your application code:
|
533
|
+
|
534
|
+
- The only instance variables defined by default in the scope of the +route+ block
|
535
|
+
are <tt>@_request</tt> and <tt>@_response</tt>.
|
536
|
+
- The only methods defined (beyond the default methods for +Object+) are:
|
537
|
+
+env+, +opts+, +request+, +response+, +call+, +session+, and +_route+ (private).
|
538
|
+
- Constants inside the Roda namespace are all prefixed with +Roda+
|
539
|
+
(e.g., <tt>Roda::RodaRequest</tt>).
|
538
540
|
|
539
541
|
== Captures
|
540
542
|
|
541
|
-
You may have noticed that some matchers yield a value to the block.
|
542
|
-
for determining if a matcher will yield a value are simple:
|
543
|
+
You may have noticed that some matchers yield a value to the block.
|
544
|
+
The rules for determining if a matcher will yield a value are simple:
|
543
545
|
|
544
546
|
1. Regexp captures: <tt>/posts\/(\d+)-(.*)/</tt> will yield two values, corresponding to each capture.
|
545
547
|
2. String placeholders: <tt>"users/:id"</tt> will yield the value in the position of +:id+.
|
546
548
|
3. Symbols: +:foobar+ will yield if a segment is available.
|
547
549
|
4. File extensions: <tt>:extension=>"css"</tt> will yield the basename of the matched file.
|
548
|
-
5. Parameters: <tt>:param=>"user"</tt> will yield the value of the parameter user
|
550
|
+
5. Parameters: <tt>:param=>"user"</tt> will yield the value of the parameter +user+, if present.
|
549
551
|
|
550
|
-
The first case is important because it shows the underlying effect of
|
551
|
-
captures.
|
552
|
+
The first case is important because it shows the underlying effect of Regexp captures.
|
552
553
|
|
553
|
-
In the second case, the substring +:id+ gets replaced by <tt>([^\\/]+)</tt>
|
554
|
-
|
555
|
-
to the first form we saw.
|
554
|
+
In the second case, the substring +:id+ gets replaced by <tt>([^\\/]+)</tt>
|
555
|
+
and the Regexp becomes <tt>/users\/([^\/]+)/</tt> before performing the match.
|
556
|
+
Thus, it reverts to the first form we saw.
|
556
557
|
|
557
|
-
In the third case, the symbol, no matter what it says,
|
558
|
-
by <tt>/([^\\/]+)/</tt>, and again we are in presence of case 1.
|
558
|
+
In the third case, the symbol, no matter what it says,
|
559
|
+
gets replaced by <tt>/([^\\/]+)/</tt>, and again we are in presence of case 1.
|
559
560
|
|
560
561
|
The fourth case, again, reverts to the basic matcher: it generates the string
|
561
562
|
<tt>/([^\/]+?)\.#{ext}\z/</tt> before performing the match.
|
562
563
|
|
563
|
-
The fifth case is different: it checks if the
|
564
|
+
The fifth case is different: it checks if the parameter supplied is present
|
564
565
|
in the request (via POST or QUERY_STRING) and it pushes the value as a capture.
|
565
566
|
|
566
567
|
== Composition
|
@@ -588,19 +589,19 @@ inside a Roda app, using +r.run+:
|
|
588
589
|
|
589
590
|
run App.app
|
590
591
|
|
591
|
-
This will take any path starting with +/api+ and send it to +API+.
|
592
|
-
example, +API+ is a Roda app, but it could easily be
|
593
|
-
other Rack app.
|
592
|
+
This will take any path starting with +/api+ and send it to +API+.
|
593
|
+
In this example, +API+ is a Roda app, but it could easily be
|
594
|
+
a Sinatra, Rails, or other Rack app.
|
594
595
|
|
595
|
-
When you use +r.run+, Roda calls the given Rack app (+API+ in this
|
596
|
-
|
597
|
-
for the current application.
|
596
|
+
When you use +r.run+, Roda calls the given Rack app (+API+ in this case);
|
597
|
+
whatever the Rack app returns will be returned
|
598
|
+
as the response for the current application.
|
598
599
|
|
599
600
|
=== multi_route plugin
|
600
601
|
|
601
|
-
If you are just looking to split up the main route block up by branches,
|
602
|
-
should use the +multi_route+ plugin,
|
603
|
-
the route block:
|
602
|
+
If you are just looking to split up the main route block up by branches,
|
603
|
+
you should use the +multi_route+ plugin,
|
604
|
+
which keeps the current scope of the +route+ block:
|
604
605
|
|
605
606
|
class App < Roda
|
606
607
|
plugin :multi_route
|
@@ -620,20 +621,20 @@ the route block:
|
|
620
621
|
|
621
622
|
run App.app
|
622
623
|
|
623
|
-
This allows you to set instance variables in the main route block
|
624
|
-
have access to them inside the +api+ route block.
|
624
|
+
This allows you to set instance variables in the main +route+ block
|
625
|
+
and still have access to them inside the +api+ +route+ block.
|
625
626
|
|
626
627
|
== Testing
|
627
628
|
|
628
629
|
It is very easy to test Roda with {Rack::Test}[https://github.com/brynary/rack-test]
|
629
|
-
or {Capybara}[https://github.com/jnicklas/capybara].
|
630
|
-
{RSpec}[http://rspec.info].
|
631
|
-
RSpec is installed.
|
630
|
+
or {Capybara}[https://github.com/jnicklas/capybara].
|
631
|
+
Roda's own tests use {RSpec}[http://rspec.info].
|
632
|
+
The default Rake task will run the specs for Roda, if RSpec is installed.
|
632
633
|
|
633
634
|
== Settings
|
634
635
|
|
635
|
-
Each Roda app can store settings in the +opts+ hash.
|
636
|
-
inherited if you happen to subclass +Roda+.
|
636
|
+
Each Roda app can store settings in the +opts+ hash.
|
637
|
+
The settings are inherited if you happen to subclass +Roda+.
|
637
638
|
|
638
639
|
Roda.opts[:layout] = "guest"
|
639
640
|
|
@@ -645,26 +646,27 @@ inherited if you happen to subclass +Roda+.
|
|
645
646
|
Users.opts[:layout] # => 'guest'
|
646
647
|
Admin.opts[:layout] # => 'admin'
|
647
648
|
|
648
|
-
Feel free to store whatever you find convenient.
|
649
|
-
Roda only does a shallow clone of the settings.
|
650
|
-
and plan to mutate them in subclasses,
|
651
|
-
structures inside +Roda.inherited+
|
652
|
-
|
653
|
-
|
654
|
-
|
649
|
+
Feel free to store whatever you find convenient.
|
650
|
+
Note that when subclassing, Roda only does a shallow clone of the settings.
|
651
|
+
If you store nested structures and plan to mutate them in subclasses,
|
652
|
+
it is your responsibility to dup the nested structures inside +Roda.inherited+
|
653
|
+
(making sure to call +super+).
|
654
|
+
The plugins that ship with Roda all handle this.
|
655
|
+
Also, note that this means that modifications to the parent class
|
656
|
+
made after subclassing do _not_ affect the subclass.
|
655
657
|
|
656
658
|
== Rendering
|
657
659
|
|
658
|
-
Roda ships with a +render+ plugin that provides helpers for rendering templates.
|
659
|
-
{Tilt}[https://github.com/rtomayko/tilt],
|
660
|
-
|
660
|
+
Roda ships with a +render+ plugin that provides helpers for rendering templates.
|
661
|
+
It uses {Tilt}[https://github.com/rtomayko/tilt],
|
662
|
+
a gem that interfaces with many template engines.
|
663
|
+
The +erb+ engine is used by default.
|
661
664
|
|
662
|
-
Note that in order to use this plugin you need to have Tilt installed,
|
663
|
-
with the templating engines you want to use.
|
665
|
+
Note that in order to use this plugin you need to have Tilt installed,
|
666
|
+
along with the templating engines you want to use.
|
664
667
|
|
665
668
|
This plugin adds the +render+ and +view+ methods, for rendering templates.
|
666
|
-
|
667
|
-
attempt to render the template inside the default layout template, where
|
669
|
+
By default, +view+ will render the template inside the default layout template;
|
668
670
|
+render+ will just render the template.
|
669
671
|
|
670
672
|
class App < Roda
|
@@ -674,8 +676,8 @@ attempt to render the template inside the default layout template, where
|
|
674
676
|
@var = '1'
|
675
677
|
|
676
678
|
r.is "render" do
|
677
|
-
# Renders the views/home.erb template, which will have access to
|
678
|
-
# instance variable @var, as well as local variable content
|
679
|
+
# Renders the views/home.erb template, which will have access to
|
680
|
+
# the instance variable @var, as well as local variable content.
|
679
681
|
render("home", :locals=>{:content => "hello, world"})
|
680
682
|
end
|
681
683
|
|
@@ -706,9 +708,10 @@ You can override the default rendering options by passing a hash to the plugin:
|
|
706
708
|
|
707
709
|
== Sessions
|
708
710
|
|
709
|
-
By default, Roda doesn't turn on sessions,
|
710
|
-
|
711
|
-
use the <tt>Rack::Session::Cookie</tt> middleware
|
711
|
+
By default, Roda doesn't turn on sessions,
|
712
|
+
but most users are going to want to turn on session support.
|
713
|
+
The simplest way to do this is to use the <tt>Rack::Session::Cookie</tt> middleware
|
714
|
+
that comes with Rack:
|
712
715
|
|
713
716
|
require "roda"
|
714
717
|
|
@@ -718,39 +721,39 @@ use the <tt>Rack::Session::Cookie</tt> middleware that comes with rack:
|
|
718
721
|
|
719
722
|
== Security
|
720
723
|
|
721
|
-
Web application security is a very large topic,
|
722
|
-
things you can do with Roda
|
723
|
-
vulnerabilities.
|
724
|
+
Web application security is a very large topic,
|
725
|
+
but here are some things you can do with Roda
|
726
|
+
to prevent some common web application vulnerabilities.
|
724
727
|
|
725
728
|
=== Session Security
|
726
729
|
|
727
|
-
If you are using sessions, you should also always set a session
|
728
|
-
|
729
|
-
secret is not disclosed,
|
730
|
-
|
731
|
-
|
730
|
+
If you are using sessions, you should also always set a session secret,
|
731
|
+
using the +:secret+ option as shown above.
|
732
|
+
Make sure that this secret is not disclosed,
|
733
|
+
because if an attacker knows the +:secret+ value,
|
734
|
+
they can inject arbitrary session values.
|
735
|
+
In the worst case scenario, this can lead to remote code execution.
|
732
736
|
|
733
|
-
Keep in mind that with <tt>Rack::Session::Cookie</tt>,
|
734
|
-
the session cookie is not encrypted,
|
735
|
-
|
736
|
-
secret.
|
737
|
+
Keep in mind that with <tt>Rack::Session::Cookie</tt>,
|
738
|
+
the content in the session cookie is not encrypted,
|
739
|
+
just signed to prevent tampering.
|
740
|
+
This means you should not store any secret data in the session.
|
737
741
|
|
738
742
|
=== Cross Site Request Forgery (CSRF)
|
739
743
|
|
740
744
|
CSRF can be prevented by using the +csrf+ plugin that ships with Roda,
|
741
|
-
which uses the {rack_csrf}[https://github.com/baldowl/rack_csrf]
|
742
|
-
|
743
|
-
html as appropriate.
|
745
|
+
which uses the {rack_csrf}[https://github.com/baldowl/rack_csrf] library.
|
746
|
+
Just make sure that you include the CSRF token tags in your HTML, as appropriate.
|
744
747
|
|
745
|
-
It's also possible to use the <tt>Rack::Csrf</tt> middleware directly
|
748
|
+
It's also possible to use the <tt>Rack::Csrf</tt> middleware directly;
|
746
749
|
you don't have to use the +csrf+ plugin.
|
747
750
|
|
748
751
|
=== Cross Site Scripting (XSS)
|
749
752
|
|
750
753
|
The easiest way to prevent XSS with Roda is to use a template library
|
751
|
-
that automatically escapes output by default.
|
752
|
-
to the render plugin sets the
|
753
|
-
default, so that in your templates:
|
754
|
+
that automatically escapes output by default.
|
755
|
+
The +:escape+ option to the +render+ plugin sets the ERb template processor
|
756
|
+
to escape by default, so that in your templates:
|
754
757
|
|
755
758
|
<%= '<>' %> # outputs <>
|
756
759
|
<%== '<>' %> # outputs <>
|
@@ -759,10 +762,10 @@ This support requires {Erubis}[http://www.kuwata-lab.com/erubis/].
|
|
759
762
|
|
760
763
|
=== Other
|
761
764
|
|
762
|
-
For prevention of some other vulnerabilities,
|
763
|
-
directory traversal, session hijacking, and IP spoofing,
|
764
|
-
{Rack::Protection}[https://github.com/rkh/rack-protection]
|
765
|
-
a
|
765
|
+
For prevention of some other vulnerabilities,
|
766
|
+
such as click-jacking, directory traversal, session hijacking, and IP spoofing,
|
767
|
+
consider using {Rack::Protection}[https://github.com/rkh/rack-protection].
|
768
|
+
This is a Rack middleware that can be added in the usual way:
|
766
769
|
|
767
770
|
require 'roda'
|
768
771
|
require 'rack/protection'
|
@@ -773,63 +776,17 @@ a rack middleware that can be added the usual way:
|
|
773
776
|
|
774
777
|
== Plugins
|
775
778
|
|
776
|
-
Roda
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
backtracking_array :: Allows array matchers to backtrack if later matchers
|
788
|
-
do not match.
|
789
|
-
caching :: Adds request and response methods related to http caching.
|
790
|
-
chunked :: Adds support for streaming template responses using
|
791
|
-
Transfer-Encoding: chunked.
|
792
|
-
content_for :: Allows storage of content in one template and retrieval of
|
793
|
-
that content in a different template.
|
794
|
-
csrf :: Adds CSRF protection and helper methods using
|
795
|
-
{rack_csrf}[https://github.com/baldowl/rack_csrf].
|
796
|
-
default_headers :: Override the default response headers used.
|
797
|
-
error_email :: Adds an +error_email+ method that can be used to email when
|
798
|
-
an exception is raised.
|
799
|
-
error_handler :: Adds a +error+ block that is called for all responses that
|
800
|
-
raise exceptions.
|
801
|
-
flash :: Adds a flash handler.
|
802
|
-
h :: Adds h method for html escaping.
|
803
|
-
halt :: Augments request#halt method to take status and/or body or status,
|
804
|
-
headers, and body.
|
805
|
-
head :: Treat HEAD requests like GET requests with an empty response body.
|
806
|
-
header_matchers :: Adds host, header, and accept hash matchers.
|
807
|
-
hooks :: Adds before and after methods to run code before and after requests.
|
808
|
-
indifferent_params :: Adds params method with indifferent access to params,
|
809
|
-
allowing use of symbol keys for accessing params.
|
810
|
-
json :: Allows match blocks to return arrays and hashes, using a json
|
811
|
-
representation as the response body.
|
812
|
-
middleware :: Allows the Roda app to be used as a rack middleware, calling the
|
813
|
-
next middleware if no route matches.
|
814
|
-
multi_route :: Adds the ability for multiple named route blocks, with the
|
815
|
-
ability to dispatch to them add any point in the main route block.
|
816
|
-
not_allowed :: Adds support for automatically returning 405 Method Not Allowed
|
817
|
-
responses.
|
818
|
-
not_found :: Adds a +not_found+ block that is called for all 404 responses
|
819
|
-
without bodies.
|
820
|
-
pass :: Adds a pass method allowing you to skip the current +r.on+ block as if
|
821
|
-
it did not match.
|
822
|
-
path :: Adds support for named paths.
|
823
|
-
per_thread_caching :: Switches the thread-safe cache from a shared cache to a
|
824
|
-
per-thread cache.
|
825
|
-
render :: Adds support for rendering templates via tilt, as described above.
|
826
|
-
render_each :: Render a template for each value in an enumerable.
|
827
|
-
streaming :: Adds support for streaming responses.
|
828
|
-
symbol_matchers :: Adds support for symbol-specific matching regexps.
|
829
|
-
symbol_views :: Allows match blocks to return template name symbols, uses the
|
830
|
-
template view as the response body.
|
831
|
-
view_subdirs :: Allows for setting a view subdirectory to use on a per-request
|
832
|
-
basis.
|
779
|
+
By design, Roda has a very small core, providing only the essentials.
|
780
|
+
All nonessential features are added via plugins.
|
781
|
+
This is why Roda is referred to as a routing tree web framework toolkit.
|
782
|
+
Using a combination of Roda plugins,
|
783
|
+
you can build the routing tree web framework that needs your needs.
|
784
|
+
|
785
|
+
Roda's plugins can override any Roda method and call +super+
|
786
|
+
to get the default behavior, which makes Roda very extensible.
|
787
|
+
|
788
|
+
{Roda ships with a large number of plugins}[http://roda.jeremyevans.net/documentation.html#included-plugins],
|
789
|
+
and {some other libraries ship with support for Roda}[http://roda.jeremyevans.net/documentation.html#external].
|
833
790
|
|
834
791
|
=== External Plugins
|
835
792
|
|
@@ -841,8 +798,8 @@ autoforme :: Adds support for easily creating a simple administrative front
|
|
841
798
|
|
842
799
|
=== How to create plugins
|
843
800
|
|
844
|
-
Authoring your own plugins is pretty straightforward.
|
845
|
-
which may contain any of the following modules:
|
801
|
+
Authoring your own plugins is pretty straightforward.
|
802
|
+
Plugins are just modules, which may contain any of the following modules:
|
846
803
|
|
847
804
|
InstanceMethods :: module included in the Roda class
|
848
805
|
ClassMethods :: module that extends the Roda class
|
@@ -851,16 +808,16 @@ RequestClassMethods :: module extending the class of the request
|
|
851
808
|
ResponseMethods :: module included in the class of the response
|
852
809
|
ResponseClassMethods :: module extending the class of the response
|
853
810
|
|
854
|
-
If the plugin responds to +load_dependencies+, it will be called first,
|
855
|
-
be used if the plugin depends on another plugin.
|
811
|
+
If the plugin responds to +load_dependencies+, it will be called first,
|
812
|
+
and should be used if the plugin depends on another plugin.
|
856
813
|
|
857
|
-
If the plugin responds to +configure+, it will be called last,
|
858
|
-
used to configure the plugin.
|
814
|
+
If the plugin responds to +configure+, it will be called last,
|
815
|
+
and should be used to configure the plugin.
|
859
816
|
|
860
|
-
Both +load_dependencies+ and +configure+ are called
|
861
|
-
and block given to the plugin call.
|
817
|
+
Both +load_dependencies+ and +configure+ are called
|
818
|
+
with the additional arguments and block that was given to the plugin call.
|
862
819
|
|
863
|
-
So a simple plugin to add an instance method would be:
|
820
|
+
So, a simple plugin to add an instance method would be:
|
864
821
|
|
865
822
|
module MarkdownHelper
|
866
823
|
module InstanceMethods
|
@@ -874,14 +831,15 @@ So a simple plugin to add an instance method would be:
|
|
874
831
|
|
875
832
|
=== Registering plugins
|
876
833
|
|
877
|
-
If you want to ship a Roda plugin in a gem,
|
878
|
-
Roda load it automatically via <tt>Roda.plugin :plugin_name</tt>,
|
879
|
-
place it where it can be required via +roda/plugins/plugin_name
|
880
|
-
then have the file register it as a plugin via
|
881
|
-
<tt>Roda::RodaPlugins.register_plugin</tt>.
|
882
|
-
that you store your plugin module
|
834
|
+
If you want to ship a Roda plugin in a gem,
|
835
|
+
but still have Roda load it automatically via <tt>Roda.plugin :plugin_name</tt>,
|
836
|
+
you should place it where it can be required via +roda/plugins/plugin_name+
|
837
|
+
and then have the file register it as a plugin via
|
838
|
+
<tt>Roda::RodaPlugins.register_plugin</tt>.
|
839
|
+
It's recommended, but not required, that you store your plugin module
|
840
|
+
in the <tt>Roda::RodaPlugins</tt> namespace:
|
883
841
|
|
884
|
-
|
842
|
+
class Roda
|
885
843
|
module RodaPlugins
|
886
844
|
module Markdown
|
887
845
|
module InstanceMethods
|
@@ -895,10 +853,11 @@ that you store your plugin module in the <tt>Roda::RodaPlugins</tt> namespace:
|
|
895
853
|
end
|
896
854
|
end
|
897
855
|
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
(e.g
|
856
|
+
To avoid namespace pollution,
|
857
|
+
you should avoid creating your module directly in the +Roda+ namespace.
|
858
|
+
Additionally, any instance variables created inside +InstanceMethods+
|
859
|
+
should be prefixed with an underscore (e.g., <tt>@_variable</tt>)
|
860
|
+
to avoid polluting the scope.
|
902
861
|
|
903
862
|
== License
|
904
863
|
|