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.
data/lib/dbee/version.rb CHANGED
@@ -8,5 +8,5 @@
8
8
  #
9
9
 
10
10
  module Dbee
11
- VERSION = '2.0.2'
11
+ VERSION = '3.0.0'
12
12
  end
@@ -11,52 +11,17 @@ require 'spec_helper'
11
11
  require 'fixtures/models'
12
12
 
13
13
  describe Dbee::Base do
14
- describe '#to_model' do
15
- it 'compiles correctly' do
16
- model_name = 'Theaters, Members, and Movies'
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
- expected_model = Dbee::Model.make(expected_config)
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
- key_paths = %w[
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
@@ -0,0 +1,58 @@
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
+
12
+ # rubocop:disable Lint/EmptyClass
13
+ # These are intentionally empty to keep the tests focused.
14
+ module A
15
+ class B; end
16
+
17
+ class E; end
18
+ end
19
+
20
+ class B; end
21
+
22
+ module C
23
+ class D; end
24
+
25
+ class E; end
26
+
27
+ module F
28
+ class G; end
29
+ end
30
+ end
31
+ # rubocop:enable Lint/EmptyClass
32
+
33
+ describe Dbee::ConstantResolver do
34
+ it 'resolves nested constant with the same as an ancestor constants sibling' do
35
+ expect(subject.constantize('A::B')).to eq(::A::B)
36
+ end
37
+
38
+ it 'resolves root constant' do
39
+ expect(subject.constantize('B')).to eq(::B)
40
+ end
41
+
42
+ it 'does not resolve constant in a different parent module' do
43
+ expect { subject.constantize('C::B') }.to raise_error(NameError)
44
+ end
45
+
46
+ it 'does not resolve constant in a different parent module' do
47
+ expect { subject.constantize('D') }.to raise_error(NameError)
48
+ end
49
+
50
+ it 'resolves same leaf constants specific to their parents' do
51
+ expect(subject.constantize('A::E')).to eq(::A::E)
52
+ expect(subject.constantize('C::E')).to eq(::C::E)
53
+ end
54
+
55
+ it 'does not resolve constant without its parent' do
56
+ expect { subject.constantize('F') }.to raise_error(NameError)
57
+ end
58
+ 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
@@ -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
- describe Dbee::Model::Constraints do
13
- CONSTRAINT_CONFIG = { name: :a }.freeze
14
-
15
- CONSTRAINT_FACTORIES = {
16
- Dbee::Model::Constraints::Reference => CONSTRAINT_CONFIG.merge(parent: :b, type: :reference),
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)
@@ -55,77 +55,80 @@ describe Dbee::Model do
55
55
  end
56
56
  end
57
57
 
58
- describe '#ancestors' do
59
- let(:yaml_entities) { yaml_fixture('models.yaml') }
60
-
61
- let(:entity_hash) { yaml_entities['Theaters, Members, and Movies'] }
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
- plan = subject.ancestors!(%w[members])
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
- plan = subject.ancestors!(%w[members demos phone_numbers])
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
- subject { described_class.make(config) }
98
-
99
- specify 'equality compares attributes' do
100
- model1 = described_class.make(config)
101
- model2 = described_class.make(config)
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
- expect(model1).to eq(model2)
104
- expect(model1).to eql(model2)
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 'returns false unless comparing same object types' do
108
- expect(subject).not_to eq(config)
109
- expect(subject).not_to eq(nil)
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
- context 'README examples' do
114
- specify 'code-first and configuration-first models are equal' do
115
- config = yaml_fixture('models.yaml')['Readme']
116
- config_model = described_class.make(config)
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
- expect(config_model).to eq(code_model)
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