roda 3.16.0 → 3.17.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 64aa07cd8d8fff3df32bb1ddb87089eb0248db3b21d76eb9d851bcebe48276af
4
- data.tar.gz: 8865bb77e86f9671c60cd67bfbceb5b38761a9a662719d28dae6f2768ad07827
3
+ metadata.gz: ea47f91d6b930ef7822fef3781ef11f959ace8f9e6a907f24481c5f797678482
4
+ data.tar.gz: 13b182e1651d996e1493078301888b6671dd91ea0516f441ad1bc59cba940d6d
5
5
  SHA512:
6
- metadata.gz: 0c598bccca29ace44a814342f42e58c8fb81b0a445049281c334d00d9036fb2862db19438c61a288f6ec54d1ec65e2775719b81b3a7c42fa97f2e2dc09fe8895
7
- data.tar.gz: 7c917b0ef0116b0fba649e3386c97f42e76316ed83601f405835fd46f13c43d5cd135292be236e0c1df75d206608075a150ce9fd3c407b77e129bbd69e379379
6
+ metadata.gz: 2bc04891739a93f46839abf14fd915b2f470e835e6c200fa1f8fd8a168cda13406c424fa0e1cc1eee5ce0e1e50b0dfbbe6440d27bec2112a2c6cf3dc83951ffc
7
+ data.tar.gz: 68a1ec7f9ead2ce2fda252d36c7edf0df3c53d1aa02a246669d8d0700afd65d1a010179be92c1973b9c511386df39e2b7d1fd041228add83d2ef344e9d49a475
data/CHANGELOG CHANGED
@@ -1,3 +1,13 @@
1
+ = 3.17.0 (2019-02-15)
2
+
3
+ * Improve performance in the common case for RodaResponse#finish (jeremyevans)
4
+
5
+ * Support before hooks in the hooks plugin in the mailer and mail_processor plugins (jeremyevans)
6
+
7
+ * Allow set_layout_opts in view_options plugin to override layout if render plugin :layout option is given (jeremyevans)
8
+
9
+ * Add route_block_args plugin to control which arguments are yielded to the route block (jeremyevans, chrisfrank) (#159)
10
+
1
11
  = 3.16.0 (2019-01-18)
2
12
 
3
13
  * Add mail_processor plugin for processing mail using a routing tree (jeremyevans)
@@ -0,0 +1,62 @@
1
+ = New Features
2
+
3
+ * A route_block_args plugin has been added, allowing you to customize
4
+ which objects are yielded to the the route block. You call the
5
+ plugin with a block, which is evaluated in the context of the
6
+ instance and should return an array of arguments for the instance
7
+ to yield to the route block.
8
+
9
+ To yield both the request and response objects, you can do:
10
+
11
+ plugin :route_block_args do |r|
12
+ [r, response]
13
+ end
14
+
15
+ route do |r, response|
16
+ # ...
17
+ end
18
+
19
+ In addition to the main route block, using this plugin also affects
20
+ the arguments passed to routing blocks in the following plugins:
21
+
22
+ * class_level_routing
23
+ * mailer
24
+ * mail_processor
25
+ * multi_route
26
+ * static_routing
27
+
28
+ = Other Improvements
29
+
30
+ * The set_layout_opts method in the view_options plugin can now
31
+ override the layout template even if the render plugin :layout
32
+ option is given.
33
+
34
+ * The mailer and mail_processor plugin now integrate with the hooks
35
+ plugin to support before/after hooks.
36
+
37
+ * Dispatching to the route block and RodaResponse#finish are both
38
+ slightly faster.
39
+
40
+ * Internal before hook handling has been moved from an internal
41
+ plugin into the core, and modified so that if you are not using
42
+ the internal before hook in any plugin, there is no runtime cost.
43
+
44
+ * The core now recognizes when plugins are using the internal after
45
+ hook, and automatically loads the internal plugin supporting the
46
+ after hook.
47
+
48
+ = Backwards Compatibility
49
+
50
+ * When using the render plugin with a :layout option, the render_opts
51
+ :layout option will be set to true if the layout is enabled.
52
+ Previously, the render_opts :layout option would retain the value
53
+ given as the plugin option. Options for the layout (including the
54
+ template) are still available in the render_opts :layout_opts
55
+ option. This change was made to fix the set_layout_opts bug in the
56
+ view_options plugin.
57
+
58
+ * RodaResponse#initialize no longer sets the response status to nil
59
+ if it was already set.
60
+
61
+ * RodaResponse#finish no longer sets the status on the receiver, it
62
+ just uses the receiver's status to set the rack response status.
data/lib/roda.rb CHANGED
@@ -60,7 +60,9 @@ class Roda
60
60
  @inherit_middleware = true
61
61
  @middleware = []
62
62
  @opts = {}
63
+ @raw_route_block = nil
63
64
  @route_block = nil
65
+ @rack_app_route_block = nil
64
66
 
65
67
  # Module in which all Roda plugins should be stored. Also contains logic for
66
68
  # registering and loading plugins.
@@ -161,6 +163,15 @@ class Roda
161
163
  super
162
164
  end
163
165
 
166
+ # Rebuild the _roda_before and _roda_after methods whenever a plugin might
167
+ # have added a _roda_before_* or _roda_after_* method.
168
+ def include(*a)
169
+ res = super
170
+ def_roda_before
171
+ def_roda_after
172
+ res
173
+ end
174
+
164
175
  # When inheriting Roda, copy the shared data into the subclass,
165
176
  # and setup the request and response subclasses.
166
177
  def inherited(subclass)
@@ -174,8 +185,9 @@ class Roda
174
185
  subclass.opts[k] = v.dup
175
186
  end
176
187
  end
177
- subclass.instance_variable_set(:@route_block, @route_block)
178
- subclass.send(:build_rack_app)
188
+ if block = @raw_route_block
189
+ subclass.route(&block)
190
+ end
179
191
 
180
192
  request_class = Class.new(self::RodaRequest)
181
193
  request_class.roda_class = subclass
@@ -221,7 +233,9 @@ class Roda
221
233
  # This should only be called once per class, and if called multiple
222
234
  # times will overwrite the previous routing.
223
235
  def route(&block)
224
- @route_block = block
236
+ @raw_route_block = block
237
+ @route_block = block = convert_route_block(block)
238
+ @rack_app_route_block = rack_app_route_block(block)
225
239
  build_rack_app
226
240
  end
227
241
 
@@ -238,8 +252,7 @@ class Roda
238
252
 
239
253
  # Build the rack app to use
240
254
  def build_rack_app
241
- if block = @route_block
242
- block = rack_app_route_block(block)
255
+ if block = @rack_app_route_block
243
256
  app = lambda{|env| new(env).call(&block)}
244
257
  @middleware.reverse_each do |args, bl|
245
258
  mid, *args = args
@@ -250,11 +263,54 @@ class Roda
250
263
  end
251
264
  end
252
265
 
253
- # The route block to use when building the rack app.
266
+ # Modify the route block to use for any route block provided as input,
267
+ # which can include route blocks that are delegated to by the main route block.
254
268
  # Can be modified by plugins.
255
- def rack_app_route_block(block)
269
+ def convert_route_block(block)
256
270
  block
257
271
  end
272
+
273
+ # Build a _roda_before method that calls each _roda_before_* method
274
+ # in order, if any _roda_before_* methods are defined. Also, rebuild
275
+ # the route block if a _roda_before method is defined.
276
+ def def_roda_before
277
+ meths = private_instance_methods.grep(/\A_roda_before_\d\d/).sort.join(';')
278
+ unless meths.empty?
279
+ class_eval("def _roda_before; #{meths} end", __FILE__, __LINE__)
280
+ private :_roda_before
281
+ if @raw_route_block
282
+ route(&@raw_route_block)
283
+ end
284
+ end
285
+ end
286
+
287
+ # Build a _roda_after method that calls each _roda_after_* method
288
+ # in order, if any _roda_after_* methods are defined. Also, use
289
+ # the internal after hook plugin if the _roda_after method is defined.
290
+ def def_roda_after
291
+ meths = private_instance_methods.grep(/\A_roda_after_\d\d/).sort.map{|s| "#{s}(res)"}.join(';')
292
+ unless meths.empty?
293
+ plugin :_after_hook unless private_method_defined?(:_roda_after)
294
+ class_eval("def _roda_after(res); #{meths} end", __FILE__, __LINE__)
295
+ private :_roda_after
296
+ end
297
+ end
298
+
299
+ # The route block to use when building the rack app (or other initial
300
+ # entry point to the route block).
301
+ # By default, modifies the rack app route block to support before hooks
302
+ # if any before hooks are defined.
303
+ # Can be modified by plugins.
304
+ def rack_app_route_block(block)
305
+ if private_method_defined?(:_roda_before)
306
+ lambda do |r|
307
+ _roda_before
308
+ instance_exec(r, &block)
309
+ end
310
+ else
311
+ block
312
+ end
313
+ end
258
314
  end
259
315
 
260
316
  # Instance methods for the Roda class.
@@ -936,7 +992,6 @@ class Roda
936
992
 
937
993
  # Set the default headers when creating a response.
938
994
  def initialize
939
- @status = nil
940
995
  @headers = {}
941
996
  @body = []
942
997
  @length = 0
@@ -986,14 +1041,18 @@ class Roda
986
1041
  # # []]
987
1042
  def finish
988
1043
  b = @body
989
- empty = b.empty?
990
- s = (@status ||= empty ? 404 : default_status)
991
1044
  set_default_headers
992
1045
  h = @headers
993
1046
 
994
- if empty && (s == 304 || s == 204 || s == 205 || (s >= 100 && s <= 199))
995
- h.delete("Content-Type")
1047
+ if b.empty?
1048
+ s = @status || 404
1049
+ if (s == 304 || s == 204 || s == 205 || (s >= 100 && s <= 199))
1050
+ h.delete("Content-Type")
1051
+ else
1052
+ h["Content-Length"] ||= '0'
1053
+ end
996
1054
  else
1055
+ s = @status || default_status
997
1056
  h["Content-Length"] ||= @length.to_s
998
1057
  end
999
1058
 
@@ -7,19 +7,10 @@ class Roda
7
7
  # Allows for plugins to configure the order in which
8
8
  # after processing is done by using _roda_after_*
9
9
  # private instance methods that are called in sorted order.
10
+ # Loaded automatically by the base library if any _roda_after_*
11
+ # methods are defined.
10
12
  module AfterHook # :nodoc:
11
- module ClassMethods
12
- # Rebuild the _roda_after method whenever a plugin might
13
- # have added a _roda_after_* method.
14
- def include(*)
15
- res = super
16
- meths = private_instance_methods.grep(/\A_roda_after_\d\d/).sort.map{|s| "#{s}(res)"}.join(';')
17
- class_eval("def _roda_after(res); #{meths} end", __FILE__, __LINE__)
18
- private :_roda_after
19
- res
20
- end
21
- end
22
-
13
+ # Module for internal after hook support.
23
14
  module InstanceMethods
24
15
  # Run internal after hooks with the response
25
16
  def call
@@ -27,6 +18,13 @@ class Roda
27
18
  ensure
28
19
  _roda_after(res)
29
20
  end
21
+
22
+ private
23
+
24
+ # Empty roda_after method, so nothing breaks if the module is included.
25
+ # This method will be overridden in most classes using this module.
26
+ def _roda_after(res)
27
+ end
30
28
  end
31
29
  end
32
30
 
@@ -3,47 +3,9 @@
3
3
  #
4
4
  class Roda
5
5
  module RodaPlugins
6
- # Internal before hook module, not for external use.
7
- # Allows for plugins to configure the order in which
8
- # before processing is done by using _roda_before_*
9
- # private instance methods that are called in sorted order.
6
+ # Deprecated plugin, only exists for backwards compatibility.
7
+ # Features are now part of base library.
10
8
  module BeforeHook # :nodoc:
11
- # Rebuild the rack app if the rack app already exists,
12
- # so the before hooks are setup inside the rack app
13
- # route block.
14
- def self.configure(app)
15
- app.instance_exec do
16
- build_rack_app if @app
17
- end
18
- end
19
-
20
- module ClassMethods
21
- # Rebuild the _roda_before method whenever a plugin might
22
- # have added a _roda_before_* method.
23
- def include(*a)
24
- res = super
25
- def_roda_before
26
- res
27
- end
28
-
29
- private
30
-
31
- # Build a _roda_before method that calls each _roda_before_* method
32
- # in order.
33
- def def_roda_before
34
- meths = private_instance_methods.grep(/\A_roda_before_\d\d/).sort.join(';')
35
- class_eval("def _roda_before; #{meths} end", __FILE__, __LINE__)
36
- private :_roda_before
37
- end
38
-
39
- # Modify rack app route block to use before hook.
40
- def rack_app_route_block(block)
41
- lambda do |r|
42
- _roda_before
43
- instance_exec(r, &block)
44
- end
45
- end
46
- end
47
9
  end
48
10
 
49
11
  register_plugin(:_before_hook, BeforeHook)
@@ -52,10 +52,6 @@ class Roda
52
52
  # the normal +route+ class method to define your routing tree. This plugin does make it simpler to
53
53
  # add additional routes after the routing tree has already been defined, though.
54
54
  module ClassLevelRouting
55
- def self.load_dependencies(app)
56
- app.plugin :_after_hook
57
- end
58
-
59
55
  # Initialize the class_routes array when the plugin is loaded. Also, if the application doesn't
60
56
  # currently have a routing block, setup an empty routing block so that things will still work if
61
57
  # a routing block isn't added.
@@ -68,7 +64,7 @@ class Roda
68
64
  # Define routing methods that will store class level routes.
69
65
  [:root, :on, :is, :get, :post, :delete, :head, :options, :link, :patch, :put, :trace, :unlink].each do |meth|
70
66
  define_method(meth) do |*args, &block|
71
- opts[:class_level_routes] << [meth, args, block].freeze
67
+ opts[:class_level_routes] << [meth, args, convert_route_block(block)].freeze
72
68
  end
73
69
  end
74
70
 
@@ -94,6 +90,7 @@ class Roda
94
90
  # Reset the response so it doesn't inherit the status or any headers from
95
91
  # the original response.
96
92
  @_response.send(:initialize)
93
+ @_response.status = nil
97
94
  result.replace(_call do |r|
98
95
  opts[:class_level_routes].each do |meth, args, blk|
99
96
  r.instance_variable_set(:@remaining_path, @_original_remaining_path)
@@ -19,11 +19,6 @@ class Roda
19
19
  # plugin :common_logger, $stdout
20
20
  # plugin :common_logger, Logger.new('filename')
21
21
  module CommonLogger
22
- def self.load_dependencies(app, _=nil)
23
- app.plugin :_after_hook
24
- app.plugin :_before_hook
25
- end
26
-
27
22
  def self.configure(app, logger=nil)
28
23
  app.opts[:common_logger] = logger || app.opts[:common_logger] || $stderr
29
24
  app.opts[:common_logger_meth] = app.opts[:common_logger].method(logger.respond_to?(:write) ? :write : :<<)
@@ -36,10 +36,6 @@ class Roda
36
36
  # flash['a'] # = >'b'
37
37
  # end
38
38
  module Flash
39
- def self.load_dependencies(app)
40
- app.plugin :_after_hook
41
- end
42
-
43
39
  # Simple flash hash, where assiging to the hash updates the flash
44
40
  # used in the following request.
45
41
  class FlashHash < DelegateClass(Hash)
@@ -37,10 +37,6 @@ class Roda
37
37
  # this plugin those HEAD requests will return a 404 status, which
38
38
  # may prevent search engines from crawling your website.
39
39
  module Head
40
- def self.load_dependencies(app)
41
- app.plugin :_after_hook
42
- end
43
-
44
40
  # used to ensure proper resource release on HEAD requests
45
41
  # we do not respond to a to_path method, here.
46
42
  class CloseLater
@@ -16,10 +16,6 @@ class Roda
16
16
  module Heartbeat
17
17
  HEARTBEAT_RESPONSE = [200, {'Content-Type'=>'text/plain'}.freeze, ['OK'.freeze].freeze].freeze
18
18
 
19
- def self.load_dependencies(app, opts=OPTS)
20
- app.plugin :_before_hook
21
- end
22
-
23
19
  # Set the heartbeat path to the given path.
24
20
  def self.configure(app, opts=OPTS)
25
21
  app.opts[:heartbeat_path] = (opts[:path] || app.opts[:heartbeat_path] || "/heartbeat").dup.freeze
@@ -33,11 +33,6 @@ class Roda
33
33
  # an after block will not affect the returned status. Note that after
34
34
  # hooks can be called with nil if an exception is raised during routing.
35
35
  module Hooks
36
- def self.load_dependencies(app)
37
- app.plugin :_before_hook
38
- app.plugin :_after_hook
39
- end
40
-
41
36
  def self.configure(app)
42
37
  app.opts[:before_hook] ||= nil
43
38
  app.opts[:after_hook] ||= nil
@@ -301,7 +301,7 @@ class Roda
301
301
 
302
302
  begin
303
303
  begin
304
- scope.process_mail(&route_block)
304
+ scope.process_mail(&@rack_app_route_block)
305
305
  rescue UnhandledMail
306
306
  scope.unhandled_mail_hook
307
307
  else
@@ -140,7 +140,7 @@ class Roda
140
140
  def mail(path, *args)
141
141
  mail = ::Mail.new
142
142
  catch(:no_mail) do
143
- unless mail.equal?(new("PATH_INFO"=>path, 'SCRIPT_NAME'=>'', "REQUEST_METHOD"=>"MAIL", 'rack.input'=>StringIO.new, 'roda.mail'=>mail, 'roda.mail_args'=>args).call(&route_block))
143
+ unless mail.equal?(new("PATH_INFO"=>path, 'SCRIPT_NAME'=>'', "REQUEST_METHOD"=>"MAIL", 'rack.input'=>StringIO.new, 'roda.mail'=>mail, 'roda.mail_args'=>args).call(&@rack_app_route_block))
144
144
  raise Error, "route did not return mail instance for #{path.inspect}, #{args.inspect}"
145
145
  end
146
146
  mail
@@ -170,7 +170,7 @@ class Roda
170
170
  def route(name=nil, namespace=nil, &block)
171
171
  if name
172
172
  opts[:namespaced_routes][namespace] ||= {}
173
- opts[:namespaced_routes][namespace][name] = block
173
+ opts[:namespaced_routes][namespace][name] = convert_route_block(block)
174
174
  self::RodaRequest.clear_named_route_regexp!(namespace)
175
175
  else
176
176
  super(&block)
@@ -169,7 +169,7 @@ class Roda
169
169
  end
170
170
 
171
171
  if layout = opts.fetch(:layout, true)
172
- opts[:layout] = true unless opts.has_key?(:layout)
172
+ opts[:layout] = true
173
173
 
174
174
  case layout
175
175
  when Hash
@@ -0,0 +1,49 @@
1
+ # frozen-string-literal: true
2
+
3
+ #
4
+ class Roda
5
+ module RodaPlugins
6
+ # The route_block_args plugin lets you customize what arguments are passed to
7
+ # the +route+ block. So if you have an application that always needs access
8
+ # to the +response+, the +params+, the +env+, or the +session+, you can use
9
+ # this plugin so that any of those can be arguments to the route block.
10
+ # Example:
11
+ #
12
+ # class App < Roda
13
+ # plugin :route_block_args do
14
+ # [request, request.params, response]
15
+ # end
16
+ #
17
+ # route do |r, params, res|
18
+ # r.post do
19
+ # artist = Artist.create(name: params['name'].to_s)
20
+ # res.status = 201
21
+ # artist.id.to_s
22
+ # end
23
+ # end
24
+ # end
25
+ module RouteBlockArgs
26
+ def self.configure(app, &block)
27
+ app.instance_exec do
28
+ opts[:route_block_args] = block
29
+ route(&@raw_route_block) if @raw_route_block
30
+ end
31
+ end
32
+
33
+ # Override the route block input so that the block
34
+ # given is passed the arguments specified by the
35
+ # block given to the route_block_args plugin.
36
+ module ClassMethods
37
+ private
38
+
39
+ def convert_route_block(block)
40
+ proc do |r|
41
+ instance_exec(*instance_exec(&opts[:route_block_args]), &block)
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ register_plugin :route_block_args, RouteBlockArgs
48
+ end
49
+ end
@@ -148,10 +148,6 @@ class Roda
148
148
  class CookieTooLarge < RodaError
149
149
  end
150
150
 
151
- def self.load_dependencies(app, opts=OPTS)
152
- app.plugin :_after_hook
153
- end
154
-
155
151
  # Split given secret into a cipher secret and an hmac secret.
156
152
  def self.split_secret(name, secret)
157
153
  raise RodaError, "sessions plugin :#{name} option must be a String" unless secret.is_a?(String)
@@ -49,10 +49,6 @@ class Roda
49
49
  # static_route block to have shared behavior for different request methods,
50
50
  # while still handling the request methods differently.
51
51
  module StaticRouting
52
- def self.load_dependencies(app)
53
- app.plugin :_before_hook
54
- end
55
-
56
52
  def self.configure(app)
57
53
  app.opts[:static_routes] = {}
58
54
  end
@@ -100,7 +96,7 @@ class Roda
100
96
 
101
97
  # Add a static route for the given method.
102
98
  def add_static_route(method, path, &block)
103
- (opts[:static_routes][path] ||= {})[method] = block
99
+ (opts[:static_routes][path] ||= {})[method] = convert_route_block(block)
104
100
  end
105
101
  end
106
102
 
@@ -23,10 +23,6 @@ class Roda
23
23
  # cleared. So if you want to be sure the headers are set even in your block,
24
24
  # you need to reset them in the block.
25
25
  module StatusHandler
26
- def self.load_dependencies(app)
27
- app.plugin :_after_hook
28
- end
29
-
30
26
  def self.configure(app)
31
27
  app.opts[:status_handler] ||= {}
32
28
  end
@@ -50,7 +46,9 @@ class Roda
50
46
  # If routing returns a response we have a handler for, call that handler.
51
47
  def _roda_after_20__status_handler(result)
52
48
  if result && (block = opts[:status_handler][result[0]]) && (v = result[2]).is_a?(Array) && v.empty?
53
- @_response.headers.clear
49
+ res = @_response
50
+ res.headers.clear
51
+ res.status = result[0]
54
52
  result.replace(_call(&block))
55
53
  end
56
54
  end
data/lib/roda/version.rb CHANGED
@@ -4,7 +4,7 @@ class Roda
4
4
  RodaMajorVersion = 3
5
5
 
6
6
  # The minor version of Roda, updated for new feature releases of Roda.
7
- RodaMinorVersion = 16
7
+ RodaMinorVersion = 17
8
8
 
9
9
  # The patch version of Roda, updated only for bug fixes from the last
10
10
  # feature release.
@@ -0,0 +1,19 @@
1
+ require_relative "../spec_helper"
2
+
3
+ describe "deprecated _after_hook plugin" do
4
+ it "shouldn't break things" do
5
+ x = []
6
+ app(:_after_hook) do |r|
7
+ x << 0
8
+ 'a'
9
+ end
10
+ @app.send(:include, Module.new do
11
+ define_method(:_roda_after_00_test){|_| x << 1}
12
+ private :_roda_after_00_test
13
+ end)
14
+
15
+ body.must_equal 'a'
16
+ x.must_equal [0, 1]
17
+ end
18
+ end
19
+
@@ -161,4 +161,31 @@ describe "class_level_routing plugin" do
161
161
 
162
162
  proc{app.on{}}.must_raise
163
163
  end
164
+
165
+ it 'works with route_block_args plugin' do
166
+ app(:bare) do
167
+ plugin :class_level_routing
168
+ plugin :route_block_args do
169
+ [request.path]
170
+ end
171
+
172
+ root do |path|
173
+ "root-#{path}"
174
+ end
175
+
176
+ get do |path|
177
+ "GET-#{path}"
178
+ end
179
+
180
+ route do |path|
181
+ request.get('foo') do
182
+ path
183
+ end
184
+ end
185
+ end
186
+
187
+ body.must_equal 'root-/'
188
+ body('/a').must_equal 'GET-/a'
189
+ body('/foo').must_equal '/foo'
190
+ end
164
191
  end
@@ -398,5 +398,54 @@ describe "mail_processor plugin" do
398
398
  check{app.process_mail(new_mail{|m| m.body "Found bar\n--\nFound foo"})}.must_equal [:f, 'foo']
399
399
  check{app.process_mail(new_mail{|m| m.body "> Found baz\nFound quux"})}.must_equal [:f2, 'quux', "Found quux"]
400
400
  end
401
+
402
+ it "works with route_block_args plugin" do
403
+ @processed = processed = []
404
+ app(:bare) do
405
+ plugin :mail_processor
406
+ plugin :route_block_args do
407
+ [to, from]
408
+ end
409
+ route do |t, f|
410
+ request.handle do
411
+ processed << t << f
412
+ end
413
+ end
414
+ handled_mail do
415
+ # processed << :h << mail.to.first
416
+ end
417
+ end
418
+ check{app.process_mail(new_mail)}.must_equal [["a@example.com"], ["b@example.com"]]
419
+ end
420
+
421
+ it "works with hooks plugin, calling after hook before *_mail hooks" do
422
+ @processed = processed = []
423
+ app(:bare) do
424
+ plugin :mail_processor
425
+ plugin :hooks
426
+ before do
427
+ processed << 1
428
+ end
429
+ after do
430
+ processed << 2
431
+ end
432
+ route do |r|
433
+ processed << 3
434
+ r.handle_to('a@example.com') do
435
+ end
436
+ end
437
+ handled_mail do
438
+ processed << 4
439
+ end
440
+ unhandled_mail do
441
+ processed << 5
442
+ end
443
+ after_mail do
444
+ processed << 6
445
+ end
446
+ end
447
+ check{app.process_mail(new_mail)}.must_equal [1, 3, 2, 4, 6]
448
+ check{app.process_mail(new_mail{|m| m.to 'x@example.com'})}.must_equal [1, 3, 2, 5, 6]
449
+ end
401
450
  end
402
451
  end
@@ -244,5 +244,39 @@ describe "mailer plugin" do
244
244
  m.parts.last.content_type.must_match(/\Atext\/css/)
245
245
  m.parts.last.body.decoded.gsub("\r\n", "\n").must_equal File.read('spec/assets/css/raw.css')
246
246
  end
247
+
248
+ it "works with route_block_args plugin" do
249
+ app(:bare) do
250
+ plugin :mailer
251
+ plugin :route_block_args do
252
+ [request.path]
253
+ end
254
+ route do |path|
255
+ path
256
+ end
257
+ end
258
+ app.mail('/').body.decoded.must_equal '/'
259
+ app.mail('/foo').body.decoded.must_equal '/foo'
260
+ end
261
+
262
+ it "works with hooks plugin" do
263
+ x = []
264
+ app(:bare) do
265
+ plugin :mailer
266
+ plugin :hooks
267
+ before do
268
+ x << 1
269
+ end
270
+ after do
271
+ x << 2
272
+ end
273
+ route do
274
+ x << 3
275
+ ''
276
+ end
277
+ end
278
+ app.mail('/').body.decoded.must_equal ''
279
+ x.must_equal [1, 3, 2]
280
+ end
247
281
  end
248
282
  end
@@ -234,27 +234,23 @@ describe "multi_route plugin" do
234
234
  end
235
235
 
236
236
  it "handles namespaces in r.multi_route" do
237
- app.route("foo") do |r|
238
- @p = 'f'
239
- r.multi_route("foo")
240
- @p
237
+ app(:multi_route) do |path|
238
+ request.multi_route
239
+ path
241
240
  end
242
-
243
- app.route("bar") do |r|
244
- @p = 'b'
245
- r.multi_route("bar")
246
- @p
241
+ app.plugin :route_block_args do
242
+ [request.path, request]
247
243
  end
248
-
249
- app.route do |r|
250
- r.multi_route
244
+ app.route("foo") do |path, r|
245
+ r.multi_route("foo")
246
+ "f-#{path}"
247
+ end
248
+ app.route("bar", "foo") do |path|
249
+ "b-#{path}"
251
250
  end
252
251
 
253
- body('/foo').must_equal 'f'
254
- body('/foo/foo').must_equal 'fff'
255
- body('/foo/bar').must_equal 'ffb'
256
- body('/bar').must_equal 'b'
257
- body('/bar/foo').must_equal 'bbf'
258
- body('/bar/bar').must_equal 'bbb'
252
+ body.must_equal '/'
253
+ body('/foo').must_equal 'f-/foo'
254
+ body('/foo/bar').must_equal 'b-/foo/bar'
259
255
  end
260
256
  end
@@ -515,9 +515,9 @@ describe "render plugin" do
515
515
 
516
516
  it "render plugin call should not override existing options" do
517
517
  c = Class.new(Roda)
518
- c.plugin :render, :layout=>:foo
518
+ c.plugin :render, :layout_opts=>{:template=>'foo'}
519
519
  c.plugin :render
520
- c.render_opts[:layout].must_equal :foo
520
+ c.render_opts[:layout_opts][:template].must_equal 'foo'
521
521
  end
522
522
 
523
523
  it "should not use cache by default in subclass if not caching by default in superclass" do
@@ -0,0 +1,86 @@
1
+ require_relative "../spec_helper"
2
+
3
+ describe "route_block_args plugin" do
4
+ it "works with hooks when loaded last" do
5
+ a = []
6
+ app(:bare) do
7
+ plugin :hooks
8
+ before { a << 1 }
9
+ after { a << 2 }
10
+ plugin :route_block_args do
11
+ [request, response]
12
+ end
13
+ route do |req, res|
14
+ response.status = 401
15
+ a << req.path << res.status
16
+ "1"
17
+ end
18
+ end
19
+ body.must_equal "1"
20
+ a.must_equal [1, '/', 401, 2]
21
+ end
22
+
23
+ it "works with hooks when loaded first" do
24
+ a = []
25
+ app(:bare) do
26
+ plugin :route_block_args do
27
+ [request, response]
28
+ end
29
+ plugin :hooks
30
+ before { a << 1 }
31
+ after { a << 2 }
32
+ route do |req, res|
33
+ response.status = 401
34
+ a << req.path << res.status
35
+ "1"
36
+ end
37
+ end
38
+ body.must_equal "1"
39
+ a.must_equal [1, '/', 401, 2]
40
+ end
41
+
42
+ it "still supports a single route block argument" do
43
+ app(:bare) do
44
+ plugin :route_block_args do
45
+ request
46
+ end
47
+ route { |r| "OK" }
48
+ end
49
+
50
+ status('/').must_equal(200)
51
+ end
52
+
53
+ it "supports many route block arguments" do
54
+ app(:bare) do
55
+ plugin :route_block_args do
56
+ [request.params, request.env, response.headers, response.body]
57
+ end
58
+ route do |p, e, h, b|
59
+ h['Foo'] = 'Bar'
60
+ b << "#{p['a']}-#{e['B']}"
61
+ "x"
62
+ end
63
+ end
64
+
65
+ header('Foo', 'rack.input'=>StringIO.new).must_equal('Bar')
66
+ body('rack.input'=>StringIO.new).must_equal('-')
67
+ body('QUERY_STRING'=>'a=c', 'B'=>'D', 'rack.input'=>StringIO.new).must_equal('c-D')
68
+ end
69
+
70
+ it "works if given after the route block" do
71
+ app(:bare) do
72
+ route do |p, e, h, b|
73
+ h['Foo'] = 'Bar'
74
+ b << "#{p['a']}-#{e['B']}"
75
+ "x"
76
+ end
77
+ plugin :route_block_args do
78
+ [request.params, request.env, response.headers, response.body]
79
+ end
80
+ end
81
+
82
+ header('Foo', 'rack.input'=>StringIO.new).must_equal('Bar')
83
+ body('rack.input'=>StringIO.new).must_equal('-')
84
+ body('QUERY_STRING'=>'a=c', 'B'=>'D', 'rack.input'=>StringIO.new).must_equal('c-D')
85
+ end
86
+ end
@@ -145,4 +145,26 @@ describe "static_routing plugin" do
145
145
  end.must_raise
146
146
  end
147
147
  end
148
+
149
+ it 'works with route_block_args plugin' do
150
+ app(:bare) do
151
+ plugin :static_routing
152
+ plugin :route_block_args do
153
+ [request.request_method, request.path]
154
+ end
155
+
156
+ static_route "/foo" do |meth, path|
157
+ "#{path}-#{meth}"
158
+ end
159
+
160
+ static_get "/bar" do |meth, path|
161
+ "#{path}-#{meth}-bar"
162
+ end
163
+
164
+ route{'a'}
165
+ end
166
+
167
+ body('/foo').must_equal '/foo-GET'
168
+ body('/bar').must_equal '/bar-GET-bar'
169
+ end
148
170
  end
@@ -60,6 +60,50 @@ describe "view_options plugin" do
60
60
  body.strip.must_equal "<title>Alternative Layout: Home</title>\n<h1>Subdir: About Roda</h1>"
61
61
  end
62
62
 
63
+ it "should allow overriding :layout plugin option with set_layout_options :template" do
64
+ app(:bare) do
65
+ plugin :render, :views=>'spec/views', :allowed_paths=>['spec/views']
66
+ plugin :view_options
67
+
68
+ route do
69
+ set_view_options :views=>'spec/views/about'
70
+ set_layout_options :template=>'layout-alternative'
71
+ view('_test', :locals=>{:title=>'About Roda'}, :layout_opts=>{:locals=>{:title=>'Home'}})
72
+ end
73
+ end
74
+
75
+ body.strip.must_equal "<title>Alternative Layout: Home</title>\n<h1>Subdir: About Roda</h1>"
76
+ end
77
+
78
+ it "should allow overriding :layout_opts :template plugin option with set_layout_options :template" do
79
+ app(:bare) do
80
+ plugin :render, :views=>'spec/views', :allowed_paths=>['spec/views'], :layout_opts=>{:template=>'layout'}
81
+ plugin :view_options
82
+
83
+ route do
84
+ set_view_options :views=>'spec/views/about', :layout=>'layout-alternative'
85
+ set_layout_options :template=>'layout-alternative'
86
+ view('_test', :locals=>{:title=>'About Roda'}, :layout_opts=>{:locals=>{:title=>'Home'}})
87
+ end
88
+ end
89
+
90
+ body.strip.must_equal "<title>Alternative Layout: Home</title>\n<h1>Subdir: About Roda</h1>"
91
+ end
92
+
93
+ it "should allow overriding :layout plugin option with set_view_options :layout" do
94
+ app(:bare) do
95
+ plugin :render, :views=>'spec/views', :allowed_paths=>['spec/views'], :layout=>'layout'
96
+ plugin :view_options
97
+
98
+ route do
99
+ set_view_options :views=>'spec/views/about', :layout=>'layout-alternative'
100
+ view('_test', :locals=>{:title=>'About Roda'}, :layout_opts=>{:locals=>{:title=>'Home'}})
101
+ end
102
+ end
103
+
104
+ body.strip.must_equal "<title>Alternative Layout: Home</title>\n<h1>Subdir: About Roda</h1>"
105
+ end
106
+
63
107
  it "should set view and layout options to use" do
64
108
  app(:bare) do
65
109
  plugin :render, :allowed_paths=>['spec/views']
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: roda
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.16.0
4
+ version: 3.17.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-01-18 00:00:00.000000000 Z
11
+ date: 2019-02-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -213,6 +213,7 @@ extra_rdoc_files:
213
213
  - doc/release_notes/3.14.1.txt
214
214
  - doc/release_notes/3.15.0.txt
215
215
  - doc/release_notes/3.16.0.txt
216
+ - doc/release_notes/3.17.0.txt
216
217
  files:
217
218
  - CHANGELOG
218
219
  - MIT-LICENSE
@@ -264,6 +265,7 @@ files:
264
265
  - doc/release_notes/3.14.1.txt
265
266
  - doc/release_notes/3.15.0.txt
266
267
  - doc/release_notes/3.16.0.txt
268
+ - doc/release_notes/3.17.0.txt
267
269
  - doc/release_notes/3.2.0.txt
268
270
  - doc/release_notes/3.3.0.txt
269
271
  - doc/release_notes/3.4.0.txt
@@ -345,6 +347,7 @@ files:
345
347
  - lib/roda/plugins/request_aref.rb
346
348
  - lib/roda/plugins/request_headers.rb
347
349
  - lib/roda/plugins/response_request.rb
350
+ - lib/roda/plugins/route_block_args.rb
348
351
  - lib/roda/plugins/route_csrf.rb
349
352
  - lib/roda/plugins/run_append_slash.rb
350
353
  - lib/roda/plugins/run_handler.rb
@@ -379,6 +382,7 @@ files:
379
382
  - spec/integration_spec.rb
380
383
  - spec/matchers_spec.rb
381
384
  - spec/opts_spec.rb
385
+ - spec/plugin/_after_hook_spec.rb
382
386
  - spec/plugin/all_verbs_spec.rb
383
387
  - spec/plugin/assets_preloading_spec.rb
384
388
  - spec/plugin/assets_spec.rb
@@ -448,6 +452,7 @@ files:
448
452
  - spec/plugin/request_aref_spec.rb
449
453
  - spec/plugin/request_headers_spec.rb
450
454
  - spec/plugin/response_request_spec.rb
455
+ - spec/plugin/route_block_args_spec.rb
451
456
  - spec/plugin/route_csrf_spec.rb
452
457
  - spec/plugin/run_append_slash_spec.rb
453
458
  - spec/plugin/run_handler_spec.rb