keiyaku 0.1.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 +7 -0
- data/CHANGELOG.md +149 -0
- data/DESIGN.md +418 -0
- data/LICENSE.txt +21 -0
- data/README.md +174 -0
- data/exe/keiyaku +56 -0
- data/lib/keiyaku/adapters/faraday.rb +43 -0
- data/lib/keiyaku/adapters/http.rb +32 -0
- data/lib/keiyaku/adapters/net_http.rb +52 -0
- data/lib/keiyaku/emitter/rbs.rb +201 -0
- data/lib/keiyaku/emitter/security.rb +125 -0
- data/lib/keiyaku/emitter.rb +968 -0
- data/lib/keiyaku/names.rb +146 -0
- data/lib/keiyaku/runtime.rb +932 -0
- data/lib/keiyaku/version.rb +5 -0
- data/lib/keiyaku.rb +7 -0
- data/sig/keiyaku.rbs +216 -0
- metadata +67 -0
data/lib/keiyaku.rb
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Loads the runtime, which is all an application needs: generated clients
|
|
4
|
+
# require "keiyaku/runtime" directly. The generator is a separate file that
|
|
5
|
+
# only the `keiyaku` executable pulls in, so nothing that merely calls an API
|
|
6
|
+
# ever loads it.
|
|
7
|
+
require_relative "keiyaku/runtime"
|
data/sig/keiyaku.rbs
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# Signatures for the runtime. Generated clients declare `class Client <
|
|
2
|
+
# Keiyaku::Client` and `class Pet < Keiyaku::Model`, so without these their own
|
|
3
|
+
# RBS would not resolve — which would defeat the point of emitting it.
|
|
4
|
+
|
|
5
|
+
module Keiyaku
|
|
6
|
+
VERSION: String
|
|
7
|
+
|
|
8
|
+
# A parameter that was not passed, distinct from one passed as nil.
|
|
9
|
+
UNSET: untyped
|
|
10
|
+
|
|
11
|
+
# The words Ruby will not read as a name, which the generated method body
|
|
12
|
+
# needs too: a parameter may be called one of these.
|
|
13
|
+
KEYWORDS: Array[String]
|
|
14
|
+
|
|
15
|
+
class Error < StandardError
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class CastError < Error
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class Unsupported < Error
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class HTTPError < Error
|
|
25
|
+
attr_reader status: Integer
|
|
26
|
+
attr_reader headers: Hash[String, String]
|
|
27
|
+
attr_reader body: String?
|
|
28
|
+
|
|
29
|
+
# The decoded body: a model if the operation documents a schema for this
|
|
30
|
+
# status, otherwise whatever JSON.parse returned.
|
|
31
|
+
attr_reader parsed: untyped
|
|
32
|
+
|
|
33
|
+
def initialize: (status: Integer, headers: Hash[String, String], body: String?, ?parsed: untyped) -> void
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
class ClientError < HTTPError
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
class ServerError < HTTPError
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# The request did not happen. Every adapter raises this rather than its own
|
|
43
|
+
# library's class, so moving a client onto another HTTP stack does not
|
|
44
|
+
# change what the code around it has to rescue.
|
|
45
|
+
class ConnectionError < Error
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# What a transport failure looks like from the stdlib.
|
|
49
|
+
TRANSPORT_ERRORS: Array[Class]
|
|
50
|
+
|
|
51
|
+
# One number for both phases, or `{ open:, read: }` for two.
|
|
52
|
+
type timeout = Integer | Float | Hash[Symbol, Integer | Float]
|
|
53
|
+
|
|
54
|
+
def self.timeouts: (timeout) -> [Integer | Float, Integer | Float]
|
|
55
|
+
|
|
56
|
+
def self.camelize: (String | Symbol) -> String
|
|
57
|
+
def self.snake: (String | Symbol) -> String
|
|
58
|
+
|
|
59
|
+
# Types are values, not a type language: Integer, [Pet], { String => Pet },
|
|
60
|
+
# :bool, :any. There is nothing more precise to say about them here.
|
|
61
|
+
def self.coerce: (untyped type, untyped value, String path) -> untyped
|
|
62
|
+
def self.dump: (untyped) -> untyped
|
|
63
|
+
|
|
64
|
+
# Keyed by an exact code, by a range like "4XX", or by :default, narrowest
|
|
65
|
+
# first. The table is the operation's, so its values are whatever it holds.
|
|
66
|
+
def self.for_status: (Hash[untyped, untyped] table, Integer status) -> untyped
|
|
67
|
+
|
|
68
|
+
# Returns a Keiyaku::Model subclass built at runtime; the generator emits a
|
|
69
|
+
# proper class declaration for each one alongside the code. The fields are a
|
|
70
|
+
# positional Hash so that a property named `from` cannot displace the option.
|
|
71
|
+
def self.model: (
|
|
72
|
+
Hash[Symbol, untyped] fields, ?required: Array[Symbol], ?from: Hash[Symbol, String], ?open: untyped
|
|
73
|
+
) -> untyped
|
|
74
|
+
|
|
75
|
+
# The value type a schema becomes. Each generated model declares its own
|
|
76
|
+
# fields, `new` and `with`; what is here is what they all share.
|
|
77
|
+
class Model
|
|
78
|
+
def self.members: () -> Array[Symbol]
|
|
79
|
+
def self.types: () -> Hash[Symbol, untyped]
|
|
80
|
+
def self.json_names: () -> Hash[Symbol, String]
|
|
81
|
+
def self.required: () -> Array[Symbol]
|
|
82
|
+
|
|
83
|
+
# What `additionalProperties` said: false, true, or the type declared for
|
|
84
|
+
# the values of the properties the schema did not name.
|
|
85
|
+
def self.additional: () -> untyped
|
|
86
|
+
def self.open?: () -> bool
|
|
87
|
+
|
|
88
|
+
def self.cast: (untyped, ?String) -> untyped
|
|
89
|
+
def self.__define: (Hash[Symbol, untyped] fields, required: Array[Symbol], from: Hash[Symbol, String], open: untyped) -> void
|
|
90
|
+
|
|
91
|
+
def initialize: (**untyped) -> void
|
|
92
|
+
|
|
93
|
+
# A field by name, whether or not it is one Ruby will take through a dot,
|
|
94
|
+
# and on an open model the properties the document did not name.
|
|
95
|
+
def []: (String | Symbol) -> untyped
|
|
96
|
+
|
|
97
|
+
def with: (**untyped) -> self
|
|
98
|
+
def to_h: () -> Hash[Symbol, untyped]
|
|
99
|
+
def deconstruct: () -> Array[untyped]
|
|
100
|
+
def deconstruct_keys: (Array[Symbol]?) -> Hash[Symbol, untyped]
|
|
101
|
+
def to_json_hash: () -> Hash[String, untyped]
|
|
102
|
+
def to_json: (*untyped) -> String
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
class OneOf
|
|
106
|
+
def self.[]: (*untyped variants, on: String, ?map: Hash[untyped, untyped]) -> OneOf
|
|
107
|
+
def initialize: (Array[untyped], String, Hash[untyped, untyped]) -> void
|
|
108
|
+
def cast: (untyped, ?String) -> untyped
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Keyed by the code itself or by the range the document wrote in place of
|
|
112
|
+
# one, which is a String because `2XX` is not a number.
|
|
113
|
+
class ByStatus
|
|
114
|
+
def self.[]: (Hash[Integer | String, untyped] types) -> ByStatus
|
|
115
|
+
def initialize: (Hash[Integer | String, untyped]) -> void
|
|
116
|
+
def []: (Integer status) -> untyped
|
|
117
|
+
def types: () -> Hash[Integer | String, untyped]
|
|
118
|
+
|
|
119
|
+
private
|
|
120
|
+
|
|
121
|
+
def normalise: (untyped status) -> untyped
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
class Upload
|
|
125
|
+
attr_reader io: untyped
|
|
126
|
+
attr_reader filename: String
|
|
127
|
+
attr_reader content_type: String
|
|
128
|
+
|
|
129
|
+
def initialize: (untyped io, ?filename: String?, ?content_type: String?) -> void
|
|
130
|
+
def read: () -> String
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
module Serialize
|
|
134
|
+
def self.query: (Hash[String, untyped], ?deep: Array[String]) -> Array[[String, String]]
|
|
135
|
+
def self.deep_object: (String name, untyped value) -> Array[[String, String]]
|
|
136
|
+
def self.path: (String name, untyped value, ?explode: bool) -> String
|
|
137
|
+
def self.simple: (String name, untyped value, ?explode: bool, ?escape: bool) -> String
|
|
138
|
+
def self.simple_part: (String name, untyped value, ?escape: bool) -> String
|
|
139
|
+
def self.stringify: (untyped) -> String
|
|
140
|
+
def self.multipart: (Hash[String, untyped], String boundary) -> String
|
|
141
|
+
def self.part: (String name, untyped value, String boundary) -> String
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Anything that can make a request. Pass one as `adapter:` to put the client
|
|
145
|
+
# on an HTTP stack the application already has. Response header names may be
|
|
146
|
+
# in any case; the client lower-cases them before reading any.
|
|
147
|
+
interface _Adapter
|
|
148
|
+
def call: (Symbol verb, untyped uri, Hash[String, String] headers, String? body) -> [Integer, Hash[String, untyped], String?]
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Autoloaded from `keiyaku/adapters/net_http` by the client that was given no
|
|
152
|
+
# adapter, so nothing has to require it to get the default.
|
|
153
|
+
class NetHTTPAdapter
|
|
154
|
+
include _Adapter
|
|
155
|
+
|
|
156
|
+
def initialize: (?timeout: timeout) -> void
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Loaded only by `require "keiyaku/adapters/faraday"`. Faraday's own types
|
|
160
|
+
# are deliberately not named here: the signature has to resolve whether or
|
|
161
|
+
# not the gem is installed.
|
|
162
|
+
class FaradayAdapter
|
|
163
|
+
include _Adapter
|
|
164
|
+
|
|
165
|
+
def initialize: (?untyped connection, ?timeout: timeout?, **untyped) -> void
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Loaded only by `require "keiyaku/adapters/http"`. Wraps the http.rb gem.
|
|
169
|
+
class HTTPAdapter
|
|
170
|
+
include _Adapter
|
|
171
|
+
|
|
172
|
+
def initialize: (?untyped client, ?timeout: timeout) -> void
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
class Client
|
|
176
|
+
def self.operations: () -> Hash[Symbol, Hash[Symbol, untyped]]
|
|
177
|
+
def self.server: (?String?) -> String?
|
|
178
|
+
|
|
179
|
+
# The schemes the document declares, by its own name for each, and the
|
|
180
|
+
# requirement that holds where an operation states none of its own.
|
|
181
|
+
def self.security: (?Hash[Symbol | String, untyped] schemes, ?default: untyped) -> untyped
|
|
182
|
+
def self.default_security: () -> Array[Array[Symbol]]
|
|
183
|
+
|
|
184
|
+
# A requirement is a list of alternatives, any one of which is enough,
|
|
185
|
+
# each naming the schemes that all have to be satisfied.
|
|
186
|
+
def self.requirement: (untyped) -> Array[Array[Symbol]]
|
|
187
|
+
|
|
188
|
+
def self.get: (Symbol name, String template, **untyped) -> void
|
|
189
|
+
def self.post: (Symbol name, String template, **untyped) -> void
|
|
190
|
+
def self.put: (Symbol name, String template, **untyped) -> void
|
|
191
|
+
def self.patch: (Symbol name, String template, **untyped) -> void
|
|
192
|
+
def self.delete: (Symbol name, String template, **untyped) -> void
|
|
193
|
+
def self.head: (Symbol name, String template, **untyped) -> void
|
|
194
|
+
def self.options: (Symbol name, String template, **untyped) -> void
|
|
195
|
+
|
|
196
|
+
def self.operation: (
|
|
197
|
+
Symbol verb, Symbol name, String template,
|
|
198
|
+
?query: Array[Symbol], ?deep_object: Array[String], ?header: Hash[String, Symbol],
|
|
199
|
+
?explode: Array[String], ?required: Array[Symbol | String],
|
|
200
|
+
?body: untyped, ?form: untyped, ?multipart: untyped, ?content_type: String?, ?body_required: bool,
|
|
201
|
+
?into: untyped, ?errors: Hash[untyped, untyped], ?security: untyped
|
|
202
|
+
) -> void
|
|
203
|
+
|
|
204
|
+
def self.unsupported: (Symbol name, String reason) -> void
|
|
205
|
+
|
|
206
|
+
attr_reader base_url: String
|
|
207
|
+
|
|
208
|
+
# `auth:` is credentials by scheme name — a bare value only where the
|
|
209
|
+
# document declares one scheme, since with two there is nothing to say
|
|
210
|
+
# which it is.
|
|
211
|
+
def initialize: (
|
|
212
|
+
?base_url: String?, ?auth: Hash[Symbol | String, untyped] | String | Array[String] | nil,
|
|
213
|
+
?adapter: _Adapter?, ?timeout: timeout, ?retries: Integer, ?logger: untyped
|
|
214
|
+
) -> void
|
|
215
|
+
end
|
|
216
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: keiyaku
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- merely
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies: []
|
|
12
|
+
description: |
|
|
13
|
+
Generates Ruby clients from OpenAPI documents that carry only what is
|
|
14
|
+
specific to one API — an operation table and the schema fields. Transport,
|
|
15
|
+
parameter serialization, casting and error mapping live in a shared
|
|
16
|
+
runtime, so a nineteen-operation client is about seventy lines. A schema
|
|
17
|
+
becomes a frozen value type with RBS emitted beside it, so terse Ruby
|
|
18
|
+
costs nothing in tooling. Constructs it cannot translate faithfully are
|
|
19
|
+
refused at generation time rather than emitted as plausible guesses.
|
|
20
|
+
email:
|
|
21
|
+
- git@merely.ca
|
|
22
|
+
executables:
|
|
23
|
+
- keiyaku
|
|
24
|
+
extensions: []
|
|
25
|
+
extra_rdoc_files: []
|
|
26
|
+
files:
|
|
27
|
+
- CHANGELOG.md
|
|
28
|
+
- DESIGN.md
|
|
29
|
+
- LICENSE.txt
|
|
30
|
+
- README.md
|
|
31
|
+
- exe/keiyaku
|
|
32
|
+
- lib/keiyaku.rb
|
|
33
|
+
- lib/keiyaku/adapters/faraday.rb
|
|
34
|
+
- lib/keiyaku/adapters/http.rb
|
|
35
|
+
- lib/keiyaku/adapters/net_http.rb
|
|
36
|
+
- lib/keiyaku/emitter.rb
|
|
37
|
+
- lib/keiyaku/emitter/rbs.rb
|
|
38
|
+
- lib/keiyaku/emitter/security.rb
|
|
39
|
+
- lib/keiyaku/names.rb
|
|
40
|
+
- lib/keiyaku/runtime.rb
|
|
41
|
+
- lib/keiyaku/version.rb
|
|
42
|
+
- sig/keiyaku.rbs
|
|
43
|
+
homepage: https://github.com/onyxblade/keiyaku
|
|
44
|
+
licenses:
|
|
45
|
+
- MIT
|
|
46
|
+
metadata:
|
|
47
|
+
source_code_uri: https://github.com/onyxblade/keiyaku
|
|
48
|
+
changelog_uri: https://github.com/onyxblade/keiyaku/blob/main/CHANGELOG.md
|
|
49
|
+
rubygems_mfa_required: 'true'
|
|
50
|
+
rdoc_options: []
|
|
51
|
+
require_paths:
|
|
52
|
+
- lib
|
|
53
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
54
|
+
requirements:
|
|
55
|
+
- - ">="
|
|
56
|
+
- !ruby/object:Gem::Version
|
|
57
|
+
version: '3.2'
|
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
59
|
+
requirements:
|
|
60
|
+
- - ">="
|
|
61
|
+
- !ruby/object:Gem::Version
|
|
62
|
+
version: '0'
|
|
63
|
+
requirements: []
|
|
64
|
+
rubygems_version: 4.0.3
|
|
65
|
+
specification_version: 4
|
|
66
|
+
summary: OpenAPI clients as a table of operations, not a generated codebase.
|
|
67
|
+
test_files: []
|