dbee 2.1.0.pre.alpha → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +71 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +6 -17
- data/.tool-versions +1 -0
- data/CHANGELOG.md +21 -1
- data/README.md +154 -62
- data/dbee.gemspec +6 -10
- data/lib/dbee/base.rb +7 -49
- data/lib/dbee/dsl_schema_builder.rb +86 -0
- data/lib/dbee/key_chain.rb +11 -0
- data/lib/dbee/model/relationships/basic.rb +47 -0
- data/lib/dbee/model/relationships.rb +24 -0
- data/lib/dbee/model.rb +50 -38
- data/lib/dbee/query/field.rb +5 -6
- data/lib/dbee/query.rb +32 -20
- 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/lib/dbee.rb +9 -10
- data/spec/dbee/base_spec.rb +9 -72
- data/spec/dbee/constant_resolver_spec.rb +17 -12
- 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/filters_spec.rb +16 -17
- data/spec/dbee/query_spec.rb +56 -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 +86 -19
- data/.travis.yml +0 -24
data/lib/dbee.rb
CHANGED
@@ -13,11 +13,15 @@ require 'forwardable'
|
|
13
13
|
|
14
14
|
require_relative 'dbee/base'
|
15
15
|
require_relative 'dbee/constant_resolver'
|
16
|
+
require_relative 'dbee/dsl_schema_builder'
|
16
17
|
require_relative 'dbee/key_chain'
|
17
18
|
require_relative 'dbee/key_path'
|
18
19
|
require_relative 'dbee/model'
|
19
|
-
require_relative 'dbee/query'
|
20
20
|
require_relative 'dbee/providers'
|
21
|
+
require_relative 'dbee/query'
|
22
|
+
require_relative 'dbee/schema'
|
23
|
+
require_relative 'dbee/schema_creator'
|
24
|
+
require_relative 'dbee/schema_from_tree_based_model'
|
21
25
|
|
22
26
|
# Top-level namespace that provides the main public API.
|
23
27
|
module Dbee
|
@@ -31,16 +35,11 @@ module Dbee
|
|
31
35
|
@inflector ||= Dry::Inflector.new
|
32
36
|
end
|
33
37
|
|
34
|
-
def sql(
|
35
|
-
|
36
|
-
model =
|
37
|
-
if model.is_a?(Hash) || model.is_a?(Model)
|
38
|
-
Model.make(model)
|
39
|
-
else
|
40
|
-
model.to_model(query.key_chain)
|
41
|
-
end
|
38
|
+
def sql(schema_or_model, query_input, provider)
|
39
|
+
raise ArgumentError, 'a provider is required' unless provider
|
42
40
|
|
43
|
-
|
41
|
+
schema_compat = SchemaCreator.new(schema_or_model, query_input)
|
42
|
+
provider.sql(schema_compat.schema, schema_compat.query)
|
44
43
|
end
|
45
44
|
end
|
46
45
|
end
|
data/spec/dbee/base_spec.rb
CHANGED
@@ -11,52 +11,17 @@ require 'spec_helper'
|
|
11
11
|
require 'fixtures/models'
|
12
12
|
|
13
13
|
describe Dbee::Base do
|
14
|
-
describe '
|
15
|
-
it '
|
16
|
-
|
17
|
-
expected_config = yaml_fixture('models.yaml')[model_name]
|
14
|
+
describe '.to_schema' do
|
15
|
+
it 'passes the keychain and model to DslSchemaBuilder' do
|
16
|
+
key_chain = Dbee::KeyChain.new
|
18
17
|
|
19
|
-
|
18
|
+
schema_builder_double = double(Dbee::DslSchemaBuilder)
|
19
|
+
expect(Dbee::DslSchemaBuilder).to receive(:new).with(Models::A, key_chain).and_return(
|
20
|
+
schema_builder_double
|
21
|
+
)
|
22
|
+
expect(schema_builder_double).to receive(:to_schema)
|
20
23
|
|
21
|
-
|
22
|
-
members.demos.phone_numbers.a
|
23
|
-
members.movies.b
|
24
|
-
members.favorite_comic_movies.c
|
25
|
-
members.favorite_mystery_movies.d
|
26
|
-
members.favorite_comedy_movies.e
|
27
|
-
parent_theater.members.demos.phone_numbers.f
|
28
|
-
parent_theater.members.movies.g
|
29
|
-
parent_theater.members.favorite_comic_movies.h
|
30
|
-
parent_theater.members.favorite_mystery_movies.i
|
31
|
-
parent_theater.members.favorite_comedy_movies.j
|
32
|
-
]
|
33
|
-
|
34
|
-
key_chain = Dbee::KeyChain.new(key_paths)
|
35
|
-
|
36
|
-
actual_model = Models::Theater.to_model(key_chain)
|
37
|
-
|
38
|
-
expect(actual_model).to eq(expected_model)
|
39
|
-
end
|
40
|
-
|
41
|
-
it 'honors key_chain to flatten cyclic references' do
|
42
|
-
model_name = 'Cycle Example'
|
43
|
-
expected_config = yaml_fixture('models.yaml')[model_name]
|
44
|
-
|
45
|
-
expected_model = Dbee::Model.make(expected_config)
|
46
|
-
|
47
|
-
key_paths = %w[
|
48
|
-
b1.c.a.z
|
49
|
-
b1.d.a.y
|
50
|
-
b2.c.a.x
|
51
|
-
b2.d.a.w
|
52
|
-
b2.d.a.b1.c.z
|
53
|
-
]
|
54
|
-
|
55
|
-
key_chain = Dbee::KeyChain.new(key_paths)
|
56
|
-
|
57
|
-
actual_model = Cycles::A.to_model(key_chain)
|
58
|
-
|
59
|
-
expect(actual_model).to eq(expected_model)
|
24
|
+
Models::A.to_schema(key_chain)
|
60
25
|
end
|
61
26
|
end
|
62
27
|
|
@@ -72,32 +37,4 @@ describe Dbee::Base do
|
|
72
37
|
expect(Models::E.inherited_table_name).to eq('table_set_to_e')
|
73
38
|
end
|
74
39
|
end
|
75
|
-
|
76
|
-
describe 'partitioners' do
|
77
|
-
it 'honors partitioners on a root model' do
|
78
|
-
model_name = 'Partitioner Example 1'
|
79
|
-
expected_config = yaml_fixture('models.yaml')[model_name]
|
80
|
-
expected_model = Dbee::Model.make(expected_config)
|
81
|
-
|
82
|
-
key_paths = %w[id]
|
83
|
-
key_chain = Dbee::KeyChain.new(key_paths)
|
84
|
-
|
85
|
-
actual_model = PartitionerExamples::Dog.to_model(key_chain)
|
86
|
-
|
87
|
-
expect(actual_model).to eq(expected_model)
|
88
|
-
end
|
89
|
-
|
90
|
-
it 'honors partitioners on a child model' do
|
91
|
-
model_name = 'Partitioner Example 2'
|
92
|
-
expected_config = yaml_fixture('models.yaml')[model_name]
|
93
|
-
expected_model = Dbee::Model.make(expected_config)
|
94
|
-
|
95
|
-
key_paths = %w[id dogs.id]
|
96
|
-
key_chain = Dbee::KeyChain.new(key_paths)
|
97
|
-
|
98
|
-
actual_model = PartitionerExamples::Owner.to_model(key_chain)
|
99
|
-
|
100
|
-
expect(actual_model).to eq(expected_model)
|
101
|
-
end
|
102
|
-
end
|
103
40
|
end
|
@@ -9,23 +9,28 @@
|
|
9
9
|
|
10
10
|
require 'spec_helper'
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
class E; end
|
16
|
-
end
|
17
|
-
|
12
|
+
# rubocop:disable Lint/EmptyClass
|
13
|
+
# These are intentionally empty to keep the tests focused.
|
14
|
+
module A
|
18
15
|
class B; end
|
19
16
|
|
20
|
-
|
21
|
-
|
22
|
-
|
17
|
+
class E; end
|
18
|
+
end
|
19
|
+
|
20
|
+
class B; end
|
23
21
|
|
24
|
-
|
25
|
-
|
26
|
-
|
22
|
+
module C
|
23
|
+
class D; end
|
24
|
+
|
25
|
+
class E; end
|
26
|
+
|
27
|
+
module F
|
28
|
+
class G; end
|
27
29
|
end
|
30
|
+
end
|
31
|
+
# rubocop:enable Lint/EmptyClass
|
28
32
|
|
33
|
+
describe Dbee::ConstantResolver do
|
29
34
|
it 'resolves nested constant with the same as an ancestor constants sibling' do
|
30
35
|
expect(subject.constantize('A::B')).to eq(::A::B)
|
31
36
|
end
|
@@ -0,0 +1,106 @@
|
|
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 'fixtures/models'
|
12
|
+
|
13
|
+
describe Dbee::DslSchemaBuilder do
|
14
|
+
specify 'theaters example' do
|
15
|
+
model_name = 'Theaters, Members, and Movies from DSL'
|
16
|
+
expected_config = yaml_fixture('models.yaml')[model_name]
|
17
|
+
|
18
|
+
expected_schema = Dbee::Schema.new(expected_config)
|
19
|
+
|
20
|
+
key_paths = %w[
|
21
|
+
members.a
|
22
|
+
members.demos.phone_numbers.a
|
23
|
+
members.movies.b
|
24
|
+
members.favorite_comic_movies.c
|
25
|
+
members.favorite_mystery_movies.d
|
26
|
+
members.favorite_comedy_movies.e
|
27
|
+
parent_theater.members.demos.phone_numbers.f
|
28
|
+
parent_theater.members.movies.g
|
29
|
+
parent_theater.members.favorite_comic_movies.h
|
30
|
+
parent_theater.members.favorite_mystery_movies.i
|
31
|
+
parent_theater.members.favorite_comedy_movies.j
|
32
|
+
]
|
33
|
+
|
34
|
+
key_chain = Dbee::KeyChain.new(key_paths)
|
35
|
+
|
36
|
+
actual_schema = Models::Theater.to_schema(key_chain)
|
37
|
+
expect(actual_schema).to eq(expected_schema)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'honors key_chain to flatten cyclic references' do
|
41
|
+
model_name = 'Cycle Example'
|
42
|
+
expected_config = yaml_fixture('models.yaml')[model_name]
|
43
|
+
|
44
|
+
expected_schema = Dbee::Schema.new(expected_config)
|
45
|
+
|
46
|
+
key_paths = %w[
|
47
|
+
b1.c.a.z
|
48
|
+
b1.d.a.y
|
49
|
+
b2.c.a.x
|
50
|
+
b2.d.a.w
|
51
|
+
b2.d.a.b1.c.z
|
52
|
+
]
|
53
|
+
|
54
|
+
key_chain = Dbee::KeyChain.new(key_paths)
|
55
|
+
|
56
|
+
actual_schema = Cycles::A.to_schema(key_chain)
|
57
|
+
expect(actual_schema).to eq(expected_schema)
|
58
|
+
end
|
59
|
+
|
60
|
+
describe 'partitioners' do
|
61
|
+
it 'honors partitioners on a root model' do
|
62
|
+
model_name = 'Partitioner Example 1'
|
63
|
+
expected_config = yaml_fixture('models.yaml')[model_name]
|
64
|
+
expected_schema = Dbee::Schema.new(expected_config)
|
65
|
+
|
66
|
+
key_paths = %w[id]
|
67
|
+
key_chain = Dbee::KeyChain.new(key_paths)
|
68
|
+
|
69
|
+
actual_schema = PartitionerExamples::Dog.to_schema(key_chain)
|
70
|
+
|
71
|
+
expect(actual_schema).to eq(expected_schema)
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'honors partitioners on a child model' do
|
75
|
+
model_name = 'Partitioner Example 2'
|
76
|
+
expected_config = yaml_fixture('models.yaml')[model_name]
|
77
|
+
expected_schema = Dbee::Schema.new(expected_config)
|
78
|
+
|
79
|
+
key_paths = %w[id dogs.id]
|
80
|
+
key_chain = Dbee::KeyChain.new(key_paths)
|
81
|
+
|
82
|
+
actual_schema = PartitionerExamples::Owner.to_schema(key_chain)
|
83
|
+
|
84
|
+
expect(actual_schema).to eq(expected_schema)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'README examples' do
|
89
|
+
specify 'code-first and configuration-first models are equal' do
|
90
|
+
config = yaml_fixture('models.yaml')['Readme']
|
91
|
+
config_schema = Dbee::Schema.new(config)
|
92
|
+
|
93
|
+
key_chain = Dbee::KeyChain.new(%w[
|
94
|
+
patients.a
|
95
|
+
patients.notes.b
|
96
|
+
patients.work_phone_number.c
|
97
|
+
patients.cell_phone_number.d
|
98
|
+
patients.fax_phone_number.e
|
99
|
+
])
|
100
|
+
|
101
|
+
code_schema = ReadmeDataModels::Practice.to_schema(key_chain)
|
102
|
+
|
103
|
+
expect(config_schema).to eq(code_schema)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
data/spec/dbee/key_chain_spec.rb
CHANGED
@@ -78,4 +78,28 @@ describe Dbee::KeyChain do
|
|
78
78
|
end
|
79
79
|
end
|
80
80
|
end
|
81
|
+
|
82
|
+
describe '#to_unique_ancestors collapses the KeyChain' do
|
83
|
+
let(:collapsed_key_chain) do
|
84
|
+
described_class.new([
|
85
|
+
'z.b.c.d.any_column',
|
86
|
+
'b.c.any_column',
|
87
|
+
'any_column',
|
88
|
+
'44.55.any_column'
|
89
|
+
])
|
90
|
+
end
|
91
|
+
|
92
|
+
specify 'the empty case' do
|
93
|
+
expect(described_class.new.to_unique_ancestors).to eq described_class.new
|
94
|
+
end
|
95
|
+
|
96
|
+
specify 'when there are no ancestors, one key path is returned' do
|
97
|
+
no_query_depth = described_class.new(%w[column1 column2 column3])
|
98
|
+
expect(no_query_depth.to_unique_ancestors).to eq described_class.new(['any_column'])
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'returns a new KeyChain which has one KeyPath per ancestor' do
|
102
|
+
expect(subject.to_unique_ancestors).to eq collapsed_key_chain
|
103
|
+
end
|
104
|
+
end
|
81
105
|
end
|
@@ -9,14 +9,13 @@
|
|
9
9
|
|
10
10
|
require 'spec_helper'
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
Dbee::Model::Constraints::Static => CONSTRAINT_CONFIG.merge(value: :b, type: :static)
|
18
|
-
}.freeze
|
12
|
+
CONSTRAINT_CONFIG = { name: :a }.freeze
|
13
|
+
CONSTRAINT_FACTORIES = {
|
14
|
+
Dbee::Model::Constraints::Reference => CONSTRAINT_CONFIG.merge(parent: :b, type: :reference),
|
15
|
+
Dbee::Model::Constraints::Static => CONSTRAINT_CONFIG.merge(value: :b, type: :static)
|
16
|
+
}.freeze
|
19
17
|
|
18
|
+
describe Dbee::Model::Constraints do
|
20
19
|
CONSTRAINT_FACTORIES.each_pair do |constant, config|
|
21
20
|
it "should instantiate #{constant} objects" do
|
22
21
|
expect(described_class.make(config)).to be_a(constant)
|
data/spec/dbee/model_spec.rb
CHANGED
@@ -55,77 +55,80 @@ describe Dbee::Model do
|
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
-
describe '
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
subject { described_class.make(entity_hash) }
|
64
|
-
|
65
|
-
specify 'returns proper models' do
|
66
|
-
members = subject.models.first
|
67
|
-
|
68
|
-
expected_plan = {
|
69
|
-
%w[members] => members
|
58
|
+
describe '.make_keyed_by' do
|
59
|
+
it 'returns a hash of Models where the keys equal the names of the models' do
|
60
|
+
input = {
|
61
|
+
model1: nil,
|
62
|
+
model2: { table: :model2_table }
|
70
63
|
}
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
expect(plan).to eq(expected_plan)
|
75
|
-
end
|
76
|
-
|
77
|
-
specify 'returns proper multi-level models' do
|
78
|
-
members = subject.models.first
|
79
|
-
demos = members.models.first
|
80
|
-
phone_numbers = demos.models.first
|
81
|
-
|
82
|
-
expected_plan = {
|
83
|
-
%w[members] => members,
|
84
|
-
%w[members demos] => demos,
|
85
|
-
%w[members demos phone_numbers] => phone_numbers
|
64
|
+
expected_result = {
|
65
|
+
'model1' => described_class.new(name: 'model1'),
|
66
|
+
'model2' => described_class.new(name: 'model2', table: 'model2_table')
|
86
67
|
}
|
87
68
|
|
88
|
-
|
89
|
-
|
90
|
-
expect(plan).to eq(expected_plan)
|
69
|
+
expect(described_class.make_keyed_by(:name, input)).to eq expected_result
|
91
70
|
end
|
92
|
-
end
|
93
|
-
|
94
|
-
describe 'equality' do
|
95
|
-
let(:config) { yaml_fixture('models.yaml')['Theaters, Members, and Movies'] }
|
96
71
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
72
|
+
it 'accepts values of Dbee::Model instances' do
|
73
|
+
input = { model1: described_class.new(name: :model1) }
|
74
|
+
expected_result = { 'model1' => described_class.new(name: :model1) }
|
75
|
+
expect(described_class.make_keyed_by(:name, input)).to eq expected_result
|
76
|
+
end
|
102
77
|
|
103
|
-
|
104
|
-
|
78
|
+
it 'accepts values of Dbee::Model instances when the key attribute is a string' do
|
79
|
+
input = { 'model1' => described_class.new(name: 'model1') }
|
80
|
+
expected_result = { 'model1' => described_class.new(name: 'model1') }
|
81
|
+
expect(described_class.make_keyed_by(:name, input)).to eq expected_result
|
105
82
|
end
|
106
83
|
|
107
|
-
it '
|
108
|
-
|
109
|
-
expect
|
84
|
+
it 'raises an error when the input hash key is not equal to the name of the model' do
|
85
|
+
input = { model1: described_class.new(name: :bogus) }
|
86
|
+
expect do
|
87
|
+
described_class.make_keyed_by(:name, input)
|
88
|
+
end.to raise_error ArgumentError, "expected a name of 'model1' but got 'bogus'"
|
110
89
|
end
|
111
90
|
end
|
112
91
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
key_chain = Dbee::KeyChain.new(%w[
|
119
|
-
patients.a
|
120
|
-
patients.notes.b
|
121
|
-
patients.work_phone_number.c
|
122
|
-
patients.cell_phone_number.d
|
123
|
-
patients.fax_phone_number.e
|
124
|
-
])
|
125
|
-
|
126
|
-
code_model = ReadmeDataModels::Practice.to_model(key_chain)
|
92
|
+
describe '#to_s' do
|
93
|
+
it 'is represented by the model name' do
|
94
|
+
expect(described_class.new(name: 'foo').to_s).to eq 'foo'
|
95
|
+
end
|
96
|
+
end
|
127
97
|
|
128
|
-
|
98
|
+
equality_cases = {
|
99
|
+
tree_based: yaml_fixture('models.yaml')['Theaters, Members, and Movies Tree Based'],
|
100
|
+
graph_based: yaml_fixture('models.yaml')['Theaters, Members, and Movies from DSL']['theater']
|
101
|
+
}
|
102
|
+
equality_cases[:graph_based].merge!(name: 'theaters')
|
103
|
+
equality_cases.each do |test_case, config|
|
104
|
+
describe "equality for #{test_case}" do
|
105
|
+
let(:model1) { described_class.make(config) }
|
106
|
+
let(:model2) { described_class.make(config) }
|
107
|
+
|
108
|
+
subject { described_class.make(config) }
|
109
|
+
|
110
|
+
specify 'equality compares attributes' do
|
111
|
+
expect(model1).to eq(model2)
|
112
|
+
expect(model1).to eql(model2)
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'returns false unless comparing same object types' do
|
116
|
+
expect(subject).not_to eq(config)
|
117
|
+
expect(subject).not_to eq(nil)
|
118
|
+
end
|
119
|
+
|
120
|
+
describe 'hash codes' do
|
121
|
+
specify 'are equal when objects are equal' do
|
122
|
+
expect(model1).to eq(model2)
|
123
|
+
expect(model1.hash).to eq(model2.hash)
|
124
|
+
end
|
125
|
+
|
126
|
+
specify 'are not equal when objects are not equal' do
|
127
|
+
different_model = described_class.new(name: :oddball)
|
128
|
+
expect(model1).not_to eq(different_model)
|
129
|
+
expect(model1.hash).not_to eq(different_model.hash)
|
130
|
+
end
|
131
|
+
end
|
129
132
|
end
|
130
133
|
end
|
131
134
|
end
|
@@ -9,24 +9,23 @@
|
|
9
9
|
|
10
10
|
require 'spec_helper'
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
Dbee::Query::Filters::StartsWith => FILTERS_CONFIG.merge(type: :starts_with)
|
28
|
-
}.freeze
|
12
|
+
FILTERS_CONFIG = { key_path: 'a.b.c', value: :d }.freeze
|
13
|
+
FILTER_FACTORIES = {
|
14
|
+
Dbee::Query::Filters::Contains => FILTERS_CONFIG.merge(type: :contains),
|
15
|
+
Dbee::Query::Filters::Equals => FILTERS_CONFIG.merge(type: :equals),
|
16
|
+
Dbee::Query::Filters::GreaterThanOrEqualTo => FILTERS_CONFIG.merge(
|
17
|
+
type: :greater_than_or_equal_to
|
18
|
+
),
|
19
|
+
Dbee::Query::Filters::GreaterThan => FILTERS_CONFIG.merge(type: :greater_than),
|
20
|
+
Dbee::Query::Filters::LessThanOrEqualTo => FILTERS_CONFIG.merge(type: :less_than_or_equal_to),
|
21
|
+
Dbee::Query::Filters::LessThan => FILTERS_CONFIG.merge(type: :less_than),
|
22
|
+
Dbee::Query::Filters::NotContain => FILTERS_CONFIG.merge(type: :not_contain),
|
23
|
+
Dbee::Query::Filters::NotEquals => FILTERS_CONFIG.merge(type: :not_equals),
|
24
|
+
Dbee::Query::Filters::NotStartWith => FILTERS_CONFIG.merge(type: :not_start_with),
|
25
|
+
Dbee::Query::Filters::StartsWith => FILTERS_CONFIG.merge(type: :starts_with)
|
26
|
+
}.freeze
|
29
27
|
|
28
|
+
describe Dbee::Query::Filters do
|
30
29
|
FILTER_FACTORIES.each_pair do |constant, config|
|
31
30
|
it "should instantiate #{constant} objects" do
|
32
31
|
expect(described_class.make(config)).to be_a(constant)
|
data/spec/dbee/query_spec.rb
CHANGED
@@ -9,9 +9,64 @@
|
|
9
9
|
|
10
10
|
require 'spec_helper'
|
11
11
|
|
12
|
+
README_EXAMPLES = {
|
13
|
+
'Get all practices' => {
|
14
|
+
fields: [
|
15
|
+
{ key_path: 'id' },
|
16
|
+
{ key_path: 'active' },
|
17
|
+
{ key_path: 'name' }
|
18
|
+
]
|
19
|
+
},
|
20
|
+
'Get practices, limit to 2, offset by 3, and sort by name (descending) then id (ascending)' => {
|
21
|
+
fields: [
|
22
|
+
{ key_path: 'id' },
|
23
|
+
{ key_path: 'active' },
|
24
|
+
{ key_path: 'name' }
|
25
|
+
],
|
26
|
+
sorters: [
|
27
|
+
{ key_path: 'name', direction: :descending },
|
28
|
+
{ key_path: 'id' }
|
29
|
+
],
|
30
|
+
limit: 2,
|
31
|
+
offset: 3
|
32
|
+
},
|
33
|
+
"Get top 5 active practices and patient whose name start with 'Sm':" => {
|
34
|
+
fields: [
|
35
|
+
{ key_path: 'name', display: 'Practice Name' },
|
36
|
+
{ key_path: 'patients.first', display: 'Patient First Name' },
|
37
|
+
{ key_path: 'patients.middle', display: 'Patient Middle Name' },
|
38
|
+
{ key_path: 'patients.last', display: 'Patient Last Name' }
|
39
|
+
],
|
40
|
+
filters: [
|
41
|
+
{ type: :equals, key_path: 'active', value: true },
|
42
|
+
{ type: :starts_with, key_path: 'patients.last', value: 'Sm' }
|
43
|
+
],
|
44
|
+
limit: 5
|
45
|
+
},
|
46
|
+
'Get practice IDs, patient IDs, names, and cell phone numbers that starts with 555' => {
|
47
|
+
fields: [
|
48
|
+
{ key_path: 'id', display: 'Practice ID #' },
|
49
|
+
{ key_path: 'patients.id', display: 'Patient ID #' },
|
50
|
+
{ key_path: 'patients.first', display: 'Patient First Name' },
|
51
|
+
{ key_path: 'patients.middle', display: 'Patient Middle Name' },
|
52
|
+
{ key_path: 'patients.last', display: 'Patient Last Name' },
|
53
|
+
{ key_path: 'patients.cell_phone_numbers.phone_number', display: 'Patient Cell #' }
|
54
|
+
],
|
55
|
+
filters: [
|
56
|
+
{ type: :equals, key_path: 'active', value: true },
|
57
|
+
{
|
58
|
+
type: :starts_with,
|
59
|
+
key_path: 'patients.cell_phone_numbers.phone_number',
|
60
|
+
value: '555'
|
61
|
+
}
|
62
|
+
]
|
63
|
+
}
|
64
|
+
}.freeze
|
65
|
+
|
12
66
|
describe Dbee::Query do
|
13
67
|
let(:config) do
|
14
68
|
{
|
69
|
+
from: 'my_model',
|
15
70
|
fields: [
|
16
71
|
{ key_path: 'matt.nick.sam', display: 'some display' },
|
17
72
|
{ key_path: 'katie' },
|
@@ -36,14 +91,6 @@ describe Dbee::Query do
|
|
36
91
|
end
|
37
92
|
|
38
93
|
describe '#initialize' do
|
39
|
-
it 'should raise an ArgumentError if fields keyword is missing' do
|
40
|
-
expect { described_class.new }.to raise_error(ArgumentError)
|
41
|
-
end
|
42
|
-
|
43
|
-
it 'should raise a NoFieldsError if no fields were passed in' do
|
44
|
-
expect { described_class.new(fields: []) }.to raise_error(Dbee::Query::NoFieldsError)
|
45
|
-
end
|
46
|
-
|
47
94
|
it 'should remove duplicate filters (keep first instance)' do
|
48
95
|
query_hash = {
|
49
96
|
fields: [
|
@@ -124,60 +171,7 @@ describe Dbee::Query do
|
|
124
171
|
end
|
125
172
|
|
126
173
|
context 'README examples' do
|
127
|
-
|
128
|
-
'Get all practices' => {
|
129
|
-
fields: [
|
130
|
-
{ key_path: 'id' },
|
131
|
-
{ key_path: 'active' },
|
132
|
-
{ key_path: 'name' }
|
133
|
-
]
|
134
|
-
},
|
135
|
-
'Get all practices, limit to 10, and sort by name (descending) then id (ascending)' => {
|
136
|
-
fields: [
|
137
|
-
{ key_path: 'id' },
|
138
|
-
{ key_path: 'active' },
|
139
|
-
{ key_path: 'name' }
|
140
|
-
],
|
141
|
-
sorters: [
|
142
|
-
{ key_path: 'name', direction: :descending },
|
143
|
-
{ key_path: 'id' }
|
144
|
-
],
|
145
|
-
limit: 10
|
146
|
-
},
|
147
|
-
"Get top 5 active practices and patient whose name start with 'Sm':" => {
|
148
|
-
fields: [
|
149
|
-
{ key_path: 'name', display: 'Practice Name' },
|
150
|
-
{ key_path: 'patients.first', display: 'Patient First Name' },
|
151
|
-
{ key_path: 'patients.middle', display: 'Patient Middle Name' },
|
152
|
-
{ key_path: 'patients.last', display: 'Patient Last Name' }
|
153
|
-
],
|
154
|
-
filters: [
|
155
|
-
{ type: :equals, key_path: 'active', value: true },
|
156
|
-
{ type: :starts_with, key_path: 'patients.last', value: 'Sm' }
|
157
|
-
],
|
158
|
-
limit: 5
|
159
|
-
},
|
160
|
-
'Get practice IDs, patient IDs, names, and cell phone numbers that starts with 555' => {
|
161
|
-
fields: [
|
162
|
-
{ key_path: 'id', display: 'Practice ID #' },
|
163
|
-
{ key_path: 'patients.id', display: 'Patient ID #' },
|
164
|
-
{ key_path: 'patients.first', display: 'Patient First Name' },
|
165
|
-
{ key_path: 'patients.middle', display: 'Patient Middle Name' },
|
166
|
-
{ key_path: 'patients.last', display: 'Patient Last Name' },
|
167
|
-
{ key_path: 'patients.cell_phone_numbers.phone_number', display: 'Patient Cell #' }
|
168
|
-
],
|
169
|
-
filters: [
|
170
|
-
{ type: :equals, key_path: 'active', value: true },
|
171
|
-
{
|
172
|
-
type: :starts_with,
|
173
|
-
key_path: 'patients.cell_phone_numbers.phone_number',
|
174
|
-
value: '555'
|
175
|
-
}
|
176
|
-
]
|
177
|
-
}
|
178
|
-
}.freeze
|
179
|
-
|
180
|
-
EXAMPLES.each_pair do |name, query|
|
174
|
+
README_EXAMPLES.each_pair do |name, query|
|
181
175
|
specify name do
|
182
176
|
expect(described_class.make(query)).to be_a(described_class)
|
183
177
|
end
|