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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +24 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +7 -4
  5. data/doc/release_notes/2.0.0.txt +75 -0
  6. data/lib/roda/plugins/assets.rb +2 -2
  7. data/lib/roda/plugins/backtracking_array.rb +2 -11
  8. data/lib/roda/plugins/caching.rb +4 -2
  9. data/lib/roda/plugins/chunked.rb +4 -9
  10. data/lib/roda/plugins/class_level_routing.rb +1 -3
  11. data/lib/roda/plugins/default_headers.rb +1 -2
  12. data/lib/roda/plugins/error_email.rb +4 -14
  13. data/lib/roda/plugins/error_handler.rb +4 -4
  14. data/lib/roda/plugins/flash.rb +1 -3
  15. data/lib/roda/plugins/halt.rb +24 -5
  16. data/lib/roda/plugins/header_matchers.rb +2 -7
  17. data/lib/roda/plugins/hooks.rb +1 -3
  18. data/lib/roda/plugins/json.rb +4 -2
  19. data/lib/roda/plugins/mailer.rb +8 -7
  20. data/lib/roda/plugins/middleware.rb +21 -9
  21. data/lib/roda/plugins/not_found.rb +3 -3
  22. data/lib/roda/plugins/padrino_render.rb +60 -0
  23. data/lib/roda/plugins/param_matchers.rb +3 -3
  24. data/lib/roda/plugins/path.rb +2 -1
  25. data/lib/roda/plugins/render.rb +55 -37
  26. data/lib/roda/plugins/render_each.rb +4 -2
  27. data/lib/roda/plugins/static_path_info.rb +2 -63
  28. data/lib/roda/plugins/streaming.rb +4 -2
  29. data/lib/roda/version.rb +2 -2
  30. data/lib/roda.rb +71 -172
  31. data/spec/matchers_spec.rb +31 -82
  32. data/spec/plugin/assets_spec.rb +6 -6
  33. data/spec/plugin/error_handler_spec.rb +23 -0
  34. data/spec/plugin/halt_spec.rb +39 -0
  35. data/spec/plugin/middleware_spec.rb +7 -0
  36. data/spec/plugin/padrino_render_spec.rb +57 -0
  37. data/spec/plugin/render_each_spec.rb +1 -1
  38. data/spec/plugin/render_spec.rb +59 -5
  39. data/spec/request_spec.rb +0 -12
  40. data/spec/response_spec.rb +0 -24
  41. data/spec/views/_test.erb +1 -0
  42. metadata +7 -4
  43. data/lib/roda/plugins/delete_nil_headers.rb +0 -34
  44. 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 :param!=>'foo' do |value|
17
+ # r.on(:param! => 'foo') do |value|
18
18
  # # Matches '?foo=bar'
19
19
  # # Doesn't match '?foo=', '?bar=foo'
20
20
  # end
@@ -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={}, &block)
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
@@ -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 <%= %> escape output,
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 a Tilt[:engine].
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+ or +:path+ as one of the keys.
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[:layout] = "layout" unless opts.has_key?(:layout)
104
- opts[:layout_opts] ||= (opts[:layout_opts] || {}).dup
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
- if layout = opts[:layout]
107
- layout = {:template=>layout} unless layout.is_a?(Hash)
108
- opts[:layout_opts] = opts[:layout_opts].merge(layout)
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.extend(RodaDeprecateMutation)
121
- opts[:layout_opts].extend(RodaDeprecateMutation)
122
- opts[:template_opts].extend(RodaDeprecateMutation)
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
- subclass.opts[:render] = opts.extend(RodaDeprecateMutation)
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] || render(opts)
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
- content = render(layout, layout_opts){content}
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 template.
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
- render(template, opts)
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={}, &back)
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={}, &block)
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 = 1
4
+ RodaMajorVersion = 2
5
5
 
6
6
  # The minor version of Roda, updated for new feature releases of Roda.
7
- RodaMinorVersion = 3
7
+ RodaMinorVersion = 0
8
8
 
9
9
  # The patch version of Roda, updated only for bug fixes from the last
10
10
  # feature release.