dbee 2.0.2 → 3.0.0
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/.gitignore +1 -0
- data/.rubocop.yml +14 -10
- data/.ruby-version +1 -1
- data/.travis.yml +4 -4
- data/CHANGELOG.md +43 -0
- data/README.md +257 -43
- data/dbee.gemspec +17 -6
- data/exe/.gitkeep +0 -0
- data/lib/dbee.rb +10 -10
- data/lib/dbee/base.rb +7 -49
- data/lib/dbee/constant_resolver.rb +34 -0
- data/lib/dbee/dsl/association.rb +8 -19
- data/lib/dbee/dsl_schema_builder.rb +86 -0
- data/lib/dbee/key_chain.rb +11 -0
- data/lib/dbee/model.rb +50 -38
- data/lib/dbee/model/relationships.rb +24 -0
- data/lib/dbee/model/relationships/basic.rb +47 -0
- data/lib/dbee/query.rb +30 -24
- data/lib/dbee/query/field.rb +35 -5
- data/lib/dbee/schema.rb +66 -0
- data/lib/dbee/schema_creator.rb +107 -0
- data/lib/dbee/schema_from_tree_based_model.rb +47 -0
- data/lib/dbee/util/make_keyed_by.rb +50 -0
- data/lib/dbee/version.rb +1 -1
- data/spec/dbee/base_spec.rb +9 -72
- data/spec/dbee/constant_resolver_spec.rb +58 -0
- data/spec/dbee/dsl_schema_builder_spec.rb +106 -0
- data/spec/dbee/key_chain_spec.rb +24 -0
- data/spec/dbee/model/constraints_spec.rb +6 -7
- data/spec/dbee/model_spec.rb +62 -59
- data/spec/dbee/query/field_spec.rb +54 -6
- data/spec/dbee/query/filters_spec.rb +16 -17
- data/spec/dbee/query_spec.rb +55 -62
- data/spec/dbee/schema_creator_spec.rb +163 -0
- data/spec/dbee/schema_from_tree_based_model_spec.rb +31 -0
- data/spec/dbee/schema_spec.rb +62 -0
- data/spec/dbee_spec.rb +17 -37
- data/spec/fixtures/models.yaml +254 -56
- data/spec/spec_helper.rb +7 -0
- metadata +83 -18
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2019-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
require 'spec_helper'
|
11
|
+
require_relative '../fixtures/models'
|
12
|
+
|
13
|
+
describe Dbee::Schema do
|
14
|
+
def make_model(model_name)
|
15
|
+
raise "no model named '#{model_name}'" unless schema_config.key?(model_name)
|
16
|
+
|
17
|
+
Dbee::Model.make((schema_config[model_name] || {}).merge('name' => model_name))
|
18
|
+
end
|
19
|
+
|
20
|
+
let(:model_name) do
|
21
|
+
'Theaters, Members, and Movies from DSL'
|
22
|
+
end
|
23
|
+
let(:schema_config) { yaml_fixture('models.yaml')[model_name] }
|
24
|
+
|
25
|
+
let(:demographics_model) { make_model('demographic') }
|
26
|
+
let(:members_model) { make_model('member') }
|
27
|
+
let(:movies_model) { make_model('movie') }
|
28
|
+
let(:phone_numbers_model) { make_model('phone_number') }
|
29
|
+
let(:theaters_model) { make_model('theater') }
|
30
|
+
|
31
|
+
let(:subject) { described_class.new(schema_config) }
|
32
|
+
|
33
|
+
describe '#expand_query_path' do
|
34
|
+
specify 'one model case' do
|
35
|
+
expect(subject.expand_query_path(members_model, Dbee::KeyPath.new('id'))).to eq []
|
36
|
+
end
|
37
|
+
|
38
|
+
specify 'two model case' do
|
39
|
+
expected_path = [[members_model.relationship_for_name('movies'), movies_model]]
|
40
|
+
expect(
|
41
|
+
subject.expand_query_path(members_model, Dbee::KeyPath.new('movies.id'))
|
42
|
+
).to eq expected_path
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'traverses aliased models' do
|
46
|
+
expected_path = [
|
47
|
+
[members_model.relationship_for_name('demos'), demographics_model],
|
48
|
+
[demographics_model.relationship_for_name('phone_numbers'), phone_numbers_model]
|
49
|
+
]
|
50
|
+
|
51
|
+
expect(
|
52
|
+
subject.expand_query_path(members_model, Dbee::KeyPath.new('demos.phone_numbers.id'))
|
53
|
+
).to eq expected_path
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'raises an error given an unknown relationship' do
|
57
|
+
expect do
|
58
|
+
subject.expand_query_path(theaters_model, Dbee::KeyPath.new('demographics.id'))
|
59
|
+
end.to raise_error("model 'theater' does not have a 'demographics' relationship")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/spec/dbee_spec.rb
CHANGED
@@ -13,59 +13,39 @@ require 'fixtures/models'
|
|
13
13
|
describe Dbee do
|
14
14
|
describe '#sql' do
|
15
15
|
let(:provider) { Dbee::Providers::NullProvider.new }
|
16
|
-
|
17
|
-
let(:model_hash) do
|
18
|
-
{
|
19
|
-
name: 'something'
|
20
|
-
}
|
21
|
-
end
|
22
|
-
|
23
|
-
let(:model) { Dbee::Model.make(model_hash) }
|
24
|
-
|
25
16
|
let(:query_hash) do
|
26
17
|
{
|
18
|
+
from: 'my_model',
|
27
19
|
fields: [
|
28
20
|
{ key_path: :a }
|
29
21
|
]
|
30
22
|
}
|
31
23
|
end
|
32
|
-
|
33
24
|
let(:query) { Dbee::Query.make(query_hash) }
|
25
|
+
let(:schema) { Dbee::Schema.new({}) }
|
34
26
|
|
35
|
-
it 'accepts a hash
|
36
|
-
expect(provider).to receive(:sql).with(
|
37
|
-
|
38
|
-
described_class.sql(model_hash, query, provider)
|
39
|
-
end
|
40
|
-
|
41
|
-
it 'accepts a Dbee::Model instance as a model and passes a Model instance to provider#sql' do
|
42
|
-
expect(provider).to receive(:sql).with(model, query)
|
27
|
+
it 'accepts a query hash and a Schema and passes them into provider#sql' do
|
28
|
+
expect(provider).to receive(:sql).with(schema, query)
|
43
29
|
|
44
|
-
described_class.sql(
|
30
|
+
described_class.sql(schema, query_hash, provider)
|
45
31
|
end
|
46
32
|
|
47
|
-
it '
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
described_class.sql(model_constant, query, provider)
|
33
|
+
it 'does not allow a nil schema' do
|
34
|
+
expect do
|
35
|
+
described_class.sql(nil, query, provider)
|
36
|
+
end.to raise_error ArgumentError, /schema or model is required/
|
53
37
|
end
|
54
38
|
|
55
|
-
it '
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
described_class.sql(model, query, provider)
|
39
|
+
it 'does not allow a nil query' do
|
40
|
+
expect do
|
41
|
+
described_class.sql(schema, nil, provider)
|
42
|
+
end.to raise_error ArgumentError, /query is required/
|
61
43
|
end
|
62
44
|
|
63
|
-
it '
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
described_class.sql(model, query_hash, provider)
|
45
|
+
it 'does not allow a nil provider' do
|
46
|
+
expect do
|
47
|
+
described_class.sql(schema, query, nil)
|
48
|
+
end.to raise_error ArgumentError, /provider is required/
|
69
49
|
end
|
70
50
|
end
|
71
51
|
end
|
data/spec/fixtures/models.yaml
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Theaters, Members, and Movies:
|
1
|
+
Theaters, Members, and Movies Tree Based:
|
2
2
|
name: theater
|
3
3
|
table: theaters
|
4
4
|
models:
|
@@ -119,7 +119,217 @@ Theaters, Members, and Movies:
|
|
119
119
|
- type: static
|
120
120
|
name: genre
|
121
121
|
value: comedy
|
122
|
+
|
123
|
+
# Note that since converting from the tree does not have as much context as
|
124
|
+
# converting from the DSL, the resulting graph is a bit different.
|
125
|
+
Theaters, Members, and Movies from Tree:
|
126
|
+
theater:
|
127
|
+
table: theaters
|
128
|
+
relationships:
|
129
|
+
members:
|
130
|
+
constraints:
|
131
|
+
- type: reference
|
132
|
+
parent: id
|
133
|
+
name: tid
|
134
|
+
- type: reference
|
135
|
+
parent: partition
|
136
|
+
name: partition
|
137
|
+
parent_theater:
|
138
|
+
constraints:
|
139
|
+
- type: reference
|
140
|
+
name: id
|
141
|
+
parent: parent_theater_id
|
142
|
+
members:
|
143
|
+
relationships:
|
144
|
+
demos:
|
145
|
+
constraints:
|
146
|
+
- type: reference
|
147
|
+
parent: id
|
148
|
+
name: member_id
|
149
|
+
movies:
|
150
|
+
constraints:
|
151
|
+
- type: reference
|
152
|
+
parent: id
|
153
|
+
name: member_id
|
154
|
+
favorite_comic_movies:
|
155
|
+
constraints:
|
156
|
+
- type: reference
|
157
|
+
parent: id
|
158
|
+
name: member_id
|
159
|
+
- type: static
|
160
|
+
name: genre
|
161
|
+
value: comic
|
162
|
+
favorite_mystery_movies:
|
163
|
+
constraints:
|
164
|
+
- type: reference
|
165
|
+
parent: id
|
166
|
+
name: member_id
|
167
|
+
- type: static
|
168
|
+
name: genre
|
169
|
+
value: mystery
|
170
|
+
favorite_comedy_movies:
|
171
|
+
constraints:
|
172
|
+
- type: reference
|
173
|
+
parent: id
|
174
|
+
name: member_id
|
175
|
+
- type: static
|
176
|
+
name: genre
|
177
|
+
value: comedy
|
178
|
+
demos:
|
179
|
+
table: demographics
|
180
|
+
relationships:
|
181
|
+
phone_numbers:
|
182
|
+
constraints:
|
183
|
+
- type: reference
|
184
|
+
parent: id
|
185
|
+
name: demographic_id
|
186
|
+
phone_numbers:
|
187
|
+
movies:
|
188
|
+
favorite_comic_movies:
|
189
|
+
table: movies
|
190
|
+
favorite_mystery_movies:
|
191
|
+
table: movies
|
192
|
+
favorite_comedy_movies:
|
193
|
+
table: movies
|
194
|
+
parent_theater:
|
195
|
+
table: theaters
|
196
|
+
relationships:
|
197
|
+
members:
|
198
|
+
constraints:
|
199
|
+
- type: reference
|
200
|
+
parent: id
|
201
|
+
name: tid
|
202
|
+
- type: reference
|
203
|
+
parent: partition
|
204
|
+
name: partition
|
205
|
+
|
206
|
+
Theaters, Members, and Movies from DSL:
|
207
|
+
theater:
|
208
|
+
table: theaters
|
209
|
+
relationships:
|
210
|
+
members:
|
211
|
+
model: member
|
212
|
+
constraints:
|
213
|
+
- type: reference
|
214
|
+
parent: id
|
215
|
+
name: tid
|
216
|
+
- type: reference
|
217
|
+
parent: partition
|
218
|
+
name: partition
|
219
|
+
parent_theater:
|
220
|
+
model: theater
|
221
|
+
constraints:
|
222
|
+
- type: reference
|
223
|
+
name: id
|
224
|
+
parent: parent_theater_id
|
225
|
+
member:
|
226
|
+
table: members
|
227
|
+
relationships:
|
228
|
+
movies:
|
229
|
+
model: movie
|
230
|
+
constraints:
|
231
|
+
- type: reference
|
232
|
+
parent: id
|
233
|
+
name: member_id
|
234
|
+
demos:
|
235
|
+
model: demographic
|
236
|
+
constraints:
|
237
|
+
- type: reference
|
238
|
+
parent: id
|
239
|
+
name: member_id
|
240
|
+
favorite_comic_movies:
|
241
|
+
model: movie
|
242
|
+
constraints:
|
243
|
+
- type: reference
|
244
|
+
parent: id
|
245
|
+
name: member_id
|
246
|
+
- type: static
|
247
|
+
name: genre
|
248
|
+
value: comic
|
249
|
+
favorite_mystery_movies:
|
250
|
+
model: movie
|
251
|
+
constraints:
|
252
|
+
- type: reference
|
253
|
+
parent: id
|
254
|
+
name: member_id
|
255
|
+
- type: static
|
256
|
+
name: genre
|
257
|
+
value: mystery
|
258
|
+
favorite_comedy_movies:
|
259
|
+
model: movie
|
260
|
+
constraints:
|
261
|
+
- type: reference
|
262
|
+
parent: id
|
263
|
+
name: member_id
|
264
|
+
- type: static
|
265
|
+
name: genre
|
266
|
+
value: comedy
|
267
|
+
demographic:
|
268
|
+
table: demographics
|
269
|
+
relationships:
|
270
|
+
phone_numbers:
|
271
|
+
model: phone_number
|
272
|
+
constraints:
|
273
|
+
- type: reference
|
274
|
+
parent: id
|
275
|
+
name: demographic_id
|
276
|
+
phone_number:
|
277
|
+
table: phone_numbers
|
278
|
+
movie:
|
279
|
+
table: movies
|
280
|
+
|
122
281
|
Readme:
|
282
|
+
practice:
|
283
|
+
table: practices
|
284
|
+
relationships:
|
285
|
+
patients:
|
286
|
+
model: patient
|
287
|
+
constraints:
|
288
|
+
- type: reference
|
289
|
+
name: practice_id
|
290
|
+
parent: id
|
291
|
+
patient:
|
292
|
+
table: patients
|
293
|
+
relationships:
|
294
|
+
notes:
|
295
|
+
model: note
|
296
|
+
constraints:
|
297
|
+
- type: reference
|
298
|
+
name: patient_id
|
299
|
+
parent: id
|
300
|
+
work_phone_number:
|
301
|
+
model: phone_number
|
302
|
+
constraints:
|
303
|
+
- type: reference
|
304
|
+
name: patient_id
|
305
|
+
parent: id
|
306
|
+
- type: static
|
307
|
+
name: phone_number_type
|
308
|
+
value: work
|
309
|
+
cell_phone_number:
|
310
|
+
model: phone_number
|
311
|
+
constraints:
|
312
|
+
- type: reference
|
313
|
+
name: patient_id
|
314
|
+
parent: id
|
315
|
+
- type: static
|
316
|
+
name: phone_number_type
|
317
|
+
value: cell
|
318
|
+
fax_phone_number:
|
319
|
+
model: phone_number
|
320
|
+
constraints:
|
321
|
+
- type: reference
|
322
|
+
name: patient_id
|
323
|
+
parent: id
|
324
|
+
- type: static
|
325
|
+
name: phone_number_type
|
326
|
+
value: fax
|
327
|
+
note:
|
328
|
+
table: notes
|
329
|
+
phone_number:
|
330
|
+
table: phones
|
331
|
+
|
332
|
+
Readme Tree Based:
|
123
333
|
name: practice
|
124
334
|
table: practices
|
125
335
|
models:
|
@@ -161,63 +371,51 @@ Readme:
|
|
161
371
|
- type: static
|
162
372
|
name: phone_number_type
|
163
373
|
value: fax
|
374
|
+
|
164
375
|
Cycle Example:
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
table: cs
|
186
|
-
models:
|
187
|
-
- name: a
|
188
|
-
table: as
|
189
|
-
- name: d
|
190
|
-
table: ds
|
191
|
-
models:
|
192
|
-
- name: a
|
193
|
-
table: as
|
194
|
-
models:
|
195
|
-
- name: b1
|
196
|
-
table: bs
|
197
|
-
models:
|
198
|
-
- name: c
|
199
|
-
table: cs
|
376
|
+
a:
|
377
|
+
table: as
|
378
|
+
relationships:
|
379
|
+
b1:
|
380
|
+
model: b
|
381
|
+
b2:
|
382
|
+
model: b
|
383
|
+
b:
|
384
|
+
table: bs
|
385
|
+
relationships:
|
386
|
+
c:
|
387
|
+
d:
|
388
|
+
c:
|
389
|
+
table: cs
|
390
|
+
relationships:
|
391
|
+
a:
|
392
|
+
d:
|
393
|
+
table: ds
|
394
|
+
relationships:
|
395
|
+
a:
|
200
396
|
|
201
397
|
Partitioner Example 1:
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
398
|
+
dog:
|
399
|
+
table: animals
|
400
|
+
partitioners:
|
401
|
+
- name: type
|
402
|
+
value: Dog
|
403
|
+
- name: deleted
|
404
|
+
value: false
|
209
405
|
|
210
406
|
Partitioner Example 2:
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
407
|
+
owner:
|
408
|
+
table: owners
|
409
|
+
relationships:
|
410
|
+
dogs:
|
411
|
+
model: dog
|
412
|
+
constraints:
|
413
|
+
- name: owner_id
|
414
|
+
parent: id
|
415
|
+
dog:
|
416
|
+
table: animals
|
417
|
+
partitioners:
|
418
|
+
- name: type
|
419
|
+
value: Dog
|
420
|
+
- name: deleted
|
421
|
+
value: false
|