grape 2.4.0 → 4.0.1

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 (170) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +351 -0
  3. data/CONTRIBUTING.md +18 -10
  4. data/README.md +457 -228
  5. data/UPGRADING.md +1240 -1
  6. data/grape.gemspec +7 -7
  7. data/lib/grape/api/instance.rb +65 -116
  8. data/lib/grape/api.rb +53 -62
  9. data/lib/grape/content_types.rb +58 -3
  10. data/lib/grape/cookies.rb +2 -6
  11. data/lib/grape/declared_params_handler.rb +116 -0
  12. data/lib/grape/dry_types.rb +48 -4
  13. data/lib/grape/dsl/callbacks.rb +8 -58
  14. data/lib/grape/dsl/declared.rb +35 -0
  15. data/lib/grape/dsl/desc.rb +24 -67
  16. data/lib/grape/dsl/entity.rb +106 -0
  17. data/lib/grape/dsl/helpers.rb +80 -65
  18. data/lib/grape/dsl/inside_route.rb +56 -296
  19. data/lib/grape/dsl/logger.rb +4 -9
  20. data/lib/grape/dsl/middleware.rb +22 -40
  21. data/lib/grape/dsl/parameters.rb +86 -93
  22. data/lib/grape/dsl/request_response.rb +139 -139
  23. data/lib/grape/dsl/rescue_options.rb +25 -0
  24. data/lib/grape/dsl/routing.rb +328 -203
  25. data/lib/grape/dsl/settings.rb +29 -135
  26. data/lib/grape/dsl/validations.rb +38 -44
  27. data/lib/grape/dsl/version_options.rb +23 -0
  28. data/lib/grape/endpoint/options.rb +25 -0
  29. data/lib/grape/endpoint.rb +285 -245
  30. data/lib/grape/env.rb +8 -3
  31. data/lib/grape/error_formatter/base.rb +27 -21
  32. data/lib/grape/error_formatter/json.rb +6 -0
  33. data/lib/grape/error_formatter/txt.rb +10 -10
  34. data/lib/grape/error_formatter.rb +6 -2
  35. data/lib/grape/exceptions/base.rb +20 -44
  36. data/lib/grape/exceptions/error_response.rb +48 -0
  37. data/lib/grape/exceptions/incompatible_option_values.rb +1 -1
  38. data/lib/grape/exceptions/internal_server_error.rb +16 -0
  39. data/lib/grape/exceptions/invalid_accept_header.rb +1 -1
  40. data/lib/grape/exceptions/invalid_formatter.rb +1 -1
  41. data/lib/grape/exceptions/invalid_message_body.rb +1 -1
  42. data/lib/grape/exceptions/invalid_version_header.rb +1 -1
  43. data/lib/grape/exceptions/invalid_versioner_option.rb +1 -1
  44. data/lib/grape/exceptions/method_not_allowed.rb +1 -1
  45. data/lib/grape/exceptions/missing_group_type.rb +0 -2
  46. data/lib/grape/exceptions/missing_mime_type.rb +1 -1
  47. data/lib/grape/exceptions/request_error.rb +11 -0
  48. data/lib/grape/exceptions/unknown_auth_strategy.rb +1 -1
  49. data/lib/grape/exceptions/unknown_error_formatter.rb +11 -0
  50. data/lib/grape/exceptions/unknown_parameter.rb +1 -1
  51. data/lib/grape/exceptions/unknown_params_builder.rb +1 -1
  52. data/lib/grape/exceptions/unknown_validator.rb +1 -1
  53. data/lib/grape/exceptions/unsupported_group_type.rb +0 -2
  54. data/lib/grape/exceptions/validation.rb +25 -7
  55. data/lib/grape/exceptions/validation_array_errors.rb +4 -0
  56. data/lib/grape/exceptions/validation_errors.rb +21 -23
  57. data/lib/grape/formatter/json.rb +1 -0
  58. data/lib/grape/formatter/serializable_hash.rb +6 -9
  59. data/lib/grape/json.rb +38 -2
  60. data/lib/grape/locale/en.yml +3 -5
  61. data/lib/grape/middleware/auth/base.rb +4 -3
  62. data/lib/grape/middleware/auth/dsl.rb +9 -23
  63. data/lib/grape/middleware/base.rb +41 -36
  64. data/lib/grape/middleware/error.rb +271 -73
  65. data/lib/grape/middleware/formatter.rb +152 -63
  66. data/lib/grape/middleware/precomputed_content_types.rb +51 -0
  67. data/lib/grape/middleware/stack.rb +20 -11
  68. data/lib/grape/middleware/versioner/accept_version_header.rb +3 -3
  69. data/lib/grape/middleware/versioner/base.rb +40 -48
  70. data/lib/grape/middleware/versioner/header.rb +5 -23
  71. data/lib/grape/middleware/versioner/param.rb +2 -2
  72. data/lib/grape/middleware/versioner/path.rb +54 -7
  73. data/lib/grape/mountable.rb +22 -0
  74. data/lib/grape/namespace.rb +19 -11
  75. data/lib/grape/params_builder/base.rb +2 -0
  76. data/lib/grape/params_builder/hash_with_indifferent_access.rb +1 -1
  77. data/lib/grape/params_builder.rb +2 -19
  78. data/lib/grape/parser/json.rb +1 -1
  79. data/lib/grape/path.rb +8 -72
  80. data/lib/grape/precompiled_json.rb +50 -0
  81. data/lib/grape/request.rb +57 -20
  82. data/lib/grape/router/base_route.rb +53 -7
  83. data/lib/grape/router/greedy_route.rb +16 -6
  84. data/lib/grape/router/mustermann_pattern.rb +44 -0
  85. data/lib/grape/router/pattern/path.rb +78 -0
  86. data/lib/grape/router/pattern.rb +65 -37
  87. data/lib/grape/router/route.rb +66 -18
  88. data/lib/grape/router.rb +126 -115
  89. data/lib/grape/serve_stream/file_body.rb +7 -0
  90. data/lib/grape/serve_stream/sendfile_response.rb +3 -5
  91. data/lib/grape/serve_stream/stream_response.rb +7 -0
  92. data/lib/grape/testing.rb +33 -0
  93. data/lib/grape/util/api_description.rb +67 -0
  94. data/lib/grape/util/cache.rb +22 -2
  95. data/lib/grape/util/deep_freeze.rb +34 -0
  96. data/lib/grape/util/freeze_on_new.rb +20 -0
  97. data/lib/grape/util/inheritable_setting.rb +777 -39
  98. data/lib/grape/util/lazy/base.rb +16 -0
  99. data/lib/grape/util/lazy/block.rb +2 -9
  100. data/lib/grape/util/lazy/value.rb +2 -9
  101. data/lib/grape/util/lazy/value_enumerable.rb +13 -16
  102. data/lib/grape/util/media_type.rb +13 -9
  103. data/lib/grape/util/path_normalizer.rb +37 -0
  104. data/lib/grape/util/registry.rb +13 -3
  105. data/lib/grape/util/shadowed_rescue_handlers.rb +49 -0
  106. data/lib/grape/util/stackable_values.rb +39 -10
  107. data/lib/grape/util/translation.rb +42 -0
  108. data/lib/grape/validations/attributes_iterator.rb +61 -29
  109. data/lib/grape/validations/coerce_options.rb +21 -0
  110. data/lib/grape/validations/contract_scope.rb +8 -13
  111. data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
  112. data/lib/grape/validations/oneof_collector.rb +35 -0
  113. data/lib/grape/validations/param_scope_tracker.rb +62 -0
  114. data/lib/grape/validations/params_documentation.rb +52 -0
  115. data/lib/grape/validations/params_scope.rb +217 -304
  116. data/lib/grape/validations/shared_options.rb +19 -0
  117. data/lib/grape/validations/single_attribute_iterator.rb +6 -4
  118. data/lib/grape/validations/types/array_coercer.rb +7 -10
  119. data/lib/grape/validations/types/custom_type_coercer.rb +47 -85
  120. data/lib/grape/validations/types/custom_type_collection_coercer.rb +1 -1
  121. data/lib/grape/validations/types/dry_type_coercer.rb +10 -15
  122. data/lib/grape/validations/types/json.rb +1 -3
  123. data/lib/grape/validations/types/multiple_type_coercer.rb +5 -3
  124. data/lib/grape/validations/types/primitive_coercer.rb +14 -33
  125. data/lib/grape/validations/types/set_coercer.rb +1 -1
  126. data/lib/grape/validations/types/variant_collection_coercer.rb +16 -3
  127. data/lib/grape/validations/types.rb +29 -51
  128. data/lib/grape/validations/validations_spec.rb +164 -0
  129. data/lib/grape/validations/validators/all_or_none_of_validator.rb +6 -3
  130. data/lib/grape/validations/validators/allow_blank_validator.rb +10 -5
  131. data/lib/grape/validations/validators/at_least_one_of_validator.rb +5 -2
  132. data/lib/grape/validations/validators/base.rb +118 -34
  133. data/lib/grape/validations/validators/coerce_validator.rb +25 -37
  134. data/lib/grape/validations/validators/contract_scope_validator.rb +10 -5
  135. data/lib/grape/validations/validators/default_validator.rb +13 -20
  136. data/lib/grape/validations/validators/exactly_one_of_validator.rb +10 -3
  137. data/lib/grape/validations/validators/except_values_validator.rb +14 -4
  138. data/lib/grape/validations/validators/length_validator.rb +23 -22
  139. data/lib/grape/validations/validators/multiple_params_base.rb +12 -9
  140. data/lib/grape/validations/validators/{mutual_exclusion_validator.rb → mutually_exclusive_validator.rb} +4 -2
  141. data/lib/grape/validations/validators/oneof_validator.rb +51 -0
  142. data/lib/grape/validations/validators/presence_validator.rb +4 -2
  143. data/lib/grape/validations/validators/regexp_validator.rb +11 -3
  144. data/lib/grape/validations/validators/same_as_validator.rb +7 -15
  145. data/lib/grape/validations/validators/values_validator.rb +34 -26
  146. data/lib/grape/version.rb +1 -2
  147. data/lib/grape/xml.rb +8 -1
  148. data/lib/grape.rb +58 -31
  149. metadata +62 -39
  150. data/lib/grape/api/helpers.rb +0 -9
  151. data/lib/grape/dsl/api.rb +0 -17
  152. data/lib/grape/dsl/configuration.rb +0 -15
  153. data/lib/grape/exceptions/conflicting_types.rb +0 -11
  154. data/lib/grape/exceptions/empty_message_body.rb +0 -11
  155. data/lib/grape/exceptions/invalid_parameters.rb +0 -11
  156. data/lib/grape/exceptions/missing_option.rb +0 -11
  157. data/lib/grape/exceptions/too_deep_parameters.rb +0 -11
  158. data/lib/grape/exceptions/too_many_multipart_files.rb +0 -11
  159. data/lib/grape/exceptions/unknown_options.rb +0 -11
  160. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +0 -24
  161. data/lib/grape/extensions/hash.rb +0 -27
  162. data/lib/grape/extensions/hashie/mash.rb +0 -24
  163. data/lib/grape/middleware/globals.rb +0 -14
  164. data/lib/grape/types/invalid_value.rb +0 -8
  165. data/lib/grape/util/base_inheritable.rb +0 -43
  166. data/lib/grape/util/inheritable_values.rb +0 -29
  167. data/lib/grape/util/reverse_stackable_values.rb +0 -15
  168. data/lib/grape/util/strict_hash_configuration.rb +0 -108
  169. data/lib/grape/validations/attributes_doc.rb +0 -60
  170. data/lib/grape/validations/validator_factory.rb +0 -15
@@ -18,6 +18,13 @@ module Grape
18
18
  def ==(other)
19
19
  stream == other.stream
20
20
  end
21
+ alias eql? ==
22
+
23
+ # Mirrors #==, which keys on the stream alone and does not check the
24
+ # class, so the class must stay out of the hash too.
25
+ def hash
26
+ stream.hash
27
+ end
21
28
  end
22
29
  end
23
30
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Testing
5
+ module RunBeforeEach
6
+ def run
7
+ self.class.run_before_each(self)
8
+ super
9
+ end
10
+ end
11
+
12
+ module ClassMethods
13
+ def before_each(&block)
14
+ raise ArgumentError, 'a block is required' unless block
15
+
16
+ @before_each ||= []
17
+ @before_each << block
18
+ end
19
+
20
+ def reset_before_each
21
+ @before_each&.clear
22
+ end
23
+
24
+ def run_before_each(endpoint)
25
+ superclass.run_before_each(endpoint) unless self == Grape::Endpoint
26
+ @before_each&.each { |blk| blk.call(endpoint) }
27
+ end
28
+ end
29
+
30
+ Grape::Endpoint.prepend(RunBeforeEach)
31
+ Grape::Endpoint.extend(ClassMethods)
32
+ end
33
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Util
5
+ class ApiDescription
6
+ DSL_METHODS = %i[
7
+ body_name
8
+ consumes
9
+ default_response
10
+ deprecated
11
+ detail
12
+ entity
13
+ headers
14
+ hidden
15
+ http_codes
16
+ is_array
17
+ named
18
+ nickname
19
+ params
20
+ produces
21
+ security
22
+ summary
23
+ tags
24
+ ].freeze
25
+
26
+ def initialize(description, endpoint_configuration, &)
27
+ @endpoint_configuration = endpoint_configuration
28
+ @attributes = { description: }
29
+ instance_eval(&)
30
+ end
31
+
32
+ DSL_METHODS.each do |attribute|
33
+ define_method attribute do |value|
34
+ @attributes[attribute] = value
35
+ end
36
+ end
37
+
38
+ alias success entity
39
+ alias failure http_codes
40
+
41
+ # @deprecated Use {#default_response}. The description is read back
42
+ # through an ActiveSupport::OrderedOptions, where +default+ is
43
+ # +Hash#default+ rather than a key lookup, and grape-swagger has always
44
+ # asked the route for +default_response+.
45
+ def default(value)
46
+ Grape.deprecator.warn('`default` in a `desc` block is deprecated. Use `default_response` instead.')
47
+ default_response(value)
48
+ end
49
+
50
+ def configuration
51
+ @configuration ||= eval_endpoint_config(@endpoint_configuration)
52
+ end
53
+
54
+ def settings
55
+ @attributes
56
+ end
57
+
58
+ private
59
+
60
+ def eval_endpoint_config(configuration)
61
+ return configuration if configuration.is_a?(Hash)
62
+
63
+ configuration.evaluate
64
+ end
65
+ end
66
+ end
67
+ end
@@ -2,15 +2,35 @@
2
2
 
3
3
  module Grape
4
4
  module Util
5
+ # Base class for the lazily-filled lookup caches (coercers, dry-types,
6
+ # path parts, ...). Subclasses assign a default-block +Hash+ to +@cache+
7
+ # in their +initialize+.
8
+ #
9
+ # Lookups are synchronized: caches are written at API *definition* time
10
+ # (a +params+ block runs when the class body is evaluated), which is not
11
+ # covered by the compile-time +Grape::API::Instance::LOCK+ and can happen
12
+ # concurrently — e.g. parallel eager loading, or two threads autoloading
13
+ # different API files. A +Monitor+ (reentrant) rather than a +Mutex+,
14
+ # because a cache miss may re-enter the same cache: building a
15
+ # multiple-type coercer through +Types::CoercerCache+ builds its member
16
+ # coercers through +Types.build_coercer+, which lands in the same cache.
5
17
  class Cache
6
18
  include Singleton
7
19
 
8
20
  attr_reader :cache
9
21
 
22
+ def initialize
23
+ @monitor = Monitor.new
24
+ end
25
+
26
+ def [](key)
27
+ @monitor.synchronize { @cache[key] }
28
+ end
29
+
10
30
  class << self
11
31
  extend Forwardable
12
- def_delegators :cache, :[]
13
- def_delegators :instance, :cache
32
+
33
+ def_delegators :instance, :[], :cache
14
34
  end
15
35
  end
16
36
  end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Util
5
+ module DeepFreeze
6
+ module_function
7
+
8
+ # Recursively freezes Hash (keys and values), Array (elements), and String
9
+ # objects. All other types are returned as-is.
10
+ #
11
+ # Intentionally left unfrozen:
12
+ # - Procs / lambdas — may be deferred DB-backed callables
13
+ # - Classes / Modules — shared constants that must remain open
14
+ # - Coercers and ParamsScope — self-freeze at construction
15
+ def deep_freeze(obj)
16
+ case obj
17
+ when Hash
18
+ obj.each do |k, v|
19
+ deep_freeze(k)
20
+ deep_freeze(v)
21
+ end
22
+ obj.freeze
23
+ when Array
24
+ obj.each { |v| deep_freeze(v) }
25
+ obj.freeze
26
+ when String
27
+ obj.freeze
28
+ else
29
+ obj
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Util
5
+ # Extend into a class whose instances are shared across requests:
6
+ # +new+ returns a frozen instance, so any later ivar write — e.g.
7
+ # request-time memoization — raises FrozenError instead of being a
8
+ # latent data race.
9
+ #
10
+ # Must stay a +new+ wrapper (never an +initialize+ wrapper): the freeze
11
+ # runs after the entire initialize chain, so subclasses may assign ivars
12
+ # after +super+. Extending a hierarchy base covers its subclasses —
13
+ # singleton classes inherit along the class hierarchy.
14
+ module FreezeOnNew
15
+ def new(...)
16
+ super.freeze
17
+ end
18
+ end
19
+ end
20
+ end