apigen 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -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