grape 1.1.0 → 1.2.5

Sign up to get free protection for your applications and to get access to all the features.
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