jsapi 0.1.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 (121) hide show
  1. checksums.yaml +7 -0
  2. data/lib/jsapi/controller/base.rb +21 -0
  3. data/lib/jsapi/controller/error_result.rb +21 -0
  4. data/lib/jsapi/controller/methods.rb +144 -0
  5. data/lib/jsapi/controller/parameters.rb +86 -0
  6. data/lib/jsapi/controller/parameters_invalid.rb +25 -0
  7. data/lib/jsapi/controller/response.rb +84 -0
  8. data/lib/jsapi/controller.rb +13 -0
  9. data/lib/jsapi/dsl/callbacks.rb +32 -0
  10. data/lib/jsapi/dsl/class_methods.rb +85 -0
  11. data/lib/jsapi/dsl/definitions.rb +102 -0
  12. data/lib/jsapi/dsl/error.rb +38 -0
  13. data/lib/jsapi/dsl/examples.rb +30 -0
  14. data/lib/jsapi/dsl/node.rb +62 -0
  15. data/lib/jsapi/dsl/openapi/callback.rb +23 -0
  16. data/lib/jsapi/dsl/openapi/root.rb +12 -0
  17. data/lib/jsapi/dsl/openapi.rb +4 -0
  18. data/lib/jsapi/dsl/operation.rb +118 -0
  19. data/lib/jsapi/dsl/parameter.rb +10 -0
  20. data/lib/jsapi/dsl/request_body.rb +10 -0
  21. data/lib/jsapi/dsl/response.rb +33 -0
  22. data/lib/jsapi/dsl/schema.rb +87 -0
  23. data/lib/jsapi/dsl.rb +24 -0
  24. data/lib/jsapi/json/array.rb +35 -0
  25. data/lib/jsapi/json/boolean.rb +17 -0
  26. data/lib/jsapi/json/integer.rb +15 -0
  27. data/lib/jsapi/json/null.rb +27 -0
  28. data/lib/jsapi/json/number.rb +15 -0
  29. data/lib/jsapi/json/object.rb +53 -0
  30. data/lib/jsapi/json/string.rb +29 -0
  31. data/lib/jsapi/json/value.rb +47 -0
  32. data/lib/jsapi/json.rb +41 -0
  33. data/lib/jsapi/meta/attributes/class_methods.rb +112 -0
  34. data/lib/jsapi/meta/attributes/type_caster.rb +48 -0
  35. data/lib/jsapi/meta/attributes.rb +4 -0
  36. data/lib/jsapi/meta/base.rb +41 -0
  37. data/lib/jsapi/meta/base_reference.rb +33 -0
  38. data/lib/jsapi/meta/definitions.rb +226 -0
  39. data/lib/jsapi/meta/example/model.rb +44 -0
  40. data/lib/jsapi/meta/example/reference.rb +15 -0
  41. data/lib/jsapi/meta/example.rb +19 -0
  42. data/lib/jsapi/meta/existence.rb +69 -0
  43. data/lib/jsapi/meta/invalid_argument_error.rb +11 -0
  44. data/lib/jsapi/meta/openapi/callback/model.rb +36 -0
  45. data/lib/jsapi/meta/openapi/callback/reference.rb +16 -0
  46. data/lib/jsapi/meta/openapi/callback.rb +21 -0
  47. data/lib/jsapi/meta/openapi/contact.rb +34 -0
  48. data/lib/jsapi/meta/openapi/external_documentation.rb +28 -0
  49. data/lib/jsapi/meta/openapi/info.rb +52 -0
  50. data/lib/jsapi/meta/openapi/license.rb +28 -0
  51. data/lib/jsapi/meta/openapi/link/model.rb +48 -0
  52. data/lib/jsapi/meta/openapi/link/reference.rb +16 -0
  53. data/lib/jsapi/meta/openapi/link.rb +21 -0
  54. data/lib/jsapi/meta/openapi/oauth_flow.rb +50 -0
  55. data/lib/jsapi/meta/openapi/root.rb +134 -0
  56. data/lib/jsapi/meta/openapi/security_requirement.rb +27 -0
  57. data/lib/jsapi/meta/openapi/security_scheme/api_key.rb +38 -0
  58. data/lib/jsapi/meta/openapi/security_scheme/base.rb +16 -0
  59. data/lib/jsapi/meta/openapi/security_scheme/http/basic.rb +31 -0
  60. data/lib/jsapi/meta/openapi/security_scheme/http/bearer.rb +37 -0
  61. data/lib/jsapi/meta/openapi/security_scheme/http/other.rb +37 -0
  62. data/lib/jsapi/meta/openapi/security_scheme/http.rb +31 -0
  63. data/lib/jsapi/meta/openapi/security_scheme/oauth2.rb +47 -0
  64. data/lib/jsapi/meta/openapi/security_scheme/open_id_connect.rb +33 -0
  65. data/lib/jsapi/meta/openapi/security_scheme.rb +51 -0
  66. data/lib/jsapi/meta/openapi/server.rb +34 -0
  67. data/lib/jsapi/meta/openapi/server_variable.rb +34 -0
  68. data/lib/jsapi/meta/openapi/tag.rb +34 -0
  69. data/lib/jsapi/meta/openapi/version.rb +41 -0
  70. data/lib/jsapi/meta/openapi.rb +16 -0
  71. data/lib/jsapi/meta/operation.rb +186 -0
  72. data/lib/jsapi/meta/parameter/model.rb +170 -0
  73. data/lib/jsapi/meta/parameter/reference.rb +30 -0
  74. data/lib/jsapi/meta/parameter.rb +19 -0
  75. data/lib/jsapi/meta/property.rb +62 -0
  76. data/lib/jsapi/meta/reference_error.rb +12 -0
  77. data/lib/jsapi/meta/request_body/model.rb +65 -0
  78. data/lib/jsapi/meta/request_body/reference.rb +14 -0
  79. data/lib/jsapi/meta/request_body.rb +19 -0
  80. data/lib/jsapi/meta/rescue_handler.rb +26 -0
  81. data/lib/jsapi/meta/response/model.rb +72 -0
  82. data/lib/jsapi/meta/response/reference.rb +17 -0
  83. data/lib/jsapi/meta/response.rb +19 -0
  84. data/lib/jsapi/meta/schema/array.rb +42 -0
  85. data/lib/jsapi/meta/schema/base.rb +146 -0
  86. data/lib/jsapi/meta/schema/boolean.rb +9 -0
  87. data/lib/jsapi/meta/schema/boundary.rb +37 -0
  88. data/lib/jsapi/meta/schema/conversion.rb +28 -0
  89. data/lib/jsapi/meta/schema/delegator.rb +26 -0
  90. data/lib/jsapi/meta/schema/discriminator.rb +36 -0
  91. data/lib/jsapi/meta/schema/integer.rb +9 -0
  92. data/lib/jsapi/meta/schema/number.rb +9 -0
  93. data/lib/jsapi/meta/schema/numeric.rb +56 -0
  94. data/lib/jsapi/meta/schema/object.rb +85 -0
  95. data/lib/jsapi/meta/schema/reference.rb +38 -0
  96. data/lib/jsapi/meta/schema/string.rb +58 -0
  97. data/lib/jsapi/meta/schema/validation/base.rb +29 -0
  98. data/lib/jsapi/meta/schema/validation/enum.rb +26 -0
  99. data/lib/jsapi/meta/schema/validation/max_items.rb +26 -0
  100. data/lib/jsapi/meta/schema/validation/max_length.rb +26 -0
  101. data/lib/jsapi/meta/schema/validation/maximum.rb +51 -0
  102. data/lib/jsapi/meta/schema/validation/min_items.rb +26 -0
  103. data/lib/jsapi/meta/schema/validation/min_length.rb +26 -0
  104. data/lib/jsapi/meta/schema/validation/minimum.rb +51 -0
  105. data/lib/jsapi/meta/schema/validation/multiple_of.rb +24 -0
  106. data/lib/jsapi/meta/schema/validation/pattern.rb +30 -0
  107. data/lib/jsapi/meta/schema/validation.rb +12 -0
  108. data/lib/jsapi/meta/schema.rb +61 -0
  109. data/lib/jsapi/meta.rb +23 -0
  110. data/lib/jsapi/model/attributes.rb +22 -0
  111. data/lib/jsapi/model/base.rb +34 -0
  112. data/lib/jsapi/model/error.rb +15 -0
  113. data/lib/jsapi/model/errors.rb +51 -0
  114. data/lib/jsapi/model/naming.rb +28 -0
  115. data/lib/jsapi/model/nestable.rb +37 -0
  116. data/lib/jsapi/model/nested_error.rb +54 -0
  117. data/lib/jsapi/model/validations.rb +27 -0
  118. data/lib/jsapi/model.rb +15 -0
  119. data/lib/jsapi/version.rb +8 -0
  120. data/lib/jsapi.rb +8 -0
  121. metadata +162 -0
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jsapi
4
+ module Meta
5
+ module Attributes
6
+ module ClassMethods
7
+ # Defines an attribute.
8
+ def attribute(name, type = Object,
9
+ keys: nil,
10
+ values: nil,
11
+ default: nil,
12
+ default_key: nil,
13
+ writer: true)
14
+
15
+ (@attribute_names ||= []) << name.to_sym
16
+
17
+ instance_variable_name = "@#{name}"
18
+
19
+ # Attribute reader
20
+ define_method(name) do
21
+ value = instance_variable_get(instance_variable_name)
22
+ value.nil? ? default : value
23
+ end
24
+
25
+ case type
26
+ when Array
27
+ if writer
28
+ singular_name = name.to_s.singularize
29
+ type_caster = TypeCaster.new(type.first, values: values, name: singular_name)
30
+
31
+ # Attribute writer
32
+ define_method("#{name}=") do |argument|
33
+ instance_variable_set(
34
+ instance_variable_name,
35
+ Array.wrap(argument).map { |element| type_caster.cast(element) }
36
+ )
37
+ end
38
+
39
+ # add_{singular_name} method
40
+ define_method("add_#{singular_name}") do |argument = nil|
41
+ type_caster.cast(argument).tap do |casted_argument|
42
+ if instance_variable_defined?(instance_variable_name)
43
+ instance_variable_get(instance_variable_name)
44
+ else
45
+ instance_variable_set(instance_variable_name, [])
46
+ end << casted_argument
47
+ end
48
+ end
49
+ end
50
+ when Hash
51
+ singular_name = name.to_s.singularize
52
+ key_type, value_type = type.first
53
+ key_type_caster = TypeCaster.new(key_type, values: keys, name: 'key')
54
+
55
+ # hash value reader
56
+ define_method(singular_name) do |key = nil|
57
+ send(name)&.[](key_type_caster.cast(key) || default_key)
58
+ end
59
+
60
+ if writer
61
+ value_type_caster = TypeCaster.new(value_type, values: values)
62
+
63
+ # add_{singular_name} method
64
+ define_method("add_#{singular_name}") do |key_or_value, value = nil|
65
+ if value.nil? && default_key
66
+ key = default_key
67
+ value = key_or_value
68
+ else
69
+ key = key_or_value || default_key
70
+ end
71
+ raise ArgumentError, "key can't be blank" if key.blank?
72
+
73
+ casted_key = key_type_caster.cast(key)
74
+ casted_value = value_type_caster.cast(value)
75
+
76
+ if instance_variable_defined?(instance_variable_name)
77
+ instance_variable_get(instance_variable_name)
78
+ else
79
+ instance_variable_set(instance_variable_name, {})
80
+ end[casted_key] = casted_value
81
+ end
82
+ end
83
+ else
84
+ # Predicate method
85
+ define_method("#{name}?") do
86
+ value = instance_variable_get(instance_variable_name)
87
+ value.nil? ? default || false : value
88
+ end if values == [true, false]
89
+
90
+ if writer
91
+ type_caster = TypeCaster.new(type, values: values, name: name)
92
+
93
+ # Attribute writer
94
+ define_method("#{name}=") do |argument = nil|
95
+ instance_variable_set(
96
+ instance_variable_name, type_caster.cast(argument)
97
+ )
98
+ end
99
+ end
100
+ end
101
+ end
102
+
103
+ def attribute_names
104
+ names = @attribute_names || []
105
+ return names unless superclass.respond_to?(:attribute_names)
106
+
107
+ superclass.attribute_names + names
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jsapi
4
+ module Meta
5
+ module Attributes
6
+ class TypeCaster
7
+ STRING_CASTER = ->(arg) { arg&.to_s } # :nodoc:
8
+
9
+ SYMBOL_CASTER = ->(arg) { # :nodoc:
10
+ return if arg.nil?
11
+
12
+ arg = arg.to_s unless arg.respond_to?(:to_sym)
13
+ arg.to_sym
14
+ }
15
+
16
+ # Creates a new type caster for +klass+.
17
+ def initialize(klass, name: 'value', values: nil)
18
+ @caster =
19
+ case klass.name
20
+ when 'String'
21
+ STRING_CASTER
22
+ when 'Symbol'
23
+ SYMBOL_CASTER
24
+ else
25
+ ->(arg) {
26
+ return arg if arg.is_a?(klass)
27
+ return klass.from(arg) if klass.respond_to?(:from)
28
+ return klass.new if arg.nil?
29
+
30
+ klass.new(arg)
31
+ }
32
+ end
33
+ @values = values
34
+ @name = name
35
+ end
36
+
37
+ # Casts +value+. Raises an InvalidArgumentError if the (casted)
38
+ # value is invalid.
39
+ def cast(value)
40
+ casted_value = @caster.call(value)
41
+ return casted_value unless @values&.exclude?(casted_value)
42
+
43
+ raise InvalidArgumentError.new(@name, casted_value, @values)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'attributes/type_caster'
4
+ require_relative 'attributes/class_methods'
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jsapi
4
+ module Meta
5
+ # The base meta model class.
6
+ class Base
7
+ extend Attributes::ClassMethods
8
+
9
+ # Creates a new meta model. Raises an +ArgumentError+ if at least one
10
+ # keyword is not supported.
11
+ def initialize(keywords = {})
12
+ keywords.each do |key, value|
13
+ if respond_to?(method = "#{key}=")
14
+ public_send(method, value)
15
+ else
16
+ raise ArgumentError, "unsupported keyword: #{key}"
17
+ end
18
+ end
19
+ end
20
+
21
+ def inspect # :nodoc:
22
+ klass = self.class
23
+ "#<#{klass.name} #{
24
+ klass.attribute_names.map do |name|
25
+ "#{name}: #{send(name).inspect}"
26
+ end.join(', ')
27
+ }>"
28
+ end
29
+
30
+ # Returns true if and only if this is a reference.
31
+ def reference?
32
+ false
33
+ end
34
+
35
+ # Returns itself.
36
+ def resolve(*)
37
+ self
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jsapi
4
+ module Meta
5
+ class BaseReference < Base
6
+ ##
7
+ # :attr: ref
8
+ # The name of the referred object.
9
+ attribute :ref, String
10
+
11
+ # Returns the name of the method to be invoked to look up the referred
12
+ # object in a Definitions instance.
13
+ def self.lookup_method_name
14
+ @lookup_method_name ||=
15
+ name.delete_suffix('::Reference').demodulize.underscore
16
+ end
17
+
18
+ def reference? # :nodoc:
19
+ true
20
+ end
21
+
22
+ # Resolves +ref+ by looking up the object with that name in +definitions+.
23
+ #
24
+ # Raises a ReferenceError if +ref+ could not be resolved.
25
+ def resolve(definitions)
26
+ object = definitions.send(self.class.lookup_method_name, ref)
27
+ raise ReferenceError, ref if object.nil?
28
+
29
+ object.resolve(definitions)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,226 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jsapi
4
+ module Meta
5
+ class Definitions
6
+ attr_reader :examples, :openapi_root, :operations, :parameters,
7
+ :request_bodies, :rescue_handlers, :responses, :schemas
8
+
9
+ def initialize(owner = nil)
10
+ @owner = owner
11
+ @examples = {}
12
+ @operations = {}
13
+ @parameters = {}
14
+ @request_bodies = {}
15
+ @responses = {}
16
+ @schemas = {}
17
+ @rescue_handlers = []
18
+ @self_and_included = [self]
19
+ end
20
+
21
+ def add_example(name, keywords = {})
22
+ @examples[name.to_s] = Example.new(keywords)
23
+ end
24
+
25
+ def add_operation(name = nil, keywords = {})
26
+ name = name.nil? ? default_operation_name : name.to_s
27
+ @operations[name] = Operation.new(name, keywords.reverse_merge(path: default_path))
28
+ end
29
+
30
+ def add_parameter(name, keywords = {})
31
+ name = name.to_s
32
+ @parameters[name] = Parameter.new(name, keywords)
33
+ end
34
+
35
+ def add_request_body(name, keywords = {})
36
+ @request_bodies[name.to_s] = RequestBody.new(keywords)
37
+ end
38
+
39
+ def add_rescue_handler(klass, status: nil)
40
+ @rescue_handlers << RescueHandler.new(klass, status: status)
41
+ end
42
+
43
+ def add_response(name, keywords = {})
44
+ name = name.to_s
45
+ @responses[name] = Response.new(keywords)
46
+ end
47
+
48
+ def add_schema(name, keywords = {})
49
+ name = name.to_s
50
+ @schemas[name] = Schema.new(keywords)
51
+ end
52
+
53
+ def example(name)
54
+ return unless (name = name.to_s).present?
55
+
56
+ definitions = @self_and_included.find { |d| d.examples.key?(name) }
57
+ definitions.examples[name] if definitions
58
+ end
59
+
60
+ def include(definitions)
61
+ return if @self_and_included.include?(definitions)
62
+
63
+ @self_and_included << definitions
64
+ end
65
+
66
+ def inspect # :nodoc:
67
+ "#<#{self.class.name} #{
68
+ %i[owner operations parameters request_bodies responses schemas
69
+ examples openapi_root rescue_handlers].map do |name|
70
+ "#{name}: #{instance_variable_get("@#{name}").inspect}"
71
+ end.join(', ')
72
+ }>"
73
+ end
74
+
75
+ # Returns the JSON Schema document for the given schema as a +Hash+.
76
+ def json_schema_document(name)
77
+ schema(name)&.to_json_schema&.tap do |hash|
78
+ definitions =
79
+ @self_and_included
80
+ .map(&:schemas)
81
+ .reduce(&:merge)
82
+ .except(name.to_s)
83
+ .transform_values(&:to_json_schema)
84
+
85
+ hash[:definitions] = definitions if definitions.any?
86
+ end
87
+ end
88
+
89
+ # Returns a hash representing the OpenAPI document for +version+.
90
+ # Raises an +ArgumentError+ if +version+ is not supported.
91
+ def openapi_document(version = nil)
92
+ version = OpenAPI::Version.from(version)
93
+
94
+ (openapi_root&.to_openapi(version, self) || {}).tap do |h|
95
+ h[:paths] = openapi_paths(version)
96
+
97
+ if version.major == 2
98
+ h.merge!(
99
+ definitions: openapi_schemas(version),
100
+ parameters: openapi_parameters(version),
101
+ responses: openapi_responses(version)
102
+ )
103
+ else
104
+ h.deep_merge!(
105
+ components: {
106
+ schemas: openapi_schemas(version),
107
+ parameters: openapi_parameters(version),
108
+ requestBodies: openapi_request_bodies(version),
109
+ responses: openapi_responses(version),
110
+ examples: openapi_examples
111
+ }.compact.presence
112
+ )
113
+ end
114
+ end.compact
115
+ end
116
+
117
+ def openapi_root=(keywords = {})
118
+ @openapi_root = OpenAPI::Root.new(**keywords)
119
+ end
120
+
121
+ def operation(name = nil)
122
+ if (name = name.to_s).present?
123
+ definitions = @self_and_included.find { |d| d.operations.key?(name) }
124
+ definitions.operations[name] if definitions
125
+ elsif @operations.one?
126
+ # return the one and only operation
127
+ @operations.values.first
128
+ end
129
+ end
130
+
131
+ def parameter(name)
132
+ return unless (name = name.to_s).present?
133
+
134
+ definitions = @self_and_included.find { |d| d.parameters.key?(name) }
135
+ definitions.parameters[name] if definitions
136
+ end
137
+
138
+ def request_body(name)
139
+ return unless (name = name.to_s).present?
140
+
141
+ definitions = @self_and_included.find { |d| d.request_bodies.key?(name) }
142
+ definitions.request_bodies[name] if definitions
143
+ end
144
+
145
+ def rescue_handler_for(exception)
146
+ @self_and_included.each do |definitions|
147
+ definitions.rescue_handlers.each do |rescue_handler|
148
+ return rescue_handler if rescue_handler.match?(exception)
149
+ end
150
+ end
151
+ nil
152
+ end
153
+
154
+ def response(name)
155
+ return unless (name = name.to_s).present?
156
+
157
+ definitions = @self_and_included.find { |d| d.responses.key?(name) }
158
+ definitions.responses[name] if definitions
159
+ end
160
+
161
+ def schema(name)
162
+ return unless (name = name.to_s).present?
163
+
164
+ definitions = @self_and_included.find { |d| d.schemas.key?(name) }
165
+ definitions.schemas[name] if definitions
166
+ end
167
+
168
+ private
169
+
170
+ def default_operation_name
171
+ @default_operation_name ||= @owner.to_s.demodulize.delete_suffix('Controller').underscore
172
+ end
173
+
174
+ def default_path
175
+ @default_path ||= "/#{default_operation_name}"
176
+ end
177
+
178
+ def openapi_examples
179
+ @self_and_included
180
+ .map(&:examples).reduce(&:merge)
181
+ .transform_values(&:to_openapi)
182
+ .presence
183
+ end
184
+
185
+ def openapi_parameters(version)
186
+ @self_and_included
187
+ .map(&:parameters).reduce(&:merge)
188
+ .transform_values do |parameter|
189
+ parameter.to_openapi(version, self).first
190
+ end.presence
191
+ end
192
+
193
+ def openapi_paths(version)
194
+ @self_and_included
195
+ .map(&:operations).reduce(&:merge).values
196
+ .group_by { |operation| operation.path || default_path }
197
+ .transform_values do |operations|
198
+ operations.index_by(&:method).transform_values do |operation|
199
+ operation.to_openapi(version, self)
200
+ end
201
+ end.presence
202
+ end
203
+
204
+ def openapi_request_bodies(version)
205
+ @self_and_included
206
+ .map(&:request_bodies).reduce(&:merge).transform_values do |request_body|
207
+ request_body.to_openapi(version)
208
+ end.presence
209
+ end
210
+
211
+ def openapi_responses(version)
212
+ @self_and_included
213
+ .map(&:responses).reduce(&:merge).transform_values do |response|
214
+ response.to_openapi(version, self)
215
+ end.presence
216
+ end
217
+
218
+ def openapi_schemas(version)
219
+ @self_and_included
220
+ .map(&:schemas).reduce(&:merge).transform_values do |schema|
221
+ schema.to_openapi(version)
222
+ end.presence
223
+ end
224
+ end
225
+ end
226
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jsapi
4
+ module Meta
5
+ module Example
6
+ class Model < Base
7
+ ##
8
+ # :attr: description
9
+ # The optional description of the example.
10
+ attribute :description, String
11
+
12
+ ##
13
+ # :attr: external
14
+ # If true, +value+ is interpreted as a URI pointing to an external
15
+ # sample value.
16
+ attribute :external, values: [true, false]
17
+
18
+ ##
19
+ # :attr: summary
20
+ # The optional summary of the example.
21
+ attribute :summary, String
22
+
23
+ ##
24
+ # :attr: value
25
+ # The sample value.
26
+ attribute :value
27
+
28
+ # Returns a hash representing the \OpenAPI example object.
29
+ def to_openapi(*)
30
+ {}.tap do |hash|
31
+ hash[:summary] = summary if summary.present?
32
+ hash[:description] = description if description.present?
33
+
34
+ if external?
35
+ hash[:external_value] = value
36
+ else
37
+ hash[:value] = value
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jsapi
4
+ module Meta
5
+ module Example
6
+ class Reference < BaseReference
7
+ # Returns a hash representing the \OpenAPI reference object.
8
+ def to_openapi(*)
9
+ { '$ref': "#/components/examples/#{ref}" }
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'example/model'
4
+ require_relative 'example/reference'
5
+
6
+ module Jsapi
7
+ module Meta
8
+ module Example
9
+ class << self
10
+ # Creates a new example model or reference.
11
+ def new(keywords = {})
12
+ return Reference.new(keywords) if keywords.key?(:ref)
13
+
14
+ Model.new(keywords)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jsapi
4
+ module Meta
5
+ # Combines the presence concepts of the +#present?+ method and \JSON \Schema
6
+ # by four levels of existence.
7
+ class Existence
8
+ include Comparable
9
+
10
+ # The level of existence.
11
+ attr_reader :level
12
+
13
+ # Creates a new instance with the specified level.
14
+ def initialize(level)
15
+ @level = level
16
+ end
17
+
18
+ # The parameter or property can be omitted, corresponds to the opposite
19
+ # of +required+ as specified by \JSON \Schema.
20
+ ALLOW_OMITTED = new(1)
21
+
22
+ # The parameter or property value can be +nil+, corresponds to
23
+ # <code>nullable: true</code> as specified by \JSON \Schema.
24
+ ALLOW_NIL = new(2)
25
+
26
+ # The parameter or property value must respond to +nil?+ with +false+.
27
+ ALLOW_EMPTY = new(3)
28
+
29
+ # The parameter or property value must respond to +present?+ with +true+
30
+ # or must be +false+.
31
+ PRESENT = new(4)
32
+
33
+ # Creates a new instance from +value+.
34
+ def self.from(value)
35
+ return value if value.is_a?(Existence)
36
+
37
+ case value
38
+ when :present, true
39
+ PRESENT
40
+ when :allow_empty
41
+ ALLOW_EMPTY
42
+ when :allow_nil, :allow_null
43
+ ALLOW_NIL
44
+ when :allow_omitted, false, nil
45
+ ALLOW_OMITTED
46
+ else
47
+ raise ArgumentError, "invalid existence: #{value}"
48
+ end
49
+ end
50
+
51
+ def ==(other) # :nodoc:
52
+ other.is_a?(Existence) && level == other.level
53
+ end
54
+
55
+ def <=>(other) # :nodoc:
56
+ level <=> other.level
57
+ end
58
+
59
+ def inspect # :nodoc:
60
+ "#<#{self.class.name} level: #{level}>"
61
+ end
62
+
63
+ # Returns true if +object+ reaches the level of existence, false otherwise.
64
+ def reach?(object)
65
+ (object.null? ? 2 : object.empty? ? 3 : 4) >= level
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jsapi
4
+ module Meta
5
+ class InvalidArgumentError < ArgumentError
6
+ def initialize(name, value, values)
7
+ super("#{name} must be one of #{values.inspect}, is #{value.inspect}")
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jsapi
4
+ module Meta
5
+ module OpenAPI
6
+ module Callback
7
+ # Represents a callback object. Applies to \OpenAPI 3.x.
8
+ class Model < Base
9
+ ##
10
+ # :attr: operations
11
+ attribute :operations, writer: false, default: {}
12
+
13
+ # Adds a callback operation.
14
+ #
15
+ # Raises an +ArgumentError+ if +expression+ is blank.
16
+ def add_operation(expression, keywords = {})
17
+ raise ArgumentError, "expression can't be blank" if expression.blank?
18
+
19
+ (@operations ||= {})[expression.to_s] = Operation.new(nil, keywords)
20
+ end
21
+
22
+ def operation(expression) # :nodoc:
23
+ @operations&.[](expression&.to_s)
24
+ end
25
+
26
+ # Returns a hash representing the callback object.
27
+ def to_openapi(version, definitions)
28
+ operations.transform_values do |operation|
29
+ { operation.method => operation.to_openapi(version, definitions) }
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jsapi
4
+ module Meta
5
+ module OpenAPI
6
+ module Callback
7
+ class Reference < BaseReference
8
+ # Returns a hash representing the \OpenAPI reference object.
9
+ def to_openapi(*)
10
+ { '$ref': "#/components/callbacks/#{ref}" }
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end