openapi_kit-codegen 0.1.0.pre.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 (33) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +385 -0
  4. data/exe/openapi_kit +6 -0
  5. data/lib/openapi_kit/codegen/cli.rb +72 -0
  6. data/lib/openapi_kit/codegen/config.rb +114 -0
  7. data/lib/openapi_kit/codegen/emit/buffer.rb +92 -0
  8. data/lib/openapi_kit/codegen/emit/codecs.rb +221 -0
  9. data/lib/openapi_kit/codegen/emit/controllers.rb +263 -0
  10. data/lib/openapi_kit/codegen/emit/decode.rb +43 -0
  11. data/lib/openapi_kit/codegen/emit/defaults.rb +42 -0
  12. data/lib/openapi_kit/codegen/emit/emitter.rb +17 -0
  13. data/lib/openapi_kit/codegen/emit/forms.rb +56 -0
  14. data/lib/openapi_kit/codegen/emit/handlers.rb +65 -0
  15. data/lib/openapi_kit/codegen/emit/literal.rb +28 -0
  16. data/lib/openapi_kit/codegen/emit/operations.rb +277 -0
  17. data/lib/openapi_kit/codegen/emit/registry.rb +101 -0
  18. data/lib/openapi_kit/codegen/emit/routes.rb +77 -0
  19. data/lib/openapi_kit/codegen/emit/security.rb +183 -0
  20. data/lib/openapi_kit/codegen/emit/source_file.rb +30 -0
  21. data/lib/openapi_kit/codegen/emit/types.rb +153 -0
  22. data/lib/openapi_kit/codegen/generator.rb +48 -0
  23. data/lib/openapi_kit/codegen/loader.rb +730 -0
  24. data/lib/openapi_kit/codegen/model/document.rb +302 -0
  25. data/lib/openapi_kit/codegen/model/schema.rb +110 -0
  26. data/lib/openapi_kit/codegen/model/type_def.rb +89 -0
  27. data/lib/openapi_kit/codegen/naming.rb +78 -0
  28. data/lib/openapi_kit/codegen/ruby_type.rb +48 -0
  29. data/lib/openapi_kit/codegen/type_registry.rb +325 -0
  30. data/lib/openapi_kit/codegen/writer.rb +92 -0
  31. data/lib/openapi_kit/codegen.rb +37 -0
  32. data/lib/openapi_kit-codegen.rb +4 -0
  33. metadata +118 -0
@@ -0,0 +1,325 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module OpenAPIKit
5
+ module Codegen
6
+ class TypeRegistry
7
+ extend T::Sig
8
+
9
+ DEFAULT_TYPE_MAPPINGS = T.let(
10
+ {
11
+ "string" => RubyType.new(type: "::String", codec: "::OpenAPIKit::Codec::String"),
12
+ "integer" => RubyType.new(type: "::Integer", codec: "::OpenAPIKit::Codec::Integer"),
13
+ "number" => RubyType.new(type: "::Float", codec: "::OpenAPIKit::Codec::Float"),
14
+ "boolean" => RubyType.new(type: "T::Boolean", codec: "::OpenAPIKit::Codec::Boolean"),
15
+
16
+ "string:date-time" => RubyType.new(type: "::Time", codec: "::OpenAPIKit::Codec::DateTime"),
17
+ "string:date" => RubyType.new(type: "::Date", codec: "::OpenAPIKit::Codec::Date"),
18
+ "string:uuid" => RubyType.new(type: "::String", codec: "::OpenAPIKit::Codec::Uuid"),
19
+ "string:byte" => RubyType.new(type: "::String", codec: "::OpenAPIKit::Codec::Byte"),
20
+ "string:decimal" => RubyType.new(type: "::BigDecimal", codec: "::OpenAPIKit::Codec::Decimal"),
21
+
22
+ "string:time" => RubyType.new(type: "::String", codec: "::OpenAPIKit::Codec::String"),
23
+ "string:duration" => RubyType.new(type: "::String", codec: "::OpenAPIKit::Codec::String"),
24
+ "string:email" => RubyType.new(type: "::String", codec: "::OpenAPIKit::Codec::String"),
25
+ "string:idn-email" => RubyType.new(type: "::String", codec: "::OpenAPIKit::Codec::String"),
26
+ "string:hostname" => RubyType.new(type: "::String", codec: "::OpenAPIKit::Codec::String"),
27
+ "string:idn-hostname" => RubyType.new(type: "::String", codec: "::OpenAPIKit::Codec::String"),
28
+ "string:ipv4" => RubyType.new(type: "::String", codec: "::OpenAPIKit::Codec::String"),
29
+ "string:ipv6" => RubyType.new(type: "::String", codec: "::OpenAPIKit::Codec::String"),
30
+ "string:uri" => RubyType.new(type: "::String", codec: "::OpenAPIKit::Codec::String"),
31
+ "string:uri-reference" => RubyType.new(type: "::String", codec: "::OpenAPIKit::Codec::String"),
32
+ "string:uri-template" => RubyType.new(type: "::String", codec: "::OpenAPIKit::Codec::String"),
33
+ "string:iri" => RubyType.new(type: "::String", codec: "::OpenAPIKit::Codec::String"),
34
+ "string:json-pointer" => RubyType.new(type: "::String", codec: "::OpenAPIKit::Codec::String"),
35
+ "string:relative-json-pointer" => RubyType.new(type: "::String", codec: "::OpenAPIKit::Codec::String"),
36
+ "string:regex" => RubyType.new(type: "::String", codec: "::OpenAPIKit::Codec::String"),
37
+ "string:password" => RubyType.new(type: "::String", codec: "::OpenAPIKit::Codec::String"),
38
+
39
+ "integer:int32" => RubyType.new(type: "::Integer", codec: "::OpenAPIKit::Codec::Integer"),
40
+ "integer:int64" => RubyType.new(type: "::Integer", codec: "::OpenAPIKit::Codec::Integer"),
41
+
42
+ "number:float" => RubyType.new(type: "::Float", codec: "::OpenAPIKit::Codec::Float"),
43
+ "number:double" => RubyType.new(type: "::Float", codec: "::OpenAPIKit::Codec::Float"),
44
+ "number:decimal" => RubyType.new(type: "::BigDecimal", codec: "::OpenAPIKit::Codec::Decimal")
45
+ }.freeze,
46
+ T::Hash[String, RubyType]
47
+ )
48
+
49
+ sig { params(document: Model::Document, config: Config).returns(TypeRegistry) }
50
+ def self.for(document, config)
51
+ reject_binary_mapping!(config.type_mappings)
52
+
53
+ registry = new(
54
+ namespace: config.namespace,
55
+ type_mappings: DEFAULT_TYPE_MAPPINGS.merge(config.type_mappings),
56
+ types: document.types.to_h { |type| [Model::TypeDef.name_of(type), type] }
57
+ )
58
+
59
+ registry.reject_alias_cycles!
60
+ registry
61
+ end
62
+
63
+ sig { params(mappings: T::Hash[String, RubyType]).void }
64
+ def self.reject_binary_mapping!(mappings)
65
+ return unless mappings.key?("string:binary")
66
+
67
+ raise ConfigError,
68
+ "type_mappings has an entry for \"string:binary\", but openapi_kit does not convert a " \
69
+ "file with a codec: it has no wire form. An upload decodes to " \
70
+ "::ActionDispatch::Http::UploadedFile and a binary response body is a stream, " \
71
+ "neither through a codec. Convert to your own type in the handler, or use " \
72
+ "format: byte to carry bytes inside a value."
73
+ end
74
+
75
+ # An alias contributes no constant of its own: every reference to it expands to its
76
+ # target. A loop of them therefore has nothing to expand to, and would otherwise
77
+ # recur until the stack ran out.
78
+ sig { void }
79
+ def reject_alias_cycles!
80
+ @types.values.grep(Model::AliasDef).sort_by(&:name).each do |type|
81
+ walk_aliases(type.target, [type.name])
82
+ end
83
+ end
84
+
85
+ sig { returns(T::Array[String]) }
86
+ def warnings = @warnings.to_a
87
+
88
+ sig do
89
+ params(namespace: String, type_mappings: T::Hash[String, RubyType],
90
+ types: T::Hash[String, Model::TypeDef]).void
91
+ end
92
+ def initialize(namespace:, type_mappings: DEFAULT_TYPE_MAPPINGS, types: {})
93
+ @namespace = namespace
94
+ @type_mappings = type_mappings
95
+ @types = types
96
+ @warnings = T.let(Set.new, T::Set[String])
97
+ end
98
+
99
+ UPLOADED_FILE = "::ActionDispatch::Http::UploadedFile"
100
+
101
+ BINARY = "::OpenAPIKit::Body::Binary"
102
+
103
+ sig { params(schema: Model::Schema, value: String).returns(T.nilable(String)) }
104
+ def from_form_expr(schema, value:)
105
+ return nil unless schema.is_a?(Model::Ref) && @types[schema.name].is_a?(Model::FormDef)
106
+
107
+ "#{@namespace}::Types::#{schema.name}::Form.from_parts(#{value})"
108
+ end
109
+
110
+ sig { params(schema: Model::Schema).returns(String) }
111
+ def sorbet_type(schema)
112
+ case schema
113
+ when Model::Ref
114
+ aliased = alias_target(schema)
115
+ return sorbet_type(aliased) if aliased
116
+
117
+ union?(schema) ? "#{@namespace}::Types::#{schema.name}::Value" : "#{@namespace}::Types::#{schema.name}"
118
+ when Model::List then "T::Array[#{sorbet_type(schema.items)}]"
119
+ when Model::Freeform
120
+ values = schema.values
121
+ "T::Hash[::String, #{values ? sorbet_type(values) : "T.untyped"}]"
122
+ when Model::Untyped then "T.untyped"
123
+ when Model::StringSchema, Model::IntegerSchema, Model::NumberSchema, Model::BooleanSchema
124
+ ruby_type_for(schema).type
125
+ else T.absurd(schema)
126
+ end
127
+ end
128
+
129
+ sig { params(schema: Model::Schema, value: String).returns(String) }
130
+ def from_wire_expr(schema, value:)
131
+ case schema
132
+ when Model::Ref
133
+ aliased = alias_target(schema)
134
+ aliased ? from_wire_expr(aliased, value: value) : "#{codec_for(schema)}.from_wire(#{value})"
135
+ when Model::List
136
+ "OpenAPIKit::Decode.each(#{value}) { |item| #{from_wire_expr(schema.items, value: "item")} }"
137
+ when Model::Freeform
138
+ values = schema.values
139
+ return "OpenAPIKit::Decode.object(#{value})" if values.nil?
140
+
141
+ "OpenAPIKit::Decode.values(#{value}) { |item| #{from_wire_expr(values, value: "item")} }"
142
+ when Model::Untyped then value
143
+ when Model::StringSchema, Model::IntegerSchema, Model::NumberSchema, Model::BooleanSchema
144
+ "#{ruby_type_for(schema).codec}.from_wire(#{value})"
145
+ else T.absurd(schema)
146
+ end
147
+ end
148
+
149
+ NATIVE_LITERALS = T.let(
150
+ {
151
+ "::String" => [::String],
152
+ "::Integer" => [::Integer],
153
+ "::Float" => [::Float],
154
+ "T::Boolean" => [::TrueClass, ::FalseClass]
155
+ }.freeze,
156
+ T::Hash[String, T::Array[T::Class[T.anything]]]
157
+ )
158
+
159
+ sig { params(schema: Model::Schema, value: T.untyped).returns(T::Boolean) }
160
+ def literal_matches_type?(schema, value)
161
+ NATIVE_LITERALS.fetch(sorbet_type(schema), []).any? { |native| value.is_a?(native) }
162
+ end
163
+
164
+ sig { params(name: String).returns(String) }
165
+ def codec_reference(name) = "#{@namespace}::Types::#{name}::Codec"
166
+
167
+ # Rails only hands back request_parameters verbatim for a JSON object, and wraps
168
+ # anything else under "_json", so the emitters need to know which shape a body
169
+ # will arrive in.
170
+ sig { params(schema: Model::Schema).returns(T::Boolean) }
171
+ def object?(schema)
172
+ case schema
173
+ when Model::Ref then referent_object?(schema)
174
+ when Model::Freeform, Model::Untyped then true
175
+ when Model::List, Model::StringSchema, Model::IntegerSchema, Model::NumberSchema,
176
+ Model::BooleanSchema
177
+ false
178
+ else T.absurd(schema)
179
+ end
180
+ end
181
+
182
+ sig { params(schema: Model::Schema, value: String).returns(String) }
183
+ def to_wire_expr(schema, value:)
184
+ case schema
185
+ when Model::Ref
186
+ aliased = alias_target(schema)
187
+ aliased ? to_wire_expr(aliased, value: value) : "#{codec_for(schema)}.to_wire(#{value})"
188
+ when Model::List
189
+ inner = to_wire_expr(schema.items, value: "item")
190
+ inner == "item" ? value : "#{value}.map { |item| #{inner} }"
191
+ when Model::Freeform
192
+ values = schema.values
193
+ return value if values.nil?
194
+
195
+ inner = to_wire_expr(values, value: "item")
196
+ inner == "item" ? value : "#{value}.transform_values { |item| #{inner} }"
197
+ when Model::Untyped then value
198
+ when Model::StringSchema, Model::IntegerSchema, Model::NumberSchema, Model::BooleanSchema
199
+ "#{ruby_type_for(schema).codec}.to_wire(#{value})"
200
+ else T.absurd(schema)
201
+ end
202
+ end
203
+
204
+ private
205
+
206
+ sig { params(schema: Model::Schema).void }
207
+ def reject_binary!(schema)
208
+ return unless Model::Schema.file?(schema)
209
+
210
+ raise SchemaError,
211
+ "format: binary has no JSON type, so it cannot be converted by a codec. It is " \
212
+ "only valid as a top-level property of a multipart/form-data request body, or " \
213
+ "as the whole schema of a response body."
214
+ end
215
+
216
+ sig { params(schema: T.nilable(Model::Schema), seen: T::Array[String]).void }
217
+ def walk_aliases(schema, seen)
218
+ case schema
219
+ when Model::Ref
220
+ found = @types[schema.name]
221
+ return unless found.is_a?(Model::AliasDef)
222
+
223
+ if seen.include?(schema.name)
224
+ raise SchemaError,
225
+ "#{schema.name} is defined in terms of itself, through " \
226
+ "#{seen.join(" -> ")} -> #{schema.name}. A schema that is only an alias for " \
227
+ "another cannot form a cycle: give one of them properties, or break the loop."
228
+ end
229
+
230
+ walk_aliases(found.target, seen + [schema.name])
231
+ when Model::List then walk_aliases(schema.items, seen)
232
+ when Model::Freeform then walk_aliases(schema.values, seen)
233
+ end
234
+ end
235
+
236
+ sig { params(schema: Model::Ref).returns(String) }
237
+ def codec_for(schema) = codec_reference(schema.name)
238
+
239
+ sig { params(schema: Model::Ref).returns(T::Boolean) }
240
+ def union?(schema) = @types[schema.name].is_a?(Model::UnionDef)
241
+
242
+ sig { params(schema: Model::Ref).returns(T::Boolean) }
243
+ def referent_object?(schema)
244
+ found = @types[schema.name]
245
+ return true if found.nil?
246
+
247
+ case found
248
+ when Model::ObjectDef, Model::FormDef then true
249
+ when Model::EnumDef then false
250
+ when Model::UnionDef then found.members.all? { |member| object?(member) }
251
+ when Model::AliasDef then object?(found.target)
252
+ else T.absurd(found)
253
+ end
254
+ end
255
+
256
+ sig { params(schema: Model::Ref).returns(T.nilable(Model::Schema)) }
257
+ def alias_target(schema)
258
+ found = @types[schema.name]
259
+ found.is_a?(Model::AliasDef) ? found.target : nil
260
+ end
261
+
262
+ sig { params(schema: Model::Schema).returns(RubyType) }
263
+ def ruby_type_for(schema)
264
+ override = Model::Schema.meta(schema).ruby_type
265
+ return override if override
266
+
267
+ reject_binary!(schema)
268
+
269
+ type, format = scalar_kind(schema)
270
+ requested = format ? "#{type}:#{format}" : type
271
+
272
+ candidate_keys(type, format).each_with_index do |key, index|
273
+ mapping = @type_mappings[key]
274
+ next if mapping.nil?
275
+
276
+ warn_about_unrecognised_format(requested, key) if index.positive?
277
+ return mapping
278
+ end
279
+
280
+ raise SchemaError, unmapped_message(requested)
281
+ end
282
+
283
+ sig { params(type: String, format: T.nilable(String)).returns(T::Array[String]) }
284
+ def candidate_keys(type, format) = format ? ["#{type}:#{format}", type] : [type]
285
+
286
+ sig { params(schema: Model::Schema).returns([String, T.nilable(String)]) }
287
+ def scalar_kind(schema)
288
+ case schema
289
+ when Model::StringSchema then ["string", schema.format]
290
+ when Model::IntegerSchema then ["integer", schema.format]
291
+ when Model::NumberSchema then ["number", schema.format]
292
+ when Model::BooleanSchema then ["boolean", nil]
293
+ else raise SchemaError, "#{schema.class} is not a scalar"
294
+ end
295
+ end
296
+
297
+ sig { params(requested: String, used: String).void }
298
+ def warn_about_unrecognised_format(requested, used)
299
+ fallback = T.must(@type_mappings[used])
300
+ message = <<~MESSAGE.strip
301
+ #{requested.inspect} has no Ruby type mapped, so it is treated as #{used.inspect}
302
+ (#{fallback.type}). If that is wrong, map it:
303
+
304
+ #{mapping_snippet(requested)}
305
+ MESSAGE
306
+ @warnings << message
307
+ end
308
+
309
+ sig { params(key: String).returns(String) }
310
+ def unmapped_message(key)
311
+ "No Ruby type is mapped for #{key.inspect}. Add one to your config:\n\n#{mapping_snippet(key)}"
312
+ end
313
+
314
+ sig { params(key: String).returns(String) }
315
+ def mapping_snippet(key)
316
+ <<~SNIPPET.strip
317
+ type_mappings:
318
+ #{key.inspect}:
319
+ type: "::YourType"
320
+ codec: "YourApp::YourTypeCodec" # extends or includes OpenAPIKit::Codec
321
+ SNIPPET
322
+ end
323
+ end
324
+ end
325
+ end
@@ -0,0 +1,92 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "fileutils"
5
+ require "pathname"
6
+ require "prism"
7
+
8
+ module OpenAPIKit
9
+ module Codegen
10
+ class Writer
11
+ extend T::Sig
12
+
13
+ sig { params(output: Pathname).void }
14
+ def initialize(output:)
15
+ @output = output
16
+ end
17
+
18
+ sig { void }
19
+ def clean!
20
+ return unless output.directory?
21
+
22
+ existing = output.glob("**/*.rb").sort
23
+ foreign = existing.reject { |path| path.read.start_with?(MARKER) }
24
+ unless foreign.empty?
25
+ raise Error,
26
+ "Refusing to clean #{output}: #{foreign.size} file#{"s" if foreign.size > 1} " \
27
+ "not generated by openapi_kit " \
28
+ "(#{foreign.first(3).map { |f| f.relative_path_from(output) }.join(", ")}" \
29
+ "#{"…" if foreign.size > 3}). Point `output` at a directory openapi_kit owns."
30
+ end
31
+
32
+ existing.each(&:delete)
33
+ prune_empty_directories
34
+ end
35
+
36
+ sig { params(files: T::Array[Emit::SourceFile]).returns(T::Array[Pathname]) }
37
+ def write_all(files)
38
+ files.each { |file| verify_syntax!(file.path, file.contents) }
39
+ reject_duplicate_paths!(files)
40
+
41
+ clean!
42
+ files.map { |file| write(file.path, file.contents) }
43
+ end
44
+
45
+ sig { params(name: String, content: String).returns(Pathname) }
46
+ def write(name, content)
47
+ verify_syntax!(name, content)
48
+ path = output.join(name)
49
+ FileUtils.mkdir_p(path.dirname)
50
+ body = content.end_with?("\n") ? content : "#{content}\n"
51
+ path.write("#{MARKER}\n#{body}")
52
+ path
53
+ end
54
+
55
+ private
56
+
57
+ sig { returns(Pathname) }
58
+ attr_reader :output
59
+
60
+ sig { params(files: T::Array[Emit::SourceFile]).void }
61
+ def reject_duplicate_paths!(files)
62
+ duplicates = files.group_by(&:path).select { |_, group| group.size > 1 }
63
+ return if duplicates.empty?
64
+
65
+ raise Error,
66
+ "openapi_kit generated more than one file for #{duplicates.keys.sort.join(", ")}. " \
67
+ "Two names in your document normalise to the same constant. " \
68
+ "Disambiguate them, or set name_overrides in your config."
69
+ end
70
+
71
+ sig { void }
72
+ def prune_empty_directories
73
+ output.glob("**/*").select(&:directory?).sort.reverse_each do |directory|
74
+ directory.rmdir if directory.children.empty?
75
+ end
76
+ end
77
+
78
+ sig { params(name: String, content: String).void }
79
+ def verify_syntax!(name, content)
80
+ result = Prism.parse(content)
81
+ return if result.success?
82
+
83
+ error = T.must(result.errors.first)
84
+ line = error.location.start_line
85
+ raise Error,
86
+ "openapi_kit generated invalid Ruby in #{name} at line #{line}: #{error.message}\n " \
87
+ "#{content.lines[line - 1]&.strip}\n" \
88
+ "This is a bug in openapi_kit, not in your schema. Please report it."
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,37 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "openapi_kit/runtime"
5
+
6
+ require "openapi_kit/codegen/naming"
7
+ require "openapi_kit/codegen/ruby_type"
8
+ require "openapi_kit/codegen/model/schema"
9
+ require "openapi_kit/codegen/model/type_def"
10
+ require "openapi_kit/codegen/model/document"
11
+ require "openapi_kit/codegen/type_registry"
12
+ require "openapi_kit/codegen/config"
13
+ require "openapi_kit/codegen/writer"
14
+ require "openapi_kit/codegen/loader"
15
+ require "openapi_kit/codegen/emit/buffer"
16
+ require "openapi_kit/codegen/emit/source_file"
17
+ require "openapi_kit/codegen/emit/emitter"
18
+ require "openapi_kit/codegen/emit/literal"
19
+ require "openapi_kit/codegen/emit/defaults"
20
+ require "openapi_kit/codegen/emit/decode"
21
+ require "openapi_kit/codegen/emit/types"
22
+ require "openapi_kit/codegen/emit/codecs"
23
+ require "openapi_kit/codegen/emit/forms"
24
+ require "openapi_kit/codegen/emit/operations"
25
+ require "openapi_kit/codegen/emit/handlers"
26
+ require "openapi_kit/codegen/emit/security"
27
+ require "openapi_kit/codegen/emit/registry"
28
+ require "openapi_kit/codegen/emit/routes"
29
+ require "openapi_kit/codegen/emit/controllers"
30
+ require "openapi_kit/codegen/generator"
31
+ require "openapi_kit/codegen/cli"
32
+
33
+ module OpenAPIKit
34
+ module Codegen
35
+ MARKER = "# Generated by openapi_kit. Do not edit. https://github.com/Nexus-Mods/openapi_kit"
36
+ end
37
+ end
@@ -0,0 +1,4 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "openapi_kit/codegen"
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: openapi_kit-codegen
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.pre.1
5
+ platform: ruby
6
+ authors:
7
+ - Jack Robertson
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: openapi3_parser
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: 0.10.0
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: 0.10.0
26
+ - !ruby/object:Gem::Dependency
27
+ name: openapi_kit
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - '='
31
+ - !ruby/object:Gem::Version
32
+ version: 0.1.0.pre.1
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - '='
38
+ - !ruby/object:Gem::Version
39
+ version: 0.1.0.pre.1
40
+ - !ruby/object:Gem::Dependency
41
+ name: prism
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '1.0'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '1.0'
54
+ description: A spec-first code generator producing strict Sorbet interfaces bound
55
+ to implementations via dry-container, with sealed response types and an extensible
56
+ type registry.
57
+ email:
58
+ - jack.robertson@nexusmods.com
59
+ executables:
60
+ - openapi_kit
61
+ extensions: []
62
+ extra_rdoc_files: []
63
+ files:
64
+ - LICENSE.txt
65
+ - README.md
66
+ - exe/openapi_kit
67
+ - lib/openapi_kit-codegen.rb
68
+ - lib/openapi_kit/codegen.rb
69
+ - lib/openapi_kit/codegen/cli.rb
70
+ - lib/openapi_kit/codegen/config.rb
71
+ - lib/openapi_kit/codegen/emit/buffer.rb
72
+ - lib/openapi_kit/codegen/emit/codecs.rb
73
+ - lib/openapi_kit/codegen/emit/controllers.rb
74
+ - lib/openapi_kit/codegen/emit/decode.rb
75
+ - lib/openapi_kit/codegen/emit/defaults.rb
76
+ - lib/openapi_kit/codegen/emit/emitter.rb
77
+ - lib/openapi_kit/codegen/emit/forms.rb
78
+ - lib/openapi_kit/codegen/emit/handlers.rb
79
+ - lib/openapi_kit/codegen/emit/literal.rb
80
+ - lib/openapi_kit/codegen/emit/operations.rb
81
+ - lib/openapi_kit/codegen/emit/registry.rb
82
+ - lib/openapi_kit/codegen/emit/routes.rb
83
+ - lib/openapi_kit/codegen/emit/security.rb
84
+ - lib/openapi_kit/codegen/emit/source_file.rb
85
+ - lib/openapi_kit/codegen/emit/types.rb
86
+ - lib/openapi_kit/codegen/generator.rb
87
+ - lib/openapi_kit/codegen/loader.rb
88
+ - lib/openapi_kit/codegen/model/document.rb
89
+ - lib/openapi_kit/codegen/model/schema.rb
90
+ - lib/openapi_kit/codegen/model/type_def.rb
91
+ - lib/openapi_kit/codegen/naming.rb
92
+ - lib/openapi_kit/codegen/ruby_type.rb
93
+ - lib/openapi_kit/codegen/type_registry.rb
94
+ - lib/openapi_kit/codegen/writer.rb
95
+ homepage: https://github.com/Nexus-Mods/openapi_kit
96
+ licenses:
97
+ - MIT
98
+ metadata:
99
+ rubygems_mfa_required: 'true'
100
+ source_code_uri: https://github.com/Nexus-Mods/openapi_kit
101
+ rdoc_options: []
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '3.4'
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ requirements: []
115
+ rubygems_version: 3.6.9
116
+ specification_version: 4
117
+ summary: Generate Sorbet-typed Rails server stubs from an OpenAPI 3 document.
118
+ test_files: []