tiki 0.0.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.
@@ -0,0 +1,160 @@
1
+ require_relative './response'
2
+
3
+ describe do
4
+ let(:response) { Response.new 'My Response' }
5
+
6
+ describe 'a minimal response' do
7
+ it 'should return a correct spec' do
8
+ expect(response.to_spec).to eq({ description: 'My Response' })
9
+ end
10
+ end
11
+
12
+ describe 'content' do
13
+ describe 'default mime type' do
14
+ before do
15
+ response.content
16
+ end
17
+
18
+ it 'should return a correct spec' do
19
+ expect(response.to_spec).to eq(
20
+ {
21
+ description: 'My Response',
22
+ content: {
23
+ 'application/json' => {
24
+ schema: { type: :object }
25
+ }
26
+ }
27
+ }
28
+ )
29
+ end
30
+ end
31
+ end
32
+
33
+ describe 'a schema' do
34
+ describe 'a schema type' do
35
+ before do
36
+ response.schema :object
37
+ end
38
+
39
+ it 'should return a correct spec' do
40
+ expect(response.to_spec).to eq(
41
+ {
42
+ description: 'My Response',
43
+ content: {
44
+ 'application/json' => {
45
+ schema: {
46
+ type: :object
47
+ }
48
+ }
49
+ }
50
+ }
51
+ )
52
+ end
53
+ end
54
+
55
+ describe 'a ref' do
56
+ before do
57
+ response.schema ref: :Address
58
+ end
59
+
60
+ it 'should return a correct spec' do
61
+ expect(response.to_spec).to eq(
62
+ {
63
+ description: 'My Response',
64
+ content: {
65
+ 'application/json' => {
66
+ schema: {
67
+ :$ref => '#/components/schemas/Address'
68
+ }
69
+ }
70
+ }
71
+ }
72
+ )
73
+ end
74
+ end
75
+ end
76
+
77
+ describe 'an array' do
78
+ describe 'a items schema type' do
79
+ before do
80
+ response.array :string
81
+ end
82
+
83
+ it 'should return a correct spec' do
84
+ expect(response.to_spec).to eq(
85
+ {
86
+ description: 'My Response',
87
+ content: {
88
+ 'application/json' => {
89
+ schema: {
90
+ type: :array,
91
+ items: { type: :string }
92
+ }
93
+ }
94
+ }
95
+ }
96
+ )
97
+ end
98
+
99
+ describe 'a items ref' do
100
+ before do
101
+ response.array ref: :Address
102
+ end
103
+
104
+ it 'should return a correct spec' do
105
+ expect(response.to_spec).to eq(
106
+ {
107
+ description: 'My Response',
108
+ content: {
109
+ 'application/json' => {
110
+ schema: {
111
+ type: :array,
112
+ items: { :$ref => '#/components/schemas/Address' }
113
+ }
114
+ }
115
+ }
116
+ }
117
+ )
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ describe 'an object' do
124
+ before do
125
+ response.object
126
+ end
127
+
128
+ it 'should return a correct spec' do
129
+ expect(response.to_spec).to eq(
130
+ {
131
+ description: 'My Response',
132
+ content: {
133
+ 'application/json' => {
134
+ schema: { type: :object }
135
+ }
136
+ }
137
+ }
138
+ )
139
+ end
140
+ end
141
+
142
+ describe 'a ref' do
143
+ before do
144
+ response.ref :Address
145
+ end
146
+
147
+ it 'should return a correct spec' do
148
+ expect(response.to_spec).to eq(
149
+ {
150
+ description: 'My Response',
151
+ content: {
152
+ 'application/json' => {
153
+ schema: { :$ref => '#/components/schemas/Address' }
154
+ }
155
+ }
156
+ }
157
+ )
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,301 @@
1
+ require_relative './props'
2
+ require_relative './reference'
3
+ require_relative './list-helpers'
4
+
5
+ using Props
6
+
7
+ PRIMITIVES = %i[string integer number boolean].freeze
8
+ DATA_TYPES = PRIMITIVES + %i[object array]
9
+
10
+ FORMATS = [
11
+ [:integer, %i[int32 int64]],
12
+ [:number, %i[float double]],
13
+ [:string, %i[byte binary date date_time datetime password], { date_time: 'date-time', datetime: 'date-time' }]
14
+
15
+ ].freeze
16
+
17
+ NUMBER_PROPS = %i[
18
+ minimum
19
+ maximum
20
+ exclusive_minimum
21
+ exclusive_maximum
22
+ multiple_of
23
+ ].freeze
24
+
25
+ STRING_PROPS = %i[
26
+ min_length
27
+ max_length
28
+ pattern
29
+ ].freeze
30
+
31
+ OBJECT_PROPS = %i[
32
+ min_properties
33
+ max_properties
34
+ ].freeze
35
+
36
+ MARKER_PROPS = %i[
37
+ unique_items
38
+ nullable
39
+ read_only
40
+ write_only
41
+ ].freeze
42
+
43
+ class Schema
44
+ props :title, :description, :format, NUMBER_PROPS, STRING_PROPS
45
+ marker_props MARKER_PROPS
46
+ named_props :format, MARKER_PROPS, NUMBER_PROPS, STRING_PROPS, OBJECT_PROPS
47
+ scalar_props :type, :title, :description, :format, :enum, MARKER_PROPS, NUMBER_PROPS, STRING_PROPS, OBJECT_PROPS
48
+ object_props :items, :all_of, :one_of, :any_of, :discriminator
49
+ hash_props :properties
50
+ object_or_scalar_props :additional_properties
51
+
52
+ def initialize(type = nil, title = nil, enum: nil, additional_properties: nil, min: nil, max: nil, length: nil,
53
+ range: nil, **named)
54
+ case type
55
+ when *PRIMITIVES, :object, :array
56
+ @type = type
57
+ when String, Symbol
58
+ @ref = create_reference type
59
+ when Array
60
+ @type = :array
61
+ items type.first
62
+ when OrList
63
+ one_of(*type.list)
64
+ when AndList
65
+ all_of(*type.list)
66
+ end
67
+ additional_properties(additional_properties) if additional_properties
68
+ enum(enum) if enum
69
+ length(length) if length.is_a? Range
70
+ range(range) if range.is_a? Range
71
+ named_props named
72
+ @title = title
73
+ min(min) if min
74
+ max(max) if max
75
+ end
76
+
77
+ def length(length)
78
+ return unless length.is_a? Range
79
+
80
+ @max_length = length.max
81
+ @min_length = length.min
82
+ end
83
+
84
+ def range(range)
85
+ return unless range.is_a? Range
86
+
87
+ @maximum = range.max + (range.exclude_end? ? 1 : 0)
88
+ @minimum = range.min
89
+ @exclusive_maximum = range.exclude_end? || nil
90
+ end
91
+
92
+ def min(min)
93
+ case @type
94
+ when :number, :integer
95
+ minimum min
96
+ when :string
97
+ min_length min
98
+ end
99
+ end
100
+
101
+ def max(max)
102
+ case @type
103
+ when :number, :integer
104
+ maximum max
105
+ when :string
106
+ max_length max
107
+ end
108
+ end
109
+
110
+ def unique_items
111
+ @unique_items = true
112
+ end
113
+
114
+ def nullable
115
+ @nullable = true
116
+ end
117
+
118
+ def read_only
119
+ @read_only = true
120
+ end
121
+
122
+ def items(type = nil, title = nil, &block)
123
+ @items = Schema.new type, title
124
+ instance_eval(&block) if block
125
+ end
126
+
127
+ def enum(*items)
128
+ @type ||= :string
129
+ @enum = items.flatten
130
+ @nullable = true if @enum.include? nil
131
+ end
132
+
133
+ def property(name, type = :string, _title = nil, optional: false, ref: nil, **named, &block)
134
+ @type ||= :object
135
+ name_str = name.to_s
136
+ if name_str.end_with? '?'
137
+ optional = true
138
+ name_str = name_str[0..-2]
139
+ end
140
+ unless optional
141
+ @required ||= []
142
+ @required << name_str
143
+ end
144
+ @properties ||= []
145
+ if ref
146
+ @properties << [name_str, Reference.new(ref)]
147
+ else
148
+ schema = Schema.new type, **named
149
+ schema.instance_eval(&block) if block
150
+ @properties << [name_str, schema]
151
+ end
152
+ end
153
+
154
+ def additional_properties(type = nil, ref: nil, &block)
155
+ if ref
156
+ @additional_properties = Reference.new ref
157
+ elsif type.is_a? Symbol
158
+ schema = Schema.new type
159
+ schema.instance_eval(&block) if block
160
+ @additional_properties = schema
161
+ else
162
+ @additional_properties = true
163
+ end
164
+ end
165
+
166
+ def object(name, title = nil, **named, &block)
167
+ property name, :object, title, **named, &block
168
+ end
169
+
170
+ def object?(name, title = nil, **named, &block)
171
+ object name, title, optional: true, **named, &block
172
+ end
173
+
174
+ def any(name, title = nil, **named, &block)
175
+ property name, :any, title, **named, &block
176
+ end
177
+
178
+ def any?(name, title = nil, **named, &block)
179
+ any name, title, optional: true, **named, &block
180
+ end
181
+
182
+ def ref(name, ref, **named)
183
+ property name, ref: ref, **named
184
+ end
185
+
186
+ def ref?(name, ref, **named)
187
+ ref(name, ref, optional: true, **named)
188
+ end
189
+
190
+ def refs(**named)
191
+ named.each_pair { |name, ref| property name, ref: ref }
192
+ end
193
+
194
+ def all_of(*refs, &block)
195
+ @all_of = SchemaList.new(*refs)
196
+ @all_of.instance_eval(&block) if block
197
+ end
198
+
199
+ def one_of(*refs, &block)
200
+ @one_of = SchemaList.new(*refs)
201
+ @one_of.instance_eval(&block) if block
202
+ end
203
+
204
+ def any_of(*refs, &block)
205
+ @any_of = SchemaList.new(*refs)
206
+ @any_of.instance_eval(&block) if block
207
+ end
208
+
209
+ def discriminator(property_name = nil, **mapping, &block)
210
+ @discriminator = Discriminator.new property_name, **mapping, &block
211
+ end
212
+
213
+ def to_spec
214
+ if @type == :any
215
+ {}
216
+ elsif @ref
217
+ { '$ref': @ref }
218
+ else
219
+ props = {}
220
+ scalar_props props
221
+ object_props props
222
+ hash_props props
223
+ object_or_scalar_props props
224
+ props[:required] = @required if @required
225
+ props
226
+ end
227
+ end
228
+
229
+ def self.property_shortcuts(shortcut_name)
230
+ define_method "#{shortcut_name}?" do |name, title = nil, **named, &block|
231
+ send shortcut_name, name, title, optional: true, **named, &block
232
+ end
233
+ define_method "#{shortcut_name}s" do |*names|
234
+ names.each { |name| send shortcut_name, name }
235
+ end
236
+ end
237
+
238
+ PRIMITIVES.each do |primitive|
239
+ define_method primitive do |name, title = nil, **named, &block|
240
+ property name, primitive, title, **named, &block
241
+ end
242
+ property_shortcuts primitive
243
+ end
244
+
245
+ FORMATS.each do |(primitive, formats, mapping)|
246
+ formats.each do |format|
247
+ format_str = (mapping && mapping[format]) || format.to_s
248
+ define_method format do |name, title = nil, **named, &block|
249
+ send primitive, name, title, format: format_str, **named, &block
250
+ end
251
+ property_shortcuts format
252
+ end
253
+ end
254
+ end
255
+
256
+ class SchemaList
257
+ def initialize(*types)
258
+ @schemas = types.map { |type| Schema.new(type) }
259
+ end
260
+
261
+ def schema(type: :object, ref: nil, **named, &block)
262
+ if ref
263
+ @schemas << Reference.new(ref)
264
+ else
265
+ schema = Schema.new type, **named
266
+ schema.instance_eval(&block) if block
267
+ @schemas << schema
268
+ end
269
+ end
270
+
271
+ def ref(ref)
272
+ schema(ref: ref)
273
+ end
274
+
275
+ def to_spec
276
+ @schemas.map(&:to_spec)
277
+ end
278
+ end
279
+
280
+ class Discriminator
281
+ props :property_name
282
+ scalar_props :property_name
283
+
284
+ def initialize(property_name = nil, **mapping)
285
+ @property_name = property_name
286
+ @mapping = mapping.transform_values { |ref| Reference.new ref }
287
+ end
288
+
289
+ def mapping(**mapping)
290
+ @mapping.merge! mapping
291
+ end
292
+
293
+ def to_spec
294
+ props = {}
295
+ scalar_props props
296
+ props[:mapping] = @mapping.transform_values(&:ref) unless @mapping.empty?
297
+ props
298
+ end
299
+
300
+ alias property property_name
301
+ end
@@ -0,0 +1,33 @@
1
+ require_relative './schema.rb'
2
+
3
+ describe do
4
+ describe 'a string schema' do
5
+ let(:schema) { Schema.new :string }
6
+ it 'should return a spec with type object' do
7
+ expect(schema.to_spec).to eq({ type: :string })
8
+ end
9
+ end
10
+
11
+ describe 'a string schema with max_length' do
12
+ let(:schema) { Schema.new :string, max_length: 80 }
13
+ it 'should return a spec with type object' do
14
+ expect(schema.to_spec).to eq({ type: :string, maxLength: 80 })
15
+ end
16
+ end
17
+
18
+ describe 'an array schema with string items' do
19
+ let(:schema) { Schema.new :array }
20
+ it 'should return a spec with string items' do
21
+ schema.items :string
22
+ expect(schema.to_spec).to eq({ type: :array, items: { type: :string } })
23
+ end
24
+ end
25
+
26
+ describe 'an object schema with a string property' do
27
+ let(:schema) { Schema.new :object }
28
+ it 'should return a spec with a property' do
29
+ schema.property :foo, :string
30
+ expect(schema.to_spec).to eq({ type: :object, properties: { foo: { type: :string } } })
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,69 @@
1
+ require_relative './props'
2
+
3
+ using Props
4
+
5
+ class ServerVariable
6
+ props :default, :enum, :description
7
+ named_props :enum
8
+ scalar_props :default, :enum, :description
9
+
10
+ def initialize(default = nil, **named)
11
+ @default = default
12
+ named_props named
13
+ end
14
+
15
+ def to_spec
16
+ @default = @default&.to_s
17
+ @enum&.map!(&:to_s)
18
+ scalar_props
19
+ end
20
+ end
21
+
22
+ class Server
23
+ props :url, :description
24
+ scalar_props :url, :description
25
+ hash_props :variables
26
+
27
+ def initialize(url = nil, description = nil)
28
+ @url = url
29
+ @description = description
30
+ end
31
+
32
+ def variable(name, default = nil, **named, &block)
33
+ @variables ||= []
34
+ variable = ServerVariable.new default, **named
35
+ variable.instance_eval(&block) if block
36
+ @variables << [name, variable]
37
+ end
38
+
39
+ def variables(**vars)
40
+ vars.each_pair do |name, value|
41
+ if value.is_a? Array
42
+ variable name, value.first, enum: value
43
+ else
44
+ variable name, value
45
+ end
46
+ end
47
+ end
48
+
49
+ def to_spec
50
+ props = {}
51
+ scalar_props props
52
+ hash_props props
53
+ props
54
+ end
55
+ end
56
+
57
+ module ServerMethods
58
+ def server(url = nil, description = nil, &block)
59
+ @servers ||= []
60
+ server = Server.new url, description
61
+ server.instance_eval(&block) if block
62
+ @servers << server
63
+ end
64
+
65
+ def servers(*singles, **pairs)
66
+ pairs.each_pair { |url, description| server url, description }
67
+ singles.each { |url| server url }
68
+ end
69
+ end
@@ -0,0 +1,51 @@
1
+ require_relative './server'
2
+
3
+ describe do
4
+ describe 'Server Object' do
5
+ describe 'an minimum Server' do
6
+ let(:server) { Server.new 'foo.bar/baz' }
7
+
8
+ it 'should return a correct spec' do
9
+ expect(server.to_spec).to eq(
10
+ {
11
+ url: 'foo.bar/baz'
12
+ }
13
+ )
14
+ end
15
+ end
16
+
17
+ describe 'a Server with variables' do
18
+ let(:server) { Server.new 'foo.bar/baz' }
19
+ before do
20
+ server.variable :foo, 'Foo'
21
+ server.variable :bar, 'Bar'
22
+ end
23
+
24
+ it 'should return a correct spec' do
25
+ expect(server.to_spec).to eq(
26
+ {
27
+ url: 'foo.bar/baz',
28
+ variables: {
29
+ foo: { default: 'Foo' },
30
+ bar: { default: 'Bar' }
31
+ }
32
+ }
33
+ )
34
+ end
35
+ end
36
+ end
37
+
38
+ describe 'Server Variable Object' do
39
+ let(:variable) { ServerVariable.new 'bar' }
40
+
41
+ describe 'a minimum Server Variable object' do
42
+ it 'should return a correct spec' do
43
+ expect(variable.to_spec).to eq(
44
+ {
45
+ default: 'bar'
46
+ }
47
+ )
48
+ end
49
+ end
50
+ end
51
+ end