roda 1.3.0 → 2.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.
- 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
@@ -0,0 +1,60 @@
|
|
1
|
+
class Roda
|
2
|
+
module RodaPlugins
|
3
|
+
# The padrino_render plugin adds rendering support that is
|
4
|
+
# similar to Padrino's. While not everything Padrino's
|
5
|
+
# rendering supports is supported by this plugin (yet), it
|
6
|
+
# currently handles enough to be a drop in replacement for
|
7
|
+
# some applications.
|
8
|
+
#
|
9
|
+
# Most notably, this makes the +render+ method default to
|
10
|
+
# using the layout, similar to how the +view+ method works
|
11
|
+
# in the render plugin. If you want to call render and not
|
12
|
+
# use a layout, you can use the <tt>:layout=>false</tt>
|
13
|
+
# option:
|
14
|
+
#
|
15
|
+
# render('test') # layout
|
16
|
+
# render('test', :layout=>false) # no layout
|
17
|
+
#
|
18
|
+
# This also adds a +partial+ method, which renders templates
|
19
|
+
# without the layout, but prefixes the template filename to
|
20
|
+
# use with an underscore:
|
21
|
+
#
|
22
|
+
# partial('test') # uses _test.erb
|
23
|
+
# partial('dir/test') # uses dir/_test.erb
|
24
|
+
#
|
25
|
+
#
|
26
|
+
module PadrinoRender
|
27
|
+
OPTS = {}.freeze
|
28
|
+
SLASH = '/'.freeze
|
29
|
+
|
30
|
+
# Depend on the render plugin, since this overrides
|
31
|
+
# some of its methods.
|
32
|
+
def self.load_dependencies(app, opts=OPTS)
|
33
|
+
app.plugin :render, opts
|
34
|
+
end
|
35
|
+
|
36
|
+
module InstanceMethods
|
37
|
+
# Call view with the given arguments, so that render
|
38
|
+
# uses a layout by default.
|
39
|
+
def render(template, opts=OPTS)
|
40
|
+
view(template, opts)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Renders the given template without a layout, but
|
44
|
+
# prefixes the template filename to use with an
|
45
|
+
# underscore.
|
46
|
+
def partial(template, opts=OPTS)
|
47
|
+
opts = parse_template_opts(template, opts)
|
48
|
+
if opts[:template]
|
49
|
+
template = opts[:template].split(SLASH)
|
50
|
+
template[-1] = "_#{template[-1]}"
|
51
|
+
opts[:template] = template.join(SLASH)
|
52
|
+
end
|
53
|
+
render_template(opts)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
register_plugin(:padrino_render, PadrinoRender)
|
59
|
+
end
|
60
|
+
end
|
@@ -6,15 +6,15 @@ class Roda
|
|
6
6
|
# It adds a :param matcher for matching on any param with the
|
7
7
|
# same name, yielding the value of the param.
|
8
8
|
#
|
9
|
-
# r.on :param=>'foo' do |value|
|
9
|
+
# r.on :param => 'foo' do |value|
|
10
10
|
# # Matches '?foo=bar', '?foo='
|
11
11
|
# # Doesn't match '?bar=foo'
|
12
12
|
# end
|
13
13
|
#
|
14
14
|
# It adds a :param! matcher for matching on any non-empty param
|
15
|
-
# with the same name, yielding the value of the param
|
15
|
+
# with the same name, yielding the value of the param.
|
16
16
|
#
|
17
|
-
# r.on
|
17
|
+
# r.on(:param! => 'foo') do |value|
|
18
18
|
# # Matches '?foo=bar'
|
19
19
|
# # Doesn't match '?foo=', '?bar=foo'
|
20
20
|
# end
|
data/lib/roda/plugins/path.rb
CHANGED
@@ -32,10 +32,11 @@ class Roda
|
|
32
32
|
# a block to a block that is instance_execed.
|
33
33
|
module Path
|
34
34
|
DEFAULT_PORTS = {'http' => 80, 'https' => 443}.freeze
|
35
|
+
OPTS = {}.freeze
|
35
36
|
|
36
37
|
module ClassMethods
|
37
38
|
# Create a new instance method for the named path. See plugin module documentation for options.
|
38
|
-
def path(name, path=nil, opts=
|
39
|
+
def path(name, path=nil, opts=OPTS, &block)
|
39
40
|
if path.is_a?(Hash)
|
40
41
|
raise RodaError, "cannot provide two option hashses to Roda.path" unless opts.empty?
|
41
42
|
opts = path
|
data/lib/roda/plugins/render.rb
CHANGED
@@ -28,7 +28,7 @@ class Roda
|
|
28
28
|
# to true unless RACK_ENV is development to automatically use the
|
29
29
|
# default template cache.
|
30
30
|
# :engine :: The tilt engine to use for rendering, defaults to 'erb'.
|
31
|
-
# :escape :: Use Roda's Erubis escaping support, which makes
|
31
|
+
# :escape :: Use Roda's Erubis escaping support, which makes <tt><%= %></tt> escape output,
|
32
32
|
# <tt><%== %></tt> not escape output, and handles postfix conditions inside
|
33
33
|
# <tt><%= %></tt> tags.
|
34
34
|
# :ext :: The file extension to assume for view files, defaults to the :engine
|
@@ -65,7 +65,7 @@ class Roda
|
|
65
65
|
# :template_block :: Pass this block when creating the underlying template,
|
66
66
|
# ignored when using :inline.
|
67
67
|
# :template_class :: Provides the template class to use, inside of using
|
68
|
-
# Tilt or
|
68
|
+
# Tilt or <tt>Tilt[:engine]</tt>.
|
69
69
|
#
|
70
70
|
# Here's how those options are used:
|
71
71
|
#
|
@@ -73,7 +73,8 @@ class Roda
|
|
73
73
|
# render(:path=>'/path/to/template.erb')
|
74
74
|
#
|
75
75
|
# If you pass a hash as the first argument to +view+ or +render+, it should
|
76
|
-
# have either +:inline
|
76
|
+
# have either +:template+, +:inline+, +:path+, or +:content+ (for +view+) as
|
77
|
+
# one of the keys.
|
77
78
|
module Render
|
78
79
|
OPTS={}.freeze
|
79
80
|
|
@@ -85,27 +86,30 @@ class Roda
|
|
85
86
|
|
86
87
|
# Setup default rendering options. See Render for details.
|
87
88
|
def self.configure(app, opts=OPTS)
|
89
|
+
orig_opts = opts
|
88
90
|
if app.opts[:render]
|
89
91
|
app.opts[:render] = app.opts[:render].merge(opts)
|
90
92
|
else
|
91
93
|
app.opts[:render] = opts.dup
|
92
94
|
end
|
93
95
|
|
94
|
-
if opts[:opts] && !opts[:template_opts]
|
95
|
-
RodaPlugins.deprecate("The render plugin :opts option is deprecated and will be removed in Roda 2. Switch to using the :template_opts option")
|
96
|
-
app.opts[:render][:template_opts] = opts[:opts]
|
97
|
-
end
|
98
|
-
|
99
96
|
opts = app.opts[:render]
|
100
97
|
opts[:engine] ||= "erb"
|
101
98
|
opts[:ext] = nil unless opts.has_key?(:ext)
|
102
99
|
opts[:views] ||= File.expand_path("views", Dir.pwd)
|
103
|
-
opts[:
|
104
|
-
|
100
|
+
opts[:layout_opts] = (opts[:layout_opts] || {}).dup
|
101
|
+
|
102
|
+
if layout = orig_opts.fetch(:layout, true)
|
103
|
+
opts[:layout] = true unless opts.has_key?(:layout)
|
105
104
|
|
106
|
-
|
107
|
-
|
108
|
-
|
105
|
+
case layout
|
106
|
+
when Hash
|
107
|
+
opts[:layout_opts].merge!(layout)
|
108
|
+
when true
|
109
|
+
opts[:layout_opts][:template] ||= 'layout'
|
110
|
+
else
|
111
|
+
opts[:layout_opts][:template] = layout
|
112
|
+
end
|
109
113
|
end
|
110
114
|
|
111
115
|
template_opts = opts[:template_opts] = (opts[:template_opts] || {}).dup
|
@@ -117,9 +121,9 @@ class Roda
|
|
117
121
|
template_opts[:engine_class] = ErubisEscaping::Eruby
|
118
122
|
end
|
119
123
|
opts[:cache] = app.thread_safe_cache if opts.fetch(:cache, ENV['RACK_ENV'] != 'development')
|
120
|
-
opts.
|
121
|
-
opts[:
|
122
|
-
opts
|
124
|
+
opts[:layout_opts].freeze
|
125
|
+
opts[:template_opts].freeze
|
126
|
+
opts.freeze
|
123
127
|
end
|
124
128
|
|
125
129
|
module ClassMethods
|
@@ -128,11 +132,9 @@ class Roda
|
|
128
132
|
# affecting the parent class.
|
129
133
|
def inherited(subclass)
|
130
134
|
super
|
131
|
-
opts = subclass.opts[:render].dup
|
132
|
-
opts[:layout_opts] = opts[:layout_opts].dup.extend(RodaDeprecateMutation)
|
133
|
-
opts[:template_opts] = opts[:template_opts].dup.extend(RodaDeprecateMutation)
|
135
|
+
opts = subclass.opts[:render] = subclass.opts[:render].dup
|
134
136
|
opts[:cache] = thread_safe_cache if opts[:cache]
|
135
|
-
|
137
|
+
opts.freeze
|
136
138
|
end
|
137
139
|
|
138
140
|
# Return the render options for this class.
|
@@ -148,10 +150,6 @@ class Roda
|
|
148
150
|
cached_template(opts) do
|
149
151
|
template_opts = render_opts[:template_opts]
|
150
152
|
current_template_opts = opts[:template_opts]
|
151
|
-
if opts[:opts] && !current_template_opts
|
152
|
-
RodaPlugins.deprecate("The render method :opts option is deprecated and will be removed in Roda 2. Switch to using the :template_opts option")
|
153
|
-
current_template_opts = opts[:opts]
|
154
|
-
end
|
155
153
|
template_opts = template_opts.merge(current_template_opts) if current_template_opts
|
156
154
|
opts[:template_class].new(opts[:path], 1, template_opts, &opts[:template_block])
|
157
155
|
end.render(self, (opts[:locals]||OPTS), &block)
|
@@ -169,15 +167,10 @@ class Roda
|
|
169
167
|
# and render it inside the layout. See Render for details.
|
170
168
|
def view(template, opts=OPTS)
|
171
169
|
opts = parse_template_opts(template, opts)
|
172
|
-
content = opts[:content] ||
|
173
|
-
|
174
|
-
if layout = opts.fetch(:layout, (OPTS if render_opts[:layout]))
|
175
|
-
layout_opts = render_opts[:layout_opts]
|
176
|
-
if opts[:layout_opts]
|
177
|
-
layout_opts = opts[:layout_opts].merge(layout_opts)
|
178
|
-
end
|
170
|
+
content = opts[:content] || render_template(opts)
|
179
171
|
|
180
|
-
|
172
|
+
if layout_opts = view_layout_opts(opts)
|
173
|
+
content = render_template(layout_opts){content}
|
181
174
|
end
|
182
175
|
|
183
176
|
content
|
@@ -185,6 +178,10 @@ class Roda
|
|
185
178
|
|
186
179
|
private
|
187
180
|
|
181
|
+
# Private alias for render. Should be used by other plugins when they want to render a template
|
182
|
+
# without a layout, as plugins can override render to use a layout.
|
183
|
+
alias render_template render
|
184
|
+
|
188
185
|
# If caching templates, attempt to retrieve the template from the cache. Otherwise, just yield
|
189
186
|
# to get the template.
|
190
187
|
def cached_template(opts, &block)
|
@@ -214,10 +211,6 @@ class Roda
|
|
214
211
|
|
215
212
|
if render_opts[:cache]
|
216
213
|
template_opts = opts[:template_opts]
|
217
|
-
if opts[:opts] && !template_opts
|
218
|
-
RodaPlugins.deprecate("The render method :opts option is deprecated and will be removed in Roda 2. Switch to using the :template_opts option")
|
219
|
-
template_opts = opts[:opts]
|
220
|
-
end
|
221
214
|
template_block = opts[:template_block] if !content
|
222
215
|
|
223
216
|
key = if template_class || template_opts || template_block
|
@@ -242,11 +235,36 @@ class Roda
|
|
242
235
|
opts[:template].to_s
|
243
236
|
end
|
244
237
|
|
245
|
-
# The path for the given
|
238
|
+
# The template path for the given options.
|
246
239
|
def template_path(opts)
|
247
240
|
render_opts = render_opts()
|
248
241
|
"#{opts[:views] || render_opts[:views]}/#{template_name(opts)}.#{opts[:ext] || render_opts[:ext] || render_opts[:engine]}"
|
249
242
|
end
|
243
|
+
|
244
|
+
# If a layout should be used, return a hash of options for
|
245
|
+
# rendering the layout template. If a layout should not be
|
246
|
+
# used, return nil.
|
247
|
+
def view_layout_opts(opts)
|
248
|
+
if layout = opts.fetch(:layout, render_opts[:layout])
|
249
|
+
layout_opts = if opts[:layout_opts]
|
250
|
+
opts[:layout_opts].merge(render_opts[:layout_opts])
|
251
|
+
else
|
252
|
+
render_opts[:layout_opts].dup
|
253
|
+
end
|
254
|
+
|
255
|
+
case layout
|
256
|
+
when Hash
|
257
|
+
layout_opts.merge!(layout)
|
258
|
+
when true
|
259
|
+
# use default layout
|
260
|
+
else
|
261
|
+
layout_opts[:template] = layout
|
262
|
+
end
|
263
|
+
|
264
|
+
layout_opts
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
250
268
|
end
|
251
269
|
end
|
252
270
|
|
@@ -23,6 +23,8 @@ class Roda
|
|
23
23
|
# the template will be +bar+. You can use <tt>:local=>nil</tt> to
|
24
24
|
# not set a local variable inside the template.
|
25
25
|
module RenderEach
|
26
|
+
OPTS = {}.freeze
|
27
|
+
|
26
28
|
# Load the render plugin before this plugin, since this plugin
|
27
29
|
# calls the render method.
|
28
30
|
def self.load_dependencies(app)
|
@@ -36,7 +38,7 @@ class Roda
|
|
36
38
|
# :local :: The local variable to use for the current enum value
|
37
39
|
# inside the template. An explicit +nil+ value does not
|
38
40
|
# set a local variable. If not set, uses the template name.
|
39
|
-
def render_each(enum, template, opts=
|
41
|
+
def render_each(enum, template, opts=OPTS)
|
40
42
|
if as = opts.has_key?(:local)
|
41
43
|
as = opts[:local]
|
42
44
|
else
|
@@ -54,7 +56,7 @@ class Roda
|
|
54
56
|
|
55
57
|
enum.map do |v|
|
56
58
|
locals[as] = v if as
|
57
|
-
|
59
|
+
render_template(template, opts)
|
58
60
|
end.join
|
59
61
|
end
|
60
62
|
end
|
@@ -1,71 +1,10 @@
|
|
1
1
|
class Roda
|
2
2
|
module RodaPlugins
|
3
|
-
# The static_path_info plugin changes Roda's behavior so that the
|
4
|
-
# SCRIPT_NAME/PATH_INFO environment settings are not modified
|
5
|
-
# while the request is beind routed, improving performance. If
|
6
|
-
# you have any helpers that operate on PATH_INFO or SCRIPT_NAME,
|
7
|
-
# their behavior will not change depending on where they are
|
8
|
-
# called in the routing tree.
|
9
|
-
#
|
10
|
-
# This still updates SCRIPT_NAME/PATH_INFO before dispatching to
|
11
|
-
# another rack app via +r.run+.
|
12
3
|
module StaticPathInfo
|
13
|
-
module RequestMethods
|
14
|
-
PATH_INFO = "PATH_INFO".freeze
|
15
|
-
SCRIPT_NAME = "SCRIPT_NAME".freeze
|
16
|
-
|
17
|
-
# The current path to match requests against. This is initialized
|
18
|
-
# to PATH_INFO when the request is created.
|
19
|
-
attr_reader :remaining_path
|
20
|
-
|
21
|
-
# Set remaining_path when initializing.
|
22
|
-
def initialize(*)
|
23
|
-
super
|
24
|
-
@remaining_path = @env[PATH_INFO]
|
25
|
-
end
|
26
|
-
|
27
|
-
# The already matched part of the path, including the original SCRIPT_NAME.
|
28
|
-
def matched_path
|
29
|
-
e = @env
|
30
|
-
e[SCRIPT_NAME] + e[PATH_INFO].chomp(@remaining_path)
|
31
|
-
end
|
32
|
-
|
33
|
-
# Update SCRIPT_NAME/PATH_INFO based on the current remaining_path
|
34
|
-
# before dispatching to another rack app, so the app still works as
|
35
|
-
# a URL mapper.
|
36
|
-
def run(_)
|
37
|
-
e = @env
|
38
|
-
path = @remaining_path
|
39
|
-
begin
|
40
|
-
script_name = e[SCRIPT_NAME]
|
41
|
-
path_info = e[PATH_INFO]
|
42
|
-
e[SCRIPT_NAME] += path_info.chomp(path)
|
43
|
-
e[PATH_INFO] = path
|
44
|
-
super
|
45
|
-
ensure
|
46
|
-
e[SCRIPT_NAME] = script_name
|
47
|
-
e[PATH_INFO] = path_info
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
private
|
52
|
-
|
53
|
-
# Update remaining_path with the remaining characters
|
54
|
-
def update_remaining_path(remaining)
|
55
|
-
@remaining_path = remaining
|
56
|
-
end
|
57
|
-
|
58
|
-
# Yield to the block, restoring the remaining_path before
|
59
|
-
# the method returns.
|
60
|
-
def keep_remaining_path
|
61
|
-
path = @remaining_path
|
62
|
-
yield
|
63
|
-
ensure
|
64
|
-
@remaining_path = path
|
65
|
-
end
|
66
|
-
end
|
67
4
|
end
|
68
5
|
|
6
|
+
# For backwards compatibilty, don't raise an error
|
7
|
+
# if trying to load the plugin
|
69
8
|
register_plugin(:static_path_info, StaticPathInfo)
|
70
9
|
end
|
71
10
|
end
|
@@ -50,6 +50,8 @@ class Roda
|
|
50
50
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
51
51
|
# OTHER DEALINGS IN THE SOFTWARE.
|
52
52
|
module Streaming
|
53
|
+
OPTS = {}.freeze
|
54
|
+
|
53
55
|
# Class of the response body in case you use #stream.
|
54
56
|
#
|
55
57
|
# Three things really matter: The front and back block (back being the
|
@@ -85,7 +87,7 @@ class Roda
|
|
85
87
|
end
|
86
88
|
|
87
89
|
# Handle streaming options, see Streaming for details.
|
88
|
-
def initialize(opts=
|
90
|
+
def initialize(opts=OPTS, &back)
|
89
91
|
@scheduler = opts[:scheduler] || Scheduler.new(self)
|
90
92
|
@back = back.to_proc
|
91
93
|
@keep_open = opts[:keep_open]
|
@@ -143,7 +145,7 @@ class Roda
|
|
143
145
|
# Immediately return a streaming response using the current response
|
144
146
|
# status and headers, calling the block to get the streaming response.
|
145
147
|
# See Streaming for details.
|
146
|
-
def stream(opts=
|
148
|
+
def stream(opts=OPTS, &block)
|
147
149
|
opts = opts.merge(:scheduler=>EventMachine) if !opts.has_key?(:scheduler) && env['async.callback']
|
148
150
|
|
149
151
|
if opts[:loop]
|
data/lib/roda/version.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
class Roda
|
2
2
|
# The major version of Roda, updated only for major changes that are
|
3
3
|
# likely to require modification to Roda apps.
|
4
|
-
RodaMajorVersion =
|
4
|
+
RodaMajorVersion = 2
|
5
5
|
|
6
6
|
# The minor version of Roda, updated for new feature releases of Roda.
|
7
|
-
RodaMinorVersion =
|
7
|
+
RodaMinorVersion = 0
|
8
8
|
|
9
9
|
# The patch version of Roda, updated only for bug fixes from the last
|
10
10
|
# feature release.
|