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,248 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module DSL
5
+ module Routing
6
+ extend ActiveSupport::Concern
7
+ include Grape::DSL::Configuration
8
+
9
+ module ClassMethods
10
+ attr_reader :endpoints
11
+
12
+ # Specify an API version.
13
+ #
14
+ # @example API with legacy support.
15
+ # class MyAPI < Grape::API
16
+ # version 'v2'
17
+ #
18
+ # get '/main' do
19
+ # {some: 'data'}
20
+ # end
21
+ #
22
+ # version 'v1' do
23
+ # get '/main' do
24
+ # {legacy: 'data'}
25
+ # end
26
+ # end
27
+ # end
28
+ #
29
+ def version(*args, &block)
30
+ if args.any?
31
+ options = args.extract_options!
32
+ options = options.reverse_merge(using: :path)
33
+ requested_versions = args.flatten.map(&:to_s)
34
+
35
+ raise Grape::Exceptions::MissingVendorOption.new if options[:using] == :header && !options.key?(:vendor)
36
+
37
+ @versions = versions | requested_versions
38
+
39
+ if block
40
+ within_namespace do
41
+ namespace_inheritable(:version, requested_versions)
42
+ namespace_inheritable(:version_options, options)
43
+
44
+ instance_eval(&block)
45
+ end
46
+ else
47
+ namespace_inheritable(:version, requested_versions)
48
+ namespace_inheritable(:version_options, options)
49
+ end
50
+ end
51
+
52
+ @versions.last if instance_variable_defined?(:@versions) && @versions
53
+ end
54
+
55
+ # Define a root URL prefix for your entire API.
56
+ def prefix(prefix = nil)
57
+ namespace_inheritable(:root_prefix, prefix&.to_s)
58
+ end
59
+
60
+ # Create a scope without affecting the URL.
61
+ #
62
+ # @param _name [Symbol] Purely placebo, just allows to name the scope to
63
+ # make the code more readable.
64
+ def scope(_name = nil, &block)
65
+ within_namespace do
66
+ nest(block)
67
+ end
68
+ end
69
+
70
+ def build_with(build_with)
71
+ namespace_inheritable(:build_params_with, build_with)
72
+ end
73
+
74
+ # Do not route HEAD requests to GET requests automatically.
75
+ def do_not_route_head!
76
+ namespace_inheritable(:do_not_route_head, true)
77
+ end
78
+
79
+ # Do not automatically route OPTIONS.
80
+ def do_not_route_options!
81
+ namespace_inheritable(:do_not_route_options, true)
82
+ end
83
+
84
+ def lint!
85
+ namespace_inheritable(:lint, true)
86
+ end
87
+
88
+ def do_not_document!
89
+ namespace_inheritable(:do_not_document, true)
90
+ end
91
+
92
+ def mount(mounts, *opts)
93
+ mounts = { mounts => '/' } unless mounts.respond_to?(:each_pair)
94
+ mounts.each_pair do |app, path|
95
+ if app.respond_to?(:mount_instance)
96
+ opts_with = opts.any? ? opts.first[:with] : {}
97
+ mount({ app.mount_instance(configuration: opts_with) => path }, *opts)
98
+ next
99
+ end
100
+ in_setting = inheritable_setting
101
+
102
+ if app.respond_to?(:inheritable_setting, true)
103
+ mount_path = Grape::Router.normalize_path(path)
104
+ app.top_level_setting.namespace_stackable[:mount_path] = mount_path
105
+
106
+ app.inherit_settings(inheritable_setting)
107
+
108
+ in_setting = app.top_level_setting
109
+
110
+ app.change!
111
+ change!
112
+ end
113
+
114
+ # When trying to mount multiple times the same endpoint, remove the previous ones
115
+ # from the list of endpoints if refresh_already_mounted parameter is true
116
+ refresh_already_mounted = opts.any? ? opts.first[:refresh_already_mounted] : false
117
+ if refresh_already_mounted && !endpoints.empty?
118
+ endpoints.delete_if do |endpoint|
119
+ endpoint.options[:app].to_s == app.to_s
120
+ end
121
+ end
122
+
123
+ endpoints << Grape::Endpoint.new(
124
+ in_setting,
125
+ method: :any,
126
+ path: path,
127
+ app: app,
128
+ route_options: { anchor: false },
129
+ forward_match: !app.respond_to?(:inheritable_setting),
130
+ for: self
131
+ )
132
+ end
133
+ end
134
+
135
+ # Defines a route that will be recognized
136
+ # by the Grape API.
137
+ #
138
+ # @param methods [HTTP Verb] One or more HTTP verbs that are accepted by this route. Set to `:any` if you want any verb to be accepted.
139
+ # @param paths [String] One or more strings representing the URL segment(s) for this route.
140
+ #
141
+ # @example Defining a basic route.
142
+ # class MyAPI < Grape::API
143
+ # route(:any, '/hello') do
144
+ # {hello: 'world'}
145
+ # end
146
+ # end
147
+ def route(methods, paths = ['/'], route_options = {}, &block)
148
+ methods = '*' if methods == :any
149
+ endpoint_options = {
150
+ method: methods,
151
+ path: paths,
152
+ for: self,
153
+ route_options: {
154
+ params: namespace_stackable_with_hash(:params) || {}
155
+ }.deep_merge(route_setting(:description) || {}).deep_merge(route_options || {})
156
+ }
157
+
158
+ new_endpoint = Grape::Endpoint.new(inheritable_setting, endpoint_options, &block)
159
+ endpoints << new_endpoint unless endpoints.any? { |e| e.equals?(new_endpoint) }
160
+
161
+ route_end
162
+ reset_validations!
163
+ end
164
+
165
+ Grape::HTTP_SUPPORTED_METHODS.each do |supported_method|
166
+ define_method supported_method.downcase do |*args, &block|
167
+ options = args.extract_options!
168
+ paths = args.first || ['/']
169
+ route(supported_method, paths, options, &block)
170
+ end
171
+ end
172
+
173
+ # Declare a "namespace", which prefixes all subordinate routes with its
174
+ # name. Any endpoints within a namespace, group, resource or segment,
175
+ # etc., will share their parent context as well as any configuration
176
+ # done in the namespace context.
177
+ #
178
+ # @example
179
+ #
180
+ # namespace :foo do
181
+ # get 'bar' do
182
+ # # defines the endpoint: GET /foo/bar
183
+ # end
184
+ # end
185
+ def namespace(space = nil, options = {}, &block)
186
+ return Namespace.joined_space_path(namespace_stackable(:namespace)) unless space || block
187
+
188
+ within_namespace do
189
+ nest(block) do
190
+ namespace_stackable(:namespace, Namespace.new(space, options)) if space
191
+ end
192
+ end
193
+ end
194
+
195
+ alias group namespace
196
+ alias resource namespace
197
+ alias resources namespace
198
+ alias segment namespace
199
+
200
+ # An array of API routes.
201
+ def routes
202
+ @routes ||= prepare_routes
203
+ end
204
+
205
+ # Remove all defined routes.
206
+ def reset_routes!
207
+ endpoints.each(&:reset_routes!)
208
+ @routes = nil
209
+ end
210
+
211
+ def reset_endpoints!
212
+ @endpoints = []
213
+ end
214
+
215
+ # This method allows you to quickly define a parameter route segment
216
+ # in your API.
217
+ #
218
+ # @param param [Symbol] The name of the parameter you wish to declare.
219
+ # @option options [Regexp] You may supply a regular expression that the declared parameter must meet.
220
+ def route_param(param, options = {}, &block)
221
+ options = options.dup
222
+
223
+ options[:requirements] = {
224
+ param.to_sym => options[:requirements]
225
+ } if options[:requirements].is_a?(Regexp)
226
+
227
+ Grape::Validations::ParamsScope.new(api: self) do
228
+ requires param, type: options[:type]
229
+ end if options.key?(:type)
230
+
231
+ namespace(":#{param}", options, &block)
232
+ end
233
+
234
+ # @return array of defined versions
235
+ def versions
236
+ @versions ||= []
237
+ end
238
+
239
+ private
240
+
241
+ def refresh_mounted_api(mounts, *opts)
242
+ opts << { refresh_already_mounted: true }
243
+ mount(mounts, *opts)
244
+ end
245
+ end
246
+ end
247
+ end
248
+ end
@@ -0,0 +1,179 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module DSL
5
+ # Keeps track of settings (implemented as key-value pairs, grouped by
6
+ # types), in two contexts: top-level settings which apply globally no
7
+ # matter where they're defined, and inheritable settings which apply only
8
+ # in the current scope and scopes nested under it.
9
+ module Settings
10
+ extend ActiveSupport::Concern
11
+
12
+ attr_writer :inheritable_setting, :top_level_setting
13
+
14
+ # Fetch our top-level settings, which apply to all endpoints in the API.
15
+ def top_level_setting
16
+ @top_level_setting ||= build_top_level_setting
17
+ end
18
+
19
+ # Fetch our current inheritable settings, which are inherited by
20
+ # nested scopes but not shared across siblings.
21
+ def inheritable_setting
22
+ @inheritable_setting ||= Grape::Util::InheritableSetting.new.tap { |new_settings| new_settings.inherit_from top_level_setting }
23
+ end
24
+
25
+ # @param type [Symbol]
26
+ # @param key [Symbol]
27
+ def unset(type, key)
28
+ setting = inheritable_setting.send(type)
29
+ setting.delete key
30
+ end
31
+
32
+ # @param type [Symbol]
33
+ # @param key [Symbol]
34
+ # @param value [Object] will be stored if the value is currently empty
35
+ # @return either the old value, if it wasn't nil, or the given value
36
+ def get_or_set(type, key, value)
37
+ setting = inheritable_setting.send(type)
38
+ if value.nil?
39
+ setting[key]
40
+ else
41
+ setting[key] = value
42
+ end
43
+ end
44
+
45
+ # @param key [Symbol]
46
+ # @param value [Object]
47
+ # @return (see #get_or_set)
48
+ def global_setting(key, value = nil)
49
+ get_or_set :global, key, value
50
+ end
51
+
52
+ # @param key [Symbol]
53
+ def unset_global_setting(key)
54
+ unset :global, key
55
+ end
56
+
57
+ # (see #global_setting)
58
+ def route_setting(key, value = nil)
59
+ get_or_set :route, key, value
60
+ end
61
+
62
+ # (see #unset_global_setting)
63
+ def unset_route_setting(key)
64
+ unset :route, key
65
+ end
66
+
67
+ # (see #global_setting)
68
+ def namespace_setting(key, value = nil)
69
+ get_or_set :namespace, key, value
70
+ end
71
+
72
+ # (see #unset_global_setting)
73
+ def unset_namespace_setting(key)
74
+ unset :namespace, key
75
+ end
76
+
77
+ # (see #global_setting)
78
+ def namespace_inheritable(key, value = nil)
79
+ get_or_set :namespace_inheritable, key, value
80
+ end
81
+
82
+ # (see #unset_global_setting)
83
+ def unset_namespace_inheritable(key)
84
+ unset :namespace_inheritable, key
85
+ end
86
+
87
+ # @param key [Symbol]
88
+ def namespace_inheritable_to_nil(key)
89
+ inheritable_setting.namespace_inheritable[key] = nil
90
+ end
91
+
92
+ # (see #global_setting)
93
+ def namespace_stackable(key, value = nil)
94
+ get_or_set :namespace_stackable, key, value
95
+ end
96
+
97
+ def namespace_reverse_stackable(key, value = nil)
98
+ get_or_set :namespace_reverse_stackable, key, value
99
+ end
100
+
101
+ def namespace_stackable_with_hash(key)
102
+ settings = get_or_set :namespace_stackable, key, nil
103
+ return if settings.blank?
104
+
105
+ settings.each_with_object({}) { |value, result| result.deep_merge!(value) }
106
+ end
107
+
108
+ def namespace_reverse_stackable_with_hash(key)
109
+ settings = get_or_set :namespace_reverse_stackable, key, nil
110
+ return if settings.blank?
111
+
112
+ settings.each_with_object({}) do |setting, result|
113
+ result.merge!(setting) { |_k, s1, _s2| s1 }
114
+ end
115
+ end
116
+
117
+ # (see #unset_global_setting)
118
+ def unset_namespace_stackable(key)
119
+ unset :namespace_stackable, key
120
+ end
121
+
122
+ # (see #global_setting)
123
+ def api_class_setting(key, value = nil)
124
+ get_or_set :api_class, key, value
125
+ end
126
+
127
+ # (see #unset_global_setting)
128
+ def unset_api_class_setting(key)
129
+ unset :api_class, key
130
+ end
131
+
132
+ # Fork our inheritable settings to a new instance, copied from our
133
+ # parent's, but separate so we won't modify it. Every call to this
134
+ # method should have an answering call to #namespace_end.
135
+ def namespace_start
136
+ @inheritable_setting = Grape::Util::InheritableSetting.new.tap { |new_settings| new_settings.inherit_from inheritable_setting }
137
+ end
138
+
139
+ # Set the inheritable settings pointer back up by one level.
140
+ def namespace_end
141
+ route_end
142
+ @inheritable_setting = inheritable_setting.parent
143
+ end
144
+
145
+ # Stop defining settings for the current route and clear them for the
146
+ # next, within a namespace.
147
+ def route_end
148
+ inheritable_setting.route_end
149
+ end
150
+
151
+ # Execute the block within a context where our inheritable settings are forked
152
+ # to a new copy (see #namespace_start).
153
+ def within_namespace(&block)
154
+ namespace_start
155
+
156
+ result = yield if block
157
+
158
+ namespace_end
159
+ reset_validations!
160
+
161
+ result
162
+ end
163
+
164
+ private
165
+
166
+ # Builds the current class :inheritable_setting. If available, it inherits from
167
+ # the superclass's :inheritable_setting.
168
+ def build_top_level_setting
169
+ Grape::Util::InheritableSetting.new.tap do |setting|
170
+ # Doesn't try to inherit settings from +Grape::API::Instance+ which also responds to
171
+ # +inheritable_setting+, however, it doesn't contain any user-defined settings.
172
+ # Otherwise, it would lead to an extra instance of +Grape::Util::InheritableSetting+
173
+ # in the chain for every endpoint.
174
+ setting.inherit_from superclass.inheritable_setting if defined?(superclass) && superclass.respond_to?(:inheritable_setting) && superclass != Grape::API::Instance
175
+ end
176
+ end
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module DSL
5
+ module Validations
6
+ extend ActiveSupport::Concern
7
+
8
+ include Grape::DSL::Configuration
9
+
10
+ module ClassMethods
11
+ # Clears all defined parameters and validations. The main purpose of it is to clean up
12
+ # settings, so next endpoint won't interfere with previous one.
13
+ #
14
+ # params do
15
+ # # params for the endpoint below this block
16
+ # end
17
+ # post '/current' do
18
+ # # whatever
19
+ # end
20
+ #
21
+ # # somewhere between them the reset_validations! method gets called
22
+ #
23
+ # params do
24
+ # # params for the endpoint below this block
25
+ # end
26
+ # post '/next' do
27
+ # # whatever
28
+ # end
29
+ def reset_validations!
30
+ unset_namespace_stackable :declared_params
31
+ unset_namespace_stackable :validations
32
+ unset_namespace_stackable :params
33
+ end
34
+
35
+ # Opens a root-level ParamsScope, defining parameter coercions and
36
+ # validations for the endpoint.
37
+ # @yield instance context of the new scope
38
+ def params(&block)
39
+ Grape::Validations::ParamsScope.new(api: self, type: Hash, &block)
40
+ end
41
+
42
+ # Declare the contract to be used for the endpoint's parameters.
43
+ # @param contract [Class<Dry::Validation::Contract> | Dry::Schema::Processor]
44
+ # The contract or schema to be used for validation. Optional.
45
+ # @yield a block yielding a new instance of Dry::Schema::Params
46
+ # subclass, allowing to define the schema inline. When the
47
+ # +contract+ parameter is a schema, it will be used as a parent. Optional.
48
+ def contract(contract = nil, &block)
49
+ raise ArgumentError, 'Either contract or block must be provided' unless contract || block
50
+ raise ArgumentError, 'Cannot inherit from contract, only schema' if block && contract.respond_to?(:schema)
51
+
52
+ Grape::Validations::ContractScope.new(self, contract, &block)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end