reynard 0.5.1 → 0.6.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.
- checksums.yaml +4 -4
- data/README.md +81 -0
- data/lib/reynard/context.rb +3 -3
- data/lib/reynard/http/response.rb +34 -3
- data/lib/reynard/media_type.rb +3 -12
- data/lib/reynard/model.rb +16 -2
- data/lib/reynard/object_builder.rb +53 -35
- data/lib/reynard/schema.rb +101 -5
- data/lib/reynard/specification.rb +4 -54
- data/lib/reynard/template.rb +1 -1
- data/lib/reynard/version.rb +1 -1
- data/lib/reynard.rb +1 -1
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 149366d0bcc24959907a263c6223a2bb75cc7004a06cf153d1ef2dc35e26b91d
|
4
|
+
data.tar.gz: 8f698ffa4c958564a902d1039807324c9b1182e0a6638dedb16fc6eb3b6eb00f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5de35106aeb06b3478d807d34f5da335684530f6f4f28c789fe52d8a0aafd7f13dab27154ac39cfdc354c4e65bd6da755ea16d669603eae280c027f12d54d152
|
7
|
+
data.tar.gz: ff64d0913efc4e8a2b7c5b8420f94cefcf13567633906d48459ce8a653f5a65f8bd9d8c24368c68a8445320012561a24ff42568065232703675af4944b59f301
|
data/README.md
CHANGED
@@ -79,8 +79,79 @@ response.code #=> '200'
|
|
79
79
|
response.content_type #=> 'application/json'
|
80
80
|
response['Content-Type'] #=> 'application/json'
|
81
81
|
response.body #=> '{"name":"Sam Seven"}'
|
82
|
+
response.parsed_body #=> { "name" => "Sam Seven" }
|
82
83
|
```
|
83
84
|
|
85
|
+
## Schema and models
|
86
|
+
|
87
|
+
Reynard has an object builder that allows you to get a value object backed by model classes based on the resource schema.
|
88
|
+
|
89
|
+
For example, when the schema for a response is something like this:
|
90
|
+
|
91
|
+
```yaml
|
92
|
+
book:
|
93
|
+
type: object
|
94
|
+
properties:
|
95
|
+
name:
|
96
|
+
type: string
|
97
|
+
author:
|
98
|
+
type: object
|
99
|
+
properties:
|
100
|
+
name:
|
101
|
+
type: string
|
102
|
+
```
|
103
|
+
|
104
|
+
And the parsed body from the response is:
|
105
|
+
|
106
|
+
```json
|
107
|
+
{
|
108
|
+
"name": "Erebus",
|
109
|
+
"author": { "name": "Palin" }
|
110
|
+
}
|
111
|
+
```
|
112
|
+
|
113
|
+
You should be able to access it using:
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
response.object.class #=> Reynard::Models::Book
|
117
|
+
response.object.author.class #=> Reynard::Models::Author
|
118
|
+
response.object.author.name #=> 'Palin'
|
119
|
+
```
|
120
|
+
|
121
|
+
### Model name
|
122
|
+
|
123
|
+
Model names are determined in order:
|
124
|
+
|
125
|
+
1. From the `title` attribute of a schema
|
126
|
+
2. From the `$ref` pointing to the schema
|
127
|
+
3. From the path to the definition of the schema
|
128
|
+
|
129
|
+
```yaml
|
130
|
+
application/json:
|
131
|
+
schema:
|
132
|
+
$ref: "#/components/schemas/Book"
|
133
|
+
components:
|
134
|
+
schemas:
|
135
|
+
Book:
|
136
|
+
type: object
|
137
|
+
title: LibraryBook
|
138
|
+
```
|
139
|
+
|
140
|
+
In this example it would use the `title` and the model name would be `LibraryBook`. Otherwise it would use `Book` from the end of the `$ref`.
|
141
|
+
|
142
|
+
If neither of those are available it would look at the full expanded path.
|
143
|
+
|
144
|
+
```
|
145
|
+
books:
|
146
|
+
type: array
|
147
|
+
items:
|
148
|
+
type: object
|
149
|
+
```
|
150
|
+
|
151
|
+
For example, in case of an array item it would look at `books` and singularize it to `Book`.
|
152
|
+
|
153
|
+
If you run into issues where Reynard doesn't properly build an object for a nested resource, it's probably because of a naming issue. It's advised to add a `title` property to the schema definition with a unique name in that case.
|
154
|
+
|
84
155
|
## Logging
|
85
156
|
|
86
157
|
When you want to know what the Reynard client is doing you can enable logging.
|
@@ -97,6 +168,16 @@ The logging should be compatible with the Ruby on Rails logger.
|
|
97
168
|
reynard.logger(Rails.logger).execute
|
98
169
|
```
|
99
170
|
|
171
|
+
## Debugging
|
172
|
+
|
173
|
+
You can turn on debug logging in `Net::HTTP` by setting the `DEBUG` environment variable. After setting this, all HTTP interaction will be written to STDERR.
|
174
|
+
|
175
|
+
```sh
|
176
|
+
env DEBUG=true ruby script.rb
|
177
|
+
```
|
178
|
+
|
179
|
+
Internally this will set `http.debug_output = $stderr` on the HTTP object in the client.
|
180
|
+
|
100
181
|
## Mocking
|
101
182
|
|
102
183
|
You can mock Reynard requests by changing the HTTP implementation. The class **must** implement a single `request` method that accepts an URI and net/http request object. It **must** return a net/http response object or an object with the exact same interface.
|
data/lib/reynard/context.rb
CHANGED
@@ -14,7 +14,7 @@ class Reynard
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def base_url(base_url)
|
17
|
-
copy(base_url:
|
17
|
+
copy(base_url:)
|
18
18
|
end
|
19
19
|
|
20
20
|
def operation(operation_name)
|
@@ -43,7 +43,7 @@ class Reynard
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def logger(logger)
|
46
|
-
copy(logger:
|
46
|
+
copy(logger:)
|
47
47
|
end
|
48
48
|
|
49
49
|
def execute
|
@@ -71,7 +71,7 @@ class Reynard
|
|
71
71
|
Reynard::Http::Response.new(
|
72
72
|
specification: @specification,
|
73
73
|
request_context: @request_context,
|
74
|
-
http_response:
|
74
|
+
http_response:
|
75
75
|
)
|
76
76
|
end
|
77
77
|
end
|
@@ -14,6 +14,38 @@ class Reynard
|
|
14
14
|
@http_response = http_response
|
15
15
|
end
|
16
16
|
|
17
|
+
# True when the response code is in the 1xx range.
|
18
|
+
def informational?
|
19
|
+
code.start_with?('1')
|
20
|
+
end
|
21
|
+
|
22
|
+
# True when the response code is in the 2xx range.
|
23
|
+
def success?
|
24
|
+
code.start_with?('2')
|
25
|
+
end
|
26
|
+
|
27
|
+
# True when the response code is in the 3xx range.
|
28
|
+
def redirection?
|
29
|
+
code.start_with?('3')
|
30
|
+
end
|
31
|
+
|
32
|
+
# True when the response code is in the 4xx range.
|
33
|
+
def client_error?
|
34
|
+
code.start_with?('4')
|
35
|
+
end
|
36
|
+
|
37
|
+
# True when the response code is in the 5xx range.
|
38
|
+
def server_error?
|
39
|
+
code.start_with?('5')
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns the parsed response body.
|
43
|
+
def parsed_body
|
44
|
+
return @parsed_body if defined?(@parsed_body)
|
45
|
+
|
46
|
+
@parsed_body = MultiJson.load(@http_response.body)
|
47
|
+
end
|
48
|
+
|
17
49
|
# Instantiates an object based on the schema that fits the response.
|
18
50
|
def object
|
19
51
|
return @object if defined?(@object)
|
@@ -37,10 +69,9 @@ class Reynard
|
|
37
69
|
end
|
38
70
|
|
39
71
|
def build_object_with_media_type(media_type)
|
40
|
-
ObjectBuilder.new(
|
41
|
-
media_type: media_type,
|
72
|
+
::Reynard::ObjectBuilder.new(
|
42
73
|
schema: @specification.schema(media_type.node),
|
43
|
-
|
74
|
+
parsed_body:
|
44
75
|
).call
|
45
76
|
end
|
46
77
|
|
data/lib/reynard/media_type.rb
CHANGED
@@ -1,21 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Reynard
|
4
|
-
# Holds node reference
|
4
|
+
# Holds node reference a media type in the API specification.
|
5
5
|
class MediaType
|
6
|
-
attr_reader :node
|
6
|
+
attr_reader :node
|
7
7
|
|
8
|
-
def initialize(node
|
8
|
+
def initialize(node:)
|
9
9
|
@node = node
|
10
|
-
@schema_name = schema_name
|
11
|
-
end
|
12
|
-
|
13
|
-
def media_type
|
14
|
-
@node[6]
|
15
|
-
end
|
16
|
-
|
17
|
-
def response_code
|
18
|
-
@node[4]
|
19
10
|
end
|
20
11
|
end
|
21
12
|
end
|
data/lib/reynard/model.rb
CHANGED
@@ -3,13 +3,18 @@
|
|
3
3
|
class Reynard
|
4
4
|
# Superclass for dynamic classes generated by the object builder.
|
5
5
|
class Model
|
6
|
+
class << self
|
7
|
+
# Holds references to the full schema for the model if available.
|
8
|
+
attr_accessor :schema
|
9
|
+
end
|
10
|
+
|
6
11
|
def initialize(attributes)
|
7
12
|
self.attributes = attributes
|
8
13
|
end
|
9
14
|
|
10
15
|
def attributes=(attributes)
|
11
16
|
attributes.each do |name, value|
|
12
|
-
instance_variable_set("@#{name}", value)
|
17
|
+
instance_variable_set("@#{name}", self.class.cast(name, value))
|
13
18
|
end
|
14
19
|
end
|
15
20
|
|
@@ -21,9 +26,18 @@ class Reynard
|
|
21
26
|
end
|
22
27
|
|
23
28
|
def respond_to_missing?(attribute_name, *)
|
24
|
-
|
29
|
+
instance_variable_defined?("@#{attribute_name}")
|
25
30
|
rescue NameError
|
26
31
|
false
|
27
32
|
end
|
33
|
+
|
34
|
+
def self.cast(name, value)
|
35
|
+
return value unless schema
|
36
|
+
|
37
|
+
property = schema.property_schema(name)
|
38
|
+
return value unless property
|
39
|
+
|
40
|
+
Reynard::ObjectBuilder.new(schema: property, parsed_body: value).call
|
41
|
+
end
|
28
42
|
end
|
29
43
|
end
|
@@ -5,60 +5,78 @@ require 'ostruct'
|
|
5
5
|
class Reynard
|
6
6
|
# Defines dynamic classes based on schema and instantiates them for a response.
|
7
7
|
class ObjectBuilder
|
8
|
-
|
9
|
-
|
8
|
+
attr_reader :schema, :parsed_body
|
9
|
+
|
10
|
+
def initialize(schema:, parsed_body:, model_name: nil)
|
10
11
|
@schema = schema
|
11
|
-
@
|
12
|
+
@parsed_body = parsed_body
|
13
|
+
@model_name = model_name
|
12
14
|
end
|
13
15
|
|
14
|
-
def
|
15
|
-
|
16
|
-
self.class.model_class(@media_type.schema_name, @schema.object_type)
|
17
|
-
elsif @schema.object_type == 'array'
|
18
|
-
Array
|
19
|
-
else
|
20
|
-
Reynard::Model
|
21
|
-
end
|
16
|
+
def model_name
|
17
|
+
@model_name || @schema.model_name
|
22
18
|
end
|
23
19
|
|
24
|
-
def
|
25
|
-
if @
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
end
|
20
|
+
def model_class
|
21
|
+
return @model_class if defined?(@model_class)
|
22
|
+
|
23
|
+
@model_class =
|
24
|
+
self.class.model_class_get(model_name) || self.class.model_class_set(model_name, schema)
|
30
25
|
end
|
31
26
|
|
32
27
|
def call
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
28
|
+
case schema.type
|
29
|
+
when 'object'
|
30
|
+
model_class.new(parsed_body)
|
31
|
+
when 'array'
|
32
|
+
cast_array
|
37
33
|
else
|
38
|
-
|
34
|
+
parsed_body
|
39
35
|
end
|
40
36
|
end
|
41
37
|
|
42
|
-
def
|
43
|
-
|
38
|
+
def self.model_class_get(model_name)
|
39
|
+
Kernel.const_get("::Reynard::Models::#{model_name}")
|
40
|
+
rescue NameError
|
41
|
+
nil
|
44
42
|
end
|
45
43
|
|
46
|
-
def self.
|
47
|
-
|
44
|
+
def self.model_class_set(model_name, schema)
|
45
|
+
if schema.type == 'array'
|
46
|
+
array_model_class_set(model_name)
|
47
|
+
else
|
48
|
+
object_model_class_set(model_name, schema)
|
49
|
+
end
|
48
50
|
end
|
49
51
|
|
50
|
-
def self.
|
51
|
-
|
52
|
-
|
53
|
-
|
52
|
+
def self.array_model_class_set(model_name)
|
53
|
+
return Array unless model_name
|
54
|
+
|
55
|
+
::Reynard::Models.const_set(model_name, Class.new(Array))
|
54
56
|
end
|
55
57
|
|
56
|
-
def self.
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
58
|
+
def self.object_model_class_set(model_name, schema)
|
59
|
+
return Reynard::Model unless model_name
|
60
|
+
|
61
|
+
model_class = Class.new(Reynard::Model)
|
62
|
+
model_class.schema = schema
|
63
|
+
::Reynard::Models.const_set(model_name, model_class)
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def cast_array
|
69
|
+
return unless parsed_body
|
70
|
+
|
71
|
+
item_schema = schema.item_schema
|
72
|
+
array = model_class.new
|
73
|
+
parsed_body.each do |item|
|
74
|
+
array << self.class.new(
|
75
|
+
schema: item_schema,
|
76
|
+
parsed_body: item
|
77
|
+
).call
|
61
78
|
end
|
79
|
+
array
|
62
80
|
end
|
63
81
|
end
|
64
82
|
end
|
data/lib/reynard/schema.rb
CHANGED
@@ -1,14 +1,110 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Reynard
|
4
|
-
# Holds
|
4
|
+
# Holds a references to a schema definition in the specification.
|
5
5
|
class Schema
|
6
|
-
attr_reader :node, :
|
6
|
+
attr_reader :node, :namespace
|
7
7
|
|
8
|
-
def initialize(
|
8
|
+
def initialize(specification:, node:, namespace: nil)
|
9
|
+
@specification = specification
|
9
10
|
@node = node
|
10
|
-
@
|
11
|
-
|
11
|
+
@namespace = namespace
|
12
|
+
end
|
13
|
+
|
14
|
+
def type
|
15
|
+
return @type if defined?(@type)
|
16
|
+
|
17
|
+
@type = @specification.dig(*node, 'type')
|
18
|
+
end
|
19
|
+
|
20
|
+
def model_name
|
21
|
+
return @model_name if defined?(@model_name)
|
22
|
+
|
23
|
+
@model_name = find_model_name
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns the schema for items when the current schema is an array.
|
27
|
+
def item_schema
|
28
|
+
return unless type == 'array'
|
29
|
+
|
30
|
+
self.class.new(
|
31
|
+
specification: @specification,
|
32
|
+
node: [*node, 'items'],
|
33
|
+
namespace: [*namespace, model_name]
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns the schema for a propery in the schema.
|
38
|
+
def property_schema(name)
|
39
|
+
property_node = [*node, 'properties', name.to_s]
|
40
|
+
return unless @specification.dig(*property_node)
|
41
|
+
|
42
|
+
self.class.new(
|
43
|
+
specification: @specification,
|
44
|
+
node: property_node,
|
45
|
+
namespace: [*namespace, model_name]
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.title_model_name(model_name)
|
50
|
+
return unless model_name
|
51
|
+
|
52
|
+
model_name
|
53
|
+
.gsub(/[^[:alpha:]]/, ' ')
|
54
|
+
.gsub(/\s{2,}/, ' ')
|
55
|
+
.gsub(/(\s+)([[:alpha:]])/) { Regexp.last_match(2).upcase }
|
56
|
+
.strip
|
57
|
+
end
|
58
|
+
|
59
|
+
# Extracts a model name from a ref when there is a usable value.
|
60
|
+
#
|
61
|
+
# ref_model_name("#/components/schemas/Library") => "Library"
|
62
|
+
def self.ref_model_name(ref)
|
63
|
+
return unless ref
|
64
|
+
|
65
|
+
normalize_ref_model_name(ref.split('/')&.last)
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.normalize_ref_model_name(model_name)
|
69
|
+
# 1. Unescape encoded characters to create an UTF-8 string
|
70
|
+
# 2. Remove extensions for regularly used external schema files
|
71
|
+
# 3. Replace all non-alphabetic characters with a space (not allowed in Ruby constant)
|
72
|
+
# 4. Camelcase
|
73
|
+
Rack::Utils.unescape_path(model_name)
|
74
|
+
.gsub(/(.yml|.yaml|.json)\Z/, '')
|
75
|
+
.gsub(/[^[:alpha:]]/, ' ')
|
76
|
+
.gsub(/(\s+)([[:alpha:]])/) { Regexp.last_match(2).upcase }
|
77
|
+
.gsub(/\A(.)/) { Regexp.last_match(1).upcase }
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
# Returns a model name based on the schema's title or $ref.
|
83
|
+
def find_model_name
|
84
|
+
title_model_name || ref_model_name || node_model_name
|
85
|
+
end
|
86
|
+
|
87
|
+
def title_model_name
|
88
|
+
title = @specification.dig(*node, 'title')
|
89
|
+
return unless title
|
90
|
+
|
91
|
+
self.class.title_model_name(title)
|
92
|
+
end
|
93
|
+
|
94
|
+
def ref_model_name
|
95
|
+
parent = @specification.dig(*node[..-2])
|
96
|
+
ref = parent.dig('schema', '$ref') || parent.dig('items', '$ref')
|
97
|
+
return unless ref
|
98
|
+
|
99
|
+
self.class.ref_model_name(ref)
|
100
|
+
end
|
101
|
+
|
102
|
+
def node_model_name
|
103
|
+
self.class.title_model_name(node_property_name.capitalize.gsub(/[_-]/, ' '))
|
104
|
+
end
|
105
|
+
|
106
|
+
def node_property_name
|
107
|
+
node.last == 'items' ? node.at(-2).chomp('s') : node.last
|
12
108
|
end
|
13
109
|
end
|
14
110
|
end
|
@@ -66,10 +66,7 @@ class Reynard
|
|
66
66
|
response, media_type = media_type_response(responses, response_code, media_type)
|
67
67
|
return unless response
|
68
68
|
|
69
|
-
MediaType.new(
|
70
|
-
node: [*operation_node, 'responses', response_code, 'content', media_type],
|
71
|
-
schema_name: schema_name(response)
|
72
|
-
)
|
69
|
+
MediaType.new(node: [*operation_node, 'responses', response_code, 'content', media_type])
|
73
70
|
end
|
74
71
|
|
75
72
|
def media_type_response(responses, response_code, media_type)
|
@@ -83,14 +80,9 @@ class Reynard
|
|
83
80
|
end
|
84
81
|
|
85
82
|
def schema(media_type_node)
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
Schema.new(
|
90
|
-
node: [*media_type_node, 'schema'],
|
91
|
-
object_type: schema['type'],
|
92
|
-
item_schema_name: item_schema_name(schema)
|
93
|
-
)
|
83
|
+
return unless dig(*media_type_node, 'schema')
|
84
|
+
|
85
|
+
Schema.new(specification: self, node: [*media_type_node, 'schema'])
|
94
86
|
end
|
95
87
|
|
96
88
|
def self.media_type_matches?(media_type, expression)
|
@@ -100,26 +92,6 @@ class Reynard
|
|
100
92
|
false
|
101
93
|
end
|
102
94
|
|
103
|
-
def self.normalize_model_name(name)
|
104
|
-
# 1. Unescape encoded characters to create an UTF-8 string
|
105
|
-
# 2. Remove extensions for regularly used external schema files
|
106
|
-
# 3. Replace all non-alphabetic characters with a space (not allowed in Ruby constant)
|
107
|
-
# 4. Camelcase
|
108
|
-
Rack::Utils.unescape_path(name)
|
109
|
-
.gsub(/(.yml|.yaml|.json)\Z/, '')
|
110
|
-
.gsub(/[^[:alpha:]]/, ' ')
|
111
|
-
.gsub(/(\s+)([[:alpha:]])/) { Regexp.last_match(2).upcase }
|
112
|
-
.gsub(/\A(.)/) { Regexp.last_match(1).upcase }
|
113
|
-
end
|
114
|
-
|
115
|
-
def self.normalize_model_title(title)
|
116
|
-
title
|
117
|
-
.gsub(/[^[:alpha:]]/, ' ')
|
118
|
-
.gsub(/\s{2,}/, ' ')
|
119
|
-
.gsub(/(\s+)([[:alpha:]])/) { Regexp.last_match(2).upcase }
|
120
|
-
.strip
|
121
|
-
end
|
122
|
-
|
123
95
|
private
|
124
96
|
|
125
97
|
def read
|
@@ -156,27 +128,5 @@ class Reynard
|
|
156
128
|
# rubocop:enable Metrics/AbcSize
|
157
129
|
# rubocop:enable Metrics/CyclomaticComplexity
|
158
130
|
# rubocop:enable Metrics/MethodLength
|
159
|
-
|
160
|
-
def schema_name(response)
|
161
|
-
extract_schema_name(response['schema'])
|
162
|
-
end
|
163
|
-
|
164
|
-
def item_schema_name(schema)
|
165
|
-
if schema['type'] == 'array'
|
166
|
-
extract_schema_name(schema['items'])
|
167
|
-
else
|
168
|
-
extract_schema_name(schema)
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
172
|
-
def extract_schema_name(definition)
|
173
|
-
ref = definition['$ref']
|
174
|
-
return self.class.normalize_model_name(ref&.split('/')&.last) if ref
|
175
|
-
|
176
|
-
title = definition['title']
|
177
|
-
return unless title
|
178
|
-
|
179
|
-
self.class.normalize_model_title(title)
|
180
|
-
end
|
181
131
|
end
|
182
132
|
end
|
data/lib/reynard/template.rb
CHANGED
data/lib/reynard/version.rb
CHANGED
data/lib/reynard.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: reynard
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Manfred Stienstra
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-11-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: multi_json
|
@@ -143,7 +143,7 @@ licenses:
|
|
143
143
|
- MIT
|
144
144
|
metadata:
|
145
145
|
rubygems_mfa_required: 'true'
|
146
|
-
post_install_message:
|
146
|
+
post_install_message:
|
147
147
|
rdoc_options: []
|
148
148
|
require_paths:
|
149
149
|
- lib
|
@@ -151,15 +151,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
151
151
|
requirements:
|
152
152
|
- - ">"
|
153
153
|
- !ruby/object:Gem::Version
|
154
|
-
version: '
|
154
|
+
version: '3.1'
|
155
155
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
156
156
|
requirements:
|
157
157
|
- - ">="
|
158
158
|
- !ruby/object:Gem::Version
|
159
159
|
version: '0'
|
160
160
|
requirements: []
|
161
|
-
rubygems_version: 3.
|
162
|
-
signing_key:
|
161
|
+
rubygems_version: 3.3.7
|
162
|
+
signing_key:
|
163
163
|
specification_version: 4
|
164
164
|
summary: Minimal OpenAPI client.
|
165
165
|
test_files: []
|