grape 1.1.0 → 1.2.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +128 -43
- data/LICENSE +1 -1
- data/README.md +394 -47
- data/UPGRADING.md +111 -0
- data/grape.gemspec +3 -1
- data/lib/grape.rb +98 -66
- data/lib/grape/api.rb +136 -175
- data/lib/grape/api/instance.rb +280 -0
- data/lib/grape/config.rb +32 -0
- data/lib/grape/dsl/callbacks.rb +20 -0
- data/lib/grape/dsl/desc.rb +39 -7
- data/lib/grape/dsl/inside_route.rb +12 -6
- data/lib/grape/dsl/middleware.rb +7 -0
- data/lib/grape/dsl/parameters.rb +9 -4
- data/lib/grape/dsl/routing.rb +5 -1
- data/lib/grape/dsl/validations.rb +4 -3
- data/lib/grape/eager_load.rb +18 -0
- data/lib/grape/endpoint.rb +42 -26
- data/lib/grape/error_formatter.rb +1 -1
- data/lib/grape/exceptions/base.rb +9 -1
- data/lib/grape/exceptions/invalid_response.rb +9 -0
- data/lib/grape/exceptions/validation_errors.rb +4 -2
- data/lib/grape/formatter.rb +1 -1
- data/lib/grape/locale/en.yml +2 -0
- data/lib/grape/middleware/auth/base.rb +2 -4
- data/lib/grape/middleware/base.rb +2 -0
- data/lib/grape/middleware/error.rb +9 -4
- data/lib/grape/middleware/helpers.rb +10 -0
- data/lib/grape/middleware/stack.rb +1 -1
- data/lib/grape/middleware/versioner/header.rb +4 -4
- data/lib/grape/parser.rb +1 -1
- data/lib/grape/request.rb +1 -1
- data/lib/grape/router/attribute_translator.rb +2 -0
- data/lib/grape/router/route.rb +2 -2
- data/lib/grape/util/base_inheritable.rb +34 -0
- data/lib/grape/util/endpoint_configuration.rb +6 -0
- data/lib/grape/util/inheritable_values.rb +5 -25
- data/lib/grape/util/lazy_block.rb +25 -0
- data/lib/grape/util/lazy_value.rb +95 -0
- data/lib/grape/util/reverse_stackable_values.rb +7 -36
- data/lib/grape/util/stackable_values.rb +19 -22
- data/lib/grape/validations/attributes_iterator.rb +5 -3
- data/lib/grape/validations/multiple_attributes_iterator.rb +11 -0
- data/lib/grape/validations/params_scope.rb +20 -14
- data/lib/grape/validations/single_attribute_iterator.rb +13 -0
- data/lib/grape/validations/types/custom_type_coercer.rb +1 -1
- data/lib/grape/validations/types/file.rb +1 -1
- data/lib/grape/validations/validator_factory.rb +6 -11
- data/lib/grape/validations/validators/all_or_none.rb +6 -13
- data/lib/grape/validations/validators/as.rb +2 -3
- data/lib/grape/validations/validators/at_least_one_of.rb +5 -13
- data/lib/grape/validations/validators/base.rb +11 -10
- data/lib/grape/validations/validators/coerce.rb +4 -0
- data/lib/grape/validations/validators/default.rb +1 -1
- data/lib/grape/validations/validators/exactly_one_of.rb +6 -23
- data/lib/grape/validations/validators/multiple_params_base.rb +14 -10
- data/lib/grape/validations/validators/mutual_exclusion.rb +6 -18
- data/lib/grape/validations/validators/same_as.rb +23 -0
- data/lib/grape/version.rb +1 -1
- data/spec/grape/api/defines_boolean_in_params_spec.rb +37 -0
- data/spec/grape/api/routes_with_requirements_spec.rb +59 -0
- data/spec/grape/api_remount_spec.rb +466 -0
- data/spec/grape/api_spec.rb +379 -1
- data/spec/grape/config_spec.rb +17 -0
- data/spec/grape/dsl/desc_spec.rb +40 -16
- data/spec/grape/dsl/middleware_spec.rb +8 -0
- data/spec/grape/dsl/routing_spec.rb +10 -0
- data/spec/grape/endpoint_spec.rb +40 -4
- data/spec/grape/exceptions/base_spec.rb +65 -0
- data/spec/grape/exceptions/invalid_response_spec.rb +11 -0
- data/spec/grape/exceptions/validation_errors_spec.rb +6 -4
- data/spec/grape/integration/rack_spec.rb +22 -6
- data/spec/grape/middleware/auth/dsl_spec.rb +3 -3
- data/spec/grape/middleware/base_spec.rb +8 -0
- data/spec/grape/middleware/exception_spec.rb +1 -1
- data/spec/grape/middleware/formatter_spec.rb +15 -5
- data/spec/grape/middleware/versioner/header_spec.rb +6 -0
- data/spec/grape/named_api_spec.rb +19 -0
- data/spec/grape/request_spec.rb +24 -0
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +29 -0
- data/spec/grape/validations/params_scope_spec.rb +184 -8
- data/spec/grape/validations/single_attribute_iterator_spec.rb +33 -0
- data/spec/grape/validations/validators/all_or_none_spec.rb +138 -30
- data/spec/grape/validations/validators/at_least_one_of_spec.rb +173 -29
- data/spec/grape/validations/validators/coerce_spec.rb +10 -2
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +202 -38
- data/spec/grape/validations/validators/mutual_exclusion_spec.rb +184 -27
- data/spec/grape/validations/validators/same_as_spec.rb +63 -0
- data/spec/grape/validations_spec.rb +33 -21
- data/spec/spec_helper.rb +4 -1
- metadata +35 -23
- data/Appraisals +0 -32
- data/Dangerfile +0 -2
- data/Gemfile +0 -33
- data/Gemfile.lock +0 -231
- data/Guardfile +0 -10
- data/RELEASING.md +0 -111
- data/Rakefile +0 -25
- data/benchmark/simple.rb +0 -27
- data/benchmark/simple_with_type_coercer.rb +0 -22
- data/gemfiles/multi_json.gemfile +0 -35
- data/gemfiles/multi_xml.gemfile +0 -35
- data/gemfiles/rack_1.5.2.gemfile +0 -35
- data/gemfiles/rack_edge.gemfile +0 -35
- data/gemfiles/rails_3.gemfile +0 -36
- data/gemfiles/rails_4.gemfile +0 -35
- data/gemfiles/rails_5.gemfile +0 -35
- data/gemfiles/rails_edge.gemfile +0 -35
- data/pkg/grape-0.17.0.gem +0 -0
- data/pkg/grape-0.19.0.gem +0 -0
data/lib/grape/dsl/middleware.rb
CHANGED
@@ -21,6 +21,13 @@ module Grape
|
|
21
21
|
namespace_stackable(:middleware, arr)
|
22
22
|
end
|
23
23
|
|
24
|
+
def insert(*args, &block)
|
25
|
+
arr = [:insert, *args]
|
26
|
+
arr << block if block_given?
|
27
|
+
|
28
|
+
namespace_stackable(:middleware, arr)
|
29
|
+
end
|
30
|
+
|
24
31
|
def insert_before(*args, &block)
|
25
32
|
arr = [:insert_before, *args]
|
26
33
|
arr << block if block_given?
|
data/lib/grape/dsl/parameters.rb
CHANGED
@@ -211,10 +211,15 @@ module Grape
|
|
211
211
|
# block yet.
|
212
212
|
# @return [Boolean] whether the parameter has been defined
|
213
213
|
def declared_param?(param)
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
214
|
+
if lateral?
|
215
|
+
# Elements of @declared_params of lateral scope are pushed in @parent. So check them in @parent.
|
216
|
+
@parent.declared_param?(param)
|
217
|
+
else
|
218
|
+
# @declared_params also includes hashes of options and such, but those
|
219
|
+
# won't be flattened out.
|
220
|
+
@declared_params.flatten.any? do |declared_param|
|
221
|
+
first_hash_key_or_param(declared_param) == param
|
222
|
+
end
|
218
223
|
end
|
219
224
|
end
|
220
225
|
|
data/lib/grape/dsl/routing.rb
CHANGED
@@ -77,9 +77,13 @@ module Grape
|
|
77
77
|
namespace_inheritable(:do_not_route_options, true)
|
78
78
|
end
|
79
79
|
|
80
|
-
def mount(mounts)
|
80
|
+
def mount(mounts, opts = {})
|
81
81
|
mounts = { mounts => '/' } unless mounts.respond_to?(:each_pair)
|
82
82
|
mounts.each_pair do |app, path|
|
83
|
+
if app.respond_to?(:mount_instance)
|
84
|
+
mount(app.mount_instance(configuration: opts[:with] || {}) => path)
|
85
|
+
next
|
86
|
+
end
|
83
87
|
in_setting = inheritable_setting
|
84
88
|
|
85
89
|
if app.respond_to?(:inheritable_setting, true)
|
@@ -27,10 +27,11 @@ module Grape
|
|
27
27
|
setting = description_field(:params)
|
28
28
|
setting ||= description_field(:params, {})
|
29
29
|
Array(names).each do |name|
|
30
|
-
|
31
|
-
setting[
|
30
|
+
full_name = name[:full_name].to_s
|
31
|
+
setting[full_name] ||= {}
|
32
|
+
setting[full_name].merge!(opts)
|
32
33
|
|
33
|
-
namespace_stackable(:params,
|
34
|
+
namespace_stackable(:params, full_name => opts)
|
34
35
|
end
|
35
36
|
end
|
36
37
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
Grape.eager_load!
|
2
|
+
Grape::Http.eager_load!
|
3
|
+
Grape::Exceptions.eager_load!
|
4
|
+
Grape::Extensions.eager_load!
|
5
|
+
Grape::Extensions::ActiveSupport.eager_load!
|
6
|
+
Grape::Extensions::Hashie.eager_load!
|
7
|
+
Grape::Middleware.eager_load!
|
8
|
+
Grape::Middleware::Auth.eager_load!
|
9
|
+
Grape::Middleware::Versioner.eager_load!
|
10
|
+
Grape::Util.eager_load!
|
11
|
+
Grape::ErrorFormatter.eager_load!
|
12
|
+
Grape::Formatter.eager_load!
|
13
|
+
Grape::Parser.eager_load!
|
14
|
+
Grape::DSL.eager_load!
|
15
|
+
Grape::API.eager_load!
|
16
|
+
Grape::Presenters.eager_load!
|
17
|
+
Grape::ServeFile.eager_load!
|
18
|
+
Rack::Head # AutoLoads the Rack::Head
|
data/lib/grape/endpoint.rb
CHANGED
@@ -200,7 +200,7 @@ module Grape
|
|
200
200
|
end
|
201
201
|
|
202
202
|
def merge_route_options(**default)
|
203
|
-
options[:route_options].clone.
|
203
|
+
options[:route_options].clone.merge!(**default)
|
204
204
|
end
|
205
205
|
|
206
206
|
def map_routes
|
@@ -245,32 +245,37 @@ module Grape
|
|
245
245
|
@request = Grape::Request.new(env, build_params_with: namespace_inheritable(:build_params_with))
|
246
246
|
@params = @request.params
|
247
247
|
@headers = @request.headers
|
248
|
+
begin
|
249
|
+
cookies.read(@request)
|
250
|
+
self.class.run_before_each(self)
|
251
|
+
run_filters befores, :before
|
252
|
+
|
253
|
+
if (allowed_methods = env[Grape::Env::GRAPE_ALLOWED_METHODS])
|
254
|
+
raise Grape::Exceptions::MethodNotAllowed, header.merge('Allow' => allowed_methods) unless options?
|
255
|
+
header 'Allow', allowed_methods
|
256
|
+
response_object = ''
|
257
|
+
status 204
|
258
|
+
else
|
259
|
+
run_filters before_validations, :before_validation
|
260
|
+
run_validators validations, request
|
261
|
+
remove_renamed_params
|
262
|
+
run_filters after_validations, :after_validation
|
263
|
+
response_object = @block ? @block.call(self) : nil
|
264
|
+
end
|
248
265
|
|
249
|
-
|
250
|
-
|
251
|
-
run_filters befores, :before
|
266
|
+
run_filters afters, :after
|
267
|
+
cookies.write(header)
|
252
268
|
|
253
|
-
|
254
|
-
|
255
|
-
header 'Allow', allowed_methods
|
256
|
-
response_object = ''
|
257
|
-
status 204
|
258
|
-
else
|
259
|
-
run_filters before_validations, :before_validation
|
260
|
-
run_validators validations, request
|
261
|
-
run_filters after_validations, :after_validation
|
262
|
-
response_object = @block ? @block.call(self) : nil
|
263
|
-
end
|
269
|
+
# status verifies body presence when DELETE
|
270
|
+
@body ||= response_object
|
264
271
|
|
265
|
-
|
266
|
-
|
272
|
+
# The body commonly is an Array of Strings, the application instance itself, or a File-like object
|
273
|
+
response_object = file || [body]
|
267
274
|
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
response_object = file || [body]
|
273
|
-
[status, header, response_object]
|
275
|
+
[status, header, response_object]
|
276
|
+
ensure
|
277
|
+
run_filters finallies, :finally
|
278
|
+
end
|
274
279
|
end
|
275
280
|
end
|
276
281
|
|
@@ -319,7 +324,14 @@ module Grape
|
|
319
324
|
Module.new { helpers.each { |mod_to_include| include mod_to_include } }
|
320
325
|
end
|
321
326
|
|
322
|
-
|
327
|
+
def remove_renamed_params
|
328
|
+
return unless route_setting(:renamed_params)
|
329
|
+
route_setting(:renamed_params).flat_map(&:keys).each do |renamed_param|
|
330
|
+
@params.delete(renamed_param)
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
private :build_stack, :build_helpers, :remove_renamed_params
|
323
335
|
|
324
336
|
def helpers
|
325
337
|
lazy_initialize! && @helpers
|
@@ -341,7 +353,7 @@ module Grape
|
|
341
353
|
def run_validators(validator_factories, request)
|
342
354
|
validation_errors = []
|
343
355
|
|
344
|
-
validators = validator_factories.map(
|
356
|
+
validators = validator_factories.map { |options| Grape::Validations::ValidatorFactory.create_validator(options) }
|
345
357
|
|
346
358
|
ActiveSupport::Notifications.instrument('endpoint_run_validators.grape', endpoint: self, validators: validators, request: request) do
|
347
359
|
validators.each do |validator|
|
@@ -351,7 +363,7 @@ module Grape
|
|
351
363
|
validation_errors << e
|
352
364
|
break if validator.fail_fast?
|
353
365
|
rescue Grape::Exceptions::ValidationArrayErrors => e
|
354
|
-
validation_errors
|
366
|
+
validation_errors.concat e.errors
|
355
367
|
break if validator.fail_fast?
|
356
368
|
end
|
357
369
|
end
|
@@ -384,6 +396,10 @@ module Grape
|
|
384
396
|
namespace_stackable(:afters) || []
|
385
397
|
end
|
386
398
|
|
399
|
+
def finallies
|
400
|
+
namespace_stackable(:finallies) || []
|
401
|
+
end
|
402
|
+
|
387
403
|
def validations
|
388
404
|
route_setting(:saved_validations) || []
|
389
405
|
end
|
@@ -14,7 +14,7 @@ module Grape
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def formatters(options)
|
17
|
-
builtin_formatters.merge(default_elements).merge(options[:error_formatters] || {})
|
17
|
+
builtin_formatters.merge(default_elements).merge!(options[:error_formatters] || {})
|
18
18
|
end
|
19
19
|
|
20
20
|
def formatter_for(api_format, **options)
|
@@ -74,7 +74,15 @@ module Grape
|
|
74
74
|
options = options.dup
|
75
75
|
options[:default] &&= options[:default].to_s
|
76
76
|
message = ::I18n.translate(key, **options)
|
77
|
-
message.present? ? message :
|
77
|
+
message.present? ? message : fallback_message(key, **options)
|
78
|
+
end
|
79
|
+
|
80
|
+
def fallback_message(key, **options)
|
81
|
+
if ::I18n.enforce_available_locales && !::I18n.available_locales.include?(FALLBACK_LOCALE)
|
82
|
+
key
|
83
|
+
else
|
84
|
+
::I18n.translate(key, locale: FALLBACK_LOCALE, **options)
|
85
|
+
end
|
78
86
|
end
|
79
87
|
end
|
80
88
|
end
|
@@ -39,14 +39,16 @@ module Grape
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def full_messages
|
42
|
-
map { |attributes, error| full_message(attributes, error) }
|
42
|
+
messages = map { |attributes, error| full_message(attributes, error) }
|
43
|
+
messages.uniq!
|
44
|
+
messages
|
43
45
|
end
|
44
46
|
|
45
47
|
private
|
46
48
|
|
47
49
|
def full_message(attributes, error)
|
48
50
|
I18n.t(
|
49
|
-
'grape.errors.format'
|
51
|
+
'grape.errors.format',
|
50
52
|
default: '%{attributes} %{message}',
|
51
53
|
attributes: attributes.count == 1 ? translate_attribute(attributes.first) : translate_attributes(attributes),
|
52
54
|
message: error.message
|
data/lib/grape/formatter.rb
CHANGED
@@ -14,7 +14,7 @@ module Grape
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def formatters(options)
|
17
|
-
builtin_formmaters.merge(default_elements).merge(options[:formatters] || {})
|
17
|
+
builtin_formmaters.merge(default_elements).merge!(options[:formatters] || {})
|
18
18
|
end
|
19
19
|
|
20
20
|
def formatter_for(api_format, **options)
|
data/lib/grape/locale/en.yml
CHANGED
@@ -9,6 +9,7 @@ en:
|
|
9
9
|
blank: 'is empty'
|
10
10
|
values: 'does not have a valid value'
|
11
11
|
except_values: 'has a value not allowed'
|
12
|
+
same_as: 'is not the same as %{parameter}'
|
12
13
|
missing_vendor_option:
|
13
14
|
problem: 'missing :vendor option.'
|
14
15
|
summary: 'when version using header, you must specify :vendor option. '
|
@@ -49,4 +50,5 @@ en:
|
|
49
50
|
invalid_version_header:
|
50
51
|
problem: 'Invalid version header'
|
51
52
|
resolution: '%{message}'
|
53
|
+
invalid_response: 'Invalid response'
|
52
54
|
|
@@ -4,6 +4,8 @@ module Grape
|
|
4
4
|
module Middleware
|
5
5
|
module Auth
|
6
6
|
class Base
|
7
|
+
include Helpers
|
8
|
+
|
7
9
|
attr_accessor :options, :app, :env
|
8
10
|
|
9
11
|
def initialize(app, **options)
|
@@ -11,10 +13,6 @@ module Grape
|
|
11
13
|
@options = options
|
12
14
|
end
|
13
15
|
|
14
|
-
def context
|
15
|
-
env[Grape::Env::API_ENDPOINT]
|
16
|
-
end
|
17
|
-
|
18
16
|
def call(env)
|
19
17
|
dup._call(env)
|
20
18
|
end
|
@@ -21,7 +21,7 @@ module Grape
|
|
21
21
|
},
|
22
22
|
rescue_handlers: {}, # rescue handler blocks
|
23
23
|
base_only_rescue_handlers: {}, # rescue handler blocks rescuing only the base class
|
24
|
-
all_rescue_handler: nil # rescue handler block to rescue from all exceptions
|
24
|
+
all_rescue_handler: nil, # rescue handler block to rescue from all exceptions
|
25
25
|
}
|
26
26
|
end
|
27
27
|
|
@@ -32,7 +32,6 @@ module Grape
|
|
32
32
|
|
33
33
|
def call!(env)
|
34
34
|
@env = env
|
35
|
-
|
36
35
|
begin
|
37
36
|
error_response(catch(:error) do
|
38
37
|
return @app.call(@env)
|
@@ -73,7 +72,7 @@ module Grape
|
|
73
72
|
if headers[Grape::Http::Headers::CONTENT_TYPE] == TEXT_HTML
|
74
73
|
message = ERB::Util.html_escape(message)
|
75
74
|
end
|
76
|
-
Rack::Response.new([message], status, headers)
|
75
|
+
Rack::Response.new([message], status, headers)
|
77
76
|
end
|
78
77
|
|
79
78
|
def format_message(message, backtrace, original_exception = nil)
|
@@ -127,7 +126,13 @@ module Grape
|
|
127
126
|
handler = public_method(handler)
|
128
127
|
end
|
129
128
|
|
130
|
-
handler.arity.zero? ? instance_exec(&handler) : instance_exec(error, &handler)
|
129
|
+
response = handler.arity.zero? ? instance_exec(&handler) : instance_exec(error, &handler)
|
130
|
+
|
131
|
+
if response.is_a?(Rack::Response)
|
132
|
+
response
|
133
|
+
else
|
134
|
+
run_rescue_handler(:default_rescue_handler, Grape::Exceptions::InvalidResponse.new)
|
135
|
+
end
|
131
136
|
end
|
132
137
|
end
|
133
138
|
end
|
@@ -96,7 +96,7 @@ module Grape
|
|
96
96
|
# @param [Array] other_specs An array of middleware specifications (e.g. [[:use, klass], [:insert_before, *args]])
|
97
97
|
def concat(other_specs)
|
98
98
|
@others << Array(other_specs).reject { |o| o.first == :use }
|
99
|
-
merge_with
|
99
|
+
merge_with(Array(other_specs).select { |o| o.first == :use })
|
100
100
|
end
|
101
101
|
|
102
102
|
protected
|
@@ -99,7 +99,7 @@ module Grape
|
|
99
99
|
def available_media_types
|
100
100
|
available_media_types = []
|
101
101
|
|
102
|
-
content_types.
|
102
|
+
content_types.each_key do |extension|
|
103
103
|
versions.reverse_each do |version|
|
104
104
|
available_media_types += [
|
105
105
|
"application/vnd.#{vendor}-#{version}+#{extension}",
|
@@ -111,7 +111,7 @@ module Grape
|
|
111
111
|
|
112
112
|
available_media_types << "application/vnd.#{vendor}"
|
113
113
|
|
114
|
-
content_types.
|
114
|
+
content_types.each_value do |media_type|
|
115
115
|
available_media_types << media_type
|
116
116
|
end
|
117
117
|
|
@@ -173,7 +173,7 @@ module Grape
|
|
173
173
|
# @return [Boolean] whether the content type sets a vendor
|
174
174
|
def vendor?(media_type)
|
175
175
|
_, subtype = Rack::Accept::Header.parse_media_type(media_type)
|
176
|
-
subtype[HAS_VENDOR_REGEX]
|
176
|
+
subtype.present? && subtype[HAS_VENDOR_REGEX]
|
177
177
|
end
|
178
178
|
|
179
179
|
def request_vendor(media_type)
|
@@ -190,7 +190,7 @@ module Grape
|
|
190
190
|
# @return [Boolean] whether the content type sets an API version
|
191
191
|
def version?(media_type)
|
192
192
|
_, subtype = Rack::Accept::Header.parse_media_type(media_type)
|
193
|
-
subtype[HAS_VERSION_REGEX]
|
193
|
+
subtype.present? && subtype[HAS_VERSION_REGEX]
|
194
194
|
end
|
195
195
|
end
|
196
196
|
end
|
data/lib/grape/parser.rb
CHANGED
data/lib/grape/request.rb
CHANGED
@@ -5,7 +5,7 @@ module Grape
|
|
5
5
|
alias rack_params params
|
6
6
|
|
7
7
|
def initialize(env, options = {})
|
8
|
-
extend options[:build_params_with] || Grape
|
8
|
+
extend options[:build_params_with] || Grape.config.param_builder
|
9
9
|
super(env)
|
10
10
|
end
|
11
11
|
|
data/lib/grape/router/route.rb
CHANGED
@@ -98,9 +98,9 @@ module Grape
|
|
98
98
|
path, line = *location.scan(SOURCE_LOCATION_REGEXP).first
|
99
99
|
path = File.realpath(path) if Pathname.new(path).relative?
|
100
100
|
expected ||= name
|
101
|
-
warn <<-
|
101
|
+
warn <<-WARNING
|
102
102
|
#{path}:#{line}: The route_xxx methods such as route_#{name} have been deprecated, please use #{expected}.
|
103
|
-
|
103
|
+
WARNING
|
104
104
|
end
|
105
105
|
end
|
106
106
|
end
|