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
@@ -0,0 +1,34 @@
|
|
1
|
+
module Grape
|
2
|
+
module Util
|
3
|
+
# Base for classes which need to operate with own values kept
|
4
|
+
# in the hash and inherited values kept in a Hash-like object.
|
5
|
+
class BaseInheritable
|
6
|
+
attr_accessor :inherited_values
|
7
|
+
attr_accessor :new_values
|
8
|
+
|
9
|
+
# @param inherited_values [Object] An object implementing an interface
|
10
|
+
# of the Hash class.
|
11
|
+
def initialize(inherited_values = {})
|
12
|
+
@inherited_values = inherited_values
|
13
|
+
@new_values = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def delete(key)
|
17
|
+
new_values.delete key
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize_copy(other)
|
21
|
+
super
|
22
|
+
self.inherited_values = other.inherited_values
|
23
|
+
self.new_values = other.new_values.dup
|
24
|
+
end
|
25
|
+
|
26
|
+
def keys
|
27
|
+
combined = inherited_values.keys
|
28
|
+
combined.concat(new_values.keys)
|
29
|
+
combined.uniq!
|
30
|
+
combined
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -1,14 +1,8 @@
|
|
1
|
+
require_relative 'base_inheritable'
|
2
|
+
|
1
3
|
module Grape
|
2
4
|
module Util
|
3
|
-
class InheritableValues
|
4
|
-
attr_accessor :inherited_values
|
5
|
-
attr_accessor :new_values
|
6
|
-
|
7
|
-
def initialize(inherited_values = {})
|
8
|
-
self.inherited_values = inherited_values
|
9
|
-
self.new_values = {}
|
10
|
-
end
|
11
|
-
|
5
|
+
class InheritableValues < BaseInheritable
|
12
6
|
def [](name)
|
13
7
|
values[name]
|
14
8
|
end
|
@@ -17,26 +11,12 @@ module Grape
|
|
17
11
|
new_values[name] = value
|
18
12
|
end
|
19
13
|
|
20
|
-
def delete(key)
|
21
|
-
new_values.delete key
|
22
|
-
end
|
23
|
-
|
24
14
|
def merge(new_hash)
|
25
|
-
values.merge(new_hash)
|
26
|
-
end
|
27
|
-
|
28
|
-
def keys
|
29
|
-
(new_values.keys + inherited_values.keys).sort.uniq
|
15
|
+
values.merge!(new_hash)
|
30
16
|
end
|
31
17
|
|
32
18
|
def to_hash
|
33
|
-
values
|
34
|
-
end
|
35
|
-
|
36
|
-
def initialize_copy(other)
|
37
|
-
super
|
38
|
-
self.inherited_values = other.inherited_values
|
39
|
-
self.new_values = other.new_values.dup
|
19
|
+
values
|
40
20
|
end
|
41
21
|
|
42
22
|
protected
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Grape
|
2
|
+
module Util
|
3
|
+
class LazyBlock
|
4
|
+
def initialize(&new_block)
|
5
|
+
@block = new_block
|
6
|
+
end
|
7
|
+
|
8
|
+
def evaluate_from(configuration)
|
9
|
+
@block.call(configuration)
|
10
|
+
end
|
11
|
+
|
12
|
+
def evaluate
|
13
|
+
@block.call({})
|
14
|
+
end
|
15
|
+
|
16
|
+
def lazy?
|
17
|
+
true
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
evaluate.to_s
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Grape
|
2
|
+
module Util
|
3
|
+
class LazyValue
|
4
|
+
attr_reader :access_keys
|
5
|
+
def initialize(value, access_keys = [])
|
6
|
+
@value = value
|
7
|
+
@access_keys = access_keys
|
8
|
+
end
|
9
|
+
|
10
|
+
def evaluate_from(configuration)
|
11
|
+
matching_lazy_value = configuration.fetch(@access_keys)
|
12
|
+
matching_lazy_value.evaluate
|
13
|
+
end
|
14
|
+
|
15
|
+
def evaluate
|
16
|
+
@value
|
17
|
+
end
|
18
|
+
|
19
|
+
def lazy?
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
def reached_by(parent_access_keys, access_key)
|
24
|
+
@access_keys = parent_access_keys + [access_key]
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_s
|
29
|
+
evaluate.to_s
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class LazyValueEnumerable < LazyValue
|
34
|
+
def [](key)
|
35
|
+
if @value_hash[key].nil?
|
36
|
+
LazyValue.new(nil).reached_by(access_keys, key)
|
37
|
+
else
|
38
|
+
@value_hash[key].reached_by(access_keys, key)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def fetch(access_keys)
|
43
|
+
fetched_keys = access_keys.dup
|
44
|
+
value = self[fetched_keys.shift]
|
45
|
+
fetched_keys.any? ? value.fetch(fetched_keys) : value
|
46
|
+
end
|
47
|
+
|
48
|
+
def []=(key, value)
|
49
|
+
@value_hash[key] = if value.is_a?(Hash)
|
50
|
+
LazyValueHash.new(value)
|
51
|
+
elsif value.is_a?(Array)
|
52
|
+
LazyValueArray.new(value)
|
53
|
+
else
|
54
|
+
LazyValue.new(value)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class LazyValueArray < LazyValueEnumerable
|
60
|
+
def initialize(array)
|
61
|
+
super
|
62
|
+
@value_hash = []
|
63
|
+
array.each_with_index do |value, index|
|
64
|
+
self[index] = value
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def evaluate
|
69
|
+
evaluated = []
|
70
|
+
@value_hash.each_with_index do |value, index|
|
71
|
+
evaluated[index] = value.evaluate
|
72
|
+
end
|
73
|
+
evaluated
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
class LazyValueHash < LazyValueEnumerable
|
78
|
+
def initialize(hash)
|
79
|
+
super
|
80
|
+
@value_hash = {}.with_indifferent_access
|
81
|
+
hash.each do |key, value|
|
82
|
+
self[key] = value
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def evaluate
|
87
|
+
evaluated = {}.with_indifferent_access
|
88
|
+
@value_hash.each do |key, value|
|
89
|
+
evaluated[key] = value.evaluate
|
90
|
+
end
|
91
|
+
evaluated
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -1,45 +1,16 @@
|
|
1
|
+
require_relative 'stackable_values'
|
2
|
+
|
1
3
|
module Grape
|
2
4
|
module Util
|
3
|
-
class ReverseStackableValues
|
4
|
-
|
5
|
-
attr_accessor :new_values
|
6
|
-
|
7
|
-
def initialize(inherited_values = {})
|
8
|
-
@inherited_values = inherited_values
|
9
|
-
@new_values = {}
|
10
|
-
end
|
5
|
+
class ReverseStackableValues < StackableValues
|
6
|
+
protected
|
11
7
|
|
12
|
-
def
|
8
|
+
def concat_values(inherited_value, new_value)
|
13
9
|
[].tap do |value|
|
14
|
-
value.concat(
|
15
|
-
value.concat(
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def []=(name, value)
|
20
|
-
@new_values[name] ||= []
|
21
|
-
@new_values[name].push value
|
22
|
-
end
|
23
|
-
|
24
|
-
def delete(key)
|
25
|
-
new_values.delete key
|
26
|
-
end
|
27
|
-
|
28
|
-
def keys
|
29
|
-
(@new_values.keys + @inherited_values.keys).sort.uniq
|
30
|
-
end
|
31
|
-
|
32
|
-
def to_hash
|
33
|
-
keys.each_with_object({}) do |key, result|
|
34
|
-
result[key] = self[key]
|
10
|
+
value.concat(new_value)
|
11
|
+
value.concat(inherited_value)
|
35
12
|
end
|
36
13
|
end
|
37
|
-
|
38
|
-
def initialize_copy(other)
|
39
|
-
super
|
40
|
-
self.inherited_values = other.inherited_values
|
41
|
-
self.new_values = other.new_values.dup
|
42
|
-
end
|
43
14
|
end
|
44
15
|
end
|
45
16
|
end
|
@@ -1,23 +1,25 @@
|
|
1
|
+
require_relative 'base_inheritable'
|
2
|
+
|
1
3
|
module Grape
|
2
4
|
module Util
|
3
|
-
class StackableValues
|
4
|
-
attr_accessor :inherited_values
|
5
|
-
attr_accessor :new_values
|
5
|
+
class StackableValues < BaseInheritable
|
6
6
|
attr_reader :frozen_values
|
7
7
|
|
8
|
-
def initialize(
|
9
|
-
|
10
|
-
|
8
|
+
def initialize(*_args)
|
9
|
+
super
|
10
|
+
|
11
11
|
@frozen_values = {}
|
12
12
|
end
|
13
13
|
|
14
14
|
def [](name)
|
15
15
|
return @frozen_values[name] if @frozen_values.key? name
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
17
|
+
inherited_value = @inherited_values[name]
|
18
|
+
new_value = @new_values[name] || []
|
19
|
+
|
20
|
+
return new_value unless inherited_value
|
21
|
+
|
22
|
+
concat_values(inherited_value, new_value)
|
21
23
|
end
|
22
24
|
|
23
25
|
def []=(name, value)
|
@@ -26,14 +28,6 @@ module Grape
|
|
26
28
|
@new_values[name].push value
|
27
29
|
end
|
28
30
|
|
29
|
-
def delete(key)
|
30
|
-
new_values.delete key
|
31
|
-
end
|
32
|
-
|
33
|
-
def keys
|
34
|
-
(@new_values.keys + @inherited_values.keys).sort.uniq
|
35
|
-
end
|
36
|
-
|
37
31
|
def to_hash
|
38
32
|
keys.each_with_object({}) do |key, result|
|
39
33
|
result[key] = self[key]
|
@@ -44,10 +38,13 @@ module Grape
|
|
44
38
|
@frozen_values[key] = self[key].freeze
|
45
39
|
end
|
46
40
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
41
|
+
protected
|
42
|
+
|
43
|
+
def concat_values(inherited_value, new_value)
|
44
|
+
[].tap do |value|
|
45
|
+
value.concat(inherited_value)
|
46
|
+
value.concat(new_value)
|
47
|
+
end
|
51
48
|
end
|
52
49
|
end
|
53
50
|
end
|
@@ -41,11 +41,13 @@ module Grape
|
|
41
41
|
@scope.index = index
|
42
42
|
end
|
43
43
|
|
44
|
-
@attrs
|
45
|
-
yield resource_params, attr_name, inside_array
|
46
|
-
end
|
44
|
+
yield_attributes(resource_params, @attrs, &block)
|
47
45
|
end
|
48
46
|
end
|
47
|
+
|
48
|
+
def yield_attributes(_resource_params, _attrs)
|
49
|
+
raise NotImplementedError
|
50
|
+
end
|
49
51
|
end
|
50
52
|
end
|
51
53
|
end
|
@@ -36,6 +36,10 @@ module Grape
|
|
36
36
|
configure_declared_params
|
37
37
|
end
|
38
38
|
|
39
|
+
def configuration
|
40
|
+
@api.configuration.evaluate
|
41
|
+
end
|
42
|
+
|
39
43
|
# @return [Boolean] whether or not this entire scope needs to be
|
40
44
|
# validated
|
41
45
|
def should_validate?(parameters)
|
@@ -52,6 +56,7 @@ module Grape
|
|
52
56
|
|
53
57
|
return true unless @dependent_on
|
54
58
|
return params.any? { |param| meets_dependency?(param, request_params) } if params.is_a?(Array)
|
59
|
+
return false unless params.respond_to?(:with_indifferent_access)
|
55
60
|
params = params.with_indifferent_access
|
56
61
|
|
57
62
|
@dependent_on.each do |dependency|
|
@@ -71,7 +76,7 @@ module Grape
|
|
71
76
|
def full_name(name, index: nil)
|
72
77
|
if nested?
|
73
78
|
# Find our containing element's name, and append ours.
|
74
|
-
|
79
|
+
"#{@parent.full_name(@element)}#{brackets(@index || index)}#{brackets(name)}"
|
75
80
|
elsif lateral?
|
76
81
|
# Find the name of the element as if it was at the same nesting level
|
77
82
|
# as our parent. We need to forward our index upward to achieve this.
|
@@ -119,8 +124,9 @@ module Grape
|
|
119
124
|
@parent.push_declared_params(attrs, opts)
|
120
125
|
else
|
121
126
|
if opts && opts[:as]
|
122
|
-
@api.route_setting(:
|
123
|
-
@api.route_setting(:
|
127
|
+
@api.route_setting(:renamed_params, @api.route_setting(:renamed_params) || [])
|
128
|
+
@api.route_setting(:renamed_params) << { attrs.first => opts[:as] }
|
129
|
+
attrs = [opts[:as]]
|
124
130
|
end
|
125
131
|
|
126
132
|
@declared_params.concat attrs
|
@@ -178,14 +184,12 @@ module Grape
|
|
178
184
|
raise Grape::Exceptions::UnsupportedGroupTypeError.new unless Grape::Validations::Types.group?(type)
|
179
185
|
end
|
180
186
|
|
181
|
-
opts = attrs[1] || { type: Array }
|
182
|
-
|
183
187
|
self.class.new(
|
184
188
|
api: @api,
|
185
|
-
element: attrs.first,
|
189
|
+
element: attrs[1][:as] || attrs.first,
|
186
190
|
parent: self,
|
187
191
|
optional: optional,
|
188
|
-
type:
|
192
|
+
type: type || Array,
|
189
193
|
&block
|
190
194
|
)
|
191
195
|
end
|
@@ -406,13 +410,15 @@ module Grape
|
|
406
410
|
|
407
411
|
raise Grape::Exceptions::UnknownValidator.new(type) unless validator_class
|
408
412
|
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
413
|
+
validator_options = {
|
414
|
+
attributes: attrs,
|
415
|
+
options: options,
|
416
|
+
required: doc_attrs[:required],
|
417
|
+
params_scope: self,
|
418
|
+
opts: opts,
|
419
|
+
validator_class: validator_class
|
420
|
+
}
|
421
|
+
@api.namespace_stackable(:validations, validator_options)
|
416
422
|
end
|
417
423
|
|
418
424
|
def validate_value_coercion(coerce_type, *values_list)
|