apigen 0.0.6 → 0.0.7
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/apigen/formats/json_base.rb +109 -0
- data/lib/apigen/formats/jsonschema.rb +11 -59
- data/lib/apigen/formats/openapi.rb +10 -64
- data/lib/apigen/formats/swagger.rb +10 -60
- data/lib/apigen/migration.rb +55 -0
- data/lib/apigen/model.rb +1 -234
- data/lib/apigen/models/array_type.rb +34 -0
- data/lib/apigen/models/enum_type.rb +25 -0
- data/lib/apigen/models/model.rb +78 -0
- data/lib/apigen/models/object_property.rb +39 -0
- data/lib/apigen/models/object_type.rb +77 -0
- data/lib/apigen/models/oneof_type.rb +33 -0
- data/lib/apigen/models/primary_type.rb +33 -0
- data/lib/apigen/models/reference_type.rb +27 -0
- data/lib/apigen/models/registry.rb +39 -0
- data/lib/apigen/rest.rb +1 -269
- data/lib/apigen/rest/api.rb +73 -0
- data/lib/apigen/rest/endpoint.rb +160 -0
- data/lib/apigen/rest/input.rb +39 -0
- data/lib/apigen/rest/output.rb +45 -0
- metadata +16 -1
data/lib/apigen/model.rb
CHANGED
@@ -1,236 +1,3 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
require_relative './util'
|
5
|
-
|
6
|
-
module Apigen
|
7
|
-
PRIMARY_TYPES = Set.new %i[string int32 bool void]
|
8
|
-
|
9
|
-
##
|
10
|
-
# ModelRegistry is where all model definitions are stored.
|
11
|
-
class ModelRegistry
|
12
|
-
attr_reader :models
|
13
|
-
|
14
|
-
def initialize
|
15
|
-
@models = {}
|
16
|
-
end
|
17
|
-
|
18
|
-
def model(name, &block)
|
19
|
-
model = Apigen::Model.new name
|
20
|
-
raise 'You must pass a block when calling `model`.' unless block_given?
|
21
|
-
model.instance_eval(&block)
|
22
|
-
@models[model.name] = model
|
23
|
-
end
|
24
|
-
|
25
|
-
def validate
|
26
|
-
@models.each do |_key, model|
|
27
|
-
model.validate self
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def check_type(type)
|
32
|
-
if type.is_a? Symbol
|
33
|
-
raise "Unknown type :#{type}." unless @models.key?(type) || PRIMARY_TYPES.include?(type)
|
34
|
-
elsif type.is_a?(ObjectType) || type.is_a?(ArrayType) || type.is_a?(OptionalType)
|
35
|
-
type.validate self
|
36
|
-
else
|
37
|
-
raise "Cannot process type #{type.class.name}"
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def to_s
|
42
|
-
@models.map do |key, model|
|
43
|
-
"#{key}: #{model}"
|
44
|
-
end.join "\n"
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
##
|
49
|
-
# Model represents a data model with a specific name, e.g. "User" with an object type.
|
50
|
-
class Model
|
51
|
-
attr_reader :name
|
52
|
-
attribute_setter_getter :description
|
53
|
-
attribute_setter_getter :example
|
54
|
-
|
55
|
-
def initialize(name)
|
56
|
-
@name = name
|
57
|
-
@type = nil
|
58
|
-
@description = nil
|
59
|
-
end
|
60
|
-
|
61
|
-
def type(shape = nil, &block)
|
62
|
-
return @type unless shape
|
63
|
-
@type = Model.type shape, &block
|
64
|
-
end
|
65
|
-
|
66
|
-
def self.type(shape, &block)
|
67
|
-
if shape.to_s.end_with? '?'
|
68
|
-
shape = shape[0..-2].to_sym
|
69
|
-
optional = true
|
70
|
-
else
|
71
|
-
optional = false
|
72
|
-
end
|
73
|
-
case shape
|
74
|
-
when :object
|
75
|
-
object = ObjectType.new
|
76
|
-
object.instance_eval(&block)
|
77
|
-
type = object
|
78
|
-
when :array
|
79
|
-
array = ArrayType.new
|
80
|
-
array.instance_eval(&block)
|
81
|
-
type = array
|
82
|
-
when :optional
|
83
|
-
optional = OptionalType.new
|
84
|
-
optional.instance_eval(&block)
|
85
|
-
type = optional
|
86
|
-
else
|
87
|
-
type = shape
|
88
|
-
end
|
89
|
-
optional ? OptionalType.new(type) : type
|
90
|
-
end
|
91
|
-
|
92
|
-
def validate(model_registry)
|
93
|
-
raise 'One of the models is missing a name.' unless @name
|
94
|
-
raise "Use `type :model_type [block]` to assign a type to :#{@name}." unless @type
|
95
|
-
model_registry.check_type @type
|
96
|
-
end
|
97
|
-
|
98
|
-
def to_s
|
99
|
-
@type.to_s
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
##
|
104
|
-
# ObjectType represents an object type, with specific fields.
|
105
|
-
class ObjectType
|
106
|
-
attr_reader :properties
|
107
|
-
|
108
|
-
def initialize
|
109
|
-
@properties = {}
|
110
|
-
end
|
111
|
-
|
112
|
-
# rubocop:disable Style/MethodMissingSuper
|
113
|
-
def method_missing(field_name, *args, &block)
|
114
|
-
raise "Field :#{field_name} is defined multiple times." if @properties.key? field_name
|
115
|
-
field_type = args[0]
|
116
|
-
field_description = args[1]
|
117
|
-
block_called = false
|
118
|
-
if block_given?
|
119
|
-
block_wrapper = lambda do
|
120
|
-
block_called = true
|
121
|
-
yield
|
122
|
-
end
|
123
|
-
end
|
124
|
-
property = ObjectProperty.new(
|
125
|
-
Apigen::Model.type(field_type, &block_wrapper),
|
126
|
-
field_description
|
127
|
-
)
|
128
|
-
property.instance_eval(&block) if block_given? && !block_called
|
129
|
-
@properties[field_name] = property
|
130
|
-
end
|
131
|
-
# rubocop:enable Style/MethodMissingSuper
|
132
|
-
|
133
|
-
def respond_to_missing?(_method_name, _include_private = false)
|
134
|
-
true
|
135
|
-
end
|
136
|
-
|
137
|
-
def validate(model_registry)
|
138
|
-
@properties.each do |_key, property|
|
139
|
-
model_registry.check_type property.type
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
def to_s
|
144
|
-
repr ''
|
145
|
-
end
|
146
|
-
|
147
|
-
def repr(indent)
|
148
|
-
repr = '{'
|
149
|
-
@properties.each do |key, property|
|
150
|
-
type_repr = if property.type.respond_to? :repr
|
151
|
-
property.type.repr(indent + ' ')
|
152
|
-
else
|
153
|
-
property.type.to_s
|
154
|
-
end
|
155
|
-
repr += "\n#{indent} #{key}: #{type_repr}"
|
156
|
-
end
|
157
|
-
repr += "\n#{indent}}"
|
158
|
-
repr
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
##
|
163
|
-
# ObjectProperty is a specific property in an ObjectType.
|
164
|
-
class ObjectProperty
|
165
|
-
attr_reader :type
|
166
|
-
attribute_setter_getter :description
|
167
|
-
attribute_setter_getter :example
|
168
|
-
|
169
|
-
def initialize(type, description = nil)
|
170
|
-
@type = type
|
171
|
-
@description = description
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
##
|
176
|
-
# ArrayType represents an array type, with a given item type.
|
177
|
-
class ArrayType
|
178
|
-
def initialize(type = nil)
|
179
|
-
@type = type
|
180
|
-
end
|
181
|
-
|
182
|
-
def type(item_type = nil, &block)
|
183
|
-
return @type unless item_type
|
184
|
-
@type = Apigen::Model.type item_type, &block
|
185
|
-
end
|
186
|
-
|
187
|
-
def validate(model_registry)
|
188
|
-
raise 'Use `type [typename]` to specify the type of types in an array.' unless @type
|
189
|
-
model_registry.check_type @type
|
190
|
-
end
|
191
|
-
|
192
|
-
def to_s
|
193
|
-
repr ''
|
194
|
-
end
|
195
|
-
|
196
|
-
def repr(indent)
|
197
|
-
type_repr = if @type.respond_to? :repr
|
198
|
-
@type.repr indent
|
199
|
-
else
|
200
|
-
@type.to_s
|
201
|
-
end
|
202
|
-
"ArrayType<#{type_repr}>"
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
##
|
207
|
-
# OptionalType represents a type whose value may be absent.
|
208
|
-
class OptionalType
|
209
|
-
def initialize(type = nil)
|
210
|
-
@type = type
|
211
|
-
end
|
212
|
-
|
213
|
-
def type(item_type = nil, &block)
|
214
|
-
return @type unless item_type
|
215
|
-
@type = Apigen::Model.type item_type, &block
|
216
|
-
end
|
217
|
-
|
218
|
-
def validate(model_registry)
|
219
|
-
raise 'Use `type [typename]` to specify an optional type.' unless @type
|
220
|
-
model_registry.check_type @type
|
221
|
-
end
|
222
|
-
|
223
|
-
def to_s
|
224
|
-
repr ''
|
225
|
-
end
|
226
|
-
|
227
|
-
def repr(indent)
|
228
|
-
type_repr = if @type.respond_to? :repr
|
229
|
-
@type.repr indent
|
230
|
-
else
|
231
|
-
@type.to_s
|
232
|
-
end
|
233
|
-
"OptionalType<#{type_repr}>"
|
234
|
-
end
|
235
|
-
end
|
236
|
-
end
|
3
|
+
require_relative './models/model'
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Apigen
|
4
|
+
##
|
5
|
+
# ArrayType represents an array type, with a given item type.
|
6
|
+
class ArrayType
|
7
|
+
def initialize(type = nil)
|
8
|
+
@type = type
|
9
|
+
end
|
10
|
+
|
11
|
+
def type(item_type = nil, &block)
|
12
|
+
return @type unless item_type
|
13
|
+
@type = Apigen::Model.type item_type, &block
|
14
|
+
end
|
15
|
+
|
16
|
+
def validate(model_registry)
|
17
|
+
raise 'Use `type [typename]` to specify the type of items in an array.' unless @type
|
18
|
+
model_registry.check_type @type
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
repr ''
|
23
|
+
end
|
24
|
+
|
25
|
+
def repr(indent)
|
26
|
+
type_repr = if @type.respond_to? :repr
|
27
|
+
@type.repr indent
|
28
|
+
else
|
29
|
+
@type.to_s
|
30
|
+
end
|
31
|
+
"ArrayType<#{type_repr}>"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../util'
|
4
|
+
|
5
|
+
module Apigen
|
6
|
+
##
|
7
|
+
# EnumType represents an enum (a type that can be one of several constants).
|
8
|
+
class EnumType
|
9
|
+
attr_reader :values
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@values = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def value(val)
|
16
|
+
@values << val
|
17
|
+
end
|
18
|
+
|
19
|
+
def validate(_model_registry)
|
20
|
+
@values.each do |val|
|
21
|
+
raise 'Enums only support string values' unless val.is_a? String
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../util'
|
4
|
+
require_relative './array_type'
|
5
|
+
require_relative './enum_type'
|
6
|
+
require_relative './object_type'
|
7
|
+
require_relative './oneof_type'
|
8
|
+
require_relative './primary_type'
|
9
|
+
require_relative './reference_type'
|
10
|
+
require_relative './registry'
|
11
|
+
|
12
|
+
module Apigen
|
13
|
+
##
|
14
|
+
# Model represents a data model with a specific name, e.g. "User" with an object type.
|
15
|
+
class Model
|
16
|
+
attr_reader :name
|
17
|
+
attribute_setter_getter :description
|
18
|
+
attribute_setter_getter :example
|
19
|
+
|
20
|
+
def initialize(name)
|
21
|
+
@name = name
|
22
|
+
@type = nil
|
23
|
+
@description = nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def type(shape = nil, &block)
|
27
|
+
return @type unless shape
|
28
|
+
@type = Model.type shape, &block
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.type(shape, &block)
|
32
|
+
case shape
|
33
|
+
when :object
|
34
|
+
object = ObjectType.new
|
35
|
+
object.instance_eval(&block)
|
36
|
+
object
|
37
|
+
when :array
|
38
|
+
array = ArrayType.new
|
39
|
+
array.instance_eval(&block)
|
40
|
+
array
|
41
|
+
when :oneof
|
42
|
+
oneof = OneofType.new
|
43
|
+
oneof.instance_eval(&block)
|
44
|
+
oneof
|
45
|
+
when :enum
|
46
|
+
enum = EnumType.new
|
47
|
+
enum.instance_eval(&block)
|
48
|
+
enum
|
49
|
+
else
|
50
|
+
raise "A block should not be provided with :#{shape}." if block_given?
|
51
|
+
primary_or_reference_type(shape)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private_class_method def self.primary_or_reference_type(shape)
|
56
|
+
if PrimaryType.primary?(shape)
|
57
|
+
PrimaryType.new(shape)
|
58
|
+
else
|
59
|
+
ReferenceType.new(shape)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def validate(model_registry)
|
64
|
+
raise 'One of the models is missing a name.' unless @name
|
65
|
+
raise "Use `type :model_type [block]` to assign a type to :#{@name}." unless @type
|
66
|
+
model_registry.check_type @type
|
67
|
+
end
|
68
|
+
|
69
|
+
def update_object_properties(&block)
|
70
|
+
raise "#{@name} is not an object type" unless @type.is_a? ObjectType
|
71
|
+
@type.instance_eval(&block)
|
72
|
+
end
|
73
|
+
|
74
|
+
def to_s
|
75
|
+
@type.to_s
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../util'
|
4
|
+
|
5
|
+
module Apigen
|
6
|
+
##
|
7
|
+
# ObjectProperty is a specific property in an ObjectType.
|
8
|
+
class ObjectProperty
|
9
|
+
attr_reader :type
|
10
|
+
attr_writer :required
|
11
|
+
attribute_setter_getter :description
|
12
|
+
attribute_setter_getter :example
|
13
|
+
|
14
|
+
def initialize(type, description = nil, example = nil)
|
15
|
+
@type = type
|
16
|
+
@description = description
|
17
|
+
@example = example
|
18
|
+
@required = true
|
19
|
+
end
|
20
|
+
|
21
|
+
def required(required)
|
22
|
+
@required = required
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def required?
|
27
|
+
@required
|
28
|
+
end
|
29
|
+
|
30
|
+
def explain(&block)
|
31
|
+
raise 'You must pass a block to `explain`.' unless block_given?
|
32
|
+
instance_eval(&block)
|
33
|
+
end
|
34
|
+
|
35
|
+
def ==(other)
|
36
|
+
other.is_a?(ObjectProperty) && type == other.type && required? == other.required? && description == other.description && example == other.example
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../util'
|
4
|
+
require_relative './object_property'
|
5
|
+
|
6
|
+
module Apigen
|
7
|
+
##
|
8
|
+
# ObjectType represents an object type, with specific properties.
|
9
|
+
class ObjectType
|
10
|
+
attr_reader :properties
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@properties = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def add(&block)
|
17
|
+
instance_eval(&block)
|
18
|
+
end
|
19
|
+
|
20
|
+
def remove(*property_names)
|
21
|
+
property_names.each do |property_name|
|
22
|
+
raise "Cannot remove nonexistent property :#{property_name}." unless @properties.delete(property_name)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# rubocop:disable Style/MethodMissingSuper
|
27
|
+
def method_missing(property_name, property_shape, &block)
|
28
|
+
raise "Property :#{property_name} is defined multiple times." if @properties.key? property_name
|
29
|
+
raise "Property type must be a symbol, found #{property_shape}." unless property_shape.is_a? Symbol
|
30
|
+
if property_shape.to_s.end_with? '?'
|
31
|
+
property_shape = property_shape[0..-2].to_sym
|
32
|
+
required = false
|
33
|
+
else
|
34
|
+
required = true
|
35
|
+
end
|
36
|
+
property = ObjectProperty.new(Apigen::Model.type(property_shape, &block))
|
37
|
+
property.required = required
|
38
|
+
@properties[property_name] = property
|
39
|
+
end
|
40
|
+
# rubocop:enable Style/MethodMissingSuper
|
41
|
+
|
42
|
+
def respond_to_missing?(_method_name, _include_private = false)
|
43
|
+
true
|
44
|
+
end
|
45
|
+
|
46
|
+
def validate(model_registry)
|
47
|
+
@properties.each do |_key, property|
|
48
|
+
model_registry.check_type property.type
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_s
|
53
|
+
repr ''
|
54
|
+
end
|
55
|
+
|
56
|
+
def repr(indent)
|
57
|
+
repr = '{'
|
58
|
+
@properties.each do |key, property|
|
59
|
+
repr += "\n#{indent} #{property_repr(indent, key, property)}"
|
60
|
+
end
|
61
|
+
repr += "\n#{indent}}"
|
62
|
+
repr
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def property_repr(indent, key, property)
|
68
|
+
type_repr = if property.type.respond_to? :repr
|
69
|
+
property.type.repr(indent + ' ')
|
70
|
+
else
|
71
|
+
property.type.to_s
|
72
|
+
end
|
73
|
+
type_repr += '?' unless property.required?
|
74
|
+
"#{key}: #{type_repr}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|