jsapi 1.4.1 → 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 (111) hide show
  1. checksums.yaml +4 -4
  2. data/lib/jsapi/controller/actions/class_methods.rb +61 -0
  3. data/lib/jsapi/controller/actions.rb +13 -0
  4. data/lib/jsapi/controller/authentication/class_methods.rb +65 -0
  5. data/lib/jsapi/controller/authentication/credentials/api_key.rb +24 -0
  6. data/lib/jsapi/controller/authentication/credentials/http/base.rb +25 -0
  7. data/lib/jsapi/controller/authentication/credentials/http/basic.rb +34 -0
  8. data/lib/jsapi/controller/authentication/credentials/http/bearer.rb +30 -0
  9. data/lib/jsapi/controller/authentication/credentials/http.rb +5 -0
  10. data/lib/jsapi/controller/authentication/credentials.rb +38 -0
  11. data/lib/jsapi/controller/authentication.rb +70 -0
  12. data/lib/jsapi/controller/base.rb +5 -4
  13. data/lib/jsapi/controller/methods/callbacks/callback.rb +80 -0
  14. data/lib/jsapi/controller/methods/callbacks/class_methods.rb +62 -0
  15. data/lib/jsapi/controller/methods/callbacks.rb +54 -0
  16. data/lib/jsapi/controller/methods.rb +188 -71
  17. data/lib/jsapi/controller/parameters.rb +24 -20
  18. data/lib/jsapi/controller/response.rb +71 -39
  19. data/lib/jsapi/controller.rb +2 -1
  20. data/lib/jsapi/dsl/base.rb +38 -5
  21. data/lib/jsapi/dsl/class_methods.rb +2 -2
  22. data/lib/jsapi/dsl/definitions.rb +41 -27
  23. data/lib/jsapi/dsl/operation.rb +10 -109
  24. data/lib/jsapi/dsl/parameter.rb +1 -1
  25. data/lib/jsapi/dsl/path.rb +41 -18
  26. data/lib/jsapi/dsl/request_body.rb +1 -1
  27. data/lib/jsapi/dsl/response.rb +9 -6
  28. data/lib/jsapi/dsl/schema.rb +11 -5
  29. data/lib/jsapi/dsl/shared_operation_methods.rb +140 -0
  30. data/lib/jsapi/dsl.rb +1 -2
  31. data/lib/jsapi/json/array.rb +2 -2
  32. data/lib/jsapi/json/object.rb +6 -6
  33. data/lib/jsapi/json.rb +4 -6
  34. data/lib/jsapi/media/range.rb +39 -6
  35. data/lib/jsapi/media/type.rb +8 -4
  36. data/lib/jsapi/media.rb +5 -0
  37. data/lib/jsapi/messages.rb +19 -0
  38. data/lib/jsapi/meta/callback/base.rb +63 -8
  39. data/lib/jsapi/meta/content.rb +59 -0
  40. data/lib/jsapi/meta/definitions.rb +291 -141
  41. data/lib/jsapi/meta/example/base.rb +41 -8
  42. data/lib/jsapi/meta/existence.rb +1 -1
  43. data/lib/jsapi/meta/header/base.rb +4 -2
  44. data/lib/jsapi/meta/info.rb +3 -1
  45. data/lib/jsapi/meta/license.rb +11 -5
  46. data/lib/jsapi/meta/model/attributes/class_methods.rb +150 -0
  47. data/lib/jsapi/meta/model/attributes/frozen_error.rb +16 -0
  48. data/lib/jsapi/meta/model/attributes/type_caster.rb +56 -0
  49. data/lib/jsapi/meta/model/attributes.rb +24 -118
  50. data/lib/jsapi/meta/model/base.rb +2 -5
  51. data/lib/jsapi/meta/model/reference.rb +46 -10
  52. data/lib/jsapi/meta/model/wrappable.rb +23 -0
  53. data/lib/jsapi/meta/model/wrapper.rb +26 -0
  54. data/lib/jsapi/meta/model.rb +2 -1
  55. data/lib/jsapi/meta/oauth_flow.rb +1 -1
  56. data/lib/jsapi/meta/openapi/extensions.rb +5 -6
  57. data/lib/jsapi/meta/openapi/version.rb +2 -2
  58. data/lib/jsapi/meta/operation.rb +177 -71
  59. data/lib/jsapi/meta/parameter/base.rb +9 -5
  60. data/lib/jsapi/meta/parameter/wrapper.rb +13 -0
  61. data/lib/jsapi/meta/parameter.rb +3 -0
  62. data/lib/jsapi/meta/path.rb +59 -13
  63. data/lib/jsapi/meta/pathname.rb +3 -0
  64. data/lib/jsapi/meta/property.rb +10 -0
  65. data/lib/jsapi/meta/request_body/base.rb +69 -32
  66. data/lib/jsapi/meta/request_body/wrapper.rb +13 -0
  67. data/lib/jsapi/meta/request_body.rb +3 -0
  68. data/lib/jsapi/meta/rescue_handler.rb +18 -17
  69. data/lib/jsapi/meta/response/base.rb +86 -49
  70. data/lib/jsapi/meta/response/reference.rb +11 -1
  71. data/lib/jsapi/meta/response/wrapper.rb +26 -0
  72. data/lib/jsapi/meta/response.rb +3 -0
  73. data/lib/jsapi/meta/schema/additional_properties.rb +8 -0
  74. data/lib/jsapi/meta/schema/array.rb +20 -8
  75. data/lib/jsapi/meta/schema/base.rb +10 -9
  76. data/lib/jsapi/meta/schema/numeric.rb +26 -20
  77. data/lib/jsapi/meta/schema/object.rb +60 -44
  78. data/lib/jsapi/meta/schema/reference.rb +1 -8
  79. data/lib/jsapi/meta/schema/string.rb +12 -6
  80. data/lib/jsapi/meta/schema/wrapper.rb +31 -0
  81. data/lib/jsapi/meta/schema.rb +22 -9
  82. data/lib/jsapi/meta/security_requirement.rb +2 -2
  83. data/lib/jsapi/meta/security_scheme/api_key.rb +5 -2
  84. data/lib/jsapi/meta/security_scheme/base.rb +7 -5
  85. data/lib/jsapi/meta/security_scheme/http/basic.rb +5 -7
  86. data/lib/jsapi/meta/security_scheme/http/bearer.rb +5 -5
  87. data/lib/jsapi/meta/security_scheme/http/other.rb +1 -3
  88. data/lib/jsapi/meta/security_scheme/mutual_tls.rb +1 -3
  89. data/lib/jsapi/meta/security_scheme/oauth2.rb +18 -13
  90. data/lib/jsapi/meta/security_scheme/open_id_connect.rb +4 -4
  91. data/lib/jsapi/meta/security_scheme.rb +4 -4
  92. data/lib/jsapi/meta/server.rb +4 -2
  93. data/lib/jsapi/meta/tag.rb +9 -3
  94. data/lib/jsapi/meta.rb +2 -1
  95. data/lib/jsapi/model/base.rb +1 -1
  96. data/lib/jsapi/status/base.rb +35 -0
  97. data/lib/jsapi/status/code.rb +113 -0
  98. data/lib/jsapi/status/default.rb +16 -0
  99. data/lib/jsapi/status/range.rb +35 -0
  100. data/lib/jsapi/status.rb +37 -0
  101. data/lib/jsapi/version.rb +1 -1
  102. data/lib/jsapi.rb +2 -3
  103. metadata +32 -10
  104. data/lib/jsapi/controller/parameters_invalid.rb +0 -27
  105. data/lib/jsapi/dsl/callback.rb +0 -21
  106. data/lib/jsapi/dsl/error.rb +0 -36
  107. data/lib/jsapi/invalid_argument_error.rb +0 -12
  108. data/lib/jsapi/invalid_value_error.rb +0 -12
  109. data/lib/jsapi/invalid_value_helper.rb +0 -17
  110. data/lib/jsapi/meta/model/type_caster.rb +0 -50
  111. data/lib/jsapi/meta/schema/delegator.rb +0 -26
@@ -7,70 +7,107 @@ module Jsapi
7
7
  class Base < Model::Base
8
8
  include OpenAPI::Extensions
9
9
 
10
- delegate_missing_to :schema
10
+ # To still allow to specify content-related directives within blocks
11
+ delegate_missing_to :last_content
11
12
 
12
13
  ##
13
- # :attr: content_type
14
- # The content type. <code>"application/json"</code> by default.
15
- attribute :content_type, String, default: 'application/json'
14
+ # :attr_reader: contents
15
+ # The alternative contents of the request body. Maps instances of
16
+ # Media::Range to Content objects.
17
+ attribute :contents, { Media::Range => Content }, accessors: %i[reader writer]
16
18
 
17
19
  ##
18
20
  # :attr: description
19
21
  # The description of the request body.
20
22
  attribute :description, String
21
23
 
22
- ##
23
- # :attr_reader: examples
24
- # The Example objects.
25
- attribute :examples, { String => Example }, default_key: 'default'
26
-
27
- ##
28
- # :attr_reader: schema
29
- # The Schema of the request body.
30
- attribute :schema, accessors: %i[reader]
31
-
32
24
  def initialize(keywords = {})
33
25
  keywords = keywords.dup
34
- super(keywords.extract!(:content_type, :description, :examples, :openapi_extensions))
26
+ content_keywords = keywords.slice!(*self.class.attribute_names)
27
+
28
+ # Move content-related keywords to :contents so that the first
29
+ # key-value pair in @contents is created from them.
30
+ if content_keywords.present?
31
+ content_type = content_keywords.delete(:content_type)
35
32
 
36
- add_example(value: keywords.delete(:example)) if keywords.key?(:example)
37
- keywords[:ref] = keywords.delete(:schema) if keywords.key?(:schema)
33
+ (keywords[:contents] ||= {}).reverse_merge!(
34
+ { content_type => content_keywords }
35
+ )
36
+ end
37
+ super(keywords)
38
+ end
38
39
 
39
- @schema = Schema.new(keywords)
40
+ def attribute_changed(name) # :nodoc:
41
+ @default_media_range = @default_content = @sorted_contents = nil if name == :contents
42
+ super
40
43
  end
41
44
 
42
- # Returns true if the level of existence is greater than or equal to
43
- # +ALLOW_NIL+, false otherwise.
44
- def required?
45
- schema.existence >= Existence::ALLOW_NIL
45
+ def add_content(media_range = nil, keywords = {}) # :nodoc:
46
+ try_modify_attribute!(:contents) do
47
+ media_range, keywords = nil, media_range if media_range.is_a?(Hash)
48
+ media_range = Media::Range.from(media_range || Media::Range::APPLICATION_JSON)
49
+
50
+ (@contents ||= {})[media_range] = Content.new(keywords)
51
+ end
46
52
  end
47
53
 
48
- # Returns a hash representing the \OpenAPI 2.0 parameter object.
54
+ # Returns the most appropriate content for the given media type.
55
+ def content_for(media_type)
56
+ (@sorted_contents ||= contents.sort)
57
+ .find { |media_range, _content| media_range =~ media_type }
58
+ &.second || default_content
59
+ end
60
+
61
+ def default_content
62
+ @default_content ||= contents.values.first
63
+ end
64
+
65
+ def default_media_range
66
+ @default_media_range = contents.keys.first
67
+ end
68
+
69
+ def freeze_attributes # :nodoc:
70
+ add_content if contents.blank?
71
+ super
72
+ end
73
+
74
+ # Returns a hash representing the \OpenAPI parameter object.
75
+ #
76
+ # Applies to \OpenAPI 2.0.
49
77
  def to_openapi_parameter
78
+ schema = default_content.schema
79
+
50
80
  with_openapi_extensions(
51
81
  {
52
82
  name: 'body',
53
83
  in: 'body',
54
84
  description: description,
55
- required: required?,
85
+ required: schema.existence >= Existence::ALLOW_NIL,
56
86
  **schema.to_openapi(OpenAPI::V2_0)
57
87
  }
58
88
  )
59
89
  end
60
90
 
61
- # Returns a hash representing the \OpenAPI 3.x request body object.
91
+ # Returns a hash representing the \OpenAPI request body object.
92
+ #
93
+ # Applies to \OpenAPI 3.0 and higher.
62
94
  def to_openapi(version, *)
63
95
  with_openapi_extensions(
64
96
  description: description,
65
- content: {
66
- content_type => {
67
- schema: schema.to_openapi(version),
68
- examples: examples.transform_values(&:to_openapi).presence
69
- }.compact
70
- },
71
- required: required?
97
+ content: contents.transform_values do |content|
98
+ content.to_openapi(version)
99
+ end,
100
+ required: contents.values.all? do |content|
101
+ content.schema.existence >= Existence::ALLOW_NIL
102
+ end
72
103
  )
73
104
  end
105
+
106
+ private
107
+
108
+ def last_content
109
+ contents.values.last || add_content
110
+ end
74
111
  end
75
112
  end
76
113
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jsapi
4
+ module Meta
5
+ module RequestBody
6
+ class Wrapper < Model::Wrapper
7
+ def content_for(media_type)
8
+ Content.wrap(super, definitions)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -2,10 +2,13 @@
2
2
 
3
3
  require_relative 'request_body/base'
4
4
  require_relative 'request_body/reference'
5
+ require_relative 'request_body/wrapper'
5
6
 
6
7
  module Jsapi
7
8
  module Meta
8
9
  module RequestBody
10
+ include Model::Wrappable
11
+
9
12
  class << self
10
13
  # Creates a Base or Reference.
11
14
  def new(keywords = {})
@@ -2,31 +2,32 @@
2
2
 
3
3
  module Jsapi
4
4
  module Meta
5
- # Maps an error class to a response status.
5
+ # Specifies a rescue handler.
6
6
  class RescueHandler < Model::Base
7
7
  ##
8
8
  # :attr: error_class
9
- # The error class to be mapped.
10
- attribute :error_class, default: StandardError
9
+ # The error class to be rescued.
10
+ attribute :error_class, default: StandardError, accessors: %i[reader]
11
11
 
12
12
  ##
13
- # :attr: status
14
- # The response status. The default is <code>"default"</code>.
15
- attribute :status, default: 'default'
13
+ # :attr: status_code
14
+ # The Status::Code replacing the original status code when rescuing an
15
+ # instance of error_class.
16
+ attribute :status_code, Status::Code
16
17
 
17
- def initialize(keywords = {})
18
- super
19
- unless error_class.is_a?(Class)
20
- raise ArgumentError, "#{error_class.inspect} isn't a class"
21
- end
22
- unless error_class <= StandardError
23
- raise ArgumentError, "#{error_class.inspect} isn't a rescuable class"
24
- end
18
+ def error_class=(klass) # :nodoc:
19
+ raise ArgumentError, "#{klass.inspect} isn't a class" \
20
+ unless klass.is_a?(Class)
21
+
22
+ raise ArgumentError, "#{klass.inspect} isn't a rescuable class" \
23
+ unless klass <= StandardError
24
+
25
+ @error_class = klass
25
26
  end
26
27
 
27
- # Returns true if +exception+ is an instance of the class to be mapped, false otherwise.
28
- def match?(exception)
29
- exception.is_a?(error_class)
28
+ # Returns true if +error+ is an instance of error_class.
29
+ def match?(error)
30
+ error.is_a?(error_class)
30
31
  end
31
32
  end
32
33
  end
@@ -7,60 +7,94 @@ module Jsapi
7
7
  class Base < Model::Base
8
8
  include OpenAPI::Extensions
9
9
 
10
- delegate_missing_to :schema
10
+ # To still allow to specify content-related directives within blocks
11
+ delegate_missing_to :last_content
11
12
 
12
13
  ##
13
- # :attr: content_type
14
- # The media type of the response, <code>"application/json"</code> by default.
15
- attribute :content_type, Media::Type, default: Media::Type::APPLICATION_JSON
14
+ # :attr_reader: contents
15
+ # The alternative contents of the response. Maps instances of
16
+ # Media::Range to Content objects.
17
+ attribute :contents, { Media::Type => Content }, accessors: %i[reader writer]
16
18
 
17
19
  ##
18
20
  # :attr: description
19
21
  # The description of the response.
20
22
  attribute :description, String
21
23
 
22
- ##
23
- # :attr: examples
24
- # The Example objects.
25
- attribute :examples, { String => Example }, default_key: 'default'
26
-
27
24
  ##
28
25
  # :attr: headers
29
- # The Header objects.
26
+ # The headers of the response. Maps header names to Header objects or
27
+ # references.
30
28
  attribute :headers, { String => Header }
31
29
 
32
30
  ##
33
31
  # :attr: links
34
- # The Link objects.
32
+ # The linked operations. Maps link names to Link objects.
35
33
  attribute :links, { String => Link }
36
34
 
37
35
  ##
38
36
  # :attr: locale
39
- # The locale used when rendering a response.
37
+ # The locale to be used instead of the default locale when rendering
38
+ # a response.
40
39
  attribute :locale, Symbol
41
40
 
42
41
  ##
43
- # :attr_reader: schema
44
- # The Schema of the response.
45
- attribute :schema, accessors: %i[reader]
42
+ # :attr: nodoc
43
+ # Prevents the response to be described in generated \OpenAPI documents.
44
+ attribute :nodoc, values: [true, false], default: false
46
45
 
47
46
  ##
48
47
  # :attr: summary
49
- # The short description of the response. Applies to \OpenAPI 3.2 and higher.
48
+ # The short description of the response.
49
+ #
50
+ # Applies to \OpenAPI 3.2 and higher.
50
51
  attribute :summary, String
51
52
 
52
53
  def initialize(keywords = {})
53
54
  keywords = keywords.dup
54
- super(
55
- keywords.extract!(
56
- :content_type, :description, :examples, :headers,
57
- :links, :locale, :openapi_extensions, :summary
55
+ content_keywords = keywords.slice!(*self.class.attribute_names)
56
+
57
+ # Move content-related keywords to :contents so that the first
58
+ # key-value pair in @contents is created from them.
59
+ if content_keywords.present?
60
+ content_type = content_keywords.delete(:content_type)
61
+
62
+ (keywords[:contents] ||= {}).reverse_merge!(
63
+ { content_type => content_keywords }
58
64
  )
59
- )
60
- add_example(value: keywords.delete(:example)) if keywords.key?(:example)
61
- keywords[:ref] = keywords.delete(:schema) if keywords.key?(:schema)
65
+ end
66
+ super(keywords)
67
+ end
68
+
69
+ def attribute_changed(name) # :nodoc:
70
+ @default_media_type = nil if name == :contents
71
+ super
72
+ end
62
73
 
63
- @schema = Schema.new(keywords)
74
+ def add_content(media_type = nil, keywords = {}) # :nodoc:
75
+ try_modify_attribute!(:contents) do
76
+ media_type, keywords = nil, media_type if media_type.is_a?(Hash)
77
+ media_type = Media::Type.from(media_type || Media::Type::APPLICATION_JSON)
78
+
79
+ (@contents ||= {})[media_type] = Content.new(keywords)
80
+ end
81
+ end
82
+
83
+ def default_media_type
84
+ @default_media_type ||= contents.keys.first
85
+ end
86
+
87
+ # Returns the most appropriate media type and content for the given
88
+ # media ranges.
89
+ def media_type_and_content_for(*media_ranges)
90
+ media_ranges
91
+ .filter_map { |media_range| Media::Range.try_from(media_range) }
92
+ .sort # e.g. "text/plain" before "text/*" before "*/*"
93
+ .lazy.map do |media_range|
94
+ contents.find do |media_type_and_content|
95
+ media_range =~ media_type_and_content.first
96
+ end
97
+ end.first || contents.first
64
98
  end
65
99
 
66
100
  # Returns a hash representing the \OpenAPI response object.
@@ -69,43 +103,46 @@ module Jsapi
69
103
 
70
104
  with_openapi_extensions(
71
105
  if version == OpenAPI::V2_0
106
+ media_type, content = contents.first
107
+ example = content&.examples&.values&.first
72
108
  {
73
109
  description: description,
74
- schema: schema.to_openapi(version),
75
- headers: headers.transform_values do |header|
76
- header.to_openapi(version) unless header.reference?
77
- end.compact.presence,
78
- examples: (
79
- if (example = examples.values.first).present?
80
- { content_type.to_s => example.resolve(definitions).value }
110
+ schema: content&.schema&.to_openapi(version),
111
+ headers:
112
+ headers.transform_values do |header|
113
+ header.to_openapi(version) unless header.reference?
114
+ end.compact.presence,
115
+ examples:
116
+ if media_type.present? && example.present?
117
+ { media_type => example.resolve(definitions).value }
81
118
  end
82
- )
83
119
  }
84
120
  else
85
121
  {
86
122
  summary: (summary if version >= OpenAPI::V3_2),
87
123
  description: description,
88
- headers: headers.transform_values do |header|
89
- header.to_openapi(version)
90
- end.presence,
91
- content: {
92
- content_type.to_s => {
93
- **if content_type == Media::Type::APPLICATION_JSON_SEQ &&
94
- schema.array? && version >= OpenAPI::V3_2
95
- { itemSchema: schema.items.to_openapi(version) }
96
- else
97
- { schema: schema.to_openapi(version) }
98
- end,
99
- examples: examples.transform_values(&:to_openapi).presence
100
- }.compact
101
- },
102
- links: links.transform_values do |link|
103
- link.to_openapi(version)
104
- end.presence
124
+ headers:
125
+ headers.transform_values do |header|
126
+ header.to_openapi(version)
127
+ end.presence,
128
+ content:
129
+ contents.to_h do |nth_media_type, nth_content|
130
+ [nth_media_type, nth_content.to_openapi(version, nth_media_type)]
131
+ end.presence,
132
+ links:
133
+ links.transform_values do |link|
134
+ link.to_openapi(version)
135
+ end.presence
105
136
  }
106
137
  end
107
138
  )
108
139
  end
140
+
141
+ private
142
+
143
+ def last_content
144
+ contents.values.last || (add_content unless attributes_frozen?)
145
+ end
109
146
  end
110
147
  end
111
148
  end
@@ -4,7 +4,17 @@ module Jsapi
4
4
  module Meta
5
5
  module Response
6
6
  # Refers a reusable response.
7
- class Reference < Model::Reference; end
7
+ class Reference < Model::Reference
8
+ ##
9
+ # :attr: locale
10
+ # Overrides the locale of the referred response.
11
+ attribute :locale, Symbol
12
+
13
+ ##
14
+ # :attr: nodoc
15
+ # Prevents the reference to be described in generated \OpenAPI documents.
16
+ attribute :nodoc, values: [true, false]
17
+ end
8
18
  end
9
19
  end
10
20
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jsapi
4
+ module Meta
5
+ module Response
6
+ class Wrapper < Model::Wrapper
7
+ # The locale of the wrapped response or reference.
8
+ attr_reader :locale
9
+
10
+ def initialize(response, definitions)
11
+ @locale = response.resolve_lazily(definitions).locale
12
+ super
13
+ end
14
+
15
+ def media_type_and_content_for(*media_ranges)
16
+ super&.then do |media_type_and_content|
17
+ [
18
+ media_type_and_content.first,
19
+ Content.wrap(media_type_and_content.second, definitions)
20
+ ]
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -2,10 +2,13 @@
2
2
 
3
3
  require_relative 'response/base'
4
4
  require_relative 'response/reference'
5
+ require_relative 'response/wrapper'
5
6
 
6
7
  module Jsapi
7
8
  module Meta
8
9
  module Response
10
+ include Model::Wrappable
11
+
9
12
  class << self
10
13
  # Creates a Base or Reference.
11
14
  def new(keywords = {})
@@ -4,6 +4,8 @@ module Jsapi
4
4
  module Meta
5
5
  module Schema
6
6
  class AdditionalProperties < Model::Base
7
+ include Model::Wrappable
8
+
7
9
  delegate_missing_to :schema
8
10
 
9
11
  ##
@@ -24,6 +26,12 @@ module Jsapi
24
26
 
25
27
  @schema = Schema.new(keywords)
26
28
  end
29
+
30
+ class Wrapper < Model::Wrapper
31
+ def schema
32
+ @schema ||= Schema.wrap(super, definitions)
33
+ end
34
+ end
27
35
  end
28
36
  end
29
37
  end
@@ -4,6 +4,12 @@ module Jsapi
4
4
  module Meta
5
5
  module Schema
6
6
  class Array < Base
7
+ class Wrapper < Schema::Wrapper
8
+ def items
9
+ @items ||= Schema.wrap(super, definitions)
10
+ end
11
+ end
12
+
7
13
  ##
8
14
  # :attr: items
9
15
  # The Schema defining the kind of items.
@@ -20,21 +26,27 @@ module Jsapi
20
26
  attribute :min_items, accessors: %i[reader]
21
27
 
22
28
  def items=(keywords = {}) # :nodoc:
23
- if keywords.key?(:schema)
24
- keywords = keywords.dup
25
- keywords[:ref] = keywords.delete(:schema)
29
+ try_modify_attribute!(:items) do
30
+ if keywords.key?(:schema)
31
+ keywords = keywords.dup
32
+ keywords[:ref] = keywords.delete(:schema)
33
+ end
34
+ @items = Schema.new(keywords)
26
35
  end
27
- @items = Schema.new(keywords)
28
36
  end
29
37
 
30
38
  def max_items=(value) # :nodoc:
31
- add_validation('max_items', Validation::MaxItems.new(value))
32
- @max_items = value
39
+ try_modify_attribute!(:max_items) do
40
+ add_validation('max_items', Validation::MaxItems.new(value))
41
+ @max_items = value
42
+ end
33
43
  end
34
44
 
35
45
  def min_items=(value) # :nodoc:
36
- add_validation('min_items', Validation::MinItems.new(value))
37
- @min_items = value
46
+ try_modify_attribute!(:min_items) do
47
+ add_validation('min_items', Validation::MinItems.new(value))
48
+ @min_items = value
49
+ end
38
50
  end
39
51
 
40
52
  def to_json_schema # :nodoc:
@@ -3,6 +3,8 @@
3
3
  module Jsapi
4
4
  module Meta
5
5
  module Schema
6
+ TYPES = %w[array boolean integer number object string].freeze # :nodoc:
7
+
6
8
  class Base < Model::Base
7
9
  include OpenAPI::Extensions
8
10
 
@@ -13,7 +15,7 @@ module Jsapi
13
15
 
14
16
  ##
15
17
  # :attr: deprecated
16
- # Specifies whether or not the schema is deprecated.
18
+ # Specifies whether the schema is marked as deprecated.
17
19
  attribute :deprecated, values: [true, false]
18
20
 
19
21
  ##
@@ -33,7 +35,7 @@ module Jsapi
33
35
 
34
36
  ##
35
37
  # :attr: external_docs
36
- # The OpenAPI::ExternalDocumentation object.
38
+ # The ExternalDocumentation object.
37
39
  attribute :external_docs, ExternalDocumentation
38
40
 
39
41
  ##
@@ -63,16 +65,15 @@ module Jsapi
63
65
  super(keywords)
64
66
  end
65
67
 
66
- # Returns the default value within +context+.
67
- def default_value(definitions = nil, context: nil)
68
- return default unless default.nil?
69
-
70
- definitions&.default_value(type, context: context)
68
+ TYPES.each do |type|
69
+ define_method(:"#{type}?") { self.type == type }
71
70
  end
72
71
 
73
72
  def enum=(value) # :nodoc:
74
- add_validation('enum', Validation::Enum.new(value))
75
- @enum = value
73
+ try_modify_attribute!(:enum) do
74
+ add_validation('enum', Validation::Enum.new(value))
75
+ @enum = value
76
+ end
76
77
  end
77
78
 
78
79
  # Returns true if and only if values can be +null+ as specified by \JSON \Schema.
@@ -21,34 +21,40 @@ module Jsapi
21
21
  attribute :multiple_of, accessors: %i[reader]
22
22
 
23
23
  def maximum=(value) # :nodoc:
24
- boundary = Boundary.from(value)
25
-
26
- add_validation(
27
- 'maximum',
28
- Validation::Maximum.new(
29
- boundary.value,
30
- exclusive: boundary.exclusive?
24
+ try_modify_attribute!(:maximum) do
25
+ boundary = Boundary.from(value)
26
+
27
+ add_validation(
28
+ 'maximum',
29
+ Validation::Maximum.new(
30
+ boundary.value,
31
+ exclusive: boundary.exclusive?
32
+ )
31
33
  )
32
- )
33
- @maximum = boundary
34
+ @maximum = boundary
35
+ end
34
36
  end
35
37
 
36
38
  def minimum=(value) # :nodoc:
37
- boundary = Boundary.from(value)
38
-
39
- add_validation(
40
- 'minimum',
41
- Validation::Minimum.new(
42
- boundary.value,
43
- exclusive: boundary.exclusive?
39
+ try_modify_attribute!(:minimum) do
40
+ boundary = Boundary.from(value)
41
+
42
+ add_validation(
43
+ 'minimum',
44
+ Validation::Minimum.new(
45
+ boundary.value,
46
+ exclusive: boundary.exclusive?
47
+ )
44
48
  )
45
- )
46
- @minimum = boundary
49
+ @minimum = boundary
50
+ end
47
51
  end
48
52
 
49
53
  def multiple_of=(value) # :nodoc:
50
- add_validation('multiple_of', Validation::MultipleOf.new(value))
51
- @multiple_of = value
54
+ try_modify_attribute!(:multiple_of) do
55
+ add_validation('multiple_of', Validation::MultipleOf.new(value))
56
+ @multiple_of = value
57
+ end
52
58
  end
53
59
  end
54
60
  end