hashematics 1.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 +7 -0
- data/.editorconfig +8 -0
- data/.gitignore +4 -0
- data/.rubocop.yml +11 -0
- data/.ruby-version +1 -0
- data/.travis.yml +20 -0
- data/CHANGELOG.md +7 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +123 -0
- data/Guardfile +16 -0
- data/LICENSE +7 -0
- data/README.md +660 -0
- data/bin/benchmark +134 -0
- data/bin/console +11 -0
- data/hashematics.gemspec +32 -0
- data/lib/hashematics.rb +10 -0
- data/lib/hashematics/category.rb +67 -0
- data/lib/hashematics/configuration.rb +90 -0
- data/lib/hashematics/dictionary.rb +72 -0
- data/lib/hashematics/graph.rb +62 -0
- data/lib/hashematics/group.rb +57 -0
- data/lib/hashematics/hashematics.rb +36 -0
- data/lib/hashematics/id.rb +29 -0
- data/lib/hashematics/key.rb +76 -0
- data/lib/hashematics/object_interface.rb +35 -0
- data/lib/hashematics/record.rb +60 -0
- data/lib/hashematics/record_set.rb +29 -0
- data/lib/hashematics/type.rb +72 -0
- data/lib/hashematics/version.rb +12 -0
- data/lib/hashematics/visitor.rb +50 -0
- data/spec/examples/person.rb +36 -0
- data/spec/fixtures/config.yml +44 -0
- data/spec/fixtures/data.csv +9 -0
- data/spec/fixtures/people.yml +84 -0
- data/spec/hashematics/category_spec.rb +62 -0
- data/spec/hashematics/graph_spec.rb +572 -0
- data/spec/hashematics/key_spec.rb +37 -0
- data/spec/hashematics/object_interface_spec.rb +42 -0
- data/spec/hashematics/record_set_spec.rb +24 -0
- data/spec/hashematics/record_spec.rb +49 -0
- data/spec/hashematics/type_spec.rb +104 -0
- data/spec/spec_helper.rb +42 -0
- metadata +211 -0
@@ -0,0 +1,36 @@
|
|
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
|
+
class Person
|
11
|
+
attr_accessor :dob, :first, :id, :last, :smoker
|
12
|
+
|
13
|
+
def initialize(attrs = {})
|
14
|
+
attrs.each do |k, v|
|
15
|
+
send("#{k}=", v) if respond_to?(k)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def eql?(other)
|
20
|
+
to_hash == other.to_hash
|
21
|
+
end
|
22
|
+
|
23
|
+
def ==(other)
|
24
|
+
eql?(other)
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_hash
|
28
|
+
{
|
29
|
+
id: id,
|
30
|
+
first: first,
|
31
|
+
last: last,
|
32
|
+
smoker: smoker,
|
33
|
+
dob: dob
|
34
|
+
}
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
types:
|
2
|
+
person:
|
3
|
+
properties:
|
4
|
+
:id: 'ID #'
|
5
|
+
:first: First
|
6
|
+
:last: Last
|
7
|
+
:smoker: Smoker
|
8
|
+
:dob: DOB
|
9
|
+
car:
|
10
|
+
properties:
|
11
|
+
id: 'Car ID #'
|
12
|
+
make: Make
|
13
|
+
model: Model
|
14
|
+
year: Year
|
15
|
+
house:
|
16
|
+
:properties:
|
17
|
+
id: 'House ID #'
|
18
|
+
st1: Street 1
|
19
|
+
st2: Street 2
|
20
|
+
city: City
|
21
|
+
st: St
|
22
|
+
zip: Zip
|
23
|
+
plate:
|
24
|
+
:properties:
|
25
|
+
id: 'Plate ID #'
|
26
|
+
number: Plate Number
|
27
|
+
groups:
|
28
|
+
people:
|
29
|
+
by: 'ID #'
|
30
|
+
type: person
|
31
|
+
groups:
|
32
|
+
:cars:
|
33
|
+
by: 'Car ID #'
|
34
|
+
type: car
|
35
|
+
groups:
|
36
|
+
plates:
|
37
|
+
by: 'Plate ID #'
|
38
|
+
type: plate
|
39
|
+
houses:
|
40
|
+
by: 'House ID #'
|
41
|
+
type: house
|
42
|
+
cars:
|
43
|
+
by: 'Car ID #'
|
44
|
+
type: car
|
@@ -0,0 +1,9 @@
|
|
1
|
+
ID #,First,Last,Smoker,DOB,Car ID #,Make,Model,Year,House ID #,Street 1,Street 2,City,St,Zip,Plate ID #,Plate Number
|
2
|
+
1,Matt,Rizzo,false,1902-03-04,2,Jeep,Wrangler,1964,7,555 N. Michigan Ave,Suite 203,Chicago,IL,60075,12,JEEPY123
|
3
|
+
1,Matt,Rizzo,false,1902-03-04,3,Mercedes,Benz,1921,7,555 N. Michigan Ave,Suite 203,Chicago,IL,60075,13,BENZY456
|
4
|
+
1,Matt,Rizzo,false,1902-03-04,2,Jeep,Wrangler,1964,8,2813 N. Drivey Ave,Apt. 399,Roanoke,GA,99998,12,JEEPY123
|
5
|
+
1,Matt,Rizzo,false,1902-03-04,3,Mercedes,Benz,1921,8,2813 N. Drivey Ave,Apt. 399,Roanoke,GA,99998,13,BENZY4565
|
6
|
+
2,Katie,Bunny,true,1919-09-02,4,GMC,Terrainia,1999,9,123 Happy Ln.,Apt. 45,Mayhusee,RI,53210,14,GMC394
|
7
|
+
2,Katie,Bunny,true,1919-09-02,5,Buick,Macartha,1982,9,123 Happy Ln.,Apt. 45,Mayhusee,RI,53210,15,BUICKY93
|
8
|
+
3,Ralphie,Dog,true,1999-12-11,6,Audi,A1,2012,10,4500 Century Ave.,Floor 3A,Pakato,ZA,093821,,
|
9
|
+
3,Ralphie,Dog,true,1999-12-11,6,Audi,A1,2012,11,9990 Lake St.,,Pakato,ZA,093822,,
|
@@ -0,0 +1,84 @@
|
|
1
|
+
- :id: '1'
|
2
|
+
:first: Matt
|
3
|
+
:last: Rizzo
|
4
|
+
:smoker: 'false'
|
5
|
+
:dob: '1902-03-04'
|
6
|
+
:cars:
|
7
|
+
- id: '2'
|
8
|
+
make: Jeep
|
9
|
+
model: Wrangler
|
10
|
+
year: '1964'
|
11
|
+
plates:
|
12
|
+
- id: '12'
|
13
|
+
number: JEEPY123
|
14
|
+
- id: '3'
|
15
|
+
make: Mercedes
|
16
|
+
model: Benz
|
17
|
+
year: '1921'
|
18
|
+
plates:
|
19
|
+
- id: '13'
|
20
|
+
number: BENZY4565
|
21
|
+
houses:
|
22
|
+
- id: '7'
|
23
|
+
st1: 555 N. Michigan Ave
|
24
|
+
st2: Suite 203
|
25
|
+
city: Chicago
|
26
|
+
st: IL
|
27
|
+
zip: '60075'
|
28
|
+
- id: '8'
|
29
|
+
st1: 2813 N. Drivey Ave
|
30
|
+
st2: Apt. 399
|
31
|
+
city: Roanoke
|
32
|
+
st: GA
|
33
|
+
zip: '99998'
|
34
|
+
- :id: '2'
|
35
|
+
:first: Katie
|
36
|
+
:last: Bunny
|
37
|
+
:smoker: 'true'
|
38
|
+
:dob: '1919-09-02'
|
39
|
+
:cars:
|
40
|
+
- id: '4'
|
41
|
+
make: GMC
|
42
|
+
model: Terrainia
|
43
|
+
year: '1999'
|
44
|
+
plates:
|
45
|
+
- id: '14'
|
46
|
+
number: GMC394
|
47
|
+
- id: '5'
|
48
|
+
make: Buick
|
49
|
+
model: Macartha
|
50
|
+
year: '1982'
|
51
|
+
plates:
|
52
|
+
- id: '15'
|
53
|
+
number: BUICKY93
|
54
|
+
houses:
|
55
|
+
- id: '9'
|
56
|
+
st1: 123 Happy Ln.
|
57
|
+
st2: Apt. 45
|
58
|
+
city: Mayhusee
|
59
|
+
st: RI
|
60
|
+
zip: '53210'
|
61
|
+
- :id: '3'
|
62
|
+
:first: Ralphie
|
63
|
+
:last: Dog
|
64
|
+
:smoker: 'true'
|
65
|
+
:dob: '1999-12-11'
|
66
|
+
:cars:
|
67
|
+
- id: '6'
|
68
|
+
make: Audi
|
69
|
+
model: A1
|
70
|
+
year: '2012'
|
71
|
+
plates: []
|
72
|
+
houses:
|
73
|
+
- id: '10'
|
74
|
+
st1: 4500 Century Ave.
|
75
|
+
st2: Floor 3A
|
76
|
+
city: Pakato
|
77
|
+
st: ZA
|
78
|
+
zip: '093821'
|
79
|
+
- id: '11'
|
80
|
+
st1: 9990 Lake St.
|
81
|
+
st2: null
|
82
|
+
city: Pakato
|
83
|
+
st: ZA
|
84
|
+
zip: '093822'
|
@@ -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
|
+
|
12
|
+
describe ::Hashematics::Category do
|
13
|
+
let(:csv_rows) { csv_fixture('data.csv') }
|
14
|
+
|
15
|
+
it 'should require an id_key' do
|
16
|
+
expect { described_class.new(id_key: nil) }.to raise_error ArgumentError
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#add' do
|
20
|
+
context 'with id key but no parent key' do
|
21
|
+
specify '#records should return last unique rows as records' do
|
22
|
+
category = described_class.new(id_key: 'ID #')
|
23
|
+
|
24
|
+
csv_rows.each do |csv_row|
|
25
|
+
record = ::Hashematics::Record.new(csv_row)
|
26
|
+
category.add(record)
|
27
|
+
end
|
28
|
+
|
29
|
+
expected_records = [
|
30
|
+
::Hashematics::Record.new(csv_rows[3]),
|
31
|
+
::Hashematics::Record.new(csv_rows[5]),
|
32
|
+
::Hashematics::Record.new(csv_rows[7])
|
33
|
+
]
|
34
|
+
|
35
|
+
expect(category.records).to eq(expected_records)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'with parent and id keys' do
|
40
|
+
specify '#records should return unique rows as records specific to a parent' do
|
41
|
+
category = described_class.new(
|
42
|
+
parent_key: 'ID #',
|
43
|
+
id_key: 'Car ID #'
|
44
|
+
)
|
45
|
+
|
46
|
+
csv_rows.each do |csv_row|
|
47
|
+
record = ::Hashematics::Record.new(csv_row)
|
48
|
+
category.add(record)
|
49
|
+
end
|
50
|
+
|
51
|
+
expected_records = [
|
52
|
+
::Hashematics::Record.new(csv_rows[2]),
|
53
|
+
::Hashematics::Record.new(csv_rows[3])
|
54
|
+
]
|
55
|
+
|
56
|
+
parent = ::Hashematics::Record.new(csv_rows.first)
|
57
|
+
|
58
|
+
expect(category.records(parent)).to eq(expected_records)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,572 @@
|
|
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
|
+
describe ::Hashematics::Graph do
|
13
|
+
let(:csv_rows) { csv_fixture('data.csv') }
|
14
|
+
|
15
|
+
let(:configuration) { yaml_fixture('config.yml') }
|
16
|
+
|
17
|
+
let(:people) { yaml_fixture('people.yml') }
|
18
|
+
|
19
|
+
let(:groups) { ::Hashematics::Configuration.new(configuration).groups }
|
20
|
+
|
21
|
+
describe '#children' do
|
22
|
+
it 'returns list of child group names' do
|
23
|
+
graph = described_class.new(groups).add(csv_rows)
|
24
|
+
|
25
|
+
actual_children = graph.children
|
26
|
+
|
27
|
+
expected_children = groups.map(&:name)
|
28
|
+
|
29
|
+
expect(actual_children).to eq(expected_children)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#data' do
|
34
|
+
context 'with no object_class specifications' do
|
35
|
+
it 'should parse configuration and return object graph' do
|
36
|
+
graph = described_class.new(groups).add(csv_rows)
|
37
|
+
|
38
|
+
actual_people = graph.data('people')
|
39
|
+
|
40
|
+
# binding.pry
|
41
|
+
|
42
|
+
expect(actual_people).to eq(people)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'with object_class open_struct specifications' do
|
47
|
+
let(:modified_configuration) do
|
48
|
+
yaml_fixture('config.yml').tap do |c|
|
49
|
+
c.dig('types', 'person')['object_class'] = 'open_struct'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
let(:modified_groups) do
|
54
|
+
::Hashematics::Configuration.new(modified_configuration).groups
|
55
|
+
end
|
56
|
+
|
57
|
+
let(:modified_people) do
|
58
|
+
yaml_fixture('people.yml').map { |h| OpenStruct.new(h) }
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should parse configuration and return object graph' do
|
62
|
+
graph = described_class.new(modified_groups).add(csv_rows)
|
63
|
+
|
64
|
+
actual_people = graph.data(:people)
|
65
|
+
|
66
|
+
expect(actual_people).to eq(modified_people)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe 'README examples' do
|
72
|
+
specify 'Getting Started should work' do
|
73
|
+
rows = [
|
74
|
+
{
|
75
|
+
id: 1,
|
76
|
+
first: 'Bruce',
|
77
|
+
last: 'Banner'
|
78
|
+
},
|
79
|
+
{
|
80
|
+
id: 2,
|
81
|
+
first: 'Tony',
|
82
|
+
last: 'Stark'
|
83
|
+
}
|
84
|
+
]
|
85
|
+
|
86
|
+
graph = ::Hashematics.graph(rows: rows)
|
87
|
+
objects = graph.rows
|
88
|
+
|
89
|
+
expect(objects).to eq(rows)
|
90
|
+
end
|
91
|
+
|
92
|
+
specify 'Introduction to Shaping should work' do
|
93
|
+
config = {
|
94
|
+
types: {
|
95
|
+
person: {
|
96
|
+
properties: %i[id first]
|
97
|
+
}
|
98
|
+
},
|
99
|
+
groups: {
|
100
|
+
avengers: {
|
101
|
+
by: :id,
|
102
|
+
type: :person
|
103
|
+
}
|
104
|
+
}
|
105
|
+
}
|
106
|
+
|
107
|
+
rows = [
|
108
|
+
{
|
109
|
+
id: 1,
|
110
|
+
first: 'Bruce',
|
111
|
+
last: 'Banner'
|
112
|
+
},
|
113
|
+
{
|
114
|
+
id: 2,
|
115
|
+
first: 'Tony',
|
116
|
+
last: 'Stark'
|
117
|
+
}
|
118
|
+
]
|
119
|
+
|
120
|
+
graph = ::Hashematics.graph(config: config, rows: rows)
|
121
|
+
objects = graph.data(:avengers)
|
122
|
+
|
123
|
+
expected = [
|
124
|
+
{
|
125
|
+
id: 1,
|
126
|
+
first: 'Bruce'
|
127
|
+
},
|
128
|
+
{
|
129
|
+
id: 2,
|
130
|
+
first: 'Tony'
|
131
|
+
}
|
132
|
+
]
|
133
|
+
|
134
|
+
expect(objects).to eq(expected)
|
135
|
+
end
|
136
|
+
|
137
|
+
specify 'Cross-Mapping Shape Attribute Names should work' do
|
138
|
+
config = {
|
139
|
+
types: {
|
140
|
+
person: {
|
141
|
+
properties: {
|
142
|
+
id_number: :id,
|
143
|
+
first_name: :first
|
144
|
+
}
|
145
|
+
}
|
146
|
+
},
|
147
|
+
groups: {
|
148
|
+
avengers: {
|
149
|
+
by: :id,
|
150
|
+
type: :person
|
151
|
+
}
|
152
|
+
}
|
153
|
+
}
|
154
|
+
|
155
|
+
rows = [
|
156
|
+
{
|
157
|
+
id: 1,
|
158
|
+
first: 'Bruce',
|
159
|
+
last: 'Banner'
|
160
|
+
},
|
161
|
+
{
|
162
|
+
id: 2,
|
163
|
+
first: 'Tony',
|
164
|
+
last: 'Stark'
|
165
|
+
}
|
166
|
+
]
|
167
|
+
|
168
|
+
graph = ::Hashematics.graph(config: config, rows: rows)
|
169
|
+
objects = graph.data(:avengers)
|
170
|
+
|
171
|
+
expected = [
|
172
|
+
{
|
173
|
+
id_number: 1,
|
174
|
+
first_name: 'Bruce'
|
175
|
+
},
|
176
|
+
{
|
177
|
+
id_number: 2,
|
178
|
+
first_name: 'Tony'
|
179
|
+
}
|
180
|
+
]
|
181
|
+
|
182
|
+
expect(objects).to eq(expected)
|
183
|
+
end
|
184
|
+
|
185
|
+
specify 'Nested Shaping should work' do
|
186
|
+
config = {
|
187
|
+
types: {
|
188
|
+
person: {
|
189
|
+
properties: {
|
190
|
+
id: 'ID #',
|
191
|
+
first: 'First Name',
|
192
|
+
last: 'Last Name'
|
193
|
+
}
|
194
|
+
},
|
195
|
+
costume: {
|
196
|
+
properties: {
|
197
|
+
id: 'Costume ID #',
|
198
|
+
name: 'Costume Name',
|
199
|
+
color: 'Costume Color'
|
200
|
+
}
|
201
|
+
}
|
202
|
+
},
|
203
|
+
groups: {
|
204
|
+
avengers: {
|
205
|
+
by: 'ID #',
|
206
|
+
type: :person,
|
207
|
+
groups: {
|
208
|
+
costumes: {
|
209
|
+
by: 'Costume ID #',
|
210
|
+
type: :costume
|
211
|
+
}
|
212
|
+
}
|
213
|
+
}
|
214
|
+
}
|
215
|
+
}
|
216
|
+
|
217
|
+
rows = [
|
218
|
+
{
|
219
|
+
'ID #' => 1,
|
220
|
+
'First Name' => 'Bruce',
|
221
|
+
'Last Name' => 'Banner',
|
222
|
+
'Costume ID #' => 3,
|
223
|
+
'Costume Name' => 'Basic Hulk',
|
224
|
+
'Costume Color' => 'Green'
|
225
|
+
},
|
226
|
+
{
|
227
|
+
'ID #' => 1,
|
228
|
+
'First Name' => 'Bruce',
|
229
|
+
'Last Name' => 'Banner',
|
230
|
+
'Costume ID #' => 4,
|
231
|
+
'Costume Name' => 'Mad Hulk',
|
232
|
+
'Costume Color' => 'Red'
|
233
|
+
},
|
234
|
+
{
|
235
|
+
'ID #' => 2,
|
236
|
+
'First Name' => 'Tony',
|
237
|
+
'Last Name' => 'Stark',
|
238
|
+
'Costume ID #' => 5,
|
239
|
+
'Costume Name' => 'Mark I',
|
240
|
+
'Costume Color' => 'Gray'
|
241
|
+
},
|
242
|
+
{
|
243
|
+
'ID #' => 2,
|
244
|
+
'First Name' => 'Tony',
|
245
|
+
'Last Name' => 'Stark',
|
246
|
+
'Costume ID #' => 6,
|
247
|
+
'Costume Name' => 'Mark IV',
|
248
|
+
'Costume Color' => 'Red'
|
249
|
+
},
|
250
|
+
{
|
251
|
+
'ID #' => 2,
|
252
|
+
'First Name' => 'Tony',
|
253
|
+
'Last Name' => 'Stark',
|
254
|
+
'Costume ID #' => 7,
|
255
|
+
'Costume Name' => 'Mark VI',
|
256
|
+
'Costume Color' => 'Nano-Blue'
|
257
|
+
}
|
258
|
+
]
|
259
|
+
|
260
|
+
graph = ::Hashematics.graph(config: config, rows: rows)
|
261
|
+
objects = graph.data(:avengers)
|
262
|
+
|
263
|
+
expected = [
|
264
|
+
{
|
265
|
+
id: 1,
|
266
|
+
first: 'Bruce',
|
267
|
+
last: 'Banner',
|
268
|
+
costumes: [
|
269
|
+
{ id: 3, name: 'Basic Hulk', color: 'Green' },
|
270
|
+
{ id: 4, name: 'Mad Hulk', color: 'Red' }
|
271
|
+
]
|
272
|
+
},
|
273
|
+
{
|
274
|
+
id: 2,
|
275
|
+
first: 'Tony',
|
276
|
+
last: 'Stark',
|
277
|
+
costumes: [
|
278
|
+
{ id: 5, name: 'Mark I', color: 'Gray' },
|
279
|
+
{ id: 6, name: 'Mark IV', color: 'Red' },
|
280
|
+
{ id: 7, name: 'Mark VI', color: 'Nano-Blue' }
|
281
|
+
]
|
282
|
+
}
|
283
|
+
]
|
284
|
+
|
285
|
+
expect(objects).to eq(expected)
|
286
|
+
end
|
287
|
+
|
288
|
+
specify 'Multiple Top-Level Graphs should work' do
|
289
|
+
config = {
|
290
|
+
types: {
|
291
|
+
person: {
|
292
|
+
properties: {
|
293
|
+
id: 'ID #',
|
294
|
+
first: 'First Name',
|
295
|
+
last: 'Last Name'
|
296
|
+
}
|
297
|
+
},
|
298
|
+
costume: {
|
299
|
+
properties: {
|
300
|
+
id: 'Costume ID #',
|
301
|
+
name: 'Costume Name',
|
302
|
+
color: 'Costume Color'
|
303
|
+
}
|
304
|
+
}
|
305
|
+
},
|
306
|
+
groups: {
|
307
|
+
avengers: {
|
308
|
+
by: 'ID #',
|
309
|
+
type: :person,
|
310
|
+
groups: {
|
311
|
+
costumes: {
|
312
|
+
by: 'Costume ID #',
|
313
|
+
type: :costume
|
314
|
+
}
|
315
|
+
}
|
316
|
+
},
|
317
|
+
costumes: {
|
318
|
+
by: 'Costume ID #',
|
319
|
+
type: :costume
|
320
|
+
}
|
321
|
+
}
|
322
|
+
}
|
323
|
+
|
324
|
+
rows = [
|
325
|
+
{
|
326
|
+
'ID #' => 1,
|
327
|
+
'First Name' => 'Bruce',
|
328
|
+
'Last Name' => 'Banner',
|
329
|
+
'Costume ID #' => 3,
|
330
|
+
'Costume Name' => 'Basic Hulk',
|
331
|
+
'Costume Color' => 'Green'
|
332
|
+
},
|
333
|
+
{
|
334
|
+
'ID #' => 1,
|
335
|
+
'First Name' => 'Bruce',
|
336
|
+
'Last Name' => 'Banner',
|
337
|
+
'Costume ID #' => 4,
|
338
|
+
'Costume Name' => 'Mad Hulk',
|
339
|
+
'Costume Color' => 'Red'
|
340
|
+
},
|
341
|
+
{
|
342
|
+
'ID #' => 2,
|
343
|
+
'First Name' => 'Tony',
|
344
|
+
'Last Name' => 'Stark',
|
345
|
+
'Costume ID #' => 5,
|
346
|
+
'Costume Name' => 'Mark I',
|
347
|
+
'Costume Color' => 'Gray'
|
348
|
+
},
|
349
|
+
{
|
350
|
+
'ID #' => 2,
|
351
|
+
'First Name' => 'Tony',
|
352
|
+
'Last Name' => 'Stark',
|
353
|
+
'Costume ID #' => 6,
|
354
|
+
'Costume Name' => 'Mark IV',
|
355
|
+
'Costume Color' => 'Red'
|
356
|
+
},
|
357
|
+
{
|
358
|
+
'ID #' => 2,
|
359
|
+
'First Name' => 'Tony',
|
360
|
+
'Last Name' => 'Stark',
|
361
|
+
'Costume ID #' => 7,
|
362
|
+
'Costume Name' => 'Mark VI',
|
363
|
+
'Costume Color' => 'Nano-Blue'
|
364
|
+
}
|
365
|
+
]
|
366
|
+
|
367
|
+
graph = ::Hashematics.graph(config: config, rows: rows)
|
368
|
+
objects = graph.data(:costumes)
|
369
|
+
|
370
|
+
expected = [
|
371
|
+
{ id: 3, name: 'Basic Hulk', color: 'Green' },
|
372
|
+
{ id: 4, name: 'Mad Hulk', color: 'Red' },
|
373
|
+
{ id: 5, name: 'Mark I', color: 'Gray' },
|
374
|
+
{ id: 6, name: 'Mark IV', color: 'Red' },
|
375
|
+
{ id: 7, name: 'Mark VI', color: 'Nano-Blue' }
|
376
|
+
]
|
377
|
+
|
378
|
+
expect(objects).to eq(expected)
|
379
|
+
end
|
380
|
+
|
381
|
+
specify 'Handling Blanks (skip - default) should work' do
|
382
|
+
config = {
|
383
|
+
types: {
|
384
|
+
person: {
|
385
|
+
properties: {
|
386
|
+
id: 'ID #',
|
387
|
+
first: 'First Name',
|
388
|
+
last: 'Last Name'
|
389
|
+
}
|
390
|
+
},
|
391
|
+
costume: {
|
392
|
+
properties: {
|
393
|
+
id: 'Costume ID #',
|
394
|
+
name: 'Costume Name',
|
395
|
+
color: 'Costume Color'
|
396
|
+
}
|
397
|
+
}
|
398
|
+
},
|
399
|
+
groups: {
|
400
|
+
avengers: {
|
401
|
+
by: 'ID #',
|
402
|
+
type: :person,
|
403
|
+
groups: {
|
404
|
+
costumes: {
|
405
|
+
by: 'Costume ID #',
|
406
|
+
type: :costume
|
407
|
+
}
|
408
|
+
}
|
409
|
+
},
|
410
|
+
costumes: {
|
411
|
+
by: 'Costume ID #',
|
412
|
+
type: :costume
|
413
|
+
}
|
414
|
+
}
|
415
|
+
}
|
416
|
+
|
417
|
+
rows = [
|
418
|
+
{
|
419
|
+
'ID #' => 1,
|
420
|
+
'First Name' => 'Bruce',
|
421
|
+
'Last Name' => 'Banner',
|
422
|
+
'Costume ID #' => 3,
|
423
|
+
'Costume Name' => 'Basic Hulk',
|
424
|
+
'Costume Color' => 'Green'
|
425
|
+
},
|
426
|
+
{
|
427
|
+
'ID #' => 2,
|
428
|
+
'First Name' => 'Tony',
|
429
|
+
'Last Name' => 'Stark',
|
430
|
+
'Costume ID #' => '',
|
431
|
+
'Costume Name' => '',
|
432
|
+
'Costume Color' => ''
|
433
|
+
},
|
434
|
+
{
|
435
|
+
'Costume ID #' => 4,
|
436
|
+
'Costume Name' => 'Undercover',
|
437
|
+
'Costume Color' => 'Purple'
|
438
|
+
}
|
439
|
+
]
|
440
|
+
|
441
|
+
graph = ::Hashematics.graph(config: config, rows: rows)
|
442
|
+
avengers = graph.data(:avengers)
|
443
|
+
costumes = graph.data(:costumes)
|
444
|
+
|
445
|
+
expected_avengers = [
|
446
|
+
{
|
447
|
+
id: 1,
|
448
|
+
first: 'Bruce',
|
449
|
+
last: 'Banner',
|
450
|
+
costumes: [
|
451
|
+
{ id: 3, name: 'Basic Hulk', color: 'Green' }
|
452
|
+
]
|
453
|
+
},
|
454
|
+
{
|
455
|
+
id: 2,
|
456
|
+
first: 'Tony',
|
457
|
+
last: 'Stark',
|
458
|
+
costumes: []
|
459
|
+
}
|
460
|
+
]
|
461
|
+
|
462
|
+
expected_costumes = [
|
463
|
+
{ id: 3, name: 'Basic Hulk', color: 'Green' },
|
464
|
+
{ id: 4, name: 'Undercover', color: 'Purple' }
|
465
|
+
]
|
466
|
+
|
467
|
+
expect(avengers).to eq(expected_avengers)
|
468
|
+
expect(costumes).to eq(expected_costumes)
|
469
|
+
end
|
470
|
+
|
471
|
+
specify 'Handling Blanks (include) should work' do
|
472
|
+
config = {
|
473
|
+
types: {
|
474
|
+
person: {
|
475
|
+
properties: {
|
476
|
+
id: 'ID #',
|
477
|
+
first: 'First Name',
|
478
|
+
last: 'Last Name'
|
479
|
+
}
|
480
|
+
},
|
481
|
+
costume: {
|
482
|
+
properties: {
|
483
|
+
id: 'Costume ID #',
|
484
|
+
name: 'Costume Name',
|
485
|
+
color: 'Costume Color'
|
486
|
+
}
|
487
|
+
}
|
488
|
+
},
|
489
|
+
groups: {
|
490
|
+
avengers: {
|
491
|
+
by: 'ID #',
|
492
|
+
include_blank: true,
|
493
|
+
type: :person,
|
494
|
+
groups: {
|
495
|
+
costumes: {
|
496
|
+
by: 'Costume ID #',
|
497
|
+
type: :costume
|
498
|
+
}
|
499
|
+
}
|
500
|
+
},
|
501
|
+
costumes: {
|
502
|
+
by: 'Costume ID #',
|
503
|
+
include_blank: true,
|
504
|
+
type: :costume
|
505
|
+
}
|
506
|
+
}
|
507
|
+
}
|
508
|
+
|
509
|
+
rows = [
|
510
|
+
{
|
511
|
+
'ID #' => 1,
|
512
|
+
'First Name' => 'Bruce',
|
513
|
+
'Last Name' => 'Banner',
|
514
|
+
'Costume ID #' => 3,
|
515
|
+
'Costume Name' => 'Basic Hulk',
|
516
|
+
'Costume Color' => 'Green'
|
517
|
+
},
|
518
|
+
{
|
519
|
+
'ID #' => 2,
|
520
|
+
'First Name' => 'Tony',
|
521
|
+
'Last Name' => 'Stark',
|
522
|
+
'Costume ID #' => '',
|
523
|
+
'Costume Name' => '',
|
524
|
+
'Costume Color' => ''
|
525
|
+
},
|
526
|
+
{
|
527
|
+
'Costume ID #' => 4,
|
528
|
+
'Costume Name' => 'Undercover',
|
529
|
+
'Costume Color' => 'Purple'
|
530
|
+
}
|
531
|
+
]
|
532
|
+
|
533
|
+
graph = ::Hashematics.graph(config: config, rows: rows)
|
534
|
+
avengers = graph.data(:avengers)
|
535
|
+
costumes = graph.data(:costumes)
|
536
|
+
|
537
|
+
expected_avengers = [
|
538
|
+
{
|
539
|
+
id: 1,
|
540
|
+
first: 'Bruce',
|
541
|
+
last: 'Banner',
|
542
|
+
costumes: [
|
543
|
+
{ id: 3, name: 'Basic Hulk', color: 'Green' }
|
544
|
+
]
|
545
|
+
},
|
546
|
+
{
|
547
|
+
id: 2,
|
548
|
+
first: 'Tony',
|
549
|
+
last: 'Stark',
|
550
|
+
costumes: []
|
551
|
+
},
|
552
|
+
{
|
553
|
+
id: nil,
|
554
|
+
first: nil,
|
555
|
+
last: nil,
|
556
|
+
costumes: [
|
557
|
+
{ id: 4, name: 'Undercover', color: 'Purple' }
|
558
|
+
]
|
559
|
+
}
|
560
|
+
]
|
561
|
+
|
562
|
+
expected_costumes = [
|
563
|
+
{ id: 3, name: 'Basic Hulk', color: 'Green' },
|
564
|
+
{ id: '', name: '', color: '' },
|
565
|
+
{ id: 4, name: 'Undercover', color: 'Purple' }
|
566
|
+
]
|
567
|
+
|
568
|
+
expect(avengers).to eq(expected_avengers)
|
569
|
+
expect(costumes).to eq(expected_costumes)
|
570
|
+
end
|
571
|
+
end
|
572
|
+
end
|