dbee 2.1.1 → 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 +4 -1
- data/.travis.yml +2 -1
- data/CHANGELOG.md +11 -0
- data/README.md +133 -58
- data/dbee.gemspec +5 -2
- data/lib/dbee.rb +9 -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.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 +4 -0
- data/lib/dbee/query/field.rb +1 -6
- 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 +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 +55 -54
- 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 +66 -9
@@ -0,0 +1,47 @@
|
|
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
|
+
module Dbee
|
11
|
+
# Converts a tree based model to a Schema. Note that this results in a
|
12
|
+
# different but compatible schema compared to the result of generating a
|
13
|
+
# Dbee::Schema by calling to_schema on a Dbee::Base class. This is
|
14
|
+
# because converting from a tree based model is a lossy.
|
15
|
+
class SchemaFromTreeBasedModel # :nodoc:
|
16
|
+
class << self
|
17
|
+
def convert(tree_model)
|
18
|
+
Schema.new(to_graph_based_models(tree_model))
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def to_graph_based_models(tree_model, parent_model = nil, models_attrs_by_name = {})
|
24
|
+
models_attrs_by_name[tree_model.name] = {
|
25
|
+
name: tree_model.name,
|
26
|
+
table: tree_model.table,
|
27
|
+
partitioners: tree_model.partitioners,
|
28
|
+
relationships: {}
|
29
|
+
}
|
30
|
+
|
31
|
+
parent_model && add_relationship_to_parent_model(
|
32
|
+
tree_model, models_attrs_by_name[parent_model.name]
|
33
|
+
)
|
34
|
+
|
35
|
+
tree_model.models.each do |sub_model|
|
36
|
+
to_graph_based_models(sub_model, tree_model, models_attrs_by_name)
|
37
|
+
end
|
38
|
+
|
39
|
+
models_attrs_by_name
|
40
|
+
end
|
41
|
+
|
42
|
+
def add_relationship_to_parent_model(model, graph_model_attrs)
|
43
|
+
graph_model_attrs[:relationships][model.name] = { constraints: model.constraints }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,50 @@
|
|
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
|
+
module Dbee
|
11
|
+
module Util
|
12
|
+
# Provides a "make_keyed_by" method which extends the Hashable gem's
|
13
|
+
# concept of a "make" method.
|
14
|
+
module MakeKeyedBy # :nodoc:
|
15
|
+
# Given a hash of hashes or a hash of values of instances of this class,
|
16
|
+
# a hash is returned where all of the values are instances of this class
|
17
|
+
# and the keys are the string versions of the original hash.
|
18
|
+
#
|
19
|
+
# An ArgumentError is raised if the value's <tt>key_attrib</tt> is not
|
20
|
+
# equal to the top level hash key. This ensures that the
|
21
|
+
# <tt>key_attrib</tt> is the same in the incoming hash and the value.
|
22
|
+
#
|
23
|
+
# This is useful for cases where it makes sense in the configuration
|
24
|
+
# (YAML) specification to represent certain objects in a hash structure
|
25
|
+
# instead of a list.
|
26
|
+
def make_keyed_by(key_attrib, spec_hash)
|
27
|
+
# Once Ruby 2.5 support is dropped, this can just use the block form of
|
28
|
+
# #to_h.
|
29
|
+
spec_hash.map do |key, spec|
|
30
|
+
string_key = key.to_s
|
31
|
+
[string_key, make_value_checking_key_attib!(key_attrib, string_key, spec)]
|
32
|
+
end.to_h
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def make_value_checking_key_attib!(key_attrib, key, spec)
|
38
|
+
if spec.is_a?(self)
|
39
|
+
if spec.send(key_attrib).to_s != key
|
40
|
+
err_msg = "expected a #{key_attrib} of '#{key}' but got '#{spec.send(key_attrib)}'"
|
41
|
+
raise ArgumentError, err_msg
|
42
|
+
end
|
43
|
+
spec
|
44
|
+
else
|
45
|
+
make((spec || {}).merge(key_attrib => key))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/dbee/version.rb
CHANGED
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
|