cranium 0.2.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/.gitignore +21 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +3 -0
- data/Vagrantfile +24 -0
- data/bin/cranium +9 -0
- data/config/cucumber.yml +9 -0
- data/cranium.gemspec +26 -0
- data/db/setup.sql +8 -0
- data/docker-compose.yml +8 -0
- data/examples/config.rb +14 -0
- data/examples/deduplication.rb +27 -0
- data/examples/import_csv_with_field_lookup_inserting_new_dimension_keys.rb +26 -0
- data/examples/incremental_extract.rb +17 -0
- data/examples/lookup_with_multiple_fields.rb +25 -0
- data/features/archive.feature +49 -0
- data/features/extract/incremental_extract.feature +56 -0
- data/features/extract/simple_extract.feature +85 -0
- data/features/import/import_csv_to_database_as_delta.feature +38 -0
- data/features/import/import_csv_to_database_with_delete_insert_merging.feature +51 -0
- data/features/import/import_csv_to_database_with_truncate_insert.feature +49 -0
- data/features/import/import_csv_to_database_with_update_merging.feature +46 -0
- data/features/import/import_csv_with_always_inserting_new_dimension_keys.feature +137 -0
- data/features/import/import_csv_with_field_lookup_inserting_new_dimension_keys.feature +62 -0
- data/features/import/import_csv_with_field_lookup_transformation.feature +125 -0
- data/features/import/import_csv_with_transformation.feature +55 -0
- data/features/import/import_multiple_csv_files_without_transformations.feature +44 -0
- data/features/import/import_with_load_id_from_sequence.feature +53 -0
- data/features/import/import_with_lookup_from_multiple_fields.feature +64 -0
- data/features/read.feature +56 -0
- data/features/remove.feature +44 -0
- data/features/restore_database_connection.feature +55 -0
- data/features/step_definitions/database_table_steps.rb +40 -0
- data/features/step_definitions/definition_steps.rb +3 -0
- data/features/step_definitions/execution_steps.rb +23 -0
- data/features/step_definitions/file_steps.rb +39 -0
- data/features/support/class_extensions.rb +24 -0
- data/features/support/env.rb +27 -0
- data/features/support/randomize.rb +22 -0
- data/features/support/stop_on_first_error.rb +5 -0
- data/features/transform/deduplication.feature +37 -0
- data/features/transform/empty_transformation.feature +72 -0
- data/features/transform/join.feature +180 -0
- data/features/transform/join_multiple_files_into_one_output_file.feature +46 -0
- data/features/transform/output_rows.feature +70 -0
- data/features/transform/projection.feature +34 -0
- data/features/transform/raw_ruby_transformation.feature +69 -0
- data/features/transform/split_field.feature +39 -0
- data/lib/cranium/application.rb +104 -0
- data/lib/cranium/archiver.rb +36 -0
- data/lib/cranium/attribute_dsl.rb +43 -0
- data/lib/cranium/command_line_options.rb +27 -0
- data/lib/cranium/configuration.rb +33 -0
- data/lib/cranium/data_importer.rb +35 -0
- data/lib/cranium/data_reader.rb +48 -0
- data/lib/cranium/data_transformer.rb +126 -0
- data/lib/cranium/database.rb +36 -0
- data/lib/cranium/definition_registry.rb +21 -0
- data/lib/cranium/dimension_manager.rb +65 -0
- data/lib/cranium/dsl/database_definition.rb +23 -0
- data/lib/cranium/dsl/extract_definition.rb +28 -0
- data/lib/cranium/dsl/import_definition.rb +50 -0
- data/lib/cranium/dsl/source_definition.rb +67 -0
- data/lib/cranium/dsl.rb +100 -0
- data/lib/cranium/extensions/file.rb +7 -0
- data/lib/cranium/extensions/sequel_greenplum.rb +30 -0
- data/lib/cranium/external_table.rb +75 -0
- data/lib/cranium/extract/data_extractor.rb +11 -0
- data/lib/cranium/extract/storage.rb +57 -0
- data/lib/cranium/extract/strategy/base.rb +27 -0
- data/lib/cranium/extract/strategy/incremental.rb +16 -0
- data/lib/cranium/extract/strategy/simple.rb +9 -0
- data/lib/cranium/extract/strategy.rb +7 -0
- data/lib/cranium/extract.rb +7 -0
- data/lib/cranium/import_strategy/base.rb +55 -0
- data/lib/cranium/import_strategy/delete_insert.rb +40 -0
- data/lib/cranium/import_strategy/delta.rb +8 -0
- data/lib/cranium/import_strategy/merge.rb +50 -0
- data/lib/cranium/import_strategy/truncate_insert.rb +19 -0
- data/lib/cranium/import_strategy.rb +9 -0
- data/lib/cranium/logging.rb +15 -0
- data/lib/cranium/profiling.rb +13 -0
- data/lib/cranium/progress_output.rb +37 -0
- data/lib/cranium/sequel/hash.rb +32 -0
- data/lib/cranium/sequel.rb +5 -0
- data/lib/cranium/source_registry.rb +21 -0
- data/lib/cranium/test_framework/cucumber_table.rb +140 -0
- data/lib/cranium/test_framework/database_entity.rb +29 -0
- data/lib/cranium/test_framework/database_sequence.rb +16 -0
- data/lib/cranium/test_framework/database_table.rb +33 -0
- data/lib/cranium/test_framework/upload_directory.rb +39 -0
- data/lib/cranium/test_framework/world.rb +66 -0
- data/lib/cranium/test_framework.rb +10 -0
- data/lib/cranium/transformation/duplication_index.rb +42 -0
- data/lib/cranium/transformation/index.rb +83 -0
- data/lib/cranium/transformation/join.rb +141 -0
- data/lib/cranium/transformation/sequence.rb +42 -0
- data/lib/cranium/transformation.rb +8 -0
- data/lib/cranium/transformation_record.rb +45 -0
- data/lib/cranium.rb +57 -0
- data/rake/test.rake +31 -0
- data/spec/cranium/application_spec.rb +166 -0
- data/spec/cranium/archiver_spec.rb +44 -0
- data/spec/cranium/command_line_options_spec.rb +32 -0
- data/spec/cranium/configuration_spec.rb +31 -0
- data/spec/cranium/data_importer_spec.rb +55 -0
- data/spec/cranium/data_transformer_spec.rb +16 -0
- data/spec/cranium/database_spec.rb +69 -0
- data/spec/cranium/definition_registry_spec.rb +45 -0
- data/spec/cranium/dimension_manager_spec.rb +63 -0
- data/spec/cranium/dsl/database_definition_spec.rb +23 -0
- data/spec/cranium/dsl/extract_definition_spec.rb +76 -0
- data/spec/cranium/dsl/import_definition_spec.rb +153 -0
- data/spec/cranium/dsl/source_definition_spec.rb +84 -0
- data/spec/cranium/dsl_spec.rb +119 -0
- data/spec/cranium/external_table_spec.rb +71 -0
- data/spec/cranium/extract/storage_spec.rb +125 -0
- data/spec/cranium/logging_spec.rb +37 -0
- data/spec/cranium/sequel/hash_spec.rb +56 -0
- data/spec/cranium/source_registry_spec.rb +31 -0
- data/spec/cranium/test_framework/cucumber_table_spec.rb +144 -0
- data/spec/cranium/transformation/duplication_index_spec.rb +75 -0
- data/spec/cranium/transformation/index_spec.rb +178 -0
- data/spec/cranium/transformation/join_spec.rb +43 -0
- data/spec/cranium/transformation/sequence_spec.rb +83 -0
- data/spec/cranium/transformation_record_spec.rb +78 -0
- data/spec/cranium_spec.rb +53 -0
- data/spec/spec_helper.rb +1 -0
- metadata +362 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
require_relative '../../spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Cranium::Transformation::Index do
|
|
4
|
+
|
|
5
|
+
let(:index) { Cranium::Transformation::Index.new }
|
|
6
|
+
let(:connection) { double "Greenplum connection" }
|
|
7
|
+
let(:dimension_manager) { double "DimensionManager" }
|
|
8
|
+
|
|
9
|
+
before(:each) do
|
|
10
|
+
allow(Cranium::Database).to receive(:connection).and_return(connection)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def stub_cache_query(table_name, key_fields, value_field, result)
|
|
16
|
+
allow(dimension_manager).to receive(:create_cache_for_field).with(value_field).and_return(result)
|
|
17
|
+
allow(Cranium::DimensionManager).to receive(:for).with(table_name, key_fields).and_return(dimension_manager)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
describe "#lookup" do
|
|
23
|
+
context "the first time it's called" do
|
|
24
|
+
it "should query the requested key value from the database" do
|
|
25
|
+
stub_cache_query :dim_contact, [:customer_id], :contact_key, {[1234] => "contact 1",
|
|
26
|
+
[2345] => "contact 2",
|
|
27
|
+
[3456] => "contact 3"}
|
|
28
|
+
|
|
29
|
+
expect(index.lookup(:contact_key, from_table: :dim_contact, match_column: :customer_id, to_value: 2345)).to eq("contact 2")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "should query the requested multi-key value from the database" do
|
|
33
|
+
stub_cache_query :dim_contact, [:key_1, :key_2], :contact_key, {[12, 34] => "contact 1",
|
|
34
|
+
[23, 45] => "contact 2",
|
|
35
|
+
[34, 56] => "contact 3"}
|
|
36
|
+
|
|
37
|
+
expect(index.lookup(:contact_key, from_table: :dim_contact, match: {key_1: 23, key_2: 45})).to eq("contact 2")
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
context "on subsequent calls" do
|
|
43
|
+
it "should look up the requested value from an internal cache" do
|
|
44
|
+
stub_cache_query :dim_contact, [:customer_id], :contact_key, {[1234] => "contact 1"}
|
|
45
|
+
index.lookup(:contact_key, from_table: :dim_contact, match_column: :customer_id, to_value: 1234)
|
|
46
|
+
|
|
47
|
+
stub_cache_query :dim_contact, [:customer_id], :contact_key, {[1234] => "contact 2"}
|
|
48
|
+
expect(index.lookup(:contact_key, from_table: :dim_contact, match_column: :customer_id, to_value: 1234)).to eq("contact 1")
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
it "should return :not_found if the key value was not found" do
|
|
54
|
+
stub_cache_query :dim_contact, [:customer_id], :contact_key, {[1234] => "contact 2"}
|
|
55
|
+
|
|
56
|
+
expect(index.lookup(:contact_key, from_table: :dim_contact, match_column: :customer_id, to_value: 2345)).to eq(:not_found)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
it "should raise an error if both :if_not_found_then_insert and :if_not_found_then are specified" do
|
|
61
|
+
expect do
|
|
62
|
+
index.lookup(:contact_key, if_not_found_then: -1, if_not_found_then_insert: {})
|
|
63
|
+
end.to raise_error ArgumentError, "Cannot specify both :if_not_found_then and :if_not_found_then_insert options"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
context "when :if_not_found_then is specified" do
|
|
68
|
+
it "should return the specified value if the lookup failed" do
|
|
69
|
+
stub_cache_query :dim_contact, [:customer_id], :contact_key, {[1234] => "contact"}
|
|
70
|
+
|
|
71
|
+
expect(index.lookup(:contact_key,
|
|
72
|
+
from_table: :dim_contact,
|
|
73
|
+
match_column: :customer_id,
|
|
74
|
+
to_value: 2345,
|
|
75
|
+
if_not_found_then: -1)).to eq(-1)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
context "when the specified value is a lamba expression" do
|
|
79
|
+
it "should evaluate the expression and return its value if the lookup failed" do
|
|
80
|
+
stub_cache_query :dim_contact, [:customer_id], :contact_key, {[1234] => "contact"}
|
|
81
|
+
|
|
82
|
+
expect(index.lookup(:contact_key,
|
|
83
|
+
from_table: :dim_contact,
|
|
84
|
+
match_column: :customer_id,
|
|
85
|
+
to_value: 2345,
|
|
86
|
+
if_not_found_then: -> { -1 })).to eq(-1)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
context "when :if_not_found_then_insert is specified" do
|
|
93
|
+
|
|
94
|
+
before(:each) do
|
|
95
|
+
stub_cache_query :dim_contact, [:customer_id], :contact_key, {[1234] => "contact"}
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
context "when a single key is used" do
|
|
99
|
+
it "should insert a new record into the specified table" do
|
|
100
|
+
expect(dimension_manager).to receive(:insert).with(:contact_key, {contact_key: 1, customer_id: 2345})
|
|
101
|
+
|
|
102
|
+
index.lookup :contact_key,
|
|
103
|
+
from_table: :dim_contact,
|
|
104
|
+
match_column: :customer_id,
|
|
105
|
+
to_value: 2345,
|
|
106
|
+
if_not_found_then_insert: {contact_key: 1, customer_id: 2345}
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
context "when multiple keys are used" do
|
|
111
|
+
it "should insert a new record into the specified table" do
|
|
112
|
+
stub_cache_query :dim_contact, [:key_1, :key_2], :contact_key, {[12, 34] => "contact"}
|
|
113
|
+
|
|
114
|
+
expect(dimension_manager).to receive(:insert).with(:contact_key, contact_key: 1, key_1: 23, key_2: 45)
|
|
115
|
+
|
|
116
|
+
index.lookup :contact_key,
|
|
117
|
+
from_table: :dim_contact,
|
|
118
|
+
match: {key_1: 23, key_2: 45},
|
|
119
|
+
if_not_found_then_insert: {contact_key: 1, key_1: 23, key_2: 45}
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it "should fill out the new record's lookup key automatically" do
|
|
124
|
+
expect(dimension_manager).to receive(:insert).with(:contact_key, name: "new contact", customer_id: 2345)
|
|
125
|
+
|
|
126
|
+
index.lookup :contact_key,
|
|
127
|
+
from_table: :dim_contact,
|
|
128
|
+
match_column: :customer_id,
|
|
129
|
+
to_value: 2345,
|
|
130
|
+
if_not_found_then_insert: {name: "new contact"}
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
it "should overwrite the new record's lookup key if specified" do
|
|
134
|
+
expect(dimension_manager).to receive(:insert).with(:contact_key, customer_id: 2345)
|
|
135
|
+
|
|
136
|
+
index.lookup :contact_key,
|
|
137
|
+
from_table: :dim_contact,
|
|
138
|
+
match_column: :customer_id,
|
|
139
|
+
to_value: 2345,
|
|
140
|
+
if_not_found_then_insert: {customer_id: 4567}
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
it "should return the new record's lookup value" do
|
|
144
|
+
allow(dimension_manager).to receive_messages insert: 98765
|
|
145
|
+
|
|
146
|
+
expect(index.lookup(:contact_key,
|
|
147
|
+
from_table: :dim_contact,
|
|
148
|
+
match_column: :customer_id,
|
|
149
|
+
to_value: 2345,
|
|
150
|
+
if_not_found_then_insert: {contact_key: 98765})).to eq(98765)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
describe "#insert" do
|
|
157
|
+
before(:each) do
|
|
158
|
+
stub_cache_query :dim_contact, [:contact_key], :contact_key, {[1234] => "contact"}
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
it "should insert a new record into the specified table" do
|
|
162
|
+
expect(dimension_manager).to receive(:insert).with(:contact_key, {contact_key: 1, customer_id: 2345})
|
|
163
|
+
|
|
164
|
+
index.insert :contact_key,
|
|
165
|
+
table: :dim_contact,
|
|
166
|
+
record: {contact_key: 1, customer_id: 2345}
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
it "should return the new record's lookup value" do
|
|
170
|
+
allow(dimension_manager).to receive_messages insert: 98765
|
|
171
|
+
|
|
172
|
+
expect(index.insert :contact_key,
|
|
173
|
+
table: :dim_contact,
|
|
174
|
+
record: {contact_key: 98765}).to eq(98765)
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require_relative '../../spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Cranium::Transformation::Join do
|
|
4
|
+
|
|
5
|
+
let(:join) { Cranium::Transformation::Join.new }
|
|
6
|
+
|
|
7
|
+
describe "#execute" do
|
|
8
|
+
context "when validating its parameters" do
|
|
9
|
+
before(:each) do
|
|
10
|
+
join.source_left = "left source"
|
|
11
|
+
join.source_right = "right source"
|
|
12
|
+
join.target = "target source"
|
|
13
|
+
join.match_fields = { field1: :field2 }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "should raise an error if :source_left isn't set" do
|
|
17
|
+
join.source_left = nil
|
|
18
|
+
expect { join.execute }.to raise_error "Missing left source for join transformation"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "should raise an error if :source_right isn't set" do
|
|
22
|
+
join.source_right = nil
|
|
23
|
+
expect { join.execute }.to raise_error "Missing right source for join transformation"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "should raise an error if :target isn't set" do
|
|
27
|
+
join.target = nil
|
|
28
|
+
expect { join.execute }.to raise_error "Missing target for join transformation"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "should raise an error if :match_fields is set but isn't a Hash" do
|
|
32
|
+
join.match_fields = :field
|
|
33
|
+
expect { join.execute }.to raise_error "Invalid match fields for join transformation"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "should raise an error if :type is not supported" do
|
|
37
|
+
join.type = :cross
|
|
38
|
+
expect { join.execute }.to raise_error "Invalid type for join transformation"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
require_relative '../../spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Cranium::Transformation::Sequence do
|
|
4
|
+
|
|
5
|
+
def stub_next_value(value)
|
|
6
|
+
dataset = double "dataset"
|
|
7
|
+
allow(dataset).to receive_messages first: { next_value: value }
|
|
8
|
+
allow(Cranium::Database).to receive_message_chain(:connection, :[]).with("SELECT nextval('id_seq') AS next_value").and_return(dataset)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
let(:sequence) { Cranium::Transformation::Sequence.new :id_seq }
|
|
14
|
+
|
|
15
|
+
describe ".by_name" do
|
|
16
|
+
before(:each) { Cranium::Transformation::Sequence.instance_variable_set :@sequences, nil }
|
|
17
|
+
|
|
18
|
+
it "should return a sequence with the specified name" do
|
|
19
|
+
sequence = Cranium::Transformation::Sequence.by_name(:id_seq)
|
|
20
|
+
|
|
21
|
+
expect(sequence).to be_a Cranium::Transformation::Sequence
|
|
22
|
+
expect(sequence.name).to eq(:id_seq)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "should schedule a newly created sequence's flush method as an after_import hook" do
|
|
26
|
+
expect(Cranium.application).to receive(:after_import).once
|
|
27
|
+
|
|
28
|
+
Cranium::Transformation::Sequence.by_name(:id_seq)
|
|
29
|
+
Cranium::Transformation::Sequence.by_name(:id_seq)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "should memoize previously created instances" do
|
|
33
|
+
expect(Cranium::Transformation::Sequence.by_name(:id_seq)).to equal Cranium::Transformation::Sequence.by_name(:id_seq)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
describe "#next_value" do
|
|
39
|
+
before(:each) do
|
|
40
|
+
stub_next_value 123
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
context "the first time it's called" do
|
|
44
|
+
it "should return the named sequence's next value read from the database" do
|
|
45
|
+
expect(sequence.next_value).to eq(123)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
context "on subsequent calls" do
|
|
50
|
+
it "should increment its counter internally without querying the database" do
|
|
51
|
+
expect(sequence.next_value).to eq(123)
|
|
52
|
+
|
|
53
|
+
expect(Cranium::Database).not_to receive :connection
|
|
54
|
+
|
|
55
|
+
expect(sequence.next_value).to eq(124)
|
|
56
|
+
expect(sequence.next_value).to eq(125)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
describe "#flush" do
|
|
63
|
+
let(:sequence) { Cranium::Transformation::Sequence.new :id_seq }
|
|
64
|
+
|
|
65
|
+
it "should not use the database if no value was ever requested from the sequence" do
|
|
66
|
+
expect(Cranium::Database).not_to receive :connection
|
|
67
|
+
|
|
68
|
+
sequence.flush
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it "should set the database sequence's value to the last value returned" do
|
|
72
|
+
stub_next_value 123
|
|
73
|
+
sequence.next_value
|
|
74
|
+
|
|
75
|
+
connection = double "connection"
|
|
76
|
+
allow(Cranium::Database).to receive_messages connection: connection
|
|
77
|
+
expect(connection).to receive(:run).with("SELECT setval('id_seq', 123)")
|
|
78
|
+
|
|
79
|
+
sequence.flush
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
require_relative '../spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Cranium::TransformationRecord do
|
|
4
|
+
|
|
5
|
+
let(:record) { Cranium::TransformationRecord.new [:field1, :field2, :field3], [:field1, :field2, :field3] }
|
|
6
|
+
|
|
7
|
+
describe "#input_data=" do
|
|
8
|
+
it "should set the input data row" do
|
|
9
|
+
record.input_data = ["one", "three", "five"]
|
|
10
|
+
expect(record.data).to eq({ :field1 => "one", :field2 => "three", :field3 => "five" })
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
describe "#[]" do
|
|
16
|
+
it "should access the fields of the record as if it were a Hash" do
|
|
17
|
+
record.input_data = ["one", "two", "three"]
|
|
18
|
+
expect(record[:field2]).to eq("two")
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
describe "#[]=" do
|
|
24
|
+
it "should set the fields of the record as if it were a Hash" do
|
|
25
|
+
record.input_data = ["one", "two", "three"]
|
|
26
|
+
record[:field2] = "four"
|
|
27
|
+
expect(record[:field2]).to eq("four")
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
describe "#split_field" do
|
|
33
|
+
|
|
34
|
+
let(:record) { Cranium::TransformationRecord.new [:source_field], [:target_field1, :target_field2] }
|
|
35
|
+
|
|
36
|
+
it "should store values into respective fields and drop extra values" do
|
|
37
|
+
record.input_data = ["first|second|third"]
|
|
38
|
+
record.split_field :source_field, into: [:target_field1, :target_field2], by: "|"
|
|
39
|
+
expect(record[:target_field1]).to eq("first")
|
|
40
|
+
expect(record[:target_field2]).to eq("second")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
context "when default value is not set" do
|
|
44
|
+
it "should repeat last value when there are not enough values" do
|
|
45
|
+
record.input_data = ["first"]
|
|
46
|
+
record.split_field :source_field, into: [:target_field1, :target_field2], by: "|"
|
|
47
|
+
expect(record[:target_field1]).to eq("first")
|
|
48
|
+
expect(record[:target_field2]).to eq("first")
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
context "when default value is specified" do
|
|
53
|
+
it "should use default value when there are not enough values" do
|
|
54
|
+
record.input_data = ["first"]
|
|
55
|
+
record.split_field :source_field, into: [:target_field1, :target_field2], by: "|", default_value: "--"
|
|
56
|
+
expect(record[:target_field1]).to eq("first")
|
|
57
|
+
expect(record[:target_field2]).to eq("--")
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
describe "#has_key?" do
|
|
65
|
+
it "should return true if the record contains data for the specified key" do
|
|
66
|
+
record.input_data = ["one", "two", "three"]
|
|
67
|
+
|
|
68
|
+
expect(record.has_key?(:field1)).to be_truthy
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it "should return false if the record doesn't contain data for the specified key" do
|
|
72
|
+
record.input_data = ["one", "two", "three"]
|
|
73
|
+
|
|
74
|
+
expect(record.has_key?(:field4)).to be_falsey
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require_relative 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Cranium do
|
|
4
|
+
|
|
5
|
+
describe ".application" do
|
|
6
|
+
it "should return an Application object" do
|
|
7
|
+
expect(Cranium.application).to be_a Cranium::Application
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "should return a singleton" do
|
|
11
|
+
expect(Cranium.application).to equal Cranium.application
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
describe ".configure" do
|
|
17
|
+
it "should set or modify the existing configuration" do
|
|
18
|
+
Cranium.configure do |config|
|
|
19
|
+
config.greenplum_connection_string = "greenplum connection"
|
|
20
|
+
config.mysql_connection_string = "mysql connection"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
Cranium.configure do |config|
|
|
24
|
+
config.greenplum_connection_string = "new greenplum connection"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
expect(Cranium.configuration.greenplum_connection_string).to eq("new greenplum connection")
|
|
28
|
+
expect(Cranium.configuration.mysql_connection_string).to eq("mysql connection")
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
describe ".configuration" do
|
|
34
|
+
it "should return the configuration" do
|
|
35
|
+
expect(Cranium.configuration).to be_a Cranium::Configuration
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "should not let the user modify the configuration" do
|
|
39
|
+
expect { Cranium.configuration.greenplum_connection_string = "constring" }.to raise_error(RuntimeError)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
describe ".load_arguments" do
|
|
45
|
+
it "should return the load arguments of the application" do
|
|
46
|
+
app = double "application", load_arguments: "load_arguments"
|
|
47
|
+
allow(Cranium).to receive(:application).and_return(app)
|
|
48
|
+
|
|
49
|
+
expect(Cranium.load_arguments).to eq "load_arguments"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require_relative '../lib/cranium'
|