roda 3.17.0 → 3.18.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 +4 -4
- data/CHANGELOG +48 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +22 -4
- data/doc/release_notes/3.18.0.txt +170 -0
- data/lib/roda.rb +249 -26
- data/lib/roda/plugins/_after_hook.rb +4 -26
- data/lib/roda/plugins/_before_hook.rb +30 -2
- data/lib/roda/plugins/branch_locals.rb +2 -2
- data/lib/roda/plugins/class_level_routing.rb +9 -7
- data/lib/roda/plugins/default_headers.rb +15 -1
- data/lib/roda/plugins/default_status.rb +9 -10
- data/lib/roda/plugins/direct_call.rb +38 -0
- data/lib/roda/plugins/error_email.rb +1 -1
- data/lib/roda/plugins/error_handler.rb +37 -11
- data/lib/roda/plugins/hooks.rb +28 -30
- data/lib/roda/plugins/mail_processor.rb +16 -11
- data/lib/roda/plugins/mailer.rb +1 -1
- data/lib/roda/plugins/middleware.rb +13 -3
- data/lib/roda/plugins/multi_route.rb +3 -3
- data/lib/roda/plugins/named_templates.rb +4 -4
- data/lib/roda/plugins/path.rb +13 -8
- data/lib/roda/plugins/render.rb +2 -2
- data/lib/roda/plugins/route_block_args.rb +4 -3
- data/lib/roda/plugins/route_csrf.rb +9 -4
- data/lib/roda/plugins/sessions.rb +2 -1
- data/lib/roda/plugins/shared_vars.rb +1 -1
- data/lib/roda/plugins/static_routing.rb +7 -17
- data/lib/roda/plugins/status_handler.rb +5 -3
- data/lib/roda/plugins/view_options.rb +2 -2
- data/lib/roda/version.rb +1 -1
- data/spec/define_roda_method_spec.rb +257 -0
- data/spec/plugin/class_level_routing_spec.rb +0 -27
- data/spec/plugin/default_headers_spec.rb +7 -0
- data/spec/plugin/default_status_spec.rb +31 -1
- data/spec/plugin/direct_call_spec.rb +28 -0
- data/spec/plugin/error_handler_spec.rb +27 -0
- data/spec/plugin/hooks_spec.rb +21 -0
- data/spec/plugin/middleware_spec.rb +108 -36
- data/spec/plugin/multi_route_spec.rb +12 -0
- data/spec/plugin/route_csrf_spec.rb +27 -0
- data/spec/plugin/sessions_spec.rb +26 -1
- data/spec/plugin/static_routing_spec.rb +25 -3
- data/spec/plugin/status_handler_spec.rb +17 -0
- data/spec/route_spec.rb +39 -0
- data/spec/spec_helper.rb +2 -2
- metadata +9 -3
data/lib/roda/plugins/mailer.rb
CHANGED
|
@@ -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).
|
|
143
|
+
unless mail.equal?(new("PATH_INFO"=>path, 'SCRIPT_NAME'=>'', "REQUEST_METHOD"=>"MAIL", 'rack.input'=>StringIO.new, 'roda.mail'=>mail, 'roda.mail_args'=>args)._roda_handle_main_route)
|
|
144
144
|
raise Error, "route did not return mail instance for #{path.inspect}, #{args.inspect}"
|
|
145
145
|
end
|
|
146
146
|
mail
|
|
@@ -127,16 +127,26 @@ class Roda
|
|
|
127
127
|
Forwarder.new(self, app, *args, &block)
|
|
128
128
|
end
|
|
129
129
|
end
|
|
130
|
+
end
|
|
130
131
|
|
|
132
|
+
module InstanceMethods
|
|
131
133
|
# Override the route block so that if no route matches, we throw so
|
|
132
|
-
# that the next middleware is called.
|
|
133
|
-
def
|
|
134
|
+
# that the next middleware is called. Old Dispatch API.
|
|
135
|
+
def call(&block)
|
|
134
136
|
super do |r|
|
|
135
|
-
res = instance_exec(r, &block)
|
|
137
|
+
res = instance_exec(r, &block) # call Fallback
|
|
136
138
|
throw :next, true if r.forward_next
|
|
137
139
|
res
|
|
138
140
|
end
|
|
139
141
|
end
|
|
142
|
+
|
|
143
|
+
# Override the route block so that if no route matches, we throw so
|
|
144
|
+
# that the next middleware is called.
|
|
145
|
+
def _roda_run_main_route(r)
|
|
146
|
+
res = super
|
|
147
|
+
throw :next, true if r.forward_next
|
|
148
|
+
res
|
|
149
|
+
end
|
|
140
150
|
end
|
|
141
151
|
|
|
142
152
|
module RequestMethods
|
|
@@ -169,8 +169,8 @@ class Roda
|
|
|
169
169
|
# call super.
|
|
170
170
|
def route(name=nil, namespace=nil, &block)
|
|
171
171
|
if name
|
|
172
|
-
opts[:namespaced_routes][namespace] ||= {}
|
|
173
|
-
|
|
172
|
+
routes = opts[:namespaced_routes][namespace] ||= {}
|
|
173
|
+
routes[name] = define_roda_method(routes[name] || "multi_route_#{namespace}_#{name}", 1, &convert_route_block(block))
|
|
174
174
|
self::RodaRequest.clear_named_route_regexp!(namespace)
|
|
175
175
|
else
|
|
176
176
|
super(&block)
|
|
@@ -213,7 +213,7 @@ class Roda
|
|
|
213
213
|
|
|
214
214
|
# Dispatch to the named route with the given name.
|
|
215
215
|
def route(name, namespace=nil)
|
|
216
|
-
scope.
|
|
216
|
+
scope.send(roda_class.named_route(name, namespace), self)
|
|
217
217
|
end
|
|
218
218
|
end
|
|
219
219
|
end
|
|
@@ -69,7 +69,7 @@ class Roda
|
|
|
69
69
|
|
|
70
70
|
# Store a new template block and options for the given template name.
|
|
71
71
|
def template(name, options=nil, &block)
|
|
72
|
-
opts[:named_templates][name.to_s] = [options, block].freeze
|
|
72
|
+
opts[:named_templates][name.to_s] = [options, define_roda_method("named_templates_#{name}", 0, &block)].freeze
|
|
73
73
|
nil
|
|
74
74
|
end
|
|
75
75
|
end
|
|
@@ -80,14 +80,14 @@ class Roda
|
|
|
80
80
|
# If a template name is given and it matches a named template, call
|
|
81
81
|
# the named template block to get the inline template to use.
|
|
82
82
|
def find_template(options)
|
|
83
|
-
if options[:template] && (template_opts,
|
|
83
|
+
if options[:template] && (template_opts, meth = opts[:named_templates][template_name(options)]; meth)
|
|
84
84
|
if template_opts
|
|
85
|
-
options =
|
|
85
|
+
options = template_opts.merge(options)
|
|
86
86
|
else
|
|
87
87
|
options = Hash[options]
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
-
options[:inline] =
|
|
90
|
+
options[:inline] = send(meth)
|
|
91
91
|
|
|
92
92
|
super(options)
|
|
93
93
|
else
|
data/lib/roda/plugins/path.rb
CHANGED
|
@@ -10,7 +10,7 @@ class Roda
|
|
|
10
10
|
#
|
|
11
11
|
# Additionally, you can call the +path+ class method with a class and a block, and it will register
|
|
12
12
|
# the class. You can then call the +path+ instance method with an instance of that class, and it will
|
|
13
|
-
#
|
|
13
|
+
# execute the block in the context of the route block scope with the arguments provided to path.
|
|
14
14
|
#
|
|
15
15
|
# Example:
|
|
16
16
|
#
|
|
@@ -60,9 +60,8 @@ class Roda
|
|
|
60
60
|
# method. If a Symbol or String, uses the value as the url method name.
|
|
61
61
|
# :url_only :: Do not create a path method, just a url method.
|
|
62
62
|
#
|
|
63
|
-
# Note that if :add_script_name, :url, or :url_only is used, will also create a
|
|
64
|
-
# method.
|
|
65
|
-
# a block to a block that is instance_execed.
|
|
63
|
+
# Note that if :add_script_name, :url, or :url_only is used, the path method will also create a
|
|
64
|
+
# <tt>_*_path</tt> private method.
|
|
66
65
|
module Path
|
|
67
66
|
DEFAULT_PORTS = {'http' => 80, 'https' => 443}.freeze
|
|
68
67
|
|
|
@@ -73,6 +72,7 @@ class Roda
|
|
|
73
72
|
app.instance_eval do
|
|
74
73
|
self.opts[:path_class_by_name] = opts.fetch(:by_name, ENV['RACK_ENV'] == 'development')
|
|
75
74
|
self.opts[:path_classes] ||= {}
|
|
75
|
+
self.opts[:path_class_methods] ||= {}
|
|
76
76
|
unless path_block(String)
|
|
77
77
|
path(String){|str| str}
|
|
78
78
|
end
|
|
@@ -88,6 +88,7 @@ class Roda
|
|
|
88
88
|
# Freeze the path classes when freezing the app.
|
|
89
89
|
def freeze
|
|
90
90
|
path_classes.freeze
|
|
91
|
+
opts[:path_classes_methods].freeze
|
|
91
92
|
super
|
|
92
93
|
end
|
|
93
94
|
|
|
@@ -100,6 +101,7 @@ class Roda
|
|
|
100
101
|
name = name.name
|
|
101
102
|
end
|
|
102
103
|
path_classes[name] = block
|
|
104
|
+
self.opts[:path_class_methods][name] = define_roda_method("path_#{name}", :any, &block)
|
|
103
105
|
return
|
|
104
106
|
end
|
|
105
107
|
|
|
@@ -164,6 +166,7 @@ class Roda
|
|
|
164
166
|
|
|
165
167
|
# Return the block related to the given class, or nil if there is no block.
|
|
166
168
|
def path_block(klass)
|
|
169
|
+
# RODA4: Remove
|
|
167
170
|
if opts[:path_class_by_name]
|
|
168
171
|
klass = klass.name
|
|
169
172
|
end
|
|
@@ -175,14 +178,16 @@ class Roda
|
|
|
175
178
|
# Return a path based on the class of the object. The object passed must have
|
|
176
179
|
# had its class previously registered with the application. If the app's
|
|
177
180
|
# :add_script_name option is true, this prepends the SCRIPT_NAME to the path.
|
|
178
|
-
def path(obj, *args)
|
|
181
|
+
def path(obj, *args, &block)
|
|
179
182
|
app = self.class
|
|
180
|
-
|
|
183
|
+
opts = app.opts
|
|
184
|
+
klass = opts[:path_class_by_name] ? obj.class.name : obj.class
|
|
185
|
+
unless meth = opts[:path_class_methods][klass]
|
|
181
186
|
raise RodaError, "unrecognized object given to Roda#path: #{obj.inspect}"
|
|
182
187
|
end
|
|
183
188
|
|
|
184
|
-
path =
|
|
185
|
-
path = request.script_name.to_s + path if
|
|
189
|
+
path = send(meth, obj, *args, &block)
|
|
190
|
+
path = request.script_name.to_s + path if opts[:add_script_name]
|
|
186
191
|
path
|
|
187
192
|
end
|
|
188
193
|
end
|
data/lib/roda/plugins/render.rb
CHANGED
|
@@ -342,10 +342,10 @@ class Roda
|
|
|
342
342
|
opts = found_template_opts || find_template(opts)
|
|
343
343
|
template_opts = render_opts[:template_opts]
|
|
344
344
|
if engine_opts = render_opts[:engine_opts][opts[:engine]]
|
|
345
|
-
template_opts =
|
|
345
|
+
template_opts = template_opts.merge(engine_opts)
|
|
346
346
|
end
|
|
347
347
|
if current_template_opts = opts[:template_opts]
|
|
348
|
-
template_opts =
|
|
348
|
+
template_opts = template_opts.merge(current_template_opts)
|
|
349
349
|
end
|
|
350
350
|
opts[:template_class].new(opts[:path], 1, template_opts, &opts[:template_block])
|
|
351
351
|
end
|
|
@@ -25,7 +25,7 @@ class Roda
|
|
|
25
25
|
module RouteBlockArgs
|
|
26
26
|
def self.configure(app, &block)
|
|
27
27
|
app.instance_exec do
|
|
28
|
-
|
|
28
|
+
define_roda_method(:_roda_route_block_args, 0, &block)
|
|
29
29
|
route(&@raw_route_block) if @raw_route_block
|
|
30
30
|
end
|
|
31
31
|
end
|
|
@@ -37,8 +37,9 @@ class Roda
|
|
|
37
37
|
private
|
|
38
38
|
|
|
39
39
|
def convert_route_block(block)
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
meth = define_roda_method("convert_route_block_args", :any, &super(block))
|
|
41
|
+
lambda do |_|
|
|
42
|
+
send(meth, *_roda_route_block_args)
|
|
42
43
|
end
|
|
43
44
|
end
|
|
44
45
|
end
|
|
@@ -164,11 +164,13 @@ class Roda
|
|
|
164
164
|
|
|
165
165
|
def self.configure(app, opts=OPTS, &block)
|
|
166
166
|
options = app.opts[:route_csrf] = (app.opts[:route_csrf] || DEFAULTS).merge(opts)
|
|
167
|
-
if block
|
|
168
|
-
if opts[:csrf_failure]
|
|
167
|
+
if block || opts[:csrf_failure].is_a?(Proc)
|
|
168
|
+
if block && opts[:csrf_failure]
|
|
169
169
|
raise RodaError, "Cannot specify both route_csrf plugin block and :csrf_failure option"
|
|
170
170
|
end
|
|
171
|
-
|
|
171
|
+
block ||= opts[:csrf_failure]
|
|
172
|
+
options[:csrf_failure] = :csrf_failure_method
|
|
173
|
+
app.define_roda_method(:_roda_route_csrf_failure, 1, &app.send(:convert_route_block, block))
|
|
172
174
|
end
|
|
173
175
|
options[:env_header] = "HTTP_#{options[:header].to_s.gsub('-', '_').upcase}".freeze
|
|
174
176
|
options.freeze
|
|
@@ -192,8 +194,11 @@ class Roda
|
|
|
192
194
|
throw :halt, [403, {'Content-Type'=>'text/html', 'Content-Length'=>'0'}, []]
|
|
193
195
|
when :clear_session
|
|
194
196
|
session.clear
|
|
197
|
+
when :csrf_failure_method
|
|
198
|
+
@_request.on{_roda_route_csrf_failure(@_request)}
|
|
195
199
|
when Proc
|
|
196
|
-
|
|
200
|
+
RodaPlugins.warn "Passing a Proc as the :csrf_failure option value to check_csrf! is deprecated"
|
|
201
|
+
@_request.on{instance_exec(@_request, &failure_action)} # Deprecated
|
|
197
202
|
else
|
|
198
203
|
raise RodaError, "Unsupported :csrf_failure option: #{failure_action.inspect}"
|
|
199
204
|
end
|
|
@@ -162,6 +162,7 @@ class Roda
|
|
|
162
162
|
plugin_opts = opts
|
|
163
163
|
opts = (app.opts[:sessions] || DEFAULT_OPTIONS).merge(opts)
|
|
164
164
|
co = opts[:cookie_options] = DEFAULT_COOKIE_OPTIONS.merge(opts[:cookie_options] || OPTS).freeze
|
|
165
|
+
opts[:remove_cookie_options] = co.merge(:max_age=>'0', :expires=>Time.at(0))
|
|
165
166
|
opts[:parser] ||= app.opts[:json_parser] || JSON.method(:parse)
|
|
166
167
|
opts[:serializer] ||= app.opts[:json_serializer] || :to_json.to_proc
|
|
167
168
|
|
|
@@ -231,7 +232,7 @@ class Roda
|
|
|
231
232
|
if session.empty?
|
|
232
233
|
if env[SESSION_SERIALIZED]
|
|
233
234
|
# If session was submitted and is now empty, remove the cookie
|
|
234
|
-
Rack::Utils.delete_cookie_header!(headers, opts[:key])
|
|
235
|
+
Rack::Utils.delete_cookie_header!(headers, opts[:key], opts[:remove_cookie_options])
|
|
235
236
|
# else
|
|
236
237
|
# If no session was submitted, and the session is empty
|
|
237
238
|
# then there is no need to do anything
|
|
@@ -50,7 +50,7 @@ class Roda
|
|
|
50
50
|
# while still handling the request methods differently.
|
|
51
51
|
module StaticRouting
|
|
52
52
|
def self.configure(app)
|
|
53
|
-
app.opts[:static_routes]
|
|
53
|
+
app.opts[:static_routes] ||= {}
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
module ClassMethods
|
|
@@ -96,7 +96,8 @@ class Roda
|
|
|
96
96
|
|
|
97
97
|
# Add a static route for the given method.
|
|
98
98
|
def add_static_route(method, path, &block)
|
|
99
|
-
|
|
99
|
+
routes = opts[:static_routes][path] ||= {}
|
|
100
|
+
routes[method] = define_roda_method(routes[method] || "static_route_#{method}_#{path}", 1, &convert_route_block(block))
|
|
100
101
|
end
|
|
101
102
|
end
|
|
102
103
|
|
|
@@ -107,21 +108,10 @@ class Roda
|
|
|
107
108
|
# instead having the routing tree handle the request.
|
|
108
109
|
def _roda_before_30__static_routing
|
|
109
110
|
r = @_request
|
|
110
|
-
if
|
|
111
|
-
r.
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
module RequestMethods
|
|
117
|
-
# Assume that this request matches a static route, setting
|
|
118
|
-
# the remaining path to the emptry string and passing
|
|
119
|
-
# control to the given block.
|
|
120
|
-
def static_route(&block)
|
|
121
|
-
@remaining_path = ''
|
|
122
|
-
|
|
123
|
-
always do
|
|
124
|
-
scope.instance_exec(self, &block)
|
|
111
|
+
if meth = self.class.static_route_for(r.request_method, r.path_info)
|
|
112
|
+
r.instance_variable_set(:@remaining_path, '')
|
|
113
|
+
r.send(:block_result, send(meth, r))
|
|
114
|
+
throw :halt, @_response.finish
|
|
125
115
|
end
|
|
126
116
|
end
|
|
127
117
|
end
|
|
@@ -30,7 +30,9 @@ class Roda
|
|
|
30
30
|
module ClassMethods
|
|
31
31
|
# Install the given block as a status handler for the given HTTP response code.
|
|
32
32
|
def status_handler(code, &block)
|
|
33
|
-
|
|
33
|
+
# For backwards compatibility, pass request argument if block accepts argument
|
|
34
|
+
arity = block.arity == 0 ? 0 : 1
|
|
35
|
+
opts[:status_handler][code] = [define_roda_method(:"_roda_status_handler_#{code}", arity, &block), arity]
|
|
34
36
|
end
|
|
35
37
|
|
|
36
38
|
# Freeze the hash of status handlers so that there can be no thread safety issues at runtime.
|
|
@@ -45,11 +47,11 @@ class Roda
|
|
|
45
47
|
|
|
46
48
|
# If routing returns a response we have a handler for, call that handler.
|
|
47
49
|
def _roda_after_20__status_handler(result)
|
|
48
|
-
if result && (
|
|
50
|
+
if result && (meth, arity = opts[:status_handler][result[0]]; meth) && (v = result[2]).is_a?(Array) && v.empty?
|
|
49
51
|
res = @_response
|
|
50
52
|
res.headers.clear
|
|
51
53
|
res.status = result[0]
|
|
52
|
-
result.replace(
|
|
54
|
+
result.replace(_roda_handle_route{arity == 1 ? send(meth, @_request) : send(meth)})
|
|
53
55
|
end
|
|
54
56
|
end
|
|
55
57
|
end
|
|
@@ -107,7 +107,7 @@ class Roda
|
|
|
107
107
|
# Set branch/route options to use when rendering the layout
|
|
108
108
|
def set_layout_options(opts)
|
|
109
109
|
if options = @_layout_options
|
|
110
|
-
@_layout_options =
|
|
110
|
+
@_layout_options = options.merge!(opts)
|
|
111
111
|
else
|
|
112
112
|
@_layout_options = opts
|
|
113
113
|
end
|
|
@@ -116,7 +116,7 @@ class Roda
|
|
|
116
116
|
# Set branch/route options to use when rendering the view
|
|
117
117
|
def set_view_options(opts)
|
|
118
118
|
if options = @_view_options
|
|
119
|
-
@_view_options =
|
|
119
|
+
@_view_options = options.merge!(opts)
|
|
120
120
|
else
|
|
121
121
|
@_view_options = opts
|
|
122
122
|
end
|
data/lib/roda/version.rb
CHANGED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
require_relative "spec_helper"
|
|
2
|
+
|
|
3
|
+
describe "Roda.define_roda_method" do
|
|
4
|
+
before do
|
|
5
|
+
@scope = app.new({})
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
it "should define methods using block" do
|
|
9
|
+
m0 = app.define_roda_method("x", 0){1}
|
|
10
|
+
m0.must_be_kind_of Symbol
|
|
11
|
+
m0.must_match /\A_roda_x_\d+\z/
|
|
12
|
+
@scope.send(m0).must_equal 1
|
|
13
|
+
|
|
14
|
+
m1 = app.define_roda_method("x", 1){|x| [x, 2]}
|
|
15
|
+
m1.must_be_kind_of Symbol
|
|
16
|
+
m1.must_match /\A_roda_x_\d+\z/
|
|
17
|
+
@scope.send(m1, 3).must_equal [3, 2]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "should define private methods" do
|
|
21
|
+
proc{@scope.public_send(app.define_roda_method("x", 0){1})}.must_raise NoMethodError
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "should accept symbols as method name and return the same symbol" do
|
|
25
|
+
m0 = app.define_roda_method(:_roda_foo, 0){1}
|
|
26
|
+
m0.must_equal :_roda_foo
|
|
27
|
+
@scope.send(m0).must_equal 1
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "should handle optional arguments and splats for expected_arity 0" do
|
|
31
|
+
m2 = app.define_roda_method("x", 0){|*x| [x, 3]}
|
|
32
|
+
@scope.send(m2).must_equal [[], 3]
|
|
33
|
+
|
|
34
|
+
m3 = app.define_roda_method("x", 0){|x=5| [x, 4]}
|
|
35
|
+
@scope.send(m3).must_equal [5, 4]
|
|
36
|
+
|
|
37
|
+
m4 = app.define_roda_method("x", 0){|x=6, *y| [x, y, 5]}
|
|
38
|
+
@scope.send(m4).must_equal [6, [], 5]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "should should optional arguments and splats for expected_arity 1" do
|
|
42
|
+
m2 = app.define_roda_method("x", 1){|y, *x| [y, x, 3]}
|
|
43
|
+
@scope.send(m2, :a).must_equal [:a, [], 3]
|
|
44
|
+
|
|
45
|
+
m3 = app.define_roda_method("x", 1){|y, x=5| [y, x, 4]}
|
|
46
|
+
@scope.send(m3, :b).must_equal [:b, 5, 4]
|
|
47
|
+
|
|
48
|
+
m4 = app.define_roda_method("x", 1){|y, x=6, *z| [y, x, z, 5]}
|
|
49
|
+
@scope.send(m4, :c).must_equal [:c, 6, [], 5]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "should handle differences in arity" do
|
|
53
|
+
m0 = app.define_roda_method("x", 0){|x| [x, 1]}
|
|
54
|
+
@scope.send(m0).must_equal [nil, 1]
|
|
55
|
+
|
|
56
|
+
m1 = app.define_roda_method("x", 1){2}
|
|
57
|
+
@scope.send(m1, 3).must_equal 2
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it "should raise for unexpected expected_arity" do
|
|
61
|
+
proc{app.define_roda_method("x", 2){|x|}}.must_raise Roda::RodaError
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it "should fail if :check_arity false app option is used and a block with invalid arity is passed" do
|
|
65
|
+
app.opts[:check_arity] = false
|
|
66
|
+
m0 = app.define_roda_method("x", 0){|x| [x, 1]}
|
|
67
|
+
proc{@scope.send(m0)}.must_raise ArgumentError
|
|
68
|
+
|
|
69
|
+
m1 = app.define_roda_method("x", 1){2}
|
|
70
|
+
proc{@scope.send(m1, 1)}.must_raise ArgumentError
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
deprecated "should warn if :check_arity :warn app option is used and a block with invalid arity is passed" do
|
|
74
|
+
app.opts[:check_arity] = :warn
|
|
75
|
+
m0 = app.define_roda_method("x", 0){|x| [x, 1]}
|
|
76
|
+
@scope.send(m0).must_equal [nil, 1]
|
|
77
|
+
|
|
78
|
+
m1 = app.define_roda_method("x", 1){2}
|
|
79
|
+
@scope.send(m1, 3).must_equal 2
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
[false, true].each do |warn_dynamic_arity|
|
|
83
|
+
meth = warn_dynamic_arity ? :deprecated : :it
|
|
84
|
+
send meth, "should handle expected_arity :any and do dynamic arity check/fix" do
|
|
85
|
+
if warn_dynamic_arity
|
|
86
|
+
app.opts[:check_dynamic_arity] = :warn
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
m0 = app.define_roda_method("x", :any){1}
|
|
90
|
+
@scope.send(m0).must_equal 1
|
|
91
|
+
@scope.send(m0, 2).must_equal 1
|
|
92
|
+
|
|
93
|
+
m1 = app.define_roda_method("x", :any){|x| [x, 1]}
|
|
94
|
+
@scope.send(m1).must_equal [nil, 1]
|
|
95
|
+
@scope.send(m1, 2).must_equal [2, 1]
|
|
96
|
+
@scope.send(m1, 2, 3).must_equal [2, 1]
|
|
97
|
+
|
|
98
|
+
m2 = app.define_roda_method("x", :any){|x=5| [x, 2]}
|
|
99
|
+
@scope.send(m2).must_equal [5, 2]
|
|
100
|
+
@scope.send(m2, 2).must_equal [2, 2]
|
|
101
|
+
@scope.send(m2, 2, 3).must_equal [2, 2]
|
|
102
|
+
|
|
103
|
+
m3 = app.define_roda_method("x", :any){|y, x=5| [x, y, 3]}
|
|
104
|
+
@scope.send(m3).must_equal [5, nil, 3]
|
|
105
|
+
@scope.send(m3, 2).must_equal [5, 2, 3]
|
|
106
|
+
@scope.send(m3, 2, 3).must_equal [3, 2, 3]
|
|
107
|
+
@scope.send(m3, 2, 3, 4).must_equal [3, 2, 3]
|
|
108
|
+
|
|
109
|
+
m4 = app.define_roda_method("x", :any){|*z| [z, 1]}
|
|
110
|
+
@scope.send(m4).must_equal [[], 1]
|
|
111
|
+
@scope.send(m4, 2).must_equal [[2], 1]
|
|
112
|
+
|
|
113
|
+
m5 = app.define_roda_method("x", :any){|x, *z| [x, z, 1]}
|
|
114
|
+
@scope.send(m5).must_equal [nil, [], 1]
|
|
115
|
+
@scope.send(m5, 2).must_equal [2, [], 1]
|
|
116
|
+
@scope.send(m5, 2, 3).must_equal [2, [3], 1]
|
|
117
|
+
|
|
118
|
+
m6 = app.define_roda_method("x", :any){|x=5, *z| [x, z, 2]}
|
|
119
|
+
@scope.send(m6).must_equal [5, [], 2]
|
|
120
|
+
@scope.send(m6, 2).must_equal [2, [], 2]
|
|
121
|
+
@scope.send(m6, 2, 3).must_equal [2, [3], 2]
|
|
122
|
+
|
|
123
|
+
m7 = app.define_roda_method("x", :any){|y, x=5, *z| [x, y, z, 3]}
|
|
124
|
+
@scope.send(m7).must_equal [5, nil, [], 3]
|
|
125
|
+
@scope.send(m7, 2).must_equal [5, 2, [], 3]
|
|
126
|
+
@scope.send(m7, 2, 3).must_equal [3, 2, [], 3]
|
|
127
|
+
@scope.send(m7, 2, 3, 4).must_equal [3, 2, [4], 3]
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
it "should not fix dynamic arity issues if :check_dynamic_arity false app option is used" do
|
|
132
|
+
app.opts[:check_dynamic_arity] = false
|
|
133
|
+
|
|
134
|
+
m0 = app.define_roda_method("x", :any){1}
|
|
135
|
+
@scope.send(m0).must_equal 1
|
|
136
|
+
proc{@scope.send(m0, 2)}.must_raise ArgumentError
|
|
137
|
+
|
|
138
|
+
m1 = app.define_roda_method("x", :any){|x| [x, 1]}
|
|
139
|
+
proc{@scope.send(m1)}.must_raise ArgumentError
|
|
140
|
+
@scope.send(m1, 2).must_equal [2, 1]
|
|
141
|
+
proc{@scope.send(m1, 2, 3)}.must_raise ArgumentError
|
|
142
|
+
|
|
143
|
+
m2 = app.define_roda_method("x", :any){|x=5| [x, 2]}
|
|
144
|
+
@scope.send(m2).must_equal [5, 2]
|
|
145
|
+
@scope.send(m2, 2).must_equal [2, 2]
|
|
146
|
+
proc{@scope.send(m2, 2, 3)}.must_raise ArgumentError
|
|
147
|
+
|
|
148
|
+
m3 = app.define_roda_method("x", :any){|y, x=5| [x, y, 3]}
|
|
149
|
+
proc{@scope.send(m3)}.must_raise ArgumentError
|
|
150
|
+
@scope.send(m3, 2).must_equal [5, 2, 3]
|
|
151
|
+
@scope.send(m3, 2, 3).must_equal [3, 2, 3]
|
|
152
|
+
proc{@scope.send(m3, 2, 3, 4)}.must_raise ArgumentError
|
|
153
|
+
|
|
154
|
+
m4 = app.define_roda_method("x", :any){|*z| [z, 1]}
|
|
155
|
+
@scope.send(m4).must_equal [[], 1]
|
|
156
|
+
@scope.send(m4, 2).must_equal [[2], 1]
|
|
157
|
+
|
|
158
|
+
m5 = app.define_roda_method("x", :any){|x, *z| [x, z, 1]}
|
|
159
|
+
proc{@scope.send(m5)}.must_raise ArgumentError
|
|
160
|
+
@scope.send(m5, 2).must_equal [2, [], 1]
|
|
161
|
+
@scope.send(m5, 2, 3).must_equal [2, [3], 1]
|
|
162
|
+
|
|
163
|
+
m6 = app.define_roda_method("x", :any){|x=5, *z| [x, z, 2]}
|
|
164
|
+
@scope.send(m6).must_equal [5, [], 2]
|
|
165
|
+
@scope.send(m6, 2).must_equal [2, [], 2]
|
|
166
|
+
@scope.send(m6, 2, 3).must_equal [2, [3], 2]
|
|
167
|
+
|
|
168
|
+
m7 = app.define_roda_method("x", :any){|y, x=5, *z| [x, y, z, 3]}
|
|
169
|
+
proc{@scope.send(m7)}.must_raise ArgumentError
|
|
170
|
+
@scope.send(m7, 2).must_equal [5, 2, [], 3]
|
|
171
|
+
@scope.send(m7, 2, 3).must_equal [3, 2, [], 3]
|
|
172
|
+
@scope.send(m7, 2, 3, 4).must_equal [3, 2, [4], 3]
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
if RUBY_VERSION > '2.1'
|
|
176
|
+
it "should raise for required keyword arguments for expected_arity 0 or 1" do
|
|
177
|
+
proc{eval("app.define_roda_method('x', 0){|b:| [b, 1]}", binding)}.must_raise Roda::RodaError
|
|
178
|
+
proc{eval("app.define_roda_method('x', 0){|c=1, b:| [c, b, 1]}", binding)}.must_raise Roda::RodaError
|
|
179
|
+
proc{eval("app.define_roda_method('x', 1){|x, b:| [b, 1]}", binding)}.must_raise Roda::RodaError
|
|
180
|
+
proc{eval("app.define_roda_method('x', 1){|x, c=1, b:| [c, b, 1]}", binding)}.must_raise Roda::RodaError
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
it "should ignore keyword arguments for expected_arity 0" do
|
|
184
|
+
@scope.send(eval("app.define_roda_method('x', 0){|b:2| [b, 1]}", binding)).must_equal [2, 1]
|
|
185
|
+
@scope.send(eval("app.define_roda_method('x', 0){|**b| [b, 1]}", binding)).must_equal [{}, 1]
|
|
186
|
+
@scope.send(eval("app.define_roda_method('x', 0){|c=1, b:2| [c, b, 1]}", binding)).must_equal [1, 2, 1]
|
|
187
|
+
@scope.send(eval("app.define_roda_method('x', 0){|c=1, **b| [c, b, 1]}", binding)).must_equal [1, {}, 1]
|
|
188
|
+
@scope.send(eval("app.define_roda_method('x', 0){|x, b:2| [x, b, 1]}", binding)).must_equal [nil, 2, 1]
|
|
189
|
+
@scope.send(eval("app.define_roda_method('x', 0){|x, **b| [x, b, 1]}", binding)).must_equal [nil, {}, 1]
|
|
190
|
+
@scope.send(eval("app.define_roda_method('x', 0){|x, c=1, b:2| [x, c, b, 1]}", binding)).must_equal [nil, 1, 2, 1]
|
|
191
|
+
@scope.send(eval("app.define_roda_method('x', 0){|x, c=1, **b| [x, c, b, 1]}", binding)).must_equal [nil, 1, {}, 1]
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
it "should ignore keyword arguments for expected_arity 1" do
|
|
195
|
+
@scope.send(eval("app.define_roda_method('x', 1){|b:2| [b, 1]}", binding), 3).must_equal [2, 1]
|
|
196
|
+
@scope.send(eval("app.define_roda_method('x', 1){|**b| [b, 1]}", binding), 3).must_equal [{}, 1]
|
|
197
|
+
@scope.send(eval("app.define_roda_method('x', 1){|c=1, b:2| [c, b, 1]}", binding), 3).must_equal [3, 2, 1]
|
|
198
|
+
@scope.send(eval("app.define_roda_method('x', 1){|c=1, **b| [c, b, 1]}", binding), 3).must_equal [3, {}, 1]
|
|
199
|
+
@scope.send(eval("app.define_roda_method('x', 1){|x, b:2| [x, b, 1]}", binding), 3).must_equal [3, 2, 1]
|
|
200
|
+
@scope.send(eval("app.define_roda_method('x', 1){|x, **b| [x, b, 1]}", binding), 3).must_equal [3, {}, 1]
|
|
201
|
+
@scope.send(eval("app.define_roda_method('x', 1){|x, c=1, b:2| [x, c, b, 1]}", binding), 3).must_equal [3, 1, 2, 1]
|
|
202
|
+
@scope.send(eval("app.define_roda_method('x', 1){|x, c=1, **b| [x, c, b, 1]}", binding), 3).must_equal [3, 1, {}, 1]
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
it "should handle expected_arity :any with keyword arguments" do
|
|
206
|
+
m = eval('app.define_roda_method("x", :any){|b:2| b}', binding)
|
|
207
|
+
@scope.send(m).must_equal 2
|
|
208
|
+
@scope.send(m, 4).must_equal 2
|
|
209
|
+
@scope.send(m, b: 3).must_equal 3
|
|
210
|
+
@scope.send(m, 4, b: 3).must_equal 3
|
|
211
|
+
|
|
212
|
+
m = eval('app.define_roda_method("x", :any){|b:| b}', binding)
|
|
213
|
+
proc{@scope.send(m)}.must_raise ArgumentError
|
|
214
|
+
proc{@scope.send(m, 4)}.must_raise ArgumentError
|
|
215
|
+
@scope.send(m, b: 3).must_equal 3
|
|
216
|
+
@scope.send(m, 4, b: 3).must_equal 3
|
|
217
|
+
|
|
218
|
+
m = eval('app.define_roda_method("x", :any){|**b| b}', binding)
|
|
219
|
+
@scope.send(m).must_equal({})
|
|
220
|
+
@scope.send(m, 4).must_equal({})
|
|
221
|
+
@scope.send(m, b: 3).must_equal(b: 3)
|
|
222
|
+
@scope.send(m, 4, b: 3).must_equal(b: 3)
|
|
223
|
+
|
|
224
|
+
m = eval('app.define_roda_method("x", :any){|x, b:9| [x, b, 1]}', binding)
|
|
225
|
+
@scope.send(m).must_equal [nil, 9, 1]
|
|
226
|
+
@scope.send(m, 2).must_equal [2, 9, 1]
|
|
227
|
+
@scope.send(m, 2, 3).must_equal [2, 9, 1]
|
|
228
|
+
@scope.send(m, b: 4).must_equal [{b: 4}, 9, 1]
|
|
229
|
+
@scope.send(m, 2, b: 4).must_equal [2, 4, 1]
|
|
230
|
+
@scope.send(m, 2, 3, b: 4).must_equal [2, 4, 1]
|
|
231
|
+
|
|
232
|
+
m = eval('app.define_roda_method("x", :any){|x, b:| [x, b, 1]}', binding)
|
|
233
|
+
proc{@scope.send(m)}.must_raise ArgumentError
|
|
234
|
+
proc{@scope.send(m, 2)}.must_raise ArgumentError
|
|
235
|
+
proc{@scope.send(m, 2, 3)}.must_raise ArgumentError
|
|
236
|
+
proc{@scope.send(m, b: 4)}.must_raise ArgumentError
|
|
237
|
+
@scope.send(m, 2, b: 4).must_equal [2, 4, 1]
|
|
238
|
+
@scope.send(m, 2, 3, b: 4).must_equal [2, 4, 1]
|
|
239
|
+
|
|
240
|
+
m = eval('app.define_roda_method("x", :any){|x, **b| [x, b, 1]}', binding)
|
|
241
|
+
@scope.send(m).must_equal [nil, {}, 1]
|
|
242
|
+
@scope.send(m, 2).must_equal [2, {}, 1]
|
|
243
|
+
@scope.send(m, 2, 3).must_equal [2, {}, 1]
|
|
244
|
+
@scope.send(m, b: 4).must_equal [{b: 4}, {}, 1]
|
|
245
|
+
@scope.send(m, 2, b: 4).must_equal [2, {b: 4}, 1]
|
|
246
|
+
@scope.send(m, 2, 3, b: 4).must_equal [2, {b: 4}, 1]
|
|
247
|
+
|
|
248
|
+
m = eval('m = app.define_roda_method("x", :any){|x=5, b:9| [x, b, 2]}', binding)
|
|
249
|
+
@scope.send(m).must_equal [5, 9, 2]
|
|
250
|
+
@scope.send(m, 2).must_equal [2, 9, 2]
|
|
251
|
+
@scope.send(m, 2, 3).must_equal [2, 9, 2]
|
|
252
|
+
@scope.send(m, b: 4).must_equal [5, 4, 2]
|
|
253
|
+
@scope.send(m, 2, b: 4).must_equal [2, 4, 2]
|
|
254
|
+
@scope.send(m, 2, 3, b: 4).must_equal [2, 4, 2]
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
end
|