roda 1.3.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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.