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.
Files changed (111) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +128 -43
  3. data/LICENSE +1 -1
  4. data/README.md +394 -47
  5. data/UPGRADING.md +111 -0
  6. data/grape.gemspec +3 -1
  7. data/lib/grape.rb +98 -66
  8. data/lib/grape/api.rb +136 -175
  9. data/lib/grape/api/instance.rb +280 -0
  10. data/lib/grape/config.rb +32 -0
  11. data/lib/grape/dsl/callbacks.rb +20 -0
  12. data/lib/grape/dsl/desc.rb +39 -7
  13. data/lib/grape/dsl/inside_route.rb +12 -6
  14. data/lib/grape/dsl/middleware.rb +7 -0
  15. data/lib/grape/dsl/parameters.rb +9 -4
  16. data/lib/grape/dsl/routing.rb +5 -1
  17. data/lib/grape/dsl/validations.rb +4 -3
  18. data/lib/grape/eager_load.rb +18 -0
  19. data/lib/grape/endpoint.rb +42 -26
  20. data/lib/grape/error_formatter.rb +1 -1
  21. data/lib/grape/exceptions/base.rb +9 -1
  22. data/lib/grape/exceptions/invalid_response.rb +9 -0
  23. data/lib/grape/exceptions/validation_errors.rb +4 -2
  24. data/lib/grape/formatter.rb +1 -1
  25. data/lib/grape/locale/en.yml +2 -0
  26. data/lib/grape/middleware/auth/base.rb +2 -4
  27. data/lib/grape/middleware/base.rb +2 -0
  28. data/lib/grape/middleware/error.rb +9 -4
  29. data/lib/grape/middleware/helpers.rb +10 -0
  30. data/lib/grape/middleware/stack.rb +1 -1
  31. data/lib/grape/middleware/versioner/header.rb +4 -4
  32. data/lib/grape/parser.rb +1 -1
  33. data/lib/grape/request.rb +1 -1
  34. data/lib/grape/router/attribute_translator.rb +2 -0
  35. data/lib/grape/router/route.rb +2 -2
  36. data/lib/grape/util/base_inheritable.rb +34 -0
  37. data/lib/grape/util/endpoint_configuration.rb +6 -0
  38. data/lib/grape/util/inheritable_values.rb +5 -25
  39. data/lib/grape/util/lazy_block.rb +25 -0
  40. data/lib/grape/util/lazy_value.rb +95 -0
  41. data/lib/grape/util/reverse_stackable_values.rb +7 -36
  42. data/lib/grape/util/stackable_values.rb +19 -22
  43. data/lib/grape/validations/attributes_iterator.rb +5 -3
  44. data/lib/grape/validations/multiple_attributes_iterator.rb +11 -0
  45. data/lib/grape/validations/params_scope.rb +20 -14
  46. data/lib/grape/validations/single_attribute_iterator.rb +13 -0
  47. data/lib/grape/validations/types/custom_type_coercer.rb +1 -1
  48. data/lib/grape/validations/types/file.rb +1 -1
  49. data/lib/grape/validations/validator_factory.rb +6 -11
  50. data/lib/grape/validations/validators/all_or_none.rb +6 -13
  51. data/lib/grape/validations/validators/as.rb +2 -3
  52. data/lib/grape/validations/validators/at_least_one_of.rb +5 -13
  53. data/lib/grape/validations/validators/base.rb +11 -10
  54. data/lib/grape/validations/validators/coerce.rb +4 -0
  55. data/lib/grape/validations/validators/default.rb +1 -1
  56. data/lib/grape/validations/validators/exactly_one_of.rb +6 -23
  57. data/lib/grape/validations/validators/multiple_params_base.rb +14 -10
  58. data/lib/grape/validations/validators/mutual_exclusion.rb +6 -18
  59. data/lib/grape/validations/validators/same_as.rb +23 -0
  60. data/lib/grape/version.rb +1 -1
  61. data/spec/grape/api/defines_boolean_in_params_spec.rb +37 -0
  62. data/spec/grape/api/routes_with_requirements_spec.rb +59 -0
  63. data/spec/grape/api_remount_spec.rb +466 -0
  64. data/spec/grape/api_spec.rb +379 -1
  65. data/spec/grape/config_spec.rb +17 -0
  66. data/spec/grape/dsl/desc_spec.rb +40 -16
  67. data/spec/grape/dsl/middleware_spec.rb +8 -0
  68. data/spec/grape/dsl/routing_spec.rb +10 -0
  69. data/spec/grape/endpoint_spec.rb +40 -4
  70. data/spec/grape/exceptions/base_spec.rb +65 -0
  71. data/spec/grape/exceptions/invalid_response_spec.rb +11 -0
  72. data/spec/grape/exceptions/validation_errors_spec.rb +6 -4
  73. data/spec/grape/integration/rack_spec.rb +22 -6
  74. data/spec/grape/middleware/auth/dsl_spec.rb +3 -3
  75. data/spec/grape/middleware/base_spec.rb +8 -0
  76. data/spec/grape/middleware/exception_spec.rb +1 -1
  77. data/spec/grape/middleware/formatter_spec.rb +15 -5
  78. data/spec/grape/middleware/versioner/header_spec.rb +6 -0
  79. data/spec/grape/named_api_spec.rb +19 -0
  80. data/spec/grape/request_spec.rb +24 -0
  81. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +29 -0
  82. data/spec/grape/validations/params_scope_spec.rb +184 -8
  83. data/spec/grape/validations/single_attribute_iterator_spec.rb +33 -0
  84. data/spec/grape/validations/validators/all_or_none_spec.rb +138 -30
  85. data/spec/grape/validations/validators/at_least_one_of_spec.rb +173 -29
  86. data/spec/grape/validations/validators/coerce_spec.rb +10 -2
  87. data/spec/grape/validations/validators/exactly_one_of_spec.rb +202 -38
  88. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +184 -27
  89. data/spec/grape/validations/validators/same_as_spec.rb +63 -0
  90. data/spec/grape/validations_spec.rb +33 -21
  91. data/spec/spec_helper.rb +4 -1
  92. metadata +35 -23
  93. data/Appraisals +0 -32
  94. data/Dangerfile +0 -2
  95. data/Gemfile +0 -33
  96. data/Gemfile.lock +0 -231
  97. data/Guardfile +0 -10
  98. data/RELEASING.md +0 -111
  99. data/Rakefile +0 -25
  100. data/benchmark/simple.rb +0 -27
  101. data/benchmark/simple_with_type_coercer.rb +0 -22
  102. data/gemfiles/multi_json.gemfile +0 -35
  103. data/gemfiles/multi_xml.gemfile +0 -35
  104. data/gemfiles/rack_1.5.2.gemfile +0 -35
  105. data/gemfiles/rack_edge.gemfile +0 -35
  106. data/gemfiles/rails_3.gemfile +0 -36
  107. data/gemfiles/rails_4.gemfile +0 -35
  108. data/gemfiles/rails_5.gemfile +0 -35
  109. data/gemfiles/rails_edge.gemfile +0 -35
  110. data/pkg/grape-0.17.0.gem +0 -0
  111. 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
@@ -0,0 +1,6 @@
1
+ module Grape
2
+ module Util
3
+ class EndpointConfiguration < LazyValueHash
4
+ end
5
+ end
6
+ 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.clone
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
- 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
@@ -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
- [@parent.full_name(@element), [@index || index, name].map(&method(:brackets))].compact.join
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(:aliased_params, @api.route_setting(:aliased_params) || [])
123
- @api.route_setting(:aliased_params) << { attrs.first => opts[:as] }
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: opts[: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
- factory = Grape::Validations::ValidatorFactory.new(attributes: attrs,
410
- options: options,
411
- required: doc_attrs[:required],
412
- params_scope: self,
413
- opts: opts,
414
- validator_class: validator_class)
415
- @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)
416
422
  end
417
423
 
418
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