grape 1.2.4 → 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.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +18 -4
  3. data/README.md +144 -4
  4. data/grape.gemspec +3 -1
  5. data/lib/grape.rb +94 -66
  6. data/lib/grape/api.rb +46 -4
  7. data/lib/grape/api/instance.rb +23 -12
  8. data/lib/grape/dsl/desc.rb +11 -2
  9. data/lib/grape/dsl/validations.rb +4 -3
  10. data/lib/grape/eager_load.rb +18 -0
  11. data/lib/grape/endpoint.rb +3 -3
  12. data/lib/grape/error_formatter.rb +1 -1
  13. data/lib/grape/exceptions/validation_errors.rb +4 -2
  14. data/lib/grape/formatter.rb +1 -1
  15. data/lib/grape/middleware/auth/base.rb +2 -4
  16. data/lib/grape/middleware/base.rb +2 -0
  17. data/lib/grape/middleware/helpers.rb +10 -0
  18. data/lib/grape/parser.rb +1 -1
  19. data/lib/grape/util/base_inheritable.rb +34 -0
  20. data/lib/grape/util/inheritable_values.rb +5 -25
  21. data/lib/grape/util/lazy_block.rb +25 -0
  22. data/lib/grape/util/lazy_value.rb +5 -0
  23. data/lib/grape/util/reverse_stackable_values.rb +7 -36
  24. data/lib/grape/util/stackable_values.rb +19 -22
  25. data/lib/grape/validations/attributes_iterator.rb +5 -3
  26. data/lib/grape/validations/multiple_attributes_iterator.rb +11 -0
  27. data/lib/grape/validations/params_scope.rb +12 -12
  28. data/lib/grape/validations/single_attribute_iterator.rb +13 -0
  29. data/lib/grape/validations/validator_factory.rb +6 -11
  30. data/lib/grape/validations/validators/all_or_none.rb +6 -13
  31. data/lib/grape/validations/validators/at_least_one_of.rb +5 -13
  32. data/lib/grape/validations/validators/base.rb +11 -10
  33. data/lib/grape/validations/validators/coerce.rb +4 -0
  34. data/lib/grape/validations/validators/default.rb +1 -1
  35. data/lib/grape/validations/validators/exactly_one_of.rb +6 -23
  36. data/lib/grape/validations/validators/multiple_params_base.rb +14 -10
  37. data/lib/grape/validations/validators/mutual_exclusion.rb +6 -18
  38. data/lib/grape/version.rb +1 -1
  39. data/spec/grape/api/defines_boolean_in_params_spec.rb +37 -0
  40. data/spec/grape/api_remount_spec.rb +158 -0
  41. data/spec/grape/api_spec.rb +72 -0
  42. data/spec/grape/endpoint_spec.rb +1 -1
  43. data/spec/grape/exceptions/base_spec.rb +4 -0
  44. data/spec/grape/exceptions/validation_errors_spec.rb +6 -4
  45. data/spec/grape/integration/rack_spec.rb +22 -6
  46. data/spec/grape/middleware/base_spec.rb +8 -0
  47. data/spec/grape/middleware/formatter_spec.rb +11 -1
  48. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +29 -0
  49. data/spec/grape/validations/params_scope_spec.rb +13 -0
  50. data/spec/grape/validations/single_attribute_iterator_spec.rb +33 -0
  51. data/spec/grape/validations/validators/all_or_none_spec.rb +138 -30
  52. data/spec/grape/validations/validators/at_least_one_of_spec.rb +173 -29
  53. data/spec/grape/validations/validators/coerce_spec.rb +6 -2
  54. data/spec/grape/validations/validators/exactly_one_of_spec.rb +202 -38
  55. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +184 -27
  56. data/spec/grape/validations_spec.rb +32 -20
  57. metadata +103 -115
  58. data/Appraisals +0 -28
  59. data/Dangerfile +0 -2
  60. data/Gemfile +0 -33
  61. data/Gemfile.lock +0 -231
  62. data/Guardfile +0 -10
  63. data/RELEASING.md +0 -111
  64. data/Rakefile +0 -25
  65. data/benchmark/simple.rb +0 -27
  66. data/benchmark/simple_with_type_coercer.rb +0 -22
  67. data/gemfiles/multi_json.gemfile +0 -35
  68. data/gemfiles/multi_xml.gemfile +0 -35
  69. data/gemfiles/rack_1.5.2.gemfile.lock +0 -232
  70. data/gemfiles/rack_edge.gemfile +0 -35
  71. data/gemfiles/rails_3.gemfile +0 -36
  72. data/gemfiles/rails_3.gemfile.lock +0 -288
  73. data/gemfiles/rails_4.gemfile +0 -35
  74. data/gemfiles/rails_4.gemfile.lock +0 -280
  75. data/gemfiles/rails_5.gemfile +0 -35
  76. data/gemfiles/rails_5.gemfile.lock +0 -312
  77. data/gemfiles/rails_edge.gemfile +0 -35
  78. data/pkg/grape-1.2.0.gem +0 -0
  79. data/pkg/grape-1.2.1.gem +0 -0
  80. data/pkg/grape-1.2.3.gem +0 -0
@@ -7,6 +7,11 @@ module Grape
7
7
  @access_keys = access_keys
8
8
  end
9
9
 
10
+ def evaluate_from(configuration)
11
+ matching_lazy_value = configuration.fetch(@access_keys)
12
+ matching_lazy_value.evaluate
13
+ end
14
+
10
15
  def evaluate
11
16
  @value
12
17
  end
@@ -1,45 +1,16 @@
1
+ require_relative 'stackable_values'
2
+
1
3
  module Grape
2
4
  module Util
3
- class ReverseStackableValues
4
- attr_accessor :inherited_values
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 [](name)
8
+ def concat_values(inherited_value, new_value)
13
9
  [].tap do |value|
14
- value.concat(@new_values[name] || [])
15
- value.concat(@inherited_values[name] || [])
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(inherited_values = {})
9
- @inherited_values = inherited_values
10
- @new_values = {}
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
- value = []
18
- value.concat(@inherited_values[name] || [])
19
- value.concat(@new_values[name] || [])
20
- value
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
- def initialize_copy(other)
48
- super
49
- self.inherited_values = other.inherited_values
50
- self.new_values = other.new_values.dup
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.each do |attr_name|
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
@@ -0,0 +1,11 @@
1
+ module Grape
2
+ module Validations
3
+ class MultipleAttributesIterator < AttributesIterator
4
+ private
5
+
6
+ def yield_attributes(resource_params, _attrs)
7
+ yield resource_params
8
+ end
9
+ end
10
+ end
11
+ end
@@ -76,7 +76,7 @@ module Grape
76
76
  def full_name(name, index: nil)
77
77
  if nested?
78
78
  # Find our containing element's name, and append ours.
79
- [@parent.full_name(@element), [@index || index, name].map(&method(:brackets))].compact.join
79
+ "#{@parent.full_name(@element)}#{brackets(@index || index)}#{brackets(name)}"
80
80
  elsif lateral?
81
81
  # Find the name of the element as if it was at the same nesting level
82
82
  # as our parent. We need to forward our index upward to achieve this.
@@ -184,14 +184,12 @@ module Grape
184
184
  raise Grape::Exceptions::UnsupportedGroupTypeError.new unless Grape::Validations::Types.group?(type)
185
185
  end
186
186
 
187
- opts = attrs[1] || { type: Array }
188
-
189
187
  self.class.new(
190
188
  api: @api,
191
- element: attrs.first,
189
+ element: attrs[1][:as] || attrs.first,
192
190
  parent: self,
193
191
  optional: optional,
194
- type: opts[:type],
192
+ type: type || Array,
195
193
  &block
196
194
  )
197
195
  end
@@ -412,13 +410,15 @@ module Grape
412
410
 
413
411
  raise Grape::Exceptions::UnknownValidator.new(type) unless validator_class
414
412
 
415
- factory = Grape::Validations::ValidatorFactory.new(attributes: attrs,
416
- options: options,
417
- required: doc_attrs[:required],
418
- params_scope: self,
419
- opts: opts,
420
- validator_class: validator_class)
421
- @api.namespace_stackable(:validations, factory)
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)
422
422
  end
423
423
 
424
424
  def validate_value_coercion(coerce_type, *values_list)
@@ -0,0 +1,13 @@
1
+ module Grape
2
+ module Validations
3
+ class SingleAttributeIterator < AttributesIterator
4
+ private
5
+
6
+ def yield_attributes(resource_params, attrs)
7
+ attrs.each do |attr_name|
8
+ yield resource_params, attr_name
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,17 +1,12 @@
1
1
  module Grape
2
2
  module Validations
3
3
  class ValidatorFactory
4
- def initialize(**options)
5
- @validator_class = options.delete(:validator_class)
6
- @options = options
7
- end
8
-
9
- def create_validator
10
- @validator_class.new(@options[:attributes],
11
- @options[:options],
12
- @options[:required],
13
- @options[:params_scope],
14
- @options[:opts])
4
+ def self.create_validator(**options)
5
+ options[:validator_class].new(options[:attributes],
6
+ options[:options],
7
+ options[:required],
8
+ options[:params_scope],
9
+ options[:opts])
15
10
  end
16
11
  end
17
12
  end
@@ -1,19 +1,12 @@
1
+ require 'grape/validations/validators/multiple_params_base'
2
+
1
3
  module Grape
2
4
  module Validations
3
- require 'grape/validations/validators/multiple_params_base'
4
5
  class AllOrNoneOfValidator < MultipleParamsBase
5
- def validate!(params)
6
- super
7
- if scope_requires_params && only_subset_present
8
- raise Grape::Exceptions::Validation, params: all_keys, message: message(:all_or_none)
9
- end
10
- params
11
- end
12
-
13
- private
14
-
15
- def only_subset_present
16
- scoped_params.any? { |resource_params| !keys_in_common(resource_params).empty? && keys_in_common(resource_params).length < attrs.length }
6
+ def validate_params!(params)
7
+ keys = keys_in_common(params)
8
+ return if keys.empty? || keys.length == all_keys.length
9
+ raise Grape::Exceptions::Validation, params: all_keys, message: message(:all_or_none)
17
10
  end
18
11
  end
19
12
  end
@@ -1,19 +1,11 @@
1
+ require 'grape/validations/validators/multiple_params_base'
2
+
1
3
  module Grape
2
4
  module Validations
3
- require 'grape/validations/validators/multiple_params_base'
4
5
  class AtLeastOneOfValidator < MultipleParamsBase
5
- def validate!(params)
6
- super
7
- if scope_requires_params && no_exclusive_params_are_present
8
- raise Grape::Exceptions::Validation, params: all_keys, message: message(:at_least_one)
9
- end
10
- params
11
- end
12
-
13
- private
14
-
15
- def no_exclusive_params_are_present
16
- scoped_params.any? { |resource_params| keys_in_common(resource_params).empty? }
6
+ def validate_params!(params)
7
+ return unless keys_in_common(params).empty?
8
+ raise Grape::Exceptions::Validation, params: all_keys, message: message(:at_least_one)
17
9
  end
18
10
  end
19
11
  end
@@ -35,18 +35,19 @@ module Grape
35
35
  # @raise [Grape::Exceptions::Validation] if validation failed
36
36
  # @return [void]
37
37
  def validate!(params)
38
- attributes = AttributesIterator.new(self, @scope, params)
38
+ attributes = SingleAttributeIterator.new(self, @scope, params)
39
+ # we collect errors inside array because
40
+ # there may be more than one error per field
39
41
  array_errors = []
42
+
40
43
  attributes.each do |resource_params, attr_name|
41
44
  next if !@scope.required? && resource_params.empty?
42
- next unless @required || (resource_params.respond_to?(:key?) && resource_params.key?(attr_name))
43
45
  next unless @scope.meets_dependency?(resource_params, params)
44
-
45
46
  begin
46
- validate_param!(attr_name, resource_params)
47
+ if @required || resource_params.respond_to?(:key?) && resource_params.key?(attr_name)
48
+ validate_param!(attr_name, resource_params)
49
+ end
47
50
  rescue Grape::Exceptions::Validation => e
48
- # we collect errors inside array because
49
- # there may be more than one error per field
50
51
  array_errors << e
51
52
  end
52
53
  end
@@ -56,10 +57,10 @@ module Grape
56
57
 
57
58
  def self.convert_to_short_name(klass)
58
59
  ret = klass.name.gsub(/::/, '/')
59
- .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
60
- .gsub(/([a-z\d])([A-Z])/, '\1_\2')
61
- .tr('-', '_')
62
- .downcase
60
+ ret.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
61
+ ret.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
62
+ ret.tr!('-', '_')
63
+ ret.downcase!
63
64
  File.basename(ret, '_validator')
64
65
  end
65
66
 
@@ -1,6 +1,10 @@
1
1
  module Grape
2
2
  class API
3
3
  Boolean = Virtus::Attribute::Boolean
4
+
5
+ class Instance
6
+ Boolean = Virtus::Attribute::Boolean
7
+ end
4
8
  end
5
9
 
6
10
  module Validations
@@ -18,7 +18,7 @@ module Grape
18
18
  end
19
19
 
20
20
  def validate!(params)
21
- attrs = AttributesIterator.new(self, @scope, params)
21
+ attrs = SingleAttributeIterator.new(self, @scope, params)
22
22
  attrs.each do |resource_params, attr_name|
23
23
  if resource_params.is_a?(Hash) && resource_params[attr_name].nil?
24
24
  validate_param!(attr_name, resource_params)
@@ -1,28 +1,11 @@
1
+ require 'grape/validations/validators/multiple_params_base'
2
+
1
3
  module Grape
2
4
  module Validations
3
- require 'grape/validations/validators/mutual_exclusion'
4
- class ExactlyOneOfValidator < MutualExclusionValidator
5
- def validate!(params)
6
- super
7
- if scope_requires_params && none_of_restricted_params_is_present
8
- raise Grape::Exceptions::Validation, params: all_keys, message: message(:exactly_one)
9
- end
10
- params
11
- end
12
-
13
- def message(default_key = nil)
14
- options = instance_variable_get(:@option)
15
- if options_key?(:message)
16
- (options_key?(default_key, options[:message]) ? options[:message][default_key] : options[:message])
17
- else
18
- default_key
19
- end
20
- end
21
-
22
- private
23
-
24
- def none_of_restricted_params_is_present
25
- scoped_params.any? { |resource_params| keys_in_common(resource_params).empty? }
5
+ class ExactlyOneOfValidator < MultipleParamsBase
6
+ def validate_params!(params)
7
+ return if keys_in_common(params).length == 1
8
+ raise Grape::Exceptions::Validation, params: all_keys, message: message(:exactly_one)
26
9
  end
27
10
  end
28
11
  end
@@ -1,26 +1,30 @@
1
1
  module Grape
2
2
  module Validations
3
3
  class MultipleParamsBase < Base
4
- attr_reader :scoped_params
5
-
6
4
  def validate!(params)
7
- @scoped_params = [@scope.params(params)].flatten
8
- params
9
- end
5
+ attributes = MultipleAttributesIterator.new(self, @scope, params)
6
+ array_errors = []
10
7
 
11
- private
8
+ attributes.each do |resource_params|
9
+ begin
10
+ validate_params!(resource_params)
11
+ rescue Grape::Exceptions::Validation => e
12
+ array_errors << e
13
+ end
14
+ end
12
15
 
13
- def scope_requires_params
14
- @scope.required? || scoped_params.any? { |param| param.respond_to?(:any?) && param.any? }
16
+ raise Grape::Exceptions::ValidationArrayErrors, array_errors if array_errors.any?
15
17
  end
16
18
 
19
+ private
20
+
17
21
  def keys_in_common(resource_params)
18
22
  return [] unless resource_params.is_a?(Hash)
19
- (all_keys & resource_params.stringify_keys.keys).map(&:to_s)
23
+ all_keys & resource_params.keys.map! { |attr| @scope.full_name(attr) }
20
24
  end
21
25
 
22
26
  def all_keys
23
- attrs.map(&:to_s)
27
+ attrs.map { |attr| @scope.full_name(attr) }
24
28
  end
25
29
  end
26
30
  end
@@ -1,24 +1,12 @@
1
+ require 'grape/validations/validators/multiple_params_base'
2
+
1
3
  module Grape
2
4
  module Validations
3
- require 'grape/validations/validators/multiple_params_base'
4
5
  class MutualExclusionValidator < MultipleParamsBase
5
- attr_reader :processing_keys_in_common
6
-
7
- def validate!(params)
8
- super
9
- if two_or_more_exclusive_params_are_present
10
- raise Grape::Exceptions::Validation, params: processing_keys_in_common, message: message(:mutual_exclusion)
11
- end
12
- params
13
- end
14
-
15
- private
16
-
17
- def two_or_more_exclusive_params_are_present
18
- scoped_params.any? do |resource_params|
19
- @processing_keys_in_common = keys_in_common(resource_params)
20
- @processing_keys_in_common.length > 1
21
- end
6
+ def validate_params!(params)
7
+ keys = keys_in_common(params)
8
+ return if keys.length <= 1
9
+ raise Grape::Exceptions::Validation, params: keys, message: message(:mutual_exclusion)
22
10
  end
23
11
  end
24
12
  end