roda 3.16.0 → 3.17.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 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