grape 0.1.1 → 2.4.0

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 (197) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1167 -0
  3. data/CONTRIBUTING.md +156 -0
  4. data/LICENSE +1 -1
  5. data/README.md +4192 -0
  6. data/UPGRADING.md +1697 -0
  7. data/grape.gemspec +28 -105
  8. data/grape.png +0 -0
  9. data/lib/grape/api/helpers.rb +9 -0
  10. data/lib/grape/api/instance.rb +247 -0
  11. data/lib/grape/api.rb +158 -194
  12. data/lib/grape/content_types.rb +31 -0
  13. data/lib/grape/cookies.rb +50 -0
  14. data/lib/grape/dry_types.rb +10 -0
  15. data/lib/grape/dsl/api.rb +17 -0
  16. data/lib/grape/dsl/callbacks.rb +69 -0
  17. data/lib/grape/dsl/configuration.rb +15 -0
  18. data/lib/grape/dsl/desc.rb +124 -0
  19. data/lib/grape/dsl/headers.rb +21 -0
  20. data/lib/grape/dsl/helpers.rb +108 -0
  21. data/lib/grape/dsl/inside_route.rb +454 -0
  22. data/lib/grape/dsl/logger.rb +22 -0
  23. data/lib/grape/dsl/middleware.rb +54 -0
  24. data/lib/grape/dsl/parameters.rb +266 -0
  25. data/lib/grape/dsl/request_response.rb +171 -0
  26. data/lib/grape/dsl/routing.rb +248 -0
  27. data/lib/grape/dsl/settings.rb +179 -0
  28. data/lib/grape/dsl/validations.rb +57 -0
  29. data/lib/grape/endpoint.rb +398 -68
  30. data/lib/grape/env.rb +20 -0
  31. data/lib/grape/error_formatter/base.rb +68 -0
  32. data/lib/grape/error_formatter/json.rb +28 -0
  33. data/lib/grape/error_formatter/serializable_hash.rb +7 -0
  34. data/lib/grape/error_formatter/txt.rb +21 -0
  35. data/lib/grape/error_formatter/xml.rb +11 -0
  36. data/lib/grape/error_formatter.rb +15 -0
  37. data/lib/grape/exceptions/base.rb +76 -0
  38. data/lib/grape/exceptions/conflicting_types.rb +11 -0
  39. data/lib/grape/exceptions/empty_message_body.rb +11 -0
  40. data/lib/grape/exceptions/incompatible_option_values.rb +11 -0
  41. data/lib/grape/exceptions/invalid_accept_header.rb +11 -0
  42. data/lib/grape/exceptions/invalid_formatter.rb +11 -0
  43. data/lib/grape/exceptions/invalid_message_body.rb +11 -0
  44. data/lib/grape/exceptions/invalid_parameters.rb +11 -0
  45. data/lib/grape/exceptions/invalid_response.rb +11 -0
  46. data/lib/grape/exceptions/invalid_version_header.rb +11 -0
  47. data/lib/grape/exceptions/invalid_versioner_option.rb +11 -0
  48. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +11 -0
  49. data/lib/grape/exceptions/method_not_allowed.rb +11 -0
  50. data/lib/grape/exceptions/missing_group_type.rb +13 -0
  51. data/lib/grape/exceptions/missing_mime_type.rb +11 -0
  52. data/lib/grape/exceptions/missing_option.rb +11 -0
  53. data/lib/grape/exceptions/missing_vendor_option.rb +11 -0
  54. data/lib/grape/exceptions/too_deep_parameters.rb +11 -0
  55. data/lib/grape/exceptions/too_many_multipart_files.rb +11 -0
  56. data/lib/grape/exceptions/unknown_auth_strategy.rb +11 -0
  57. data/lib/grape/exceptions/unknown_options.rb +11 -0
  58. data/lib/grape/exceptions/unknown_parameter.rb +11 -0
  59. data/lib/grape/exceptions/unknown_params_builder.rb +11 -0
  60. data/lib/grape/exceptions/unknown_validator.rb +11 -0
  61. data/lib/grape/exceptions/unsupported_group_type.rb +13 -0
  62. data/lib/grape/exceptions/validation.rb +25 -0
  63. data/lib/grape/exceptions/validation_array_errors.rb +14 -0
  64. data/lib/grape/exceptions/validation_errors.rb +53 -0
  65. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +24 -0
  66. data/lib/grape/extensions/hash.rb +27 -0
  67. data/lib/grape/extensions/hashie/mash.rb +24 -0
  68. data/lib/grape/formatter/base.rb +16 -0
  69. data/lib/grape/formatter/json.rb +13 -0
  70. data/lib/grape/formatter/serializable_hash.rb +39 -0
  71. data/lib/grape/formatter/txt.rb +11 -0
  72. data/lib/grape/formatter/xml.rb +13 -0
  73. data/lib/grape/formatter.rb +17 -0
  74. data/lib/grape/json.rb +10 -0
  75. data/lib/grape/locale/en.yml +59 -0
  76. data/lib/grape/middleware/auth/base.rb +23 -0
  77. data/lib/grape/middleware/auth/dsl.rb +39 -0
  78. data/lib/grape/middleware/auth/strategies.rb +25 -0
  79. data/lib/grape/middleware/auth/strategy_info.rb +15 -0
  80. data/lib/grape/middleware/base.rb +89 -18
  81. data/lib/grape/middleware/error.rb +129 -10
  82. data/lib/grape/middleware/filter.rb +19 -0
  83. data/lib/grape/middleware/formatter.rb +124 -82
  84. data/lib/grape/middleware/globals.rb +14 -0
  85. data/lib/grape/middleware/stack.rb +109 -0
  86. data/lib/grape/middleware/versioner/accept_version_header.rb +38 -0
  87. data/lib/grape/middleware/versioner/base.rb +74 -0
  88. data/lib/grape/middleware/versioner/header.rb +127 -0
  89. data/lib/grape/middleware/versioner/param.rb +32 -0
  90. data/lib/grape/middleware/versioner/path.rb +40 -0
  91. data/lib/grape/middleware/versioner.rb +21 -21
  92. data/lib/grape/namespace.rb +46 -0
  93. data/lib/grape/params_builder/base.rb +18 -0
  94. data/lib/grape/params_builder/hash.rb +11 -0
  95. data/lib/grape/params_builder/hash_with_indifferent_access.rb +11 -0
  96. data/lib/grape/params_builder/hashie_mash.rb +11 -0
  97. data/lib/grape/params_builder.rb +32 -0
  98. data/lib/grape/parser/base.rb +16 -0
  99. data/lib/grape/parser/json.rb +14 -0
  100. data/lib/grape/parser/xml.rb +14 -0
  101. data/lib/grape/parser.rb +15 -0
  102. data/lib/grape/path.rb +76 -0
  103. data/lib/grape/presenters/presenter.rb +11 -0
  104. data/lib/grape/railtie.rb +9 -0
  105. data/lib/grape/request.rb +190 -0
  106. data/lib/grape/router/base_route.rb +39 -0
  107. data/lib/grape/router/greedy_route.rb +20 -0
  108. data/lib/grape/router/pattern.rb +81 -0
  109. data/lib/grape/router/route.rb +62 -0
  110. data/lib/grape/router.rb +190 -0
  111. data/lib/grape/serve_stream/file_body.rb +36 -0
  112. data/lib/grape/serve_stream/sendfile_response.rb +21 -0
  113. data/lib/grape/serve_stream/stream_response.rb +23 -0
  114. data/lib/grape/types/invalid_value.rb +8 -0
  115. data/lib/grape/util/base_inheritable.rb +43 -0
  116. data/lib/grape/util/cache.rb +17 -0
  117. data/lib/grape/util/endpoint_configuration.rb +8 -0
  118. data/lib/grape/util/header.rb +13 -0
  119. data/lib/grape/util/inheritable_setting.rb +100 -0
  120. data/lib/grape/util/inheritable_values.rb +29 -0
  121. data/lib/grape/util/lazy/block.rb +29 -0
  122. data/lib/grape/util/lazy/value.rb +38 -0
  123. data/lib/grape/util/lazy/value_array.rb +21 -0
  124. data/lib/grape/util/lazy/value_enumerable.rb +34 -0
  125. data/lib/grape/util/lazy/value_hash.rb +21 -0
  126. data/lib/grape/util/media_type.rb +70 -0
  127. data/lib/grape/util/registry.rb +27 -0
  128. data/lib/grape/util/reverse_stackable_values.rb +15 -0
  129. data/lib/grape/util/stackable_values.rb +36 -0
  130. data/lib/grape/util/strict_hash_configuration.rb +108 -0
  131. data/lib/grape/validations/attributes_doc.rb +60 -0
  132. data/lib/grape/validations/attributes_iterator.rb +62 -0
  133. data/lib/grape/validations/contract_scope.rb +34 -0
  134. data/lib/grape/validations/multiple_attributes_iterator.rb +13 -0
  135. data/lib/grape/validations/params_scope.rb +538 -0
  136. data/lib/grape/validations/single_attribute_iterator.rb +26 -0
  137. data/lib/grape/validations/types/array_coercer.rb +61 -0
  138. data/lib/grape/validations/types/custom_type_coercer.rb +164 -0
  139. data/lib/grape/validations/types/custom_type_collection_coercer.rb +56 -0
  140. data/lib/grape/validations/types/dry_type_coercer.rb +66 -0
  141. data/lib/grape/validations/types/file.rb +31 -0
  142. data/lib/grape/validations/types/invalid_value.rb +17 -0
  143. data/lib/grape/validations/types/json.rb +71 -0
  144. data/lib/grape/validations/types/multiple_type_coercer.rb +57 -0
  145. data/lib/grape/validations/types/primitive_coercer.rb +73 -0
  146. data/lib/grape/validations/types/set_coercer.rb +35 -0
  147. data/lib/grape/validations/types/variant_collection_coercer.rb +51 -0
  148. data/lib/grape/validations/types.rb +213 -0
  149. data/lib/grape/validations/validator_factory.rb +15 -0
  150. data/lib/grape/validations/validators/all_or_none_of_validator.rb +16 -0
  151. data/lib/grape/validations/validators/allow_blank_validator.rb +20 -0
  152. data/lib/grape/validations/validators/as_validator.rb +14 -0
  153. data/lib/grape/validations/validators/at_least_one_of_validator.rb +15 -0
  154. data/lib/grape/validations/validators/base.rb +88 -0
  155. data/lib/grape/validations/validators/coerce_validator.rb +75 -0
  156. data/lib/grape/validations/validators/contract_scope_validator.rb +41 -0
  157. data/lib/grape/validations/validators/default_validator.rb +37 -0
  158. data/lib/grape/validations/validators/exactly_one_of_validator.rb +17 -0
  159. data/lib/grape/validations/validators/except_values_validator.rb +24 -0
  160. data/lib/grape/validations/validators/length_validator.rb +49 -0
  161. data/lib/grape/validations/validators/multiple_params_base.rb +34 -0
  162. data/lib/grape/validations/validators/mutual_exclusion_validator.rb +16 -0
  163. data/lib/grape/validations/validators/presence_validator.rb +15 -0
  164. data/lib/grape/validations/validators/regexp_validator.rb +16 -0
  165. data/lib/grape/validations/validators/same_as_validator.rb +29 -0
  166. data/lib/grape/validations/validators/values_validator.rb +60 -0
  167. data/lib/grape/validations.rb +21 -0
  168. data/lib/grape/version.rb +6 -0
  169. data/lib/grape/xml.rb +10 -0
  170. data/lib/grape.rb +81 -17
  171. metadata +250 -192
  172. data/.document +0 -5
  173. data/.gitignore +0 -23
  174. data/.rspec +0 -2
  175. data/.rvmrc +0 -1
  176. data/Gemfile +0 -21
  177. data/Gemfile.lock +0 -58
  178. data/README.markdown +0 -75
  179. data/Rakefile +0 -70
  180. data/VERSION +0 -1
  181. data/autotest/discover.rb +0 -1
  182. data/lib/grape/middleware/auth/basic.rb +0 -30
  183. data/lib/grape/middleware/auth/oauth2.rb +0 -55
  184. data/lib/grape/middleware/prefixer.rb +0 -21
  185. data/lib/grape/middleware_stack.rb +0 -35
  186. data/spec/grape/api_spec.rb +0 -343
  187. data/spec/grape/endpoint_spec.rb +0 -104
  188. data/spec/grape/middleware/auth/basic_spec.rb +0 -31
  189. data/spec/grape/middleware/auth/oauth2_spec.rb +0 -88
  190. data/spec/grape/middleware/base_spec.rb +0 -63
  191. data/spec/grape/middleware/error_spec.rb +0 -49
  192. data/spec/grape/middleware/formatter_spec.rb +0 -87
  193. data/spec/grape/middleware/prefixer_spec.rb +0 -30
  194. data/spec/grape/middleware/versioner_spec.rb +0 -40
  195. data/spec/grape/middleware_stack_spec.rb +0 -47
  196. data/spec/grape_spec.rb +0 -1
  197. data/spec/spec_helper.rb +0 -20
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Util
5
+ module Lazy
6
+ class ValueHash < ValueEnumerable
7
+ def initialize(hash)
8
+ super
9
+ @value_hash = ActiveSupport::HashWithIndifferentAccess.new
10
+ hash.each do |key, value|
11
+ self[key] = value
12
+ end
13
+ end
14
+
15
+ def evaluate
16
+ @value_hash.transform_values(&:evaluate)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Util
5
+ class MediaType
6
+ attr_reader :type, :subtype, :vendor, :version, :format
7
+
8
+ # based on the HTTP Accept header with the pattern:
9
+ # application/vnd.:vendor-:version+:format
10
+ VENDOR_VERSION_HEADER_REGEX = /\Avnd\.(?<vendor>[a-z0-9.\-_!^]+?)(?:-(?<version>[a-z0-9*.]+))?(?:\+(?<format>[a-z0-9*\-.]+))?\z/.freeze
11
+
12
+ def initialize(type:, subtype:)
13
+ @type = type
14
+ @subtype = subtype
15
+ VENDOR_VERSION_HEADER_REGEX.match(subtype) do |m|
16
+ @vendor = m[:vendor]
17
+ @version = m[:version]
18
+ @format = m[:format]
19
+ end
20
+ end
21
+
22
+ def ==(other)
23
+ eql?(other)
24
+ end
25
+
26
+ def eql?(other)
27
+ self.class == other.class &&
28
+ other.type == type &&
29
+ other.subtype == subtype &&
30
+ other.vendor == vendor &&
31
+ other.version == version &&
32
+ other.format == format
33
+ end
34
+
35
+ def hash
36
+ [self.class, type, subtype, vendor, version, format].hash
37
+ end
38
+
39
+ class << self
40
+ def best_quality(header, available_media_types)
41
+ parse(best_quality_media_type(header, available_media_types))
42
+ end
43
+
44
+ def parse(media_type)
45
+ return if media_type.blank?
46
+
47
+ type, subtype = media_type.split('/', 2)
48
+ return if type.blank? || subtype.blank?
49
+
50
+ new(type: type, subtype: subtype)
51
+ end
52
+
53
+ def match?(media_type)
54
+ return false if media_type.blank?
55
+
56
+ subtype = media_type.split('/', 2).last
57
+ return false if subtype.blank?
58
+
59
+ VENDOR_VERSION_HEADER_REGEX.match?(subtype)
60
+ end
61
+
62
+ def best_quality_media_type(header, available_media_types)
63
+ header.blank? ? available_media_types.first : Rack::Utils.best_q_match(header, available_media_types)
64
+ end
65
+ end
66
+
67
+ private_class_method :best_quality_media_type
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Util
5
+ module Registry
6
+ def register(klass)
7
+ short_name = build_short_name(klass)
8
+ return if short_name.nil?
9
+
10
+ warn "#{short_name} is already registered with class #{klass}" if registry.key?(short_name)
11
+ registry[short_name] = klass
12
+ end
13
+
14
+ private
15
+
16
+ def build_short_name(klass)
17
+ return if klass.name.blank?
18
+
19
+ klass.name.demodulize.underscore
20
+ end
21
+
22
+ def registry
23
+ @registry ||= {}.with_indifferent_access
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Util
5
+ class ReverseStackableValues < StackableValues
6
+ protected
7
+
8
+ def concat_values(inherited_value, new_value)
9
+ return inherited_value unless new_value
10
+
11
+ new_value + inherited_value
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Util
5
+ class StackableValues < BaseInheritable
6
+ # Even if there is no value, an empty array will be returned.
7
+ def [](name)
8
+ inherited_value = inherited_values[name]
9
+ new_value = new_values[name]
10
+
11
+ return new_value || [] unless inherited_value
12
+
13
+ concat_values(inherited_value, new_value)
14
+ end
15
+
16
+ def []=(name, value)
17
+ new_values[name] ||= []
18
+ new_values[name].push value
19
+ end
20
+
21
+ def to_hash
22
+ keys.each_with_object({}) do |key, result|
23
+ result[key] = self[key]
24
+ end
25
+ end
26
+
27
+ protected
28
+
29
+ def concat_values(inherited_value, new_value)
30
+ return inherited_value unless new_value
31
+
32
+ inherited_value + new_value
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Util
5
+ module StrictHashConfiguration
6
+ extend ActiveSupport::Concern
7
+
8
+ module DSL
9
+ extend ActiveSupport::Concern
10
+
11
+ module ClassMethods
12
+ def settings
13
+ config_context.to_hash
14
+ end
15
+
16
+ def configure(&block)
17
+ config_context.instance_exec(&block)
18
+ end
19
+ end
20
+ end
21
+
22
+ class SettingsContainer
23
+ def initialize
24
+ @settings = {}
25
+ @contexts = {}
26
+ end
27
+
28
+ def to_hash
29
+ @settings.to_hash
30
+ end
31
+ end
32
+
33
+ def self.config_class(*args)
34
+ new_config_class = Class.new(SettingsContainer)
35
+
36
+ args.each do |setting_name|
37
+ if setting_name.respond_to? :values
38
+ nested_settings_methods(setting_name, new_config_class)
39
+ else
40
+ simple_settings_methods(setting_name, new_config_class)
41
+ end
42
+ end
43
+
44
+ new_config_class
45
+ end
46
+
47
+ def self.simple_settings_methods(setting_name, new_config_class)
48
+ setting_name_sym = setting_name.to_sym
49
+ new_config_class.class_eval do
50
+ define_method setting_name do |new_value|
51
+ @settings[setting_name_sym] = new_value
52
+ end
53
+ end
54
+ end
55
+
56
+ def self.nested_settings_methods(setting_name, new_config_class)
57
+ new_config_class.class_eval do
58
+ setting_name.each_pair do |key, value|
59
+ define_method :"#{key}_context" do
60
+ @contexts[key] ||= Grape::Util::StrictHashConfiguration.config_class(*value).new
61
+ end
62
+
63
+ define_method key do |&block|
64
+ send(:"#{key}_context").instance_exec(&block)
65
+ end
66
+ end
67
+
68
+ define_method :to_hash do
69
+ @settings.to_hash.merge(
70
+ setting_name.each_key.with_object({}) do |k, merge_hash|
71
+ merge_hash[k] = send(:"#{k}_context").to_hash
72
+ end
73
+ )
74
+ end
75
+ end
76
+ end
77
+
78
+ def self.module(*args)
79
+ new_module = Module.new do
80
+ extend ActiveSupport::Concern
81
+ include DSL
82
+ end
83
+
84
+ new_module.tap do |mod|
85
+ class_mod = create_class_mod(args)
86
+
87
+ mod.const_set(:ClassMethods, class_mod)
88
+ end
89
+ end
90
+
91
+ def self.create_class_mod(args)
92
+ new_module = Module.new do
93
+ def config_context
94
+ @config_context ||= config_class.new
95
+ end
96
+ end
97
+
98
+ new_module.tap do |class_mod|
99
+ new_config_class = config_class(*args)
100
+
101
+ class_mod.send(:define_method, :config_class) do
102
+ @config_class ||= new_config_class
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Validations
5
+ # Documents parameters of an endpoint. If documentation isn't needed (for instance, it is an
6
+ # internal API), the class only cleans up attributes to avoid junk in RAM.
7
+
8
+ class AttributesDoc
9
+ attr_accessor :type, :values
10
+
11
+ # @param api [Grape::API::Instance]
12
+ # @param scope [Validations::ParamsScope]
13
+ def initialize(api, scope)
14
+ @api = api
15
+ @scope = scope
16
+ @type = type
17
+ end
18
+
19
+ def extract_details(validations)
20
+ details[:required] = validations.key?(:presence)
21
+
22
+ desc = validations.delete(:desc) || validations.delete(:description)
23
+
24
+ details[:desc] = desc if desc
25
+
26
+ documentation = validations.delete(:documentation)
27
+
28
+ details[:documentation] = documentation if documentation
29
+
30
+ details[:default] = validations[:default] if validations.key?(:default)
31
+
32
+ details[:min_length] = validations[:length][:min] if validations.key?(:length) && validations[:length].key?(:min)
33
+ details[:max_length] = validations[:length][:max] if validations.key?(:length) && validations[:length].key?(:max)
34
+ end
35
+
36
+ def document(attrs)
37
+ return if @api.namespace_inheritable(:do_not_document)
38
+
39
+ details[:type] = type.to_s if type
40
+ details[:values] = values if values
41
+
42
+ documented_attrs = attrs.each_with_object({}) do |name, memo|
43
+ memo[@scope.full_name(name)] = details
44
+ end
45
+
46
+ @api.namespace_stackable(:params, documented_attrs)
47
+ end
48
+
49
+ def required
50
+ details[:required]
51
+ end
52
+
53
+ protected
54
+
55
+ def details
56
+ @details ||= {}
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Validations
5
+ class AttributesIterator
6
+ include Enumerable
7
+
8
+ attr_reader :scope
9
+
10
+ def initialize(validator, scope, params)
11
+ @scope = scope
12
+ @attrs = validator.attrs
13
+ @original_params = scope.params(params)
14
+ @params = Array.wrap(@original_params)
15
+ end
16
+
17
+ def each(&block)
18
+ do_each(@params, &block) # because we need recursion for nested arrays
19
+ end
20
+
21
+ private
22
+
23
+ def do_each(params_to_process, parent_indicies = [], &block)
24
+ @scope.reset_index # gets updated depending on the size of params_to_process
25
+ params_to_process.each_with_index do |resource_params, index|
26
+ # when we get arrays of arrays it means that target element located inside array
27
+ # we need this because we want to know parent arrays indicies
28
+ if resource_params.is_a?(Array)
29
+ do_each(resource_params, [index] + parent_indicies, &block)
30
+ next
31
+ end
32
+
33
+ if @scope.type == Array
34
+ next unless @original_params.is_a?(Array) # do not validate content of array if it isn't array
35
+
36
+ # fill current and parent scopes with correct array indicies
37
+ parent_scope = @scope.parent
38
+ parent_indicies.each do |parent_index|
39
+ parent_scope.index = parent_index
40
+ parent_scope = parent_scope.parent
41
+ end
42
+ @scope.index = index
43
+ end
44
+
45
+ yield_attributes(resource_params, @attrs, &block)
46
+ end
47
+ end
48
+
49
+ def yield_attributes(_resource_params, _attrs)
50
+ raise NotImplementedError
51
+ end
52
+
53
+ # This is a special case so that we can ignore tree's where option
54
+ # values are missing lower down. Unfortunately we can remove this
55
+ # are the parameter parsing stage as they are required to ensure
56
+ # the correct indexing is maintained
57
+ def skip?(val)
58
+ val == Grape::DSL::Parameters::EmptyOptionalValue
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Validations
5
+ class ContractScope
6
+ # Declare the contract to be used for the endpoint's parameters.
7
+ # @param api [API] the API endpoint to modify.
8
+ # @param contract the contract or schema to be used for validation. Optional.
9
+ # @yield a block yielding a new schema class. Optional.
10
+ def initialize(api, contract = nil, &block)
11
+ # When block is passed, the first arg is either schema or nil.
12
+ contract = Dry::Schema.Params(parent: contract, &block) if block
13
+
14
+ if contract.respond_to?(:schema)
15
+ # It's a Dry::Validation::Contract, then.
16
+ contract = contract.new
17
+ key_map = contract.schema.key_map
18
+ else
19
+ # Dry::Schema::Processor, hopefully.
20
+ key_map = contract.key_map
21
+ end
22
+
23
+ api.namespace_stackable(:contract_key_map, key_map)
24
+
25
+ validator_options = {
26
+ validator_class: Grape::Validations.require_validator(:contract_scope),
27
+ opts: { schema: contract, fail_fast: false }
28
+ }
29
+
30
+ api.namespace_stackable(:validations, validator_options)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Validations
5
+ class MultipleAttributesIterator < AttributesIterator
6
+ private
7
+
8
+ def yield_attributes(resource_params, _attrs)
9
+ yield resource_params unless skip?(resource_params)
10
+ end
11
+ end
12
+ end
13
+ end