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.
@@ -1,236 +1,3 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'set'
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