roda 2.28.0 → 2.29.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +46 -0
  3. data/README.rdoc +25 -7
  4. data/doc/release_notes/2.29.0.txt +156 -0
  5. data/lib/roda.rb +25 -3
  6. data/lib/roda/plugins/_erubis_escaping.rb +2 -0
  7. data/lib/roda/plugins/_symbol_regexp_matchers.rb +22 -0
  8. data/lib/roda/plugins/assets.rb +3 -2
  9. data/lib/roda/plugins/branch_locals.rb +74 -0
  10. data/lib/roda/plugins/caching.rb +15 -7
  11. data/lib/roda/plugins/chunked.rb +10 -7
  12. data/lib/roda/plugins/content_for.rb +4 -1
  13. data/lib/roda/plugins/drop_body.rb +3 -2
  14. data/lib/roda/plugins/error_email.rb +3 -2
  15. data/lib/roda/plugins/error_mail.rb +3 -2
  16. data/lib/roda/plugins/head.rb +2 -1
  17. data/lib/roda/plugins/header_matchers.rb +3 -0
  18. data/lib/roda/plugins/heartbeat.rb +3 -2
  19. data/lib/roda/plugins/json.rb +5 -3
  20. data/lib/roda/plugins/json_parser.rb +3 -2
  21. data/lib/roda/plugins/mailer.rb +3 -3
  22. data/lib/roda/plugins/match_affix.rb +6 -0
  23. data/lib/roda/plugins/multi_route.rb +3 -1
  24. data/lib/roda/plugins/padrino_render.rb +3 -2
  25. data/lib/roda/plugins/params_capturing.rb +3 -3
  26. data/lib/roda/plugins/partials.rb +3 -3
  27. data/lib/roda/plugins/path.rb +4 -2
  28. data/lib/roda/plugins/path_rewriter.rb +2 -2
  29. data/lib/roda/plugins/per_thread_caching.rb +2 -0
  30. data/lib/roda/plugins/placeholder_string_matchers.rb +42 -0
  31. data/lib/roda/plugins/precompile_templates.rb +3 -2
  32. data/lib/roda/plugins/render.rb +86 -37
  33. data/lib/roda/plugins/render_each.rb +2 -1
  34. data/lib/roda/plugins/render_locals.rb +102 -0
  35. data/lib/roda/plugins/run_append_slash.rb +2 -1
  36. data/lib/roda/plugins/run_handler.rb +2 -1
  37. data/lib/roda/plugins/sinatra_helpers.rb +4 -4
  38. data/lib/roda/plugins/static_path_info.rb +2 -0
  39. data/lib/roda/plugins/static_routing.rb +1 -1
  40. data/lib/roda/plugins/streaming.rb +9 -4
  41. data/lib/roda/plugins/symbol_matchers.rb +23 -20
  42. data/lib/roda/plugins/view_options.rb +63 -28
  43. data/lib/roda/plugins/view_subdirs.rb +1 -0
  44. data/lib/roda/plugins/websockets.rb +2 -0
  45. data/lib/roda/version.rb +1 -1
  46. data/spec/composition_spec.rb +2 -2
  47. data/spec/matchers_spec.rb +6 -5
  48. data/spec/plugin/_erubis_escaping_spec.rb +5 -5
  49. data/spec/plugin/backtracking_array_spec.rb +0 -2
  50. data/spec/plugin/branch_locals_spec.rb +88 -0
  51. data/spec/plugin/content_for_spec.rb +8 -2
  52. data/spec/plugin/halt_spec.rb +8 -0
  53. data/spec/plugin/header_matchers_spec.rb +20 -5
  54. data/spec/plugin/multi_route_spec.rb +1 -1
  55. data/spec/plugin/named_templates_spec.rb +2 -2
  56. data/spec/plugin/params_capturing_spec.rb +1 -1
  57. data/spec/plugin/per_thread_caching_spec.rb +1 -1
  58. data/spec/plugin/placeholder_string_matchers_spec.rb +159 -0
  59. data/spec/plugin/render_locals_spec.rb +114 -0
  60. data/spec/plugin/render_spec.rb +83 -8
  61. data/spec/plugin/streaming_spec.rb +104 -4
  62. data/spec/plugin/symbol_matchers_spec.rb +1 -1
  63. data/spec/plugin/view_options_spec.rb +83 -7
  64. data/spec/plugin/websockets_spec.rb +7 -8
  65. data/spec/spec_helper.rb +22 -2
  66. metadata +11 -2
@@ -22,11 +22,12 @@ class Roda
22
22
  # # GET /a/ => App gets "/" as PATH_INFO
23
23
  module RunAppendSlash
24
24
  OPTS = {}.freeze
25
+ RodaPlugins.deprecate_constant(self, :OPTS)
25
26
 
26
27
  # Set plugin specific options. Options:
27
28
  # :use_redirects :: Whether to issue 302 redirects when appending the
28
29
  # trailing slash.
29
- def self.configure(app, opts=OPTS)
30
+ def self.configure(app, opts=RodaPlugins::OPTS)
30
31
  app.opts[:run_append_slash_redirect] = !!opts[:use_redirects]
31
32
  end
32
33
 
@@ -26,6 +26,7 @@ class Roda
26
26
  # end
27
27
  module RunHandler
28
28
  OPTS = {}.freeze
29
+ RodaPlugins.deprecate_constant(self, :OPTS)
29
30
 
30
31
  module RequestMethods
31
32
  # If a block is given, yield the rack response array to it. The response can
@@ -34,7 +35,7 @@ class Roda
34
35
  # If the <tt>:not_found=>:pass</tt> option is given, and the rack response
35
36
  # returned by the app is a 404 response, do not return the response, continue
36
37
  # routing normally.
37
- def run(app, opts=OPTS)
38
+ def run(app, opts=RodaPlugins::OPTS)
38
39
  res = catch(:halt){super(app)}
39
40
  yield res if block_given?
40
41
  throw(:halt, res) unless opts[:not_found] == :pass && res[0] == 404
@@ -212,7 +212,7 @@ class Roda
212
212
  # OTHER DEALINGS IN THE SOFTWARE.
213
213
  module SinatraHelpers
214
214
  OPTS = {}.freeze
215
-
215
+ RodaPlugins.deprecate_constant(self, :OPTS)
216
216
  CONTENT_TYPE = "Content-Type".freeze
217
217
  RodaPlugins.deprecate_constant(self, :CONTENT_TYPE)
218
218
  CONTENT_DISPOSITION = "Content-Disposition".freeze
@@ -248,7 +248,7 @@ class Roda
248
248
  # Add delegate methods to the route block scope
249
249
  # calling request or response methods, unless the
250
250
  # :delegate option is false.
251
- def self.configure(app, opts=OPTS)
251
+ def self.configure(app, opts=RodaPlugins::OPTS)
252
252
  app.send(:include, DelegateMethods) unless opts[:delegate] == false
253
253
  end
254
254
 
@@ -339,7 +339,7 @@ class Roda
339
339
  end
340
340
 
341
341
  # Use the contents of the file at +path+ as the response body. See plugin documentation for options.
342
- def send_file(path, opts = OPTS)
342
+ def send_file(path, opts = RodaPlugins::OPTS)
343
343
  res = response
344
344
  headers = res.headers
345
345
  if opts[:type] || !headers["Content-Type"]
@@ -441,7 +441,7 @@ class Roda
441
441
 
442
442
  # Set the Content-Type of the response body given a media type or file
443
443
  # extension. See plugin documentation for options.
444
- def content_type(type = (return @headers["Content-Type"]; nil), opts = OPTS)
444
+ def content_type(type = (return @headers["Content-Type"]; nil), opts = RodaPlugins::OPTS)
445
445
  unless (mime_type = mime_type(type) || opts[:default])
446
446
  raise RodaError, "Unknown media type: #{type}"
447
447
  end
@@ -1,5 +1,7 @@
1
1
  class Roda
2
2
  module RodaPlugins
3
+ warn 'The static_path_info plugin is deprecated and will be removed in Roda 3. It has been a no-op since Roda 2, and can just be removed from the application.'
4
+
3
5
  module StaticPathInfo
4
6
  end
5
7
 
@@ -47,7 +47,7 @@ class Roda
47
47
  # method call takes precedence over the static_route method call for /foo.
48
48
  # As shown above, you can use Roda's routing tree methods inside the
49
49
  # static_route block to have shared behavior for different request methods,
50
- # while still having handling the request methods differently.
50
+ # while still handling the request methods differently.
51
51
  #
52
52
  # Note that if you want to use the static_routing plugin and the hooks
53
53
  # plugin at the same time, you should load the hooks plugin first.
@@ -73,6 +73,7 @@ class Roda
73
73
  # OTHER DEALINGS IN THE SOFTWARE.
74
74
  module Streaming
75
75
  OPTS = {}.freeze
76
+ RodaPlugins.deprecate_constant(self, :OPTS)
76
77
 
77
78
  # Class of the response body in case you use #stream.
78
79
  #
@@ -109,7 +110,7 @@ class Roda
109
110
  end
110
111
 
111
112
  # Handle streaming options, see Streaming for details.
112
- def initialize(opts=OPTS, &back)
113
+ def initialize(opts=RodaPlugins::OPTS, &back)
113
114
  @scheduler = opts[:scheduler] || Scheduler.new(self)
114
115
  @back = back.to_proc
115
116
  @keep_open = opts[:keep_open]
@@ -117,7 +118,7 @@ class Roda
117
118
  @closed = false
118
119
 
119
120
  if opts[:callback]
120
- callback(&opts[:callback])
121
+ @callbacks << opts[:callback]
121
122
  end
122
123
  end
123
124
 
@@ -134,6 +135,7 @@ class Roda
134
135
 
135
136
  # Add the given block as a callback to call when the block closes.
136
137
  def callback(&block)
138
+ RodaPlugins.warn 'Stream#callback in the streaming plugin is deprecated and will be removed in Roda 3. Specify callback at initialization using the stream method :callback option.'
137
139
  return yield if closed?
138
140
  @callbacks << block
139
141
  end
@@ -172,8 +174,11 @@ class Roda
172
174
  # Immediately return a streaming response using the current response
173
175
  # status and headers, calling the block to get the streaming response.
174
176
  # See Streaming for details.
175
- def stream(opts=OPTS, &block)
176
- opts = opts.merge(:scheduler=>EventMachine) if !opts.has_key?(:scheduler) && env['async.callback']
177
+ def stream(opts=RodaPlugins::OPTS, &block)
178
+ if !opts.has_key?(:scheduler) && env['async.callback']
179
+ RodaPlugins.warn 'The automatic support for EventMachine in the streaming plugin is deprecated and will be removed in Roda 3.'
180
+ opts = opts.merge(:scheduler=>EventMachine)
181
+ end
177
182
 
178
183
  if opts[:loop]
179
184
  block = proc do |out|
@@ -25,8 +25,8 @@ class Roda
25
25
  # :rest :: <tt>/(.*)/</tt>, all remaining characters, if any
26
26
  # :w :: <tt>/(\w+)/</tt>, a alphanumeric segment
27
27
  #
28
- # If placeholder string matchers are supported, this feature also applies to
29
- # embedded colons in strings, so the following:
28
+ # If the placeholder_string_matchers plugin is loaded, this feature also applies to
29
+ # placeholders in strings, so the following:
30
30
  #
31
31
  # r.on "users/:username" do
32
32
  # # ...
@@ -35,33 +35,36 @@ class Roda
35
35
  # Would match +/users/foobar123+, but not +/users/foo+, +/users/FooBar123+,
36
36
  # or +/users/foobar_123+.
37
37
  #
38
- # If placeholder string matchers are supported, it also adds the following
39
- # symbol matchers:
40
- #
41
- # :format :: <tt>/(?:\.(\w+))?/</tt>, an optional format/extension
42
- # :opt :: <tt>/(?:\/([^\/]+))?</tt>, an optional segment
43
- # :optd :: <tt>/(?:\/(\d+))?</tt>, an optional decimal segment
44
- #
45
- # These are only added when placeholder string matchers are supported,
46
- # because they only make sense when used inside of a string, due to how
47
- # segment matching works. Example:
48
- #
49
- # r.is "album:opt" do |id| end
50
- # # matches /album (yielding nil) and /album/foo (yielding "foo")
51
- # # does not match /album/ or /album/foo/bar
52
- #
53
38
  # If using this plugin with the params_capturing plugin, this plugin should
54
39
  # be loaded first.
55
40
  module SymbolMatchers
41
+ def self.load_dependencies(app)
42
+ app.plugin :_symbol_regexp_matchers
43
+ end
44
+
56
45
  def self.configure(app)
57
46
  app.symbol_matcher(:d, /(\d+)/)
58
47
  app.symbol_matcher(:w, /(\w+)/)
59
48
  app.symbol_matcher(:rest, /(.*)/)
60
49
 
61
50
  if !app.opts[:verbatim_string_matcher]
62
- app.symbol_matcher(:format, /(?:\.(\w+))?/)
63
- app.symbol_matcher(:opt, /(?:\/([^\/]+))?/)
64
- app.symbol_matcher(:optd, /(?:\/(\d+))?/)
51
+ # RODA3: Remove
52
+ app::RodaRequest.class_eval do
53
+ def match_symbol_format
54
+ Roda::RodaPlugins.warn('Implicit use of the :format symbol matcher is deprecated and will be removed in Roda 3. If you want to use the :format symbol matcher, add the following code to your Roda class: symbol_matcher(:format, /(?:\.(\w+))?/)')
55
+ /(?:\.(\w+))?/
56
+ end
57
+
58
+ def match_symbol_opt
59
+ Roda::RodaPlugins.warn('Implicit use of the :opt symbol matcher is deprecated and will be removed in Roda 3. If you want to use the :opt symbol matcher, add the following code to your Roda class: symbol_matcher(:opt, /(?:\/([^\/]+))?/)')
60
+ /(?:\/([^\/]+))?/
61
+ end
62
+
63
+ def match_symbol_optd
64
+ Roda::RodaPlugins.warn('Implicit use of the :optd symbol matcher is deprecated and will be removed in Roda 3. If you want to use the :optd symbol matcher, add the following code to your Roda class: symbol_matcher(:optd, /(?:\/(\d+))?/)')
65
+ /(?:\/(\d+))?/
66
+ end
67
+ end
65
68
  end
66
69
  end
67
70
 
@@ -4,7 +4,7 @@
4
4
  class Roda
5
5
  module RodaPlugins
6
6
  # The view_options plugin allows you to override view and layout
7
- # options and locals for specific branches and routes.
7
+ # options for specific branches and routes.
8
8
  #
9
9
  # plugin :render
10
10
  # plugin :view_options
@@ -12,15 +12,14 @@ class Roda
12
12
  # route do |r|
13
13
  # r.on "users" do
14
14
  # set_layout_options :template=>'users_layout'
15
- # set_layout_locals :title=>'Users'
16
15
  # set_view_options :engine=>'haml'
17
- # set_view_locals :footer=>'(c) Roda'
18
16
  #
19
17
  # # ...
20
18
  # end
21
19
  # end
22
20
  #
23
- # The options and locals you specify have higher precedence than
21
+ # The options you specify via the set_view_options and
22
+ # set_layout_options methods have higher precedence than
24
23
  # the render plugin options, but lower precedence than options
25
24
  # you directly pass to the view/render methods.
26
25
  #
@@ -60,22 +59,22 @@ class Roda
60
59
  #
61
60
  # If you have an existing Roda application that doesn't use
62
61
  # automatic HTML escaping for <tt><%= %></tt> tags via the
63
- # :render plugin's :escape option, but you want to switch to
62
+ # :render plugin's :escape=>:erubi option, but you want to switch to
64
63
  # using the :escape option, you can now do so without making
65
64
  # all changes at once. With set_view_options, you can now
66
65
  # specify escaping or not on a per branch basis in the routing
67
66
  # tree:
68
67
  #
69
- # plugin :render, :escape=>true
68
+ # plugin :render, :escape=>:erubi
70
69
  # plugin :view_options
71
70
  #
72
71
  # route do |r|
73
72
  # # Don't escape <%= %> by default
74
- # set_view_options :template_opts=>{:engine_class=>nil}
73
+ # set_view_options :template_opts=>{:escape=>false}
75
74
  #
76
75
  # r.on "users" do
77
76
  # # Escape <%= %> in this branch
78
- # set_view_options :template_opts=>{:engine_class=>render_opts[:template_opts][:engine_class]}
77
+ # set_view_options :template_opts=>{:escape=>true}
79
78
  # end
80
79
  # end
81
80
  module ViewOptions
@@ -85,27 +84,7 @@ class Roda
85
84
  app.plugin :render
86
85
  end
87
86
 
88
- # The following methods are created via metaprogramming:
89
- # set_layout_locals :: Set locals to use in the layout
90
- # set_layout_options :: Set options to use when rendering the layout
91
- # set_view_locals :: Set locals to use in the view
92
- # set_view_options :: Set options to use when rendering the view
93
87
  module InstanceMethods
94
- %w'layout view'.each do |type|
95
- %w'locals options'.each do |var|
96
- v = "_#{type}_#{var}"
97
- module_eval(<<-END, __FILE__, __LINE__+1)
98
- def set#{v}(opts)
99
- if @#{v}
100
- @#{v} = Hash[@#{v}].merge!(opts)
101
- else
102
- @#{v} = opts
103
- end
104
- end
105
- END
106
- end
107
- end
108
-
109
88
  # Append a view subdirectory to use. If there hasn't already
110
89
  # been a view subdirectory set, this just sets it to the argument.
111
90
  # If there has already been a view subdirectory set, this sets
@@ -125,8 +104,62 @@ class Roda
125
104
  @_view_subdir = v
126
105
  end
127
106
 
107
+ # Set branch/route options to use when rendering the layout
108
+ def set_layout_options(opts)
109
+ if options = @_layout_options
110
+ @_layout_options = Hash[options].merge!(opts)
111
+ else
112
+ @_layout_options = opts
113
+ end
114
+ end
115
+
116
+ # Set branch/route options to use when rendering the view
117
+ def set_view_options(opts)
118
+ if options = @_view_options
119
+ @_view_options = Hash[options].merge!(opts)
120
+ else
121
+ @_view_options = opts
122
+ end
123
+ end
124
+
125
+ # RODA3: Remove
126
+ def set_layout_locals(opts)
127
+ RodaPlugins.warn "The set_layout_locals method in the view_options plugin is deprecated and will be removed in Roda 3. This feature has been moved to the branch_locals plugin."
128
+ if locals = @_layout_locals
129
+ @_layout_locals = Hash[locals].merge!(opts)
130
+ else
131
+ @_layout_locals = opts
132
+ end
133
+ end
134
+
135
+ # RODA3: Remove
136
+ def set_view_locals(opts)
137
+ RodaPlugins.warn "The set_view_locals method in the view_options plugin is deprecated and will be removed in Roda 3. This feature has been moved to the branch_locals plugin."
138
+ if locals = @_view_locals
139
+ @_view_locals = Hash[locals].merge!(opts)
140
+ else
141
+ @_view_locals = opts
142
+ end
143
+ end
144
+
128
145
  private
129
146
 
147
+ def render_locals
148
+ locals = super
149
+ if @_view_locals
150
+ locals = Hash[locals].merge!(@_view_locals)
151
+ end
152
+ locals
153
+ end
154
+
155
+ def layout_locals
156
+ locals = super
157
+ if @_view_locals
158
+ locals = Hash[locals].merge!(@_layout_locals)
159
+ end
160
+ locals
161
+ end
162
+
130
163
  # If view options or locals have been set and this
131
164
  # template isn't a layout template, merge the options
132
165
  # and locals into the returned hash.
@@ -138,6 +171,7 @@ class Roda
138
171
  t_opts.merge!(v_opts)
139
172
  end
140
173
 
174
+ # RODA3: Remove
141
175
  if v_locals = @_view_locals
142
176
  t_opts[:locals] = if t_locals = t_opts[:locals]
143
177
  Hash[v_locals].merge!(t_locals)
@@ -159,6 +193,7 @@ class Roda
159
193
  opts.merge!(l_opts)
160
194
  end
161
195
 
196
+ # RODA3: Remove
162
197
  if l_locals = @_layout_locals
163
198
  opts[:locals] = if o_locals = opts[:locals]
164
199
  Hash[o_locals].merge!(l_locals)
@@ -3,4 +3,5 @@
3
3
  # make attempts to load the view_subdirs plugin load the
4
4
  # view_options plugin instead.
5
5
  require 'roda/plugins/view_options'
6
+ Roda::RodaPlugins.warn "The view_subdirs plugin is a deprecated alias for the view_options plugin that will be removed in Roda 3. Use the view_options plugin instead."
6
7
  Roda::RodaPlugins.register_plugin(:view_subdirs, Roda::RodaPlugins::ViewOptions)
@@ -4,6 +4,8 @@ require 'faye/websocket'
4
4
 
5
5
  class Roda
6
6
  module RodaPlugins
7
+ warn "The websockets plugin is deprecated and will be removed in Roda 3. Consider maintaining the plugin as a separate gem if you would like to keep using it."
8
+
7
9
  # The websocket plugin adds integration support for websockets.
8
10
  # Currently, only 'faye-websocket' is supported, so eventmachine
9
11
  # is required for websockets. See the
data/lib/roda/version.rb CHANGED
@@ -4,7 +4,7 @@ class Roda
4
4
  RodaMajorVersion = 2
5
5
 
6
6
  # The minor version of Roda, updated for new feature releases of Roda.
7
- RodaMinorVersion = 28
7
+ RodaMinorVersion = 29
8
8
 
9
9
  # The patch version of Roda, updated only for bug fixes from the last
10
10
  # feature release.
@@ -19,13 +19,13 @@ describe "r.run" do
19
19
 
20
20
  it "modifies SCRIPT_NAME/PATH_INFO when calling run" do
21
21
  a = app{|r| "#{r.script_name}|#{r.path_info}"}
22
- app(:static_path_info){|r| r.on("a"){r.run a}}
22
+ app{|r| r.on("a"){r.run a}}
23
23
  body("/a/b").must_equal "/a|/b"
24
24
  end
25
25
 
26
26
  it "restores SCRIPT_NAME/PATH_INFO before returning from run" do
27
27
  a = app{|r| "#{r.script_name}|#{r.path_info}"}
28
- app(:static_path_info){|r| s = catch(:halt){r.on("a"){r.run a}}; "#{s[2].join}%#{r.script_name}|#{r.path_info}"}
28
+ app{|r| s = catch(:halt){r.on("a"){r.run a}}; "#{s[2].join}%#{r.script_name}|#{r.path_info}"}
29
29
  body("/a/b").must_equal "/a|/b%|/a/b"
30
30
  end
31
31
  end
@@ -158,7 +158,7 @@ describe "r.is" do
158
158
  end
159
159
 
160
160
  describe "matchers" do
161
- it "should handle string with embedded param" do
161
+ deprecated "should handle string with embedded param" do
162
162
  app do |r|
163
163
  r.on "posts/:id" do |id|
164
164
  id
@@ -185,6 +185,7 @@ describe "matchers" do
185
185
  '2'
186
186
  end
187
187
  end
188
+ # RODA3: remove option
188
189
  app.opts[:verbatim_string_matcher] = true
189
190
 
190
191
  status('/post/123').must_equal 404
@@ -194,7 +195,7 @@ describe "matchers" do
194
195
  body('/responses-:id').must_equal '2'
195
196
  end
196
197
 
197
- it "should handle multiple params in single string" do
198
+ deprecated "should handle multiple params in single string" do
198
199
  app do |r|
199
200
  r.on "u/:uid/posts/:id" do |uid, id|
200
201
  uid + id
@@ -206,7 +207,7 @@ describe "matchers" do
206
207
  status("/u/jdoe/pots/123").must_equal 404
207
208
  end
208
209
 
209
- it "should escape regexp metacharaters in string" do
210
+ deprecated "should escape regexp metacharaters in string" do
210
211
  app do |r|
211
212
  r.on "u/:uid/posts?/:id" do |uid, id|
212
213
  uid + id
@@ -218,7 +219,7 @@ describe "matchers" do
218
219
  status("/u/jdoe/post/123").must_equal 404
219
220
  end
220
221
 
221
- it "should handle colons by themselves" do
222
+ deprecated "should handle colons by themselves" do
222
223
  app do |r|
223
224
  r.on "u/:/:uid/posts/::id" do |uid, id|
224
225
  uid + id
@@ -394,7 +395,7 @@ describe "r.on" do
394
395
  status.must_equal 404
395
396
  end
396
397
 
397
- it "executes on arbitrary object" do
398
+ deprecated "executes on arbitrary object" do
398
399
  app do |r|
399
400
  r.on Object.new do
400
401
  "+1"