jsapi 2.0.1 → 2.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.
- checksums.yaml +4 -4
- data/lib/jsapi/controller/methods.rb +12 -0
- data/lib/jsapi/controller/response.rb +33 -146
- data/lib/jsapi/jsonify_error.rb +17 -0
- data/lib/jsapi/meta/content.rb +18 -2
- data/lib/jsapi/meta/definitions.rb +6 -1
- data/lib/jsapi/meta/example/base.rb +14 -5
- data/lib/jsapi/meta/example/builder.rb +34 -0
- data/lib/jsapi/meta/example.rb +1 -0
- data/lib/jsapi/meta/response/base.rb +11 -3
- data/lib/jsapi/meta/response/wrapper.rb +4 -0
- data/lib/jsapi/meta/schema/array.rb +10 -0
- data/lib/jsapi/meta/schema/boolean.rb +9 -1
- data/lib/jsapi/meta/schema/conversion.rb +4 -4
- data/lib/jsapi/meta/schema/integer.rb +9 -1
- data/lib/jsapi/meta/schema/number.rb +9 -1
- data/lib/jsapi/meta/schema/object.rb +73 -0
- data/lib/jsapi/meta/schema/string.rb +22 -0
- data/lib/jsapi/meta/schema/wrapper.rb +14 -0
- data/lib/jsapi/meta/schema.rb +3 -8
- data/lib/jsapi/version.rb +1 -1
- data/lib/jsapi.rb +1 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9d75f4e95d037fa1516504cff963dca3409cc1b45233fe6a46bab2155939e249
|
|
4
|
+
data.tar.gz: 3e6626ac1b7796eb94cbbdabe57df7f9b286947018133ef4cdac93f241e8214a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4fdf2b1bc207755bb94dc88f001d4d668e7b7506284358b8f08531f15869d1feb1b7eebcd5cced2277e2cba42ebdae907f254668f13f7d9295db4c42298a531a
|
|
7
|
+
data.tar.gz: 3d6a20ef5a0218e2ac75233792dc844389853bfb24167d3f9ec0f4919f00a1e7623aa7a5801c10649cb02d8c30c0eb9e781901da2d488ebc4134b3492e1bdef5
|
|
@@ -127,6 +127,12 @@ module Jsapi
|
|
|
127
127
|
[true, false].each do |bang|
|
|
128
128
|
define_method(bang ? :api_operation! : :api_operation) \
|
|
129
129
|
do |operation_name = nil, omit: nil, status: nil, strong: false, &block|
|
|
130
|
+
raise ArgumentError, Messages.invalid_value(
|
|
131
|
+
name: 'omit',
|
|
132
|
+
value: omit,
|
|
133
|
+
valid_values: %i[empty nil]
|
|
134
|
+
) if [nil, :empty, :nil].exclude?(omit)
|
|
135
|
+
|
|
130
136
|
operation = _api_operation(operation_name)
|
|
131
137
|
response_model = nil
|
|
132
138
|
|
|
@@ -247,6 +253,12 @@ module Jsapi
|
|
|
247
253
|
# - +:empty+ - All of the properties whose value is empty are omitted.
|
|
248
254
|
# - +:nil+ - All of the properties whose value is +nil+ are omitted.
|
|
249
255
|
def api_response(result, operation_name = nil, omit: nil, status: nil)
|
|
256
|
+
raise ArgumentError, Messages.invalid_value(
|
|
257
|
+
name: 'omit',
|
|
258
|
+
value: omit,
|
|
259
|
+
valid_values: %i[empty nil]
|
|
260
|
+
) if [nil, :empty, :nil].exclude?(omit)
|
|
261
|
+
|
|
250
262
|
status = Status::Code.from(status)
|
|
251
263
|
operation = _api_operation(operation_name)
|
|
252
264
|
response_model = _api_response_model(operation, status)
|
|
@@ -4,44 +4,6 @@ module Jsapi
|
|
|
4
4
|
module Controller
|
|
5
5
|
# Used to jsonify a response.
|
|
6
6
|
class Response
|
|
7
|
-
class JsonifyError < RuntimeError # :nodoc:
|
|
8
|
-
def message
|
|
9
|
-
[@path&.delete_prefix('.') || 'response body', super].join(' ')
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def prepend(origin)
|
|
13
|
-
@path = "#{origin}#{@path}"
|
|
14
|
-
self
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
class HashReader # :nodoc:
|
|
19
|
-
delegate_missing_to :@hash
|
|
20
|
-
|
|
21
|
-
def initialize(hash)
|
|
22
|
-
@hash = hash
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def [](key)
|
|
26
|
-
return unless @hash.key?(key)
|
|
27
|
-
|
|
28
|
-
(@read_keys ||= []) << key
|
|
29
|
-
@hash[key]
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def additional_properties
|
|
33
|
-
if @hash.key?('additional_properties')
|
|
34
|
-
@hash['additional_properties']
|
|
35
|
-
elsif @hash.key?(:additional_properties)
|
|
36
|
-
@hash[:additional_properties]
|
|
37
|
-
elsif @read_keys
|
|
38
|
-
@hash.except(*@read_keys)
|
|
39
|
-
else
|
|
40
|
-
@hash
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
|
|
45
7
|
# Creates a new instance to jsonify +object+ according to +content_model+.
|
|
46
8
|
#
|
|
47
9
|
# The +:omit+ option specifies on which conditions properties are omitted.
|
|
@@ -49,29 +11,25 @@ module Jsapi
|
|
|
49
11
|
#
|
|
50
12
|
# - +:empty+ - All of the properties whose value is empty are omitted.
|
|
51
13
|
# - +:nil+ - All of the properties whose value is +nil+ are omitted.
|
|
52
|
-
|
|
53
|
-
# Raises an +ArgumentError+ when +:omit+ is other than +:empty+, +:nil+ or +nil+.
|
|
54
|
-
def initialize(object, content_model, omit: nil, locale: nil)
|
|
14
|
+
def initialize(object, content_model, locale: nil, omit: nil)
|
|
55
15
|
@object = object
|
|
56
16
|
@content_model = content_model
|
|
57
|
-
|
|
58
|
-
@omittable_check =
|
|
59
|
-
case omit
|
|
60
|
-
when nil
|
|
61
|
-
nil
|
|
62
|
-
when :nil
|
|
63
|
-
->(value, schema) { schema.omittable? && value.nil? }
|
|
64
|
-
when :empty
|
|
65
|
-
->(value, schema) { schema.omittable? && value.try(:empty?) }
|
|
66
|
-
else
|
|
67
|
-
raise ArgumentError, Messages.invalid_value(
|
|
68
|
-
name: 'omit',
|
|
69
|
-
value: omit,
|
|
70
|
-
valid_values: %i[empty nil]
|
|
71
|
-
)
|
|
72
|
-
end
|
|
73
|
-
|
|
74
17
|
@locale = locale
|
|
18
|
+
@omit = omit
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Returns the \JSON representation of the response.
|
|
22
|
+
def as_json(*)
|
|
23
|
+
with_locale do
|
|
24
|
+
@content_model.schema.jsonify(
|
|
25
|
+
@object,
|
|
26
|
+
context: :response,
|
|
27
|
+
omit: @omit
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
rescue JsonifyError => e
|
|
31
|
+
e.prepend('response body') if e.path.blank?
|
|
32
|
+
raise e
|
|
75
33
|
end
|
|
76
34
|
|
|
77
35
|
def inspect # :nodoc:
|
|
@@ -80,24 +38,35 @@ module Jsapi
|
|
|
80
38
|
|
|
81
39
|
# Returns the \JSON representation of the response as a string.
|
|
82
40
|
def to_json(*)
|
|
83
|
-
|
|
41
|
+
as_json.to_json
|
|
84
42
|
end
|
|
85
43
|
|
|
86
44
|
# Writes the response in \JSON sequence text format to +stream+.
|
|
87
45
|
def write_json_seq_to(stream)
|
|
88
46
|
schema = @content_model.schema
|
|
47
|
+
object = @object
|
|
48
|
+
object = schema.default_value(context: :response) if object.nil?
|
|
49
|
+
|
|
89
50
|
with_locale do
|
|
90
51
|
items, item_schema =
|
|
91
|
-
if schema.array? &&
|
|
92
|
-
[
|
|
52
|
+
if schema.array? && object.respond_to?(:each)
|
|
53
|
+
[object, schema.items]
|
|
93
54
|
else
|
|
94
|
-
[[
|
|
55
|
+
[[object], schema]
|
|
95
56
|
end
|
|
96
57
|
|
|
97
|
-
items.
|
|
58
|
+
items.each_with_index do |item, index|
|
|
98
59
|
stream.write("\u001E") # Record separator (see RFC 7464)
|
|
99
|
-
stream.write(
|
|
60
|
+
stream.write(
|
|
61
|
+
item_schema.jsonify(
|
|
62
|
+
item,
|
|
63
|
+
context: :response,
|
|
64
|
+
omit: @omit
|
|
65
|
+
).to_json
|
|
66
|
+
)
|
|
100
67
|
stream.write("\n")
|
|
68
|
+
rescue JsonifyError => e
|
|
69
|
+
raise e.prepend("[#{index}]")
|
|
101
70
|
end
|
|
102
71
|
end
|
|
103
72
|
nil
|
|
@@ -105,88 +74,6 @@ module Jsapi
|
|
|
105
74
|
|
|
106
75
|
private
|
|
107
76
|
|
|
108
|
-
def jsonify(object, schema)
|
|
109
|
-
object = schema.default_value(context: :response) if object.nil?
|
|
110
|
-
|
|
111
|
-
if object.nil?
|
|
112
|
-
raise JsonifyError, "can't be nil" unless schema.nullable?
|
|
113
|
-
else
|
|
114
|
-
case schema.type
|
|
115
|
-
when 'array'
|
|
116
|
-
jsonify_array(object, schema)
|
|
117
|
-
when 'boolean'
|
|
118
|
-
object
|
|
119
|
-
when 'integer'
|
|
120
|
-
schema.convert(object.to_i)
|
|
121
|
-
when 'number'
|
|
122
|
-
schema.convert(object.to_f)
|
|
123
|
-
when 'object'
|
|
124
|
-
jsonify_object(object, schema)
|
|
125
|
-
when 'string'
|
|
126
|
-
schema.convert(
|
|
127
|
-
case schema.format
|
|
128
|
-
when 'date'
|
|
129
|
-
object.to_date
|
|
130
|
-
when 'date-time'
|
|
131
|
-
object.to_datetime
|
|
132
|
-
when 'duration'
|
|
133
|
-
object.iso8601
|
|
134
|
-
else
|
|
135
|
-
object.to_s
|
|
136
|
-
end
|
|
137
|
-
)
|
|
138
|
-
else
|
|
139
|
-
raise JsonifyError, "has an invalid type: #{schema.type.inspect}"
|
|
140
|
-
end
|
|
141
|
-
end
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
def jsonify_array(array, schema)
|
|
145
|
-
item_schema = schema.items
|
|
146
|
-
index = 0
|
|
147
|
-
|
|
148
|
-
Array(array).map do |item|
|
|
149
|
-
item = jsonify(item, item_schema)
|
|
150
|
-
index += 1
|
|
151
|
-
item
|
|
152
|
-
rescue JsonifyError => e
|
|
153
|
-
raise e.prepend("[#{index}]")
|
|
154
|
-
end
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
def jsonify_object(object, schema)
|
|
158
|
-
schema = schema.resolve_schema(object, context: :response)
|
|
159
|
-
additional_properties = schema.additional_properties
|
|
160
|
-
object = HashReader.new(object) if additional_properties && object.is_a?(Hash)
|
|
161
|
-
|
|
162
|
-
{}.tap do |properties|
|
|
163
|
-
# Add properties
|
|
164
|
-
schema.resolve_properties(context: :response).each_value do |property|
|
|
165
|
-
property_schema = property.schema
|
|
166
|
-
property_value = property.reader.call(object)
|
|
167
|
-
property_value = property_schema.default if property_value.nil?
|
|
168
|
-
next if @omittable_check&.call(property_value, property_schema)
|
|
169
|
-
|
|
170
|
-
properties[property.name] = jsonify(property_value, property_schema)
|
|
171
|
-
rescue JsonifyError => e
|
|
172
|
-
raise e.prepend(".#{property.name}")
|
|
173
|
-
end
|
|
174
|
-
# Add additional properties
|
|
175
|
-
if additional_properties
|
|
176
|
-
additional_properties_schema = additional_properties.schema
|
|
177
|
-
|
|
178
|
-
additional_properties.source.call(object)&.each do |key, value|
|
|
179
|
-
key = key.to_s
|
|
180
|
-
next if properties.key?(key)
|
|
181
|
-
|
|
182
|
-
properties[key] = jsonify(value, additional_properties_schema)
|
|
183
|
-
rescue JsonifyError => e
|
|
184
|
-
raise e.prepend(".#{key}")
|
|
185
|
-
end
|
|
186
|
-
end
|
|
187
|
-
end.presence
|
|
188
|
-
end
|
|
189
|
-
|
|
190
77
|
def with_locale(&block)
|
|
191
78
|
if @locale
|
|
192
79
|
I18n.with_locale(@locale, &block)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jsapi
|
|
4
|
+
# Raised when jsonifying an object fails.
|
|
5
|
+
class JsonifyError < RuntimeError
|
|
6
|
+
attr_reader :path # :nodoc:
|
|
7
|
+
|
|
8
|
+
def message
|
|
9
|
+
[@path&.delete_prefix('.'), super].compact.join(' ')
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def prepend(origin) # :nodoc:
|
|
13
|
+
@path = "#{origin}#{@path}"
|
|
14
|
+
self
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
data/lib/jsapi/meta/content.rb
CHANGED
|
@@ -36,10 +36,19 @@ module Jsapi
|
|
|
36
36
|
@schema = Schema.new(keywords)
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
+
# Returns the data value of the first example.
|
|
40
|
+
def example_data_value(definitions, locale: nil)
|
|
41
|
+
examples
|
|
42
|
+
.values.first
|
|
43
|
+
&.resolve(definitions)
|
|
44
|
+
&.data_value(builder: example_builder(definitions, locale))
|
|
45
|
+
end
|
|
46
|
+
|
|
39
47
|
# Returns a hash representing the \OpenAPI media type object describing
|
|
40
48
|
# the content. Applies to \OpenAPI 3.0 and higher.
|
|
41
|
-
def to_openapi(version,
|
|
49
|
+
def to_openapi(version, definitions = nil, locale: nil, media_type: nil)
|
|
42
50
|
version = OpenAPI::Version.from(version)
|
|
51
|
+
example_builder = nil
|
|
43
52
|
|
|
44
53
|
with_openapi_extensions(
|
|
45
54
|
**if media_type == Media::Type::APPLICATION_JSON_SEQ &&
|
|
@@ -50,10 +59,17 @@ module Jsapi
|
|
|
50
59
|
end,
|
|
51
60
|
examples:
|
|
52
61
|
examples.transform_values do |example|
|
|
53
|
-
|
|
62
|
+
example_builder ||= example_builder(definitions, locale)
|
|
63
|
+
example.to_openapi(version, builder: example_builder)
|
|
54
64
|
end.presence
|
|
55
65
|
)
|
|
56
66
|
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
def example_builder(definitions, locale)
|
|
71
|
+
Example::Builder.new(schema, definitions, locale: locale) if definitions.present?
|
|
72
|
+
end
|
|
57
73
|
end
|
|
58
74
|
end
|
|
59
75
|
end
|
|
@@ -313,6 +313,10 @@ module Jsapi
|
|
|
313
313
|
end
|
|
314
314
|
end
|
|
315
315
|
|
|
316
|
+
##
|
|
317
|
+
# :method: find_example
|
|
318
|
+
# Returns the reusable example with the specified name.
|
|
319
|
+
|
|
316
320
|
##
|
|
317
321
|
# :method: find_parameter
|
|
318
322
|
# Returns the reusable parameter with the specified name.
|
|
@@ -333,7 +337,8 @@ module Jsapi
|
|
|
333
337
|
# :method: find_security_scheme
|
|
334
338
|
# Returns the security scheme with the specified name.
|
|
335
339
|
|
|
336
|
-
%i[parameters request_bodies responses
|
|
340
|
+
%i[examples parameters request_bodies responses
|
|
341
|
+
schemas security_schemes].each do |attribute_name|
|
|
337
342
|
define_method(:"find_#{attribute_name.to_s.singularize}") do |name|
|
|
338
343
|
cached_attributes.dig(attribute_name, name&.to_s)
|
|
339
344
|
end
|
|
@@ -29,7 +29,7 @@ module Jsapi
|
|
|
29
29
|
|
|
30
30
|
##
|
|
31
31
|
# :attr: value
|
|
32
|
-
# The sample value.
|
|
32
|
+
# The sample value. Can be a +Proc+.
|
|
33
33
|
attribute :value
|
|
34
34
|
|
|
35
35
|
def external_value=(value) # :nodoc:
|
|
@@ -41,7 +41,7 @@ module Jsapi
|
|
|
41
41
|
end
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
-
def serialized_value=(value)
|
|
44
|
+
def serialized_value=(value) # :nodoc:
|
|
45
45
|
try_modify_attribute!(:serialized_value) do
|
|
46
46
|
raise 'external value and serialized value are mutually exclusive' \
|
|
47
47
|
unless external_value.nil?
|
|
@@ -50,18 +50,27 @@ module Jsapi
|
|
|
50
50
|
end
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
+
# Returns the data structure of the sample value.
|
|
54
|
+
def data_value(builder: nil)
|
|
55
|
+
value = self.value
|
|
56
|
+
return value unless value.is_a?(Proc)
|
|
57
|
+
|
|
58
|
+
value.arity.zero? ? value.call : value.call(builder)
|
|
59
|
+
end
|
|
60
|
+
|
|
53
61
|
# Returns a hash representing the \OpenAPI example object.
|
|
54
|
-
def to_openapi(version,
|
|
62
|
+
def to_openapi(version, *, builder: nil)
|
|
55
63
|
version = OpenAPI::Version.from(version)
|
|
64
|
+
data_value = data_value(builder: builder)
|
|
56
65
|
|
|
57
66
|
with_openapi_extensions(
|
|
58
67
|
summary: summary,
|
|
59
68
|
description: description,
|
|
60
69
|
**if version < OpenAPI::V3_2
|
|
61
|
-
{ value:
|
|
70
|
+
{ value: data_value }
|
|
62
71
|
else
|
|
63
72
|
{
|
|
64
|
-
dataValue:
|
|
73
|
+
dataValue: data_value,
|
|
65
74
|
serializedValue: serialized_value
|
|
66
75
|
}
|
|
67
76
|
end,
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jsapi
|
|
4
|
+
module Meta
|
|
5
|
+
module Example
|
|
6
|
+
class Builder # :nodoc:
|
|
7
|
+
def initialize(schema, definitions, locale: nil)
|
|
8
|
+
@schema = Schema.wrap(schema, definitions)
|
|
9
|
+
@locale = locale
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def generate_json(object, omit: nil)
|
|
13
|
+
raise ArgumentError, Messages.invalid_value(
|
|
14
|
+
name: 'omit',
|
|
15
|
+
value: omit,
|
|
16
|
+
valid_values: %i[empty nil]
|
|
17
|
+
) if [nil, :empty, :nil].exclude?(omit)
|
|
18
|
+
|
|
19
|
+
with_locale { @schema.jsonify(object, omit: omit) }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def with_locale(&block)
|
|
25
|
+
if @locale
|
|
26
|
+
I18n.with_locale(@locale, &block)
|
|
27
|
+
else
|
|
28
|
+
yield
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
data/lib/jsapi/meta/example.rb
CHANGED
|
@@ -104,7 +104,7 @@ module Jsapi
|
|
|
104
104
|
with_openapi_extensions(
|
|
105
105
|
if version == OpenAPI::V2_0
|
|
106
106
|
media_type, content = contents.first
|
|
107
|
-
example = content&.
|
|
107
|
+
example = content&.example_data_value(definitions, locale: locale)
|
|
108
108
|
{
|
|
109
109
|
description: description,
|
|
110
110
|
schema: content&.schema&.to_openapi(version),
|
|
@@ -114,7 +114,7 @@ module Jsapi
|
|
|
114
114
|
end.compact.presence,
|
|
115
115
|
examples:
|
|
116
116
|
if media_type.present? && example.present?
|
|
117
|
-
{ media_type => example
|
|
117
|
+
{ media_type => example }
|
|
118
118
|
end
|
|
119
119
|
}
|
|
120
120
|
else
|
|
@@ -127,7 +127,15 @@ module Jsapi
|
|
|
127
127
|
end.presence,
|
|
128
128
|
content:
|
|
129
129
|
contents.to_h do |nth_media_type, nth_content|
|
|
130
|
-
[
|
|
130
|
+
[
|
|
131
|
+
nth_media_type,
|
|
132
|
+
nth_content.to_openapi(
|
|
133
|
+
version,
|
|
134
|
+
definitions,
|
|
135
|
+
locale: locale,
|
|
136
|
+
media_type: nth_media_type
|
|
137
|
+
)
|
|
138
|
+
]
|
|
131
139
|
end.presence,
|
|
132
140
|
links:
|
|
133
141
|
links.transform_values do |link|
|
|
@@ -8,6 +8,16 @@ module Jsapi
|
|
|
8
8
|
def items
|
|
9
9
|
@items ||= Schema.wrap(super, definitions)
|
|
10
10
|
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def jsonify_value(value, context:, omit:)
|
|
15
|
+
Array(value).each_with_index.map do |item, index|
|
|
16
|
+
items.jsonify(item, context: context, omit: omit)
|
|
17
|
+
rescue JsonifyError => e
|
|
18
|
+
raise e.prepend("[#{index}]")
|
|
19
|
+
end
|
|
20
|
+
end
|
|
11
21
|
end
|
|
12
22
|
|
|
13
23
|
##
|
|
@@ -10,13 +10,13 @@ module Jsapi
|
|
|
10
10
|
base.attr_accessor :conversion
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
def convert(
|
|
14
|
-
return
|
|
13
|
+
def convert(value)
|
|
14
|
+
return value if conversion.nil?
|
|
15
15
|
|
|
16
16
|
if conversion.respond_to?(:call)
|
|
17
|
-
conversion.call(
|
|
17
|
+
conversion.call(value)
|
|
18
18
|
else
|
|
19
|
-
|
|
19
|
+
value.send(conversion)
|
|
20
20
|
end
|
|
21
21
|
end
|
|
22
22
|
end
|
|
@@ -5,6 +5,33 @@ module Jsapi
|
|
|
5
5
|
module Schema
|
|
6
6
|
class Object < Base
|
|
7
7
|
class Wrapper < Schema::Wrapper
|
|
8
|
+
class HashReader # :nodoc:
|
|
9
|
+
delegate_missing_to :@hash
|
|
10
|
+
|
|
11
|
+
def initialize(hash)
|
|
12
|
+
@hash = hash
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def [](key)
|
|
16
|
+
return unless @hash.key?(key)
|
|
17
|
+
|
|
18
|
+
(@read_keys ||= []) << key
|
|
19
|
+
@hash[key]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def additional_properties
|
|
23
|
+
if @hash.key?('additional_properties')
|
|
24
|
+
@hash['additional_properties']
|
|
25
|
+
elsif @hash.key?(:additional_properties)
|
|
26
|
+
@hash[:additional_properties]
|
|
27
|
+
elsif @read_keys
|
|
28
|
+
@hash.except(*@read_keys)
|
|
29
|
+
else
|
|
30
|
+
@hash
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
8
35
|
def additional_properties
|
|
9
36
|
AdditionalProperties.wrap(super, definitions)
|
|
10
37
|
end
|
|
@@ -58,6 +85,52 @@ module Jsapi
|
|
|
58
85
|
.new(schema.resolve(definitions), definitions)
|
|
59
86
|
.resolve_schema(object, context: context)
|
|
60
87
|
end
|
|
88
|
+
|
|
89
|
+
private
|
|
90
|
+
|
|
91
|
+
def jsonify_value(value, context:, omit:)
|
|
92
|
+
schema = resolve_schema(value, context: context)
|
|
93
|
+
additional_properties = schema.additional_properties
|
|
94
|
+
value = HashReader.new(value) if additional_properties && value.is_a?(Hash)
|
|
95
|
+
|
|
96
|
+
{}.tap do |properties|
|
|
97
|
+
# Add properties
|
|
98
|
+
schema.resolve_properties(context: context).each_value do |property|
|
|
99
|
+
property_schema = property.schema
|
|
100
|
+
property_value = property.reader.call(value)
|
|
101
|
+
property_value = property_schema.default if property_value.nil?
|
|
102
|
+
|
|
103
|
+
if property_schema.omittable?
|
|
104
|
+
next if omit == :nil && property_value.nil?
|
|
105
|
+
next if omit == :empty && (property_value.nil? || property_value.try(:empty?))
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
properties[property.name] = property_schema.jsonify(
|
|
109
|
+
property_value,
|
|
110
|
+
context: context
|
|
111
|
+
)
|
|
112
|
+
rescue JsonifyError => e
|
|
113
|
+
raise e.prepend(".#{property.name}")
|
|
114
|
+
end
|
|
115
|
+
# Add additional properties
|
|
116
|
+
if additional_properties
|
|
117
|
+
additional_properties_schema = additional_properties.schema
|
|
118
|
+
|
|
119
|
+
additional_properties.source.call(value)&.each \
|
|
120
|
+
do |property_name, property_value|
|
|
121
|
+
property_name = property_name.to_s
|
|
122
|
+
next if properties.key?(property_name)
|
|
123
|
+
|
|
124
|
+
properties[property_name] = additional_properties_schema.jsonify(
|
|
125
|
+
property_value,
|
|
126
|
+
context: context
|
|
127
|
+
)
|
|
128
|
+
rescue JsonifyError => e
|
|
129
|
+
raise e.prepend(".#{property_name}")
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end.presence
|
|
133
|
+
end
|
|
61
134
|
end
|
|
62
135
|
|
|
63
136
|
##
|
|
@@ -6,6 +6,28 @@ module Jsapi
|
|
|
6
6
|
class String < Base
|
|
7
7
|
include Conversion
|
|
8
8
|
|
|
9
|
+
class Wrapper < Schema::Wrapper # :nodoc:
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def jsonify_value(value, **)
|
|
13
|
+
convert(
|
|
14
|
+
case format
|
|
15
|
+
when 'date'
|
|
16
|
+
value.to_date.as_json
|
|
17
|
+
when 'date-time'
|
|
18
|
+
value.to_datetime.as_json
|
|
19
|
+
when 'duration'
|
|
20
|
+
value = ActiveSupport::Duration.parse(value) \
|
|
21
|
+
unless value.is_a?(ActiveSupport::Duration)
|
|
22
|
+
|
|
23
|
+
value.iso8601
|
|
24
|
+
else
|
|
25
|
+
value.to_s
|
|
26
|
+
end
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
9
31
|
##
|
|
10
32
|
# :attr: format
|
|
11
33
|
# The format of a string.
|
|
@@ -25,6 +25,20 @@ module Jsapi
|
|
|
25
25
|
|
|
26
26
|
definitions.default_value(type, context: context)
|
|
27
27
|
end
|
|
28
|
+
|
|
29
|
+
# Generates a JSON value representing +value+.
|
|
30
|
+
def jsonify(value, context: nil, omit: nil)
|
|
31
|
+
value = default_value(context: context) if value.nil?
|
|
32
|
+
raise JsonifyError, "can't be nil" if value.nil? && !nullable?
|
|
33
|
+
|
|
34
|
+
jsonify_value(value, context: context, omit: omit) unless value.nil?
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def jsonify_value(value, **)
|
|
40
|
+
value
|
|
41
|
+
end
|
|
28
42
|
end
|
|
29
43
|
end
|
|
30
44
|
end
|
data/lib/jsapi/meta/schema.rb
CHANGED
|
@@ -65,14 +65,9 @@ module Jsapi
|
|
|
65
65
|
return if schema.nil?
|
|
66
66
|
return schema if schema.is_a?(Wrapper)
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
when 'object'
|
|
72
|
-
Object::Wrapper
|
|
73
|
-
else
|
|
74
|
-
Wrapper
|
|
75
|
-
end.new(schema, definitions)
|
|
68
|
+
"#{schema.resolve(definitions).class.name}::Wrapper"
|
|
69
|
+
.constantize
|
|
70
|
+
.new(schema, definitions)
|
|
76
71
|
end
|
|
77
72
|
end
|
|
78
73
|
end
|
data/lib/jsapi/version.rb
CHANGED
data/lib/jsapi.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: jsapi
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: '2.1'
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Denis Göller
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-09-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description:
|
|
14
14
|
email: denis@dmgoeller.de
|
|
@@ -58,6 +58,7 @@ files:
|
|
|
58
58
|
- lib/jsapi/json/object.rb
|
|
59
59
|
- lib/jsapi/json/string.rb
|
|
60
60
|
- lib/jsapi/json/value.rb
|
|
61
|
+
- lib/jsapi/jsonify_error.rb
|
|
61
62
|
- lib/jsapi/media.rb
|
|
62
63
|
- lib/jsapi/media/range.rb
|
|
63
64
|
- lib/jsapi/media/type.rb
|
|
@@ -76,6 +77,7 @@ files:
|
|
|
76
77
|
- lib/jsapi/meta/definitions.rb
|
|
77
78
|
- lib/jsapi/meta/example.rb
|
|
78
79
|
- lib/jsapi/meta/example/base.rb
|
|
80
|
+
- lib/jsapi/meta/example/builder.rb
|
|
79
81
|
- lib/jsapi/meta/example/reference.rb
|
|
80
82
|
- lib/jsapi/meta/existence.rb
|
|
81
83
|
- lib/jsapi/meta/external_documentation.rb
|