roda 1.3.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +24 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +7 -4
- data/doc/release_notes/2.0.0.txt +75 -0
- data/lib/roda/plugins/assets.rb +2 -2
- data/lib/roda/plugins/backtracking_array.rb +2 -11
- data/lib/roda/plugins/caching.rb +4 -2
- data/lib/roda/plugins/chunked.rb +4 -9
- data/lib/roda/plugins/class_level_routing.rb +1 -3
- data/lib/roda/plugins/default_headers.rb +1 -2
- data/lib/roda/plugins/error_email.rb +4 -14
- data/lib/roda/plugins/error_handler.rb +4 -4
- data/lib/roda/plugins/flash.rb +1 -3
- data/lib/roda/plugins/halt.rb +24 -5
- data/lib/roda/plugins/header_matchers.rb +2 -7
- data/lib/roda/plugins/hooks.rb +1 -3
- data/lib/roda/plugins/json.rb +4 -2
- data/lib/roda/plugins/mailer.rb +8 -7
- data/lib/roda/plugins/middleware.rb +21 -9
- data/lib/roda/plugins/not_found.rb +3 -3
- data/lib/roda/plugins/padrino_render.rb +60 -0
- data/lib/roda/plugins/param_matchers.rb +3 -3
- data/lib/roda/plugins/path.rb +2 -1
- data/lib/roda/plugins/render.rb +55 -37
- data/lib/roda/plugins/render_each.rb +4 -2
- data/lib/roda/plugins/static_path_info.rb +2 -63
- data/lib/roda/plugins/streaming.rb +4 -2
- data/lib/roda/version.rb +2 -2
- data/lib/roda.rb +71 -172
- data/spec/matchers_spec.rb +31 -82
- data/spec/plugin/assets_spec.rb +6 -6
- data/spec/plugin/error_handler_spec.rb +23 -0
- data/spec/plugin/halt_spec.rb +39 -0
- data/spec/plugin/middleware_spec.rb +7 -0
- data/spec/plugin/padrino_render_spec.rb +57 -0
- data/spec/plugin/render_each_spec.rb +1 -1
- data/spec/plugin/render_spec.rb +59 -5
- data/spec/request_spec.rb +0 -12
- data/spec/response_spec.rb +0 -24
- data/spec/views/_test.erb +1 -0
- metadata +7 -4
- data/lib/roda/plugins/delete_nil_headers.rb +0 -34
- data/spec/module_spec.rb +0 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 035bdb54b566e77c48d1332fdd7ebf396a849193
|
4
|
+
data.tar.gz: 682a8a39b4dc9e25d1395102890f8650f750e12d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c219eb5e94a58c04d90256d87cbe5a86c1dc8c972666cbf61d97a34c5f3b4bc8d6b7de56fdee7aa65ef01ba70d7f4052d479c80211f49201dc94705e17e69308
|
7
|
+
data.tar.gz: ba8d204672492c04de406eb87d276c59e92af57047c4cc6bb479e7fc9efb1785fd314df682e496bc905327450571497f77581fef04a29346159de747172f07bc
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,27 @@
|
|
1
|
+
= 2.0.0 (2015-02-13)
|
2
|
+
|
3
|
+
* Allow Roda app to be used as a regular rack app even when using the middleware plugin (jeremyevans)
|
4
|
+
|
5
|
+
* Make render plugin :layout option always be true or false (jeremyevans)
|
6
|
+
|
7
|
+
* Make :layout=>true view option use the default layout (jeremyevans)
|
8
|
+
|
9
|
+
* Make error_handler plugin rescue ScriptError in addition to StandardError (jeremyevans)
|
10
|
+
|
11
|
+
* Make halt plugin integrate with symbol_views, json, and similar plugins (jeremyevans)
|
12
|
+
|
13
|
+
* Add padrino_render plugin, adding render/partial methods that work similar to Padrino (jeremyevans)
|
14
|
+
|
15
|
+
* Add Roda#render_template private method for template rendering, for use by plugins (jeremyevans)
|
16
|
+
|
17
|
+
* Make Roda#initialize take env hash, #call take route_block, remove private #_route (jeremyevans)
|
18
|
+
|
19
|
+
* Remove keep_remaining_path/update_remaining_path private request methods (jeremyevans)
|
20
|
+
|
21
|
+
* Don't modify SCRIPT_NAME/PATH_INFO during routing, merging static_path_info plugin into core (jeremyevans)
|
22
|
+
|
23
|
+
* Remove code deprecated in Roda 1.3.0 (jeremyevans)
|
24
|
+
|
1
25
|
= 1.3.0 (2015-01-13)
|
2
26
|
|
3
27
|
* Make static_path_info plugin restore original SCRIPT_NAME/PATH_INFO before returning from r.run (jeremyevans)
|
data/MIT-LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -292,7 +292,8 @@ Colons that are not followed by a <tt>\\w</tt> character are matched literally:
|
|
292
292
|
|
293
293
|
":/a" # matches "/:/a"
|
294
294
|
|
295
|
-
Note that
|
295
|
+
Note that other than colons, strings do no handle regular expression syntax, the
|
296
|
+
string is matched verbatim:
|
296
297
|
|
297
298
|
"\\d+(/\\w+)?" # matches "/\d+(/\w+)?"
|
298
299
|
"\\d+(/\\w+)?" # does not match "/123/abc"
|
@@ -499,12 +500,14 @@ you can use the module_include plugin.
|
|
499
500
|
|
500
501
|
Roda tries very hard to avoid polluting the scope of the +route+ block.
|
501
502
|
This should make it unlikely that Roda will cause namespace issues
|
502
|
-
with your application code. Some of the things Roda does
|
503
|
+
with your application code. Some of the things Roda does:
|
503
504
|
|
504
505
|
- The only instance variables defined by default in the scope of the +route+ block
|
505
|
-
are <tt>@_request</tt> and <tt>@_response</tt>.
|
506
|
+
are <tt>@_request</tt> and <tt>@_response</tt>. All instance variables in the
|
507
|
+
scope of the +route+ block used by plugins that ship with Roda are prefixed
|
508
|
+
with an underscore.
|
506
509
|
- The only methods defined (beyond the default methods for +Object+) are:
|
507
|
-
+env+, +opts+, +request+, +response+,
|
510
|
+
+call+, +env+, +opts+, +request+, +response+, and +session+.
|
508
511
|
- Constants inside the Roda namespace are all prefixed with +Roda+
|
509
512
|
(e.g., <tt>Roda::RodaRequest</tt>).
|
510
513
|
|
@@ -0,0 +1,75 @@
|
|
1
|
+
= Backwards Compatibility
|
2
|
+
|
3
|
+
* RodaResponse#set_cookie and #delete_cookie have been removed.
|
4
|
+
|
5
|
+
* Roda.request_module and .response_module have been removed.
|
6
|
+
|
7
|
+
* Roda.hash_matcher has been removed.
|
8
|
+
|
9
|
+
* The :extension hash matcher has been removed.
|
10
|
+
|
11
|
+
* The :param and :param! hash matchers have been removed.
|
12
|
+
|
13
|
+
* RodaRequest#full_path_info has been removed.
|
14
|
+
|
15
|
+
* The :opts render plugin option is no longer respected, Use the
|
16
|
+
:template_opts option instead.
|
17
|
+
|
18
|
+
* Plugin option hashes for the chunked, default_headers,
|
19
|
+
error_email, and render plugins are now frozen.
|
20
|
+
|
21
|
+
* The :header hash matcher in the header_matchers plugin now
|
22
|
+
yields the header value to the block.
|
23
|
+
|
24
|
+
* Roda.json_result_classes in the json plugin is now frozen.
|
25
|
+
|
26
|
+
* The PATH_INFO and SCRIPT_NAME env variables are no longer modified
|
27
|
+
during routing.
|
28
|
+
|
29
|
+
* Roda#initialize now takes an env hash, and #call now takes the
|
30
|
+
route block. The private #_route method has been removed.
|
31
|
+
|
32
|
+
* RodaRequest#keep_remaining_path/#updating_remaining_path private
|
33
|
+
methods have been removed.
|
34
|
+
|
35
|
+
* The render plugin's :layout option is now always set to true or
|
36
|
+
false, specifying whether a layout should be used by default.
|
37
|
+
The template used for a layout is now located as the :template
|
38
|
+
option inside :layout_opts.
|
39
|
+
|
40
|
+
= New Plugins
|
41
|
+
|
42
|
+
* A padrino_render plugin has been added, which adds render/partial
|
43
|
+
methods that work similarly to Padrino's.
|
44
|
+
|
45
|
+
= Other New Features
|
46
|
+
|
47
|
+
* A Roda#render_template private method has been added to the render
|
48
|
+
plugin. All internal users of render should switch to calling
|
49
|
+
render_template.
|
50
|
+
|
51
|
+
* The halt plugin now integrates with the symbol_views and json
|
52
|
+
plugins, allowing things like:
|
53
|
+
|
54
|
+
r.halt(:template)
|
55
|
+
r.halt('key'=>'value')
|
56
|
+
|
57
|
+
= Other Improvements
|
58
|
+
|
59
|
+
* The error_handler plugin now rescues ScriptError in addition to
|
60
|
+
StandardError. This handles SyntaxError (raised by ERB),
|
61
|
+
LoadError (raised by require), and NotImplementedError (raised
|
62
|
+
by TSort).
|
63
|
+
|
64
|
+
* Using a :layout=>true option to the render plugin's view method
|
65
|
+
now uses the default layout template, instead of a template named
|
66
|
+
'true'. It can be used to force a layout even if the render
|
67
|
+
plugin has been configured to not use a layout by default.
|
68
|
+
|
69
|
+
* Roda apps that use the middleware plugin can now be used as regular
|
70
|
+
rack apps. Previously, using the middleware plugin made it
|
71
|
+
impossible to use the app as a regular rack app.
|
72
|
+
|
73
|
+
* Roda#request and #response are now faster.
|
74
|
+
|
75
|
+
* Roda avoids creating unnecessary hashes in more places now.
|
data/lib/roda/plugins/assets.rb
CHANGED
@@ -396,7 +396,7 @@ class Roda
|
|
396
396
|
def compile_assets_files(files, type, dirs)
|
397
397
|
dirs = nil if dirs && dirs.empty?
|
398
398
|
o = assets_opts
|
399
|
-
app =
|
399
|
+
app = allocate
|
400
400
|
|
401
401
|
content = files.map do |file|
|
402
402
|
file = "#{dirs.join('/')}/#{file}" if dirs && o[:group_subdirs]
|
@@ -548,7 +548,7 @@ class Roda
|
|
548
548
|
# Render the given asset file using the render plugin, with the given options.
|
549
549
|
# +file+ should be the relative path to the file from the current directory.
|
550
550
|
def render_asset_file(file, options)
|
551
|
-
|
551
|
+
render_template({:path => file}, options)
|
552
552
|
end
|
553
553
|
end
|
554
554
|
|
@@ -36,11 +36,7 @@ class Roda
|
|
36
36
|
def _match_array(arg, rest=nil)
|
37
37
|
return super unless rest
|
38
38
|
|
39
|
-
|
40
|
-
e = @env
|
41
|
-
script = e[SCRIPT_NAME]
|
42
|
-
path = e[PATH_INFO]
|
43
|
-
end
|
39
|
+
path = @remaining_path
|
44
40
|
captures = @captures
|
45
41
|
caps = captures.dup
|
46
42
|
arg.each do |v|
|
@@ -55,12 +51,7 @@ class Roda
|
|
55
51
|
|
56
52
|
# Matching all remaining elements failed, reset state
|
57
53
|
captures.replace(caps)
|
58
|
-
|
59
|
-
@remaining_path = path
|
60
|
-
else
|
61
|
-
e[SCRIPT_NAME] = script
|
62
|
-
e[PATH_INFO] = path
|
63
|
-
end
|
54
|
+
@remaining_path = path
|
64
55
|
end
|
65
56
|
end
|
66
57
|
false
|
data/lib/roda/plugins/caching.rb
CHANGED
@@ -66,6 +66,8 @@ class Roda
|
|
66
66
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
67
67
|
# OTHER DEALINGS IN THE SOFTWARE.
|
68
68
|
module Caching
|
69
|
+
OPTS = {}.freeze
|
70
|
+
|
69
71
|
module RequestMethods
|
70
72
|
LAST_MODIFIED = 'Last-Modified'.freeze
|
71
73
|
HTTP_IF_NONE_MATCH = 'HTTP_IF_NONE_MATCH'.freeze
|
@@ -118,7 +120,7 @@ class Roda
|
|
118
120
|
#
|
119
121
|
# When the current request includes an If-Match header with a
|
120
122
|
# etag that doesn't match, immediately returns a response with a 412 status.
|
121
|
-
def etag(value, opts=
|
123
|
+
def etag(value, opts=OPTS)
|
122
124
|
# Before touching this code, please double check RFC 2616 14.24 and 14.26.
|
123
125
|
weak = opts[:weak]
|
124
126
|
new_resource = opts.fetch(:new_resource){post?}
|
@@ -194,7 +196,7 @@ class Roda
|
|
194
196
|
# be an integer number of seconds that the current request should be
|
195
197
|
# cached for. Also sets the Expires header, useful if you have
|
196
198
|
# HTTP 1.0 clients (Cache-Control is an HTTP 1.1 header).
|
197
|
-
def expires(max_age, opts=
|
199
|
+
def expires(max_age, opts=OPTS)
|
198
200
|
cache_control(opts.merge(:max_age=>max_age))
|
199
201
|
self[EXPIRES] = (Time.now + max_age).httpdate
|
200
202
|
end
|
data/lib/roda/plugins/chunked.rb
CHANGED
@@ -142,8 +142,7 @@ class Roda
|
|
142
142
|
def self.configure(app, opts=OPTS)
|
143
143
|
app.opts[:chunk_by_default] = opts[:chunk_by_default]
|
144
144
|
if opts[:headers]
|
145
|
-
app.opts[:chunk_headers] = (app.opts[:chunk_headers] || {}).merge(opts[:headers])
|
146
|
-
app.opts[:chunk_headers].extend(RodaDeprecateMutation)
|
145
|
+
app.opts[:chunk_headers] = (app.opts[:chunk_headers] || {}).merge(opts[:headers]).freeze
|
147
146
|
end
|
148
147
|
end
|
149
148
|
|
@@ -241,15 +240,11 @@ class Roda
|
|
241
240
|
@_out_buf = ''
|
242
241
|
end
|
243
242
|
|
244
|
-
if
|
245
|
-
|
246
|
-
layout_opts = render_opts[:layout_opts].merge(layout_opts)
|
247
|
-
end
|
248
|
-
|
249
|
-
@_out_buf = render(layout, layout_opts||OPTS) do
|
243
|
+
if layout_opts = view_layout_opts(opts)
|
244
|
+
@_out_buf = render_template(layout_opts) do
|
250
245
|
flush
|
251
246
|
block.call if block
|
252
|
-
yield opts[:content] ||
|
247
|
+
yield opts[:content] || render_template(template, opts)
|
253
248
|
nil
|
254
249
|
end
|
255
250
|
else
|
@@ -70,11 +70,9 @@ class Roda
|
|
70
70
|
end
|
71
71
|
|
72
72
|
module InstanceMethods
|
73
|
-
private
|
74
|
-
|
75
73
|
# If the normal routing tree doesn't handle an action, try each class level route
|
76
74
|
# to see if it matches.
|
77
|
-
def
|
75
|
+
def call
|
78
76
|
result = super
|
79
77
|
|
80
78
|
if result[0] == 404 && (v = result[2]).is_a?(Array) && v.empty?
|
@@ -20,8 +20,7 @@ class Roda
|
|
20
20
|
module DefaultHeaders
|
21
21
|
# Merge the given headers into the existing default headers, if any.
|
22
22
|
def self.configure(app, headers={})
|
23
|
-
app.opts[:default_headers] = (app.opts[:default_headers] || {}).merge(headers)
|
24
|
-
app.opts[:default_headers].extend(RodaDeprecateMutation)
|
23
|
+
app.opts[:default_headers] = (app.opts[:default_headers] || {}).merge(headers).freeze
|
25
24
|
end
|
26
25
|
|
27
26
|
module ClassMethods
|
@@ -28,6 +28,7 @@ class Roda
|
|
28
28
|
# for low traffic web applications. For high traffic web applications,
|
29
29
|
# use an error reporting service instead of this plugin.
|
30
30
|
module ErrorEmail
|
31
|
+
OPTS = {}.freeze
|
31
32
|
DEFAULTS = {
|
32
33
|
:headers=>{},
|
33
34
|
:host=>'localhost',
|
@@ -74,7 +75,7 @@ END
|
|
74
75
|
}
|
75
76
|
|
76
77
|
# Set default opts for plugin. See ErrorEmail module RDoc for options.
|
77
|
-
def self.configure(app, opts=
|
78
|
+
def self.configure(app, opts=OPTS)
|
78
79
|
email_opts = app.opts[:error_email] ||= DEFAULTS
|
79
80
|
email_opts = email_opts.merge(opts)
|
80
81
|
email_opts[:headers] = email_opts[:headers].dup
|
@@ -82,19 +83,8 @@ END
|
|
82
83
|
raise RodaError, "must provide :to and :from options to error_email plugin"
|
83
84
|
end
|
84
85
|
app.opts[:error_email] = email_opts
|
85
|
-
app.opts[:error_email].
|
86
|
-
app.opts[:error_email]
|
87
|
-
end
|
88
|
-
|
89
|
-
module ClassMethods
|
90
|
-
# Dup the error email opts in the subclass so changes to the subclass do not affect
|
91
|
-
# the superclass.
|
92
|
-
def inherited(subclass)
|
93
|
-
super
|
94
|
-
opts = subclass.opts[:error_email].dup
|
95
|
-
opts[:headers] = opts[:headers].dup.extend(RodaDeprecateMutation)
|
96
|
-
subclass.opts[:error_email] = opts.extend(RodaDeprecateMutation)
|
97
|
-
end
|
86
|
+
app.opts[:error_email][:headers].freeze
|
87
|
+
app.opts[:error_email].freeze
|
98
88
|
end
|
99
89
|
|
100
90
|
module InstanceMethods
|
@@ -46,18 +46,18 @@ class Roda
|
|
46
46
|
end
|
47
47
|
|
48
48
|
module InstanceMethods
|
49
|
-
private
|
50
|
-
|
51
49
|
# If an error occurs, set the response status to 500 and call
|
52
50
|
# the error handler.
|
53
|
-
def
|
51
|
+
def call
|
54
52
|
super
|
55
|
-
rescue => e
|
53
|
+
rescue StandardError, ScriptError => e
|
56
54
|
res = @_response = self.class::RodaResponse.new
|
57
55
|
res.status = 500
|
58
56
|
super{handle_error(e)}
|
59
57
|
end
|
60
58
|
|
59
|
+
private
|
60
|
+
|
61
61
|
# By default, have the error handler reraise the error, so using
|
62
62
|
# the plugin without installing an error handler doesn't change
|
63
63
|
# behavior.
|
data/lib/roda/plugins/flash.rb
CHANGED
@@ -87,11 +87,9 @@ class Roda
|
|
87
87
|
@_flash ||= FlashHash.new(session[KEY])
|
88
88
|
end
|
89
89
|
|
90
|
-
private
|
91
|
-
|
92
90
|
# If the routing doesn't raise an error, rotate the flash
|
93
91
|
# hash in the session so the next request has access to it.
|
94
|
-
def
|
92
|
+
def call
|
95
93
|
res = super
|
96
94
|
|
97
95
|
if f = @_flash
|
data/lib/roda/plugins/halt.rb
CHANGED
@@ -35,6 +35,23 @@ class Roda
|
|
35
35
|
# arguments and providing them as a single rack response array. With a rack response array,
|
36
36
|
# the values are used directly, while with 3 arguments, the headers given are merged into
|
37
37
|
# the existing headers and the given body is written to the existing response body.
|
38
|
+
#
|
39
|
+
# If using other plugins that recognize additional types of match block responses, such
|
40
|
+
# as +symbol_views+ and +json+, you can pass those additional types to +r.halt+:
|
41
|
+
#
|
42
|
+
# plugin :halt
|
43
|
+
# plugin :symbol_views
|
44
|
+
# plugin :json
|
45
|
+
# route do |r|
|
46
|
+
# r.halt(:template)
|
47
|
+
# r.halt(500, [{'error'=>'foo'}])
|
48
|
+
# r.halt(500, 'header=>'value', :other_template)
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# Note that when using the +json+ plugin with the +halt+ plugin, you cannot return a
|
52
|
+
# array as a single argument and have it be converted to json, since it would be interpreted
|
53
|
+
# as a rack response. You must use call +r.halt+ with either two or three argument forms
|
54
|
+
# in that case.
|
38
55
|
module Halt
|
39
56
|
module RequestMethods
|
40
57
|
# Expand default halt method to handle status codes, headers, and bodies. See Halt.
|
@@ -45,22 +62,24 @@ class Roda
|
|
45
62
|
case v = res[0]
|
46
63
|
when Integer
|
47
64
|
response.status = v
|
48
|
-
when String
|
49
|
-
response.write v
|
50
65
|
when Array
|
51
66
|
throw :halt, v
|
52
67
|
else
|
53
|
-
|
68
|
+
if result = block_result_body(v)
|
69
|
+
response.write(result)
|
70
|
+
else
|
71
|
+
raise Roda::RodaError, "singular argument given to #halt not handled: #{v.inspect}"
|
72
|
+
end
|
54
73
|
end
|
55
74
|
when 2
|
56
75
|
resp = response
|
57
76
|
resp.status = res[0]
|
58
|
-
resp.write
|
77
|
+
resp.write(block_result_body(res[1]))
|
59
78
|
when 3
|
60
79
|
resp = response
|
61
80
|
resp.status = res[0]
|
62
81
|
resp.headers.merge!(res[1])
|
63
|
-
resp.write
|
82
|
+
resp.write(block_result_body(res[2]))
|
64
83
|
else
|
65
84
|
raise Roda::RodaError, "too many arguments given to #halt (accepts 0-3, received #{res.length})"
|
66
85
|
end
|
@@ -8,7 +8,7 @@ class Roda
|
|
8
8
|
# It adds a +:header+ matcher for matching on arbitrary headers, which matches
|
9
9
|
# if the header is present:
|
10
10
|
#
|
11
|
-
# r.on :header=>'X-App-Token' do
|
11
|
+
# r.on :header=>'X-App-Token' do |header_value|
|
12
12
|
# end
|
13
13
|
#
|
14
14
|
# It adds a +:host+ matcher for matching by the host of the request:
|
@@ -45,13 +45,8 @@ class Roda
|
|
45
45
|
# Match if the given uppercase key is present inside the environment.
|
46
46
|
def match_header(key)
|
47
47
|
if v = @env[key.upcase.tr("-","_")]
|
48
|
-
|
49
|
-
@captures << v
|
50
|
-
else
|
51
|
-
RodaPlugins.deprecate("The :header hash matcher will yield the header value in Roda 2. To turn on the Roda 2 behavior, set opts[:match_header_yield] to true for your Roda class.")
|
52
|
-
end
|
48
|
+
@captures << v
|
53
49
|
end
|
54
|
-
v
|
55
50
|
end
|
56
51
|
|
57
52
|
# Match if the host of the request is the same as the hostname. +hostname+
|
data/lib/roda/plugins/hooks.rb
CHANGED
@@ -67,11 +67,9 @@ class Roda
|
|
67
67
|
end
|
68
68
|
|
69
69
|
module InstanceMethods
|
70
|
-
private
|
71
|
-
|
72
70
|
# Before routing, execute the before hooks, and
|
73
71
|
# execute the after hooks before returning.
|
74
|
-
def
|
72
|
+
def call
|
75
73
|
if b = opts[:before_hook]
|
76
74
|
instance_exec(&b)
|
77
75
|
end
|
data/lib/roda/plugins/json.rb
CHANGED
@@ -33,13 +33,15 @@ class Roda
|
|
33
33
|
#
|
34
34
|
# plugin :json, :classes=>[Array, Hash, Sequel::Model]
|
35
35
|
module Json
|
36
|
+
OPTS = {}.freeze
|
37
|
+
|
36
38
|
# Set the classes to automatically convert to JSON
|
37
|
-
def self.configure(app, opts=
|
39
|
+
def self.configure(app, opts=OPTS)
|
38
40
|
classes = opts[:classes] || [Array, Hash]
|
39
41
|
app.opts[:json_result_classes] ||= []
|
40
42
|
app.opts[:json_result_classes] += classes
|
41
43
|
app.opts[:json_result_classes].uniq!
|
42
|
-
app.opts[:json_result_classes].
|
44
|
+
app.opts[:json_result_classes].freeze
|
43
45
|
end
|
44
46
|
|
45
47
|
module ClassMethods
|
data/lib/roda/plugins/mailer.rb
CHANGED
@@ -74,7 +74,7 @@ class Roda
|
|
74
74
|
# end
|
75
75
|
# end
|
76
76
|
#
|
77
|
-
# When sending a mail via +mail+ or +sendmail+,
|
77
|
+
# When sending a mail via +mail+ or +sendmail+, a RodaError will be raised
|
78
78
|
# if the mail object does not have a body. This is similar to the 404
|
79
79
|
# status that Roda uses by default for web requests that don't have
|
80
80
|
# a body. If you want to specifically send an email with an empty body, you
|
@@ -110,6 +110,7 @@ class Roda
|
|
110
110
|
MAIL = "MAIL".freeze
|
111
111
|
CONTENT_TYPE = 'Content-Type'.freeze
|
112
112
|
TEXT_PLAIN = "text/plain".freeze
|
113
|
+
OPTS = {}.freeze
|
113
114
|
|
114
115
|
# Error raised when the using the mail class method, but the routing
|
115
116
|
# tree doesn't return the mail object.
|
@@ -117,7 +118,7 @@ class Roda
|
|
117
118
|
|
118
119
|
# Set the options for the mailer. Options:
|
119
120
|
# :content_type :: The default content type for emails (default: text/plain)
|
120
|
-
def self.configure(app, opts=
|
121
|
+
def self.configure(app, opts=OPTS)
|
121
122
|
app.opts[:mailer] = (app.opts[:mailer]||{}).merge(opts).freeze
|
122
123
|
end
|
123
124
|
|
@@ -127,7 +128,7 @@ class Roda
|
|
127
128
|
# calling +deliver+ to send the mail.
|
128
129
|
def mail(path, *args)
|
129
130
|
mail = ::Mail.new
|
130
|
-
unless mail.equal?(
|
131
|
+
unless mail.equal?(new(PATH_INFO=>path, SCRIPT_NAME=>EMPTY_STRING, REQUEST_METHOD=>MAIL, RACK_INPUT=>StringIO.new, RODA_MAIL=>mail, RODA_MAIL_ARGS=>args).call(&route_block))
|
131
132
|
raise Error, "route did not return mail instance for #{path.inspect}, #{args.inspect}"
|
132
133
|
end
|
133
134
|
mail
|
@@ -203,19 +204,19 @@ class Roda
|
|
203
204
|
end
|
204
205
|
end
|
205
206
|
|
206
|
-
private
|
207
|
-
|
208
207
|
# If this is an email request, set the mail object in the response, as well
|
209
208
|
# as the default content_type for the email.
|
210
|
-
def
|
209
|
+
def initialize(env)
|
210
|
+
super
|
211
211
|
if mail = env[RODA_MAIL]
|
212
212
|
res = @_response
|
213
213
|
res.mail = mail
|
214
214
|
res.headers.delete(CONTENT_TYPE)
|
215
215
|
end
|
216
|
-
super
|
217
216
|
end
|
218
217
|
|
218
|
+
private
|
219
|
+
|
219
220
|
# Set the text_part or html_part (depending on the method) in the related email,
|
220
221
|
# using the given body and optional headers.
|
221
222
|
def _mail_part(meth, body, headers=nil)
|
@@ -30,15 +30,14 @@ class Roda
|
|
30
30
|
#
|
31
31
|
# run App
|
32
32
|
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
# use it as a regular app.
|
33
|
+
# It is possible to use the Roda app as a regular app even when using
|
34
|
+
# the middleware plugin.
|
36
35
|
module Middleware
|
37
36
|
# Forward instances are what is actually used as middleware.
|
38
37
|
class Forwarder
|
39
38
|
# Store the current middleware and the next middleware to call.
|
40
39
|
def initialize(mid, app)
|
41
|
-
@mid = mid
|
40
|
+
@mid = mid
|
42
41
|
@app = app
|
43
42
|
end
|
44
43
|
|
@@ -49,7 +48,9 @@ class Roda
|
|
49
48
|
res = nil
|
50
49
|
|
51
50
|
call_next = catch(:next) do
|
52
|
-
|
51
|
+
scope = @mid.new(env)
|
52
|
+
scope.request.forward_next = true
|
53
|
+
res = scope.call(&@mid.route_block)
|
53
54
|
false
|
54
55
|
end
|
55
56
|
|
@@ -62,20 +63,31 @@ class Roda
|
|
62
63
|
end
|
63
64
|
|
64
65
|
module ClassMethods
|
65
|
-
# Create a Forwarder instead of a new instance.
|
66
|
+
# Create a Forwarder instead of a new instance if a non-Hash is given.
|
66
67
|
def new(app)
|
67
|
-
|
68
|
+
if app.is_a?(Hash)
|
69
|
+
super
|
70
|
+
else
|
71
|
+
Forwarder.new(self, app)
|
72
|
+
end
|
68
73
|
end
|
69
74
|
|
70
75
|
# Override the route block so that if no route matches, we throw so
|
71
76
|
# that the next middleware is called.
|
72
77
|
def route(&block)
|
73
78
|
super do |r|
|
74
|
-
instance_exec(r, &block)
|
75
|
-
throw :next, true
|
79
|
+
res = instance_exec(r, &block)
|
80
|
+
throw :next, true if r.forward_next
|
81
|
+
res
|
76
82
|
end
|
77
83
|
end
|
78
84
|
end
|
85
|
+
|
86
|
+
module RequestMethods
|
87
|
+
# Whether to forward the request to the next application. Set only if
|
88
|
+
# this request is being performed for middleware.
|
89
|
+
attr_accessor :forward_next
|
90
|
+
end
|
79
91
|
end
|
80
92
|
|
81
93
|
register_plugin(:middleware, Middleware)
|
@@ -40,11 +40,9 @@ class Roda
|
|
40
40
|
end
|
41
41
|
|
42
42
|
module InstanceMethods
|
43
|
-
private
|
44
|
-
|
45
43
|
# If routing returns a 404 response with an empty body, call
|
46
44
|
# the not_found handler.
|
47
|
-
def
|
45
|
+
def call
|
48
46
|
result = super
|
49
47
|
|
50
48
|
if result[0] == 404 && (v = result[2]).is_a?(Array) && v.empty?
|
@@ -55,6 +53,8 @@ class Roda
|
|
55
53
|
end
|
56
54
|
end
|
57
55
|
|
56
|
+
private
|
57
|
+
|
58
58
|
# Use an empty not_found_handler by default, so that loading
|
59
59
|
# the plugin without defining a not_found handler doesn't
|
60
60
|
# break things.
|