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.
@@ -10,6 +10,31 @@
10
10
  require 'spec_helper'
11
11
 
12
12
  describe Dbee::Query::Field do
13
+ let(:config) do
14
+ {
15
+ display: 'd',
16
+ key_path: 'a.b.c'
17
+ }
18
+ end
19
+
20
+ let(:config_with_aggregation_and_filters) do
21
+ config.merge(
22
+ aggregator: :ave,
23
+ filters: [
24
+ {
25
+ key_path: 'a.b',
26
+ value: 'something'
27
+ }
28
+ ]
29
+ )
30
+ end
31
+
32
+ subject { described_class.new(config_with_aggregation_and_filters) }
33
+
34
+ let(:subject_without_aggregation_and_filters) do
35
+ described_class.new(config)
36
+ end
37
+
13
38
  it 'should act as hashable' do
14
39
  expect(described_class).to respond_to(:make)
15
40
  expect(described_class).to respond_to(:array)
@@ -24,16 +49,19 @@ describe Dbee::Query::Field do
24
49
  end
25
50
 
26
51
  context 'equality' do
27
- let(:config) { { key_path: 'a.b.c', display: 'd' } }
52
+ specify '#hash produces same output as [aggregator, key_path, and display]' do
53
+ expected = [
54
+ subject.aggregator,
55
+ subject.display,
56
+ subject.filters,
57
+ subject.key_path
58
+ ].hash
28
59
 
29
- subject { described_class.new(config) }
30
-
31
- specify '#hash produces same output as concatenated string hash of key_path and display' do
32
- expect(subject.hash).to eq("#{config[:key_path]}#{config[:display]}".hash)
60
+ expect(subject.hash).to eq(expected)
33
61
  end
34
62
 
35
63
  specify '#== and #eql? compare attributes' do
36
- object2 = described_class.new(config)
64
+ object2 = described_class.new(config_with_aggregation_and_filters)
37
65
 
38
66
  expect(subject).to eq(object2)
39
67
  expect(subject).to eql(object2)
@@ -44,4 +72,24 @@ describe Dbee::Query::Field do
44
72
  expect(subject).not_to eq(nil)
45
73
  end
46
74
  end
75
+
76
+ describe '#aggregator?' do
77
+ it 'returns true if not nil' do
78
+ expect(subject.aggregator?).to be true
79
+ end
80
+
81
+ it 'returns false if nil' do
82
+ expect(subject_without_aggregation_and_filters.aggregator?).to be false
83
+ end
84
+ end
85
+
86
+ describe '#filters?' do
87
+ it 'returns true if at least one filter' do
88
+ expect(subject.filters?).to be true
89
+ end
90
+
91
+ it 'returns false if nil' do
92
+ expect(subject_without_aggregation_and_filters.filters?).to be false
93
+ end
94
+ end
47
95
  end
@@ -9,24 +9,23 @@
9
9
 
10
10
  require 'spec_helper'
11
11
 
12
- describe Dbee::Query::Filters do
13
- FILTERS_CONFIG = { key_path: 'a.b.c', value: :d }.freeze
14
-
15
- FILTER_FACTORIES = {
16
- Dbee::Query::Filters::Contains => FILTERS_CONFIG.merge(type: :contains),
17
- Dbee::Query::Filters::Equals => FILTERS_CONFIG.merge(type: :equals),
18
- Dbee::Query::Filters::GreaterThanOrEqualTo => FILTERS_CONFIG.merge(
19
- type: :greater_than_or_equal_to
20
- ),
21
- Dbee::Query::Filters::GreaterThan => FILTERS_CONFIG.merge(type: :greater_than),
22
- Dbee::Query::Filters::LessThanOrEqualTo => FILTERS_CONFIG.merge(type: :less_than_or_equal_to),
23
- Dbee::Query::Filters::LessThan => FILTERS_CONFIG.merge(type: :less_than),
24
- Dbee::Query::Filters::NotContain => FILTERS_CONFIG.merge(type: :not_contain),
25
- Dbee::Query::Filters::NotEquals => FILTERS_CONFIG.merge(type: :not_equals),
26
- Dbee::Query::Filters::NotStartWith => FILTERS_CONFIG.merge(type: :not_start_with),
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)
@@ -9,9 +9,63 @@
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 all practices, limit to 10, 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: 10
31
+ },
32
+ "Get top 5 active practices and patient whose name start with 'Sm':" => {
33
+ fields: [
34
+ { key_path: 'name', display: 'Practice Name' },
35
+ { key_path: 'patients.first', display: 'Patient First Name' },
36
+ { key_path: 'patients.middle', display: 'Patient Middle Name' },
37
+ { key_path: 'patients.last', display: 'Patient Last Name' }
38
+ ],
39
+ filters: [
40
+ { type: :equals, key_path: 'active', value: true },
41
+ { type: :starts_with, key_path: 'patients.last', value: 'Sm' }
42
+ ],
43
+ limit: 5
44
+ },
45
+ 'Get practice IDs, patient IDs, names, and cell phone numbers that starts with 555' => {
46
+ fields: [
47
+ { key_path: 'id', display: 'Practice ID #' },
48
+ { key_path: 'patients.id', display: 'Patient ID #' },
49
+ { key_path: 'patients.first', display: 'Patient First Name' },
50
+ { key_path: 'patients.middle', display: 'Patient Middle Name' },
51
+ { key_path: 'patients.last', display: 'Patient Last Name' },
52
+ { key_path: 'patients.cell_phone_numbers.phone_number', display: 'Patient Cell #' }
53
+ ],
54
+ filters: [
55
+ { type: :equals, key_path: 'active', value: true },
56
+ {
57
+ type: :starts_with,
58
+ key_path: 'patients.cell_phone_numbers.phone_number',
59
+ value: '555'
60
+ }
61
+ ]
62
+ }
63
+ }.freeze
64
+
12
65
  describe Dbee::Query do
13
66
  let(:config) do
14
67
  {
68
+ from: 'my_model',
15
69
  fields: [
16
70
  { key_path: 'matt.nick.sam', display: 'some display' },
17
71
  { key_path: 'katie' },
@@ -36,14 +90,6 @@ describe Dbee::Query do
36
90
  end
37
91
 
38
92
  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
93
  it 'should remove duplicate filters (keep first instance)' do
48
94
  query_hash = {
49
95
  fields: [
@@ -124,60 +170,7 @@ describe Dbee::Query do
124
170
  end
125
171
 
126
172
  context 'README examples' do
127
- EXAMPLES = {
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|
173
+ README_EXAMPLES.each_pair do |name, query|
181
174
  specify name do
182
175
  expect(described_class.make(query)).to be_a(described_class)
183
176
  end
@@ -0,0 +1,163 @@
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::SchemaCreator do
14
+ let(:model_hash_graph) do
15
+ {
16
+ 'model1' => {
17
+ relationships: {
18
+ model2: {
19
+ constraints: [
20
+ {
21
+ type: 'reference',
22
+ parent: 'id',
23
+ name: 'model1_id'
24
+ }
25
+ ]
26
+ }
27
+ }
28
+ },
29
+ 'model2' => nil
30
+ }
31
+ end
32
+ let(:schema) { Dbee::Schema.new(model_hash_graph) }
33
+
34
+ let(:model_hash_tree) do
35
+ {
36
+ name: 'model1',
37
+ models: [
38
+ {
39
+ name: 'model2',
40
+ constraints: [
41
+ {
42
+ type: 'reference',
43
+ parent: 'id',
44
+ name: 'model1_id'
45
+ }
46
+ ]
47
+ }
48
+ ]
49
+ }
50
+ end
51
+
52
+ let(:model_tree) { Dbee::Model.make(model_hash_tree) }
53
+
54
+ let(:query_hash) do
55
+ {
56
+ from: 'model1',
57
+ fields: [
58
+ { key_path: :a }
59
+ ]
60
+ }
61
+ end
62
+ let(:query) { Dbee::Query.make(query_hash) }
63
+
64
+ let(:query_hash_no_from) { query_hash.slice(:fields) }
65
+ let(:query_no_from) { Dbee::Query.make(query_hash_no_from) }
66
+
67
+ describe 'query parsing' do
68
+ it 'passes through Dbee::Query instances' do
69
+ expect(described_class.new(schema, query).query).to eq query
70
+ end
71
+
72
+ it 'creates a Dbee::Query from a query hash' do
73
+ expect(described_class.new(schema, query_hash).query).to eq query
74
+ end
75
+
76
+ describe 'the "from" field' do
77
+ it 'raises an error when nil' do
78
+ expect do
79
+ described_class.new(schema, query_hash_no_from)
80
+ end.to raise_error(ArgumentError, 'query requires a from model name')
81
+ end
82
+
83
+ it 'raises an error when the empty string' do
84
+ expect do
85
+ described_class.new(schema, query_hash_no_from.merge(from: ''))
86
+ end.to raise_error(ArgumentError, 'query requires a from model name')
87
+ end
88
+ end
89
+ end
90
+
91
+ describe 'tree based models' do
92
+ it 'creates a schema from a Dbee::Model' do
93
+ expect(described_class.new(model_tree, query).schema).to eq schema
94
+ end
95
+
96
+ describe 'hash detection' do
97
+ it 'creates a schema from a hash model' do
98
+ expect(described_class.new(model_hash_tree, query).schema).to eq schema
99
+ end
100
+
101
+ it 'creates a schema from a minimal hash model with no child models' do
102
+ minimal_tree_hash = { name: :practice }
103
+ minimal_schema = Dbee::Schema.new(practice: nil)
104
+
105
+ expect(described_class.new(minimal_tree_hash, {}).schema).to eq minimal_schema
106
+ end
107
+ end
108
+
109
+ describe 'query "from" field' do
110
+ it 'is set using name of the root model of the tree' do
111
+ expect(described_class.new(model_tree, query_no_from).query).to eq query
112
+ end
113
+
114
+ it 'raises an error the query "from" does not equal the root model name' do
115
+ query_hash_different_from = query_hash_no_from.merge(from: :bogus_model)
116
+
117
+ expect do
118
+ described_class.new(model_tree, query_hash_different_from)
119
+ end.to raise_error(
120
+ ArgumentError,
121
+ "expected from model to be 'model1' but got 'bogus_model'"
122
+ )
123
+ end
124
+ end
125
+ end
126
+
127
+ describe 'graph based models' do
128
+ it 'creates a schema from a hash schema' do
129
+ expect(described_class.new(model_hash_graph, query).schema).to eq schema
130
+ end
131
+
132
+ it 'passes through Dbee::Schema instances' do
133
+ expect(described_class.new(schema, query).schema).to eq schema
134
+ end
135
+
136
+ describe 'when given a Dbee::Base class' do
137
+ let(:dsl_model_class) { PartitionerExamples::Dog }
138
+
139
+ it 'creates a schema' do
140
+ expected_config = yaml_fixture('models.yaml')['Partitioner Example 1']
141
+ expected_schema = Dbee::Schema.new(expected_config)
142
+ dog_query = Dbee::Query.make(query_hash_no_from.merge(from: :dog))
143
+
144
+ expect(described_class.new(dsl_model_class, dog_query).schema).to eq expected_schema
145
+ end
146
+
147
+ it "can infer the query's 'from' field" do
148
+ expect(described_class.new(dsl_model_class, query_no_from).query.from).to eq 'dog'
149
+ end
150
+
151
+ it 'raises an error the query "from" does not equal the DSL model name' do
152
+ cat_query = Dbee::Query.make(query_hash_no_from.merge(from: :cat))
153
+
154
+ expect do
155
+ described_class.new(dsl_model_class, cat_query)
156
+ end.to raise_error(
157
+ ArgumentError,
158
+ "expected from model to be 'dog' but got 'cat'"
159
+ )
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,31 @@
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::SchemaFromTreeBasedModel do
14
+ let(:tree_config) do
15
+ Dbee::Model.make(yaml_fixture('models.yaml')['Theaters, Members, and Movies Tree Based'])
16
+ end
17
+ let(:schema_config) do
18
+ yaml_fixture('models.yaml')['Theaters, Members, and Movies from Tree']
19
+ end
20
+
21
+ it 'converts the theaters model' do
22
+ expected_schema = Dbee::Schema.new(schema_config)
23
+ expect(described_class.convert(tree_config)).to eq expected_schema
24
+ end
25
+
26
+ it 'does not raise any errors when converting the readme model' do
27
+ tree_config = Dbee::Model.make(yaml_fixture('models.yaml')['Readme Tree Based'])
28
+
29
+ expect { described_class.convert(tree_config) }.not_to raise_error
30
+ end
31
+ end