grape 2.1.0 → 2.2.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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +46 -0
  3. data/README.md +4 -3
  4. data/UPGRADING.md +8 -0
  5. data/grape.gemspec +2 -1
  6. data/lib/grape/api/instance.rb +1 -1
  7. data/lib/grape/api.rb +2 -2
  8. data/lib/grape/content_types.rb +13 -8
  9. data/lib/grape/dsl/helpers.rb +7 -3
  10. data/lib/grape/dsl/inside_route.rb +14 -3
  11. data/lib/grape/dsl/parameters.rb +1 -1
  12. data/lib/grape/dsl/request_response.rb +14 -18
  13. data/lib/grape/endpoint.rb +34 -23
  14. data/lib/grape/error_formatter/json.rb +13 -4
  15. data/lib/grape/error_formatter.rb +13 -25
  16. data/lib/grape/exceptions/validation_errors.rb +1 -1
  17. data/lib/grape/formatter.rb +15 -25
  18. data/lib/grape/locale/en.yml +1 -0
  19. data/lib/grape/middleware/base.rb +14 -13
  20. data/lib/grape/middleware/error.rb +13 -11
  21. data/lib/grape/middleware/formatter.rb +2 -2
  22. data/lib/grape/middleware/stack.rb +2 -2
  23. data/lib/grape/middleware/versioner/accept_version_header.rb +8 -31
  24. data/lib/grape/middleware/versioner/header.rb +95 -10
  25. data/lib/grape/middleware/versioner/param.rb +5 -21
  26. data/lib/grape/middleware/versioner/path.rb +11 -31
  27. data/lib/grape/middleware/versioner.rb +5 -14
  28. data/lib/grape/middleware/versioner_helpers.rb +75 -0
  29. data/lib/grape/parser.rb +8 -24
  30. data/lib/grape/router.rb +2 -1
  31. data/lib/grape/validations/attributes_iterator.rb +1 -0
  32. data/lib/grape/validations/params_scope.rb +12 -10
  33. data/lib/grape/validations/validators/exactly_one_of_validator.rb +1 -1
  34. data/lib/grape/validations/validators/length_validator.rb +10 -3
  35. data/lib/grape/version.rb +1 -1
  36. metadata +8 -9
  37. data/lib/grape/util/accept/header.rb +0 -19
  38. data/lib/grape/util/accept_header_handler.rb +0 -105
  39. data/lib/grape/util/registrable.rb +0 -15
@@ -7,20 +7,25 @@ module Grape
7
7
  def initialize(attrs, options, required, scope, **opts)
8
8
  @min = options[:min]
9
9
  @max = options[:max]
10
+ @is = options[:is]
10
11
 
11
12
  super
12
13
 
13
14
  raise ArgumentError, 'min must be an integer greater than or equal to zero' if !@min.nil? && (!@min.is_a?(Integer) || @min.negative?)
14
15
  raise ArgumentError, 'max must be an integer greater than or equal to zero' if !@max.nil? && (!@max.is_a?(Integer) || @max.negative?)
15
16
  raise ArgumentError, "min #{@min} cannot be greater than max #{@max}" if !@min.nil? && !@max.nil? && @min > @max
17
+
18
+ return if @is.nil?
19
+ raise ArgumentError, 'is must be an integer greater than zero' if !@is.is_a?(Integer) || !@is.positive?
20
+ raise ArgumentError, 'is cannot be combined with min or max' if !@min.nil? || !@max.nil?
16
21
  end
17
22
 
18
23
  def validate_param!(attr_name, params)
19
24
  param = params[attr_name]
20
25
 
21
- raise ArgumentError, "parameter #{param} does not support #length" unless param.respond_to?(:length)
26
+ return unless param.respond_to?(:length)
22
27
 
23
- return unless (!@min.nil? && param.length < @min) || (!@max.nil? && param.length > @max)
28
+ return unless (!@min.nil? && param.length < @min) || (!@max.nil? && param.length > @max) || (!@is.nil? && param.length != @is)
24
29
 
25
30
  raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: build_message)
26
31
  end
@@ -32,8 +37,10 @@ module Grape
32
37
  format I18n.t(:length, scope: 'grape.errors.messages'), min: @min, max: @max
33
38
  elsif @min
34
39
  format I18n.t(:length_min, scope: 'grape.errors.messages'), min: @min
35
- else
40
+ elsif @max
36
41
  format I18n.t(:length_max, scope: 'grape.errors.messages'), max: @max
42
+ else
43
+ format I18n.t(:length_is, scope: 'grape.errors.messages'), is: @is
37
44
  end
38
45
  end
39
46
  end
data/lib/grape/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Grape
4
4
  # The current version of Grape.
5
- VERSION = '2.1.0'
5
+ VERSION = '2.2.0'
6
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grape
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Bleigh
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-06-15 00:00:00.000000000 Z
11
+ date: 2024-09-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -172,6 +172,7 @@ files:
172
172
  - lib/grape/middleware/versioner/header.rb
173
173
  - lib/grape/middleware/versioner/param.rb
174
174
  - lib/grape/middleware/versioner/path.rb
175
+ - lib/grape/middleware/versioner_helpers.rb
175
176
  - lib/grape/namespace.rb
176
177
  - lib/grape/parser.rb
177
178
  - lib/grape/parser/json.rb
@@ -189,8 +190,6 @@ files:
189
190
  - lib/grape/serve_stream/sendfile_response.rb
190
191
  - lib/grape/serve_stream/stream_response.rb
191
192
  - lib/grape/types/invalid_value.rb
192
- - lib/grape/util/accept/header.rb
193
- - lib/grape/util/accept_header_handler.rb
194
193
  - lib/grape/util/base_inheritable.rb
195
194
  - lib/grape/util/cache.rb
196
195
  - lib/grape/util/endpoint_configuration.rb
@@ -204,7 +203,6 @@ files:
204
203
  - lib/grape/util/lazy/value_enumerable.rb
205
204
  - lib/grape/util/lazy/value_hash.rb
206
205
  - lib/grape/util/media_type.rb
207
- - lib/grape/util/registrable.rb
208
206
  - lib/grape/util/reverse_stackable_values.rb
209
207
  - lib/grape/util/stackable_values.rb
210
208
  - lib/grape/util/strict_hash_configuration.rb
@@ -252,9 +250,10 @@ licenses:
252
250
  - MIT
253
251
  metadata:
254
252
  bug_tracker_uri: https://github.com/ruby-grape/grape/issues
255
- changelog_uri: https://github.com/ruby-grape/grape/blob/v2.1.0/CHANGELOG.md
256
- documentation_uri: https://www.rubydoc.info/gems/grape/2.1.0
257
- source_code_uri: https://github.com/ruby-grape/grape/tree/v2.1.0
253
+ changelog_uri: https://github.com/ruby-grape/grape/blob/v2.2.0/CHANGELOG.md
254
+ documentation_uri: https://www.rubydoc.info/gems/grape/2.2.0
255
+ source_code_uri: https://github.com/ruby-grape/grape/tree/v2.2.0
256
+ rubygems_mfa_required: 'true'
258
257
  post_install_message:
259
258
  rdoc_options: []
260
259
  require_paths:
@@ -270,7 +269,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
270
269
  - !ruby/object:Gem::Version
271
270
  version: '0'
272
271
  requirements: []
273
- rubygems_version: 3.5.7
272
+ rubygems_version: 3.5.13
274
273
  signing_key:
275
274
  specification_version: 4
276
275
  summary: A simple Ruby framework for building REST-like APIs.
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Grape
4
- module Util
5
- module Accept
6
- module Header
7
- ALLOWED_CHARACTERS = %r{^([a-z*]+)/([a-z0-9*&\^\-_#{$ERROR_INFO}.+]+)(?:;([a-z0-9=;]+))?$}.freeze
8
- class << self
9
- # Corrected version of https://github.com/mjackson/rack-accept/blob/master/lib/rack/accept/header.rb#L40-L44
10
- def parse_media_type(media_type)
11
- # see http://tools.ietf.org/html/rfc6838#section-4.2 for allowed characters in media type names
12
- m = media_type&.match(ALLOWED_CHARACTERS)
13
- m ? [m[1], m[2], m[3] || ''] : []
14
- end
15
- end
16
- end
17
- end
18
- end
19
- end
@@ -1,105 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Grape
4
- module Util
5
- class AcceptHeaderHandler
6
- attr_reader :accept_header, :versions, :vendor, :strict, :cascade
7
-
8
- def initialize(accept_header:, versions:, **options)
9
- @accept_header = accept_header
10
- @versions = versions
11
- @vendor = options.fetch(:vendor, nil)
12
- @strict = options.fetch(:strict, false)
13
- @cascade = options.fetch(:cascade, true)
14
- end
15
-
16
- def match_best_quality_media_type!(content_types: Grape::ContentTypes::CONTENT_TYPES, allowed_methods: nil)
17
- return unless vendor
18
-
19
- strict_header_checks!
20
- media_type = Grape::Util::MediaType.best_quality(accept_header, available_media_types(content_types))
21
- if media_type
22
- yield media_type
23
- else
24
- fail!(allowed_methods)
25
- end
26
- end
27
-
28
- private
29
-
30
- def strict_header_checks!
31
- return unless strict
32
-
33
- accept_header_check!
34
- version_and_vendor_check!
35
- end
36
-
37
- def accept_header_check!
38
- return if accept_header.present?
39
-
40
- invalid_accept_header!('Accept header must be set.')
41
- end
42
-
43
- def version_and_vendor_check!
44
- return if versions.blank? || version_and_vendor?
45
-
46
- invalid_accept_header!('API vendor or version not found.')
47
- end
48
-
49
- def q_values_mime_types
50
- @q_values_mime_types ||= Rack::Utils.q_values(accept_header).map(&:first)
51
- end
52
-
53
- def version_and_vendor?
54
- q_values_mime_types.any? { |mime_type| Grape::Util::MediaType.match?(mime_type) }
55
- end
56
-
57
- def invalid_accept_header!(message)
58
- raise Grape::Exceptions::InvalidAcceptHeader.new(message, error_headers)
59
- end
60
-
61
- def invalid_version_header!(message)
62
- raise Grape::Exceptions::InvalidVersionHeader.new(message, error_headers)
63
- end
64
-
65
- def fail!(grape_allowed_methods)
66
- return grape_allowed_methods if grape_allowed_methods.present?
67
-
68
- media_types = q_values_mime_types.map { |mime_type| Grape::Util::MediaType.parse(mime_type) }
69
- vendor_not_found!(media_types) || version_not_found!(media_types)
70
- end
71
-
72
- def vendor_not_found!(media_types)
73
- return unless media_types.all? { |media_type| media_type&.vendor && media_type.vendor != vendor }
74
-
75
- invalid_accept_header!('API vendor not found.')
76
- end
77
-
78
- def version_not_found!(media_types)
79
- return unless media_types.all? { |media_type| media_type&.version && versions.exclude?(media_type.version) }
80
-
81
- invalid_version_header!('API version not found.')
82
- end
83
-
84
- def error_headers
85
- cascade ? { Grape::Http::Headers::X_CASCADE => 'pass' } : {}
86
- end
87
-
88
- def available_media_types(content_types)
89
- [].tap do |available_media_types|
90
- base_media_type = "application/vnd.#{vendor}"
91
- content_types.each_key do |extension|
92
- versions&.reverse_each do |version|
93
- available_media_types << "#{base_media_type}-#{version}+#{extension}"
94
- available_media_types << "#{base_media_type}-#{version}"
95
- end
96
- available_media_types << "#{base_media_type}+#{extension}"
97
- end
98
-
99
- available_media_types << base_media_type
100
- available_media_types.concat(content_types.values.flatten)
101
- end
102
- end
103
- end
104
- end
105
- end
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Grape
4
- module Util
5
- module Registrable
6
- def default_elements
7
- @default_elements ||= {}
8
- end
9
-
10
- def register(format, element)
11
- default_elements[format] = element unless default_elements[format]
12
- end
13
- end
14
- end
15
- end