grape 1.1.0 → 1.2.5
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.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)
|