cranium 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,69 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
require 'sequel'
|
3
|
+
require 'sequel/adapters/mock'
|
4
|
+
|
5
|
+
describe Cranium::Database do
|
6
|
+
|
7
|
+
let(:database) { Cranium::Database }
|
8
|
+
let(:connection) { Sequel::Mock::Database.new }
|
9
|
+
let(:other_connection) { Sequel::Mock::Database.new }
|
10
|
+
|
11
|
+
before(:each) do
|
12
|
+
allow(Cranium).to receive(:configuration).and_return(Cranium::Configuration.new.tap do |config|
|
13
|
+
config.greenplum_connection_string = "connection string"
|
14
|
+
config.loggers = "loggers"
|
15
|
+
end)
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
describe ".connection" do
|
20
|
+
before { Cranium::Database.instance_variable_set :@connection, nil }
|
21
|
+
|
22
|
+
it "should connect to the DB" do
|
23
|
+
expect(Sequel).to receive(:connect).with("connection string", :loggers => "loggers").and_return connection
|
24
|
+
|
25
|
+
expect(database.connection).to eq connection
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should return the same object every time" do
|
29
|
+
allow(Sequel).to receive(:connect).and_return(connection, other_connection)
|
30
|
+
|
31
|
+
expect(database.connection).to eq database.connection
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
describe ".[]" do
|
37
|
+
before do
|
38
|
+
database.instance_variable_set :@connections, nil
|
39
|
+
database.instance_variable_set :@definitions, nil
|
40
|
+
|
41
|
+
database.register_database :dwh do
|
42
|
+
connect_to "other connection string"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should return the specified database connection" do
|
47
|
+
expect(Sequel).to receive(:connect).with("other connection string", :loggers => "loggers").and_return connection
|
48
|
+
|
49
|
+
expect(database[:dwh]).to eq connection
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should memoize the result of a previous call" do
|
53
|
+
allow(Sequel).to receive(:connect).and_return(connection, other_connection)
|
54
|
+
|
55
|
+
expect(database[:dwh]).to eq database[:dwh]
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should memoize connections by name" do
|
59
|
+
database.register_database :dwh2 do
|
60
|
+
connect_to "other connection string 2"
|
61
|
+
end
|
62
|
+
|
63
|
+
allow(Sequel).to receive(:connect).and_return(connection, other_connection)
|
64
|
+
|
65
|
+
expect(database[:dwh]).not_to eq database[:dwh2]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe Cranium::DefinitionRegistry do
|
4
|
+
|
5
|
+
let(:registry) { Cranium::DefinitionRegistry.new Cranium::DSL::DatabaseDefinition }
|
6
|
+
|
7
|
+
describe "#[]" do
|
8
|
+
it "should return nil if a definition with the specified name wasn't registered yet" do
|
9
|
+
expect(registry[:name]).to be_nil
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
describe "#register_definition" do
|
15
|
+
it "should return the newly registered source" do
|
16
|
+
expect(registry.register_definition(:test_database) {}).to be_a Cranium::DSL::DatabaseDefinition
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should register a new database definition and configure it through the block passed" do
|
20
|
+
expected_definition = Cranium::DSL::DatabaseDefinition.new :test_database
|
21
|
+
expected_definition.connect_to "connection string"
|
22
|
+
|
23
|
+
registry.register_definition :test_database do
|
24
|
+
connect_to "connection string"
|
25
|
+
end
|
26
|
+
|
27
|
+
expect(registry[:test_database]).to eq expected_definition
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
it "should register a new source definition and configure it through the block passed" do
|
32
|
+
registry = Cranium::DefinitionRegistry.new Cranium::DSL::SourceDefinition
|
33
|
+
expected_definition = Cranium::DSL::SourceDefinition.new :test_source
|
34
|
+
expected_definition.file "test.csv"
|
35
|
+
|
36
|
+
registry.register_definition :test_source do
|
37
|
+
file "test.csv"
|
38
|
+
end
|
39
|
+
|
40
|
+
expect(registry[:test_source]).to eq expected_definition
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe Cranium::DimensionManager do
|
4
|
+
describe "#insert" do
|
5
|
+
|
6
|
+
context "with single key" do
|
7
|
+
let(:manager) { Cranium::DimensionManager.new :table, :source_key }
|
8
|
+
|
9
|
+
it "should store a new record for insertion" do
|
10
|
+
manager.insert :target_key, target_key: 123, name: "John"
|
11
|
+
|
12
|
+
expect(manager.rows).to eq [{target_key: 123, name: "John"}]
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should raise an error if the key field's value isn't specified in the record" do
|
16
|
+
expect { manager.insert :target_key, name: "John" }.to raise_error ArgumentError, "Required attribute 'target_key' missing"
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should return the key field's value in an array" do
|
20
|
+
expect(manager.insert(:target_key, target_key: 123, name: "John")).to eq 123
|
21
|
+
end
|
22
|
+
|
23
|
+
context "if one of the values in the record is a sequence" do
|
24
|
+
it "should insert the next value of the specified sequence" do
|
25
|
+
sequence = Cranium::Transformation::Sequence.new :id_seq
|
26
|
+
allow(sequence).to receive(:next_value).and_return(123)
|
27
|
+
|
28
|
+
manager.insert :target_key, target_key: sequence
|
29
|
+
|
30
|
+
expect(manager.rows).to eq [{target_key: 123}]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "#create_cache_for_field" do
|
37
|
+
context "with single key" do
|
38
|
+
let(:manager) { Cranium::DimensionManager.new :table, [:key] }
|
39
|
+
|
40
|
+
it "should load key-value pairs into a hash" do
|
41
|
+
database = double("Database connection")
|
42
|
+
allow(manager).to receive(:db).and_return database
|
43
|
+
|
44
|
+
expect(database).to receive(:select_map).with([:key, :value_field]).and_return([[1, 10], [2, 20]])
|
45
|
+
|
46
|
+
expect(manager.create_cache_for_field(:value_field)).to eq [1] => 10, [2] => 20
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "with multiple keys" do
|
51
|
+
let(:manager) { Cranium::DimensionManager.new :table, [:key_1, :key_2] }
|
52
|
+
|
53
|
+
it "should load key-value pairs into a hash" do
|
54
|
+
database = double("Database connection")
|
55
|
+
allow(manager).to receive(:db).and_return database
|
56
|
+
|
57
|
+
expect(database).to receive(:select_map).with([:key_1, :key_2, :value_field]).and_return([[1, 1, 11], [1, 2, 12], [2, 1, 21]])
|
58
|
+
|
59
|
+
expect(manager.create_cache_for_field(:value_field)).to eq [1, 1] => 11, [1, 2] => 12, [2, 1] => 21
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
|
3
|
+
describe Cranium::DSL::DatabaseDefinition do
|
4
|
+
|
5
|
+
let(:database) { Cranium::DSL::DatabaseDefinition.new "name" }
|
6
|
+
|
7
|
+
|
8
|
+
describe "#name" do
|
9
|
+
it "should return the name of the database definition" do
|
10
|
+
expect(database.name).to eq("name")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
describe "#connect_to" do
|
16
|
+
it "should set the attribute to the specified value" do
|
17
|
+
database.connect_to "value"
|
18
|
+
|
19
|
+
expect(database.connect_to).to eq("value")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
|
3
|
+
describe Cranium::DSL::ExtractDefinition do
|
4
|
+
|
5
|
+
let(:extract) { Cranium::DSL::ExtractDefinition.new :extract_name }
|
6
|
+
|
7
|
+
|
8
|
+
describe "#name" do
|
9
|
+
it "should return the name of the extract definition" do
|
10
|
+
expect(extract.name).to eq(:extract_name)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
describe "#storage" do
|
16
|
+
it "should return the persistent storage corresponding to the extract" do
|
17
|
+
expect(extract.storage).to be_a Cranium::Extract::Storage
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
describe "#from" do
|
23
|
+
it "should set the attribute to the specified value" do
|
24
|
+
extract.from :database
|
25
|
+
expect(extract.from).to eq(:database)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
describe "#query" do
|
31
|
+
it "should set the attribute to the specified value" do
|
32
|
+
extract.query "extract query"
|
33
|
+
expect(extract.query).to eq("extract query")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
describe "#columns" do
|
39
|
+
it "should set the attribute to the specified value" do
|
40
|
+
extract.query %w(id name status)
|
41
|
+
expect(extract.query).to eq(%w(id name status))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
describe "#incrementally_by" do
|
47
|
+
it "should set the attribute to the specified value" do
|
48
|
+
extract.incrementally_by :id
|
49
|
+
expect(extract.incrementally_by).to eq(:id)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
describe "#last_extracted_value_of" do
|
55
|
+
let(:storage) { double "extract storage" }
|
56
|
+
before { allow(Cranium::Extract::Storage).to receive(:new).with(:extract_name).and_return(storage) }
|
57
|
+
|
58
|
+
context "when there is no last extracted value for the field" do
|
59
|
+
before { allow(storage).to receive(:last_value_of).with(:id).and_return(nil) }
|
60
|
+
|
61
|
+
it "should return nil" do
|
62
|
+
expect(extract.last_extracted_value_of(:id)).to be_nil
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should return the default value if one was specified" do
|
66
|
+
expect(extract.last_extracted_value_of(:id, 0)).to eq(0)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should return the last extracted value of the field" do
|
71
|
+
allow(storage).to receive(:last_value_of).with(:id).and_return(15)
|
72
|
+
expect(extract.last_extracted_value_of(:id)).to eq(15)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
|
3
|
+
describe Cranium::DSL::ImportDefinition do
|
4
|
+
|
5
|
+
let(:import) { Cranium::DSL::ImportDefinition.new "import_name" }
|
6
|
+
|
7
|
+
describe "#into" do
|
8
|
+
it "should set the attribute to the specified value" do
|
9
|
+
import.into "new value"
|
10
|
+
|
11
|
+
expect(import.into).to eq("new value")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
describe "#name" do
|
17
|
+
it "should return the name of the import definition" do
|
18
|
+
expect(import.name).to eq("import_name")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
describe "#field_associations" do
|
24
|
+
context "when no fields are set" do
|
25
|
+
it "should return empty hash" do
|
26
|
+
expect(import.field_associations).to eq({})
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
describe "#put" do
|
33
|
+
context "when called with a Hash" do
|
34
|
+
it "should store the field associations" do
|
35
|
+
import.put :item => :id, :title => :name
|
36
|
+
|
37
|
+
expect(import.field_associations).to eq({:item => :id, :title => :name})
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context "when called with a Symbol" do
|
42
|
+
it "should store a field association between fields with the same name" do
|
43
|
+
import.put :item
|
44
|
+
|
45
|
+
expect(import.field_associations).to eq({:item => :item})
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "when called with an unsupported type" do
|
50
|
+
it "should raise an error" do
|
51
|
+
expect { import.put "unsupported" }.to raise_error ArgumentError, "Unsupported argument for Import::put"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context "when called multiple times" do
|
56
|
+
it "should merge all specified associations" do
|
57
|
+
import.put :item => :id
|
58
|
+
import.put :title => :name
|
59
|
+
import.put :category => :category, :brand => :brand
|
60
|
+
|
61
|
+
expect(import.field_associations).to eq({
|
62
|
+
:item => :id,
|
63
|
+
:title => :name,
|
64
|
+
:category => :category,
|
65
|
+
:brand => :brand
|
66
|
+
})
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
describe "#merge_fields" do
|
73
|
+
context "when no fields are set" do
|
74
|
+
it "should return empty hash" do
|
75
|
+
expect(import.merge_fields).to eq({})
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
describe "#merge_on" do
|
82
|
+
context "when called with a Hash" do
|
83
|
+
it "should set merge field associations" do
|
84
|
+
import.merge_on :item => :id, :title => :name
|
85
|
+
|
86
|
+
expect(import.merge_fields).to eq({:item => :id, :title => :name})
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context "when called with a Symbol" do
|
91
|
+
it "should store a merge field association between fields with the same name" do
|
92
|
+
import.merge_on :item
|
93
|
+
|
94
|
+
expect(import.merge_fields).to eq({:item => :item})
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context "when called with an unsupported type" do
|
99
|
+
it "should raise an error" do
|
100
|
+
expect { import.merge_on "unsupported" }.to raise_error ArgumentError, "Unsupported argument for Import::merge_on"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
context "when called multiple times" do
|
105
|
+
it "should overwrite existing merge fields" do
|
106
|
+
import.merge_on :item => :id
|
107
|
+
import.merge_on :title => :name
|
108
|
+
|
109
|
+
expect(import.merge_fields).to eq({:title => :name})
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
describe "#delete_insert_on" do
|
116
|
+
|
117
|
+
it "should return an empty array when no value was set" do
|
118
|
+
expect(import.delete_insert_on).to eq([])
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
it "should store and return the value passed" do
|
123
|
+
import.delete_insert_on :some_field
|
124
|
+
|
125
|
+
expect(import.delete_insert_on).to eq([:some_field])
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
it "should handle multiple arguments" do
|
130
|
+
import.delete_insert_on :some_field, :another_field
|
131
|
+
|
132
|
+
expect(import.delete_insert_on).to eq([:some_field, :another_field])
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
describe "#truncate_insert" do
|
139
|
+
|
140
|
+
it "should return false when no value was set" do
|
141
|
+
expect(import.truncate_insert).to eq(false)
|
142
|
+
end
|
143
|
+
|
144
|
+
|
145
|
+
it "should store and return the value passed" do
|
146
|
+
import.truncate_insert true
|
147
|
+
|
148
|
+
expect(import.truncate_insert).to eq(true)
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
describe Cranium::DSL::SourceDefinition do
|
5
|
+
|
6
|
+
let(:source) { Cranium::DSL::SourceDefinition.new "name" }
|
7
|
+
|
8
|
+
|
9
|
+
{ file: "name.csv",
|
10
|
+
delimiter: ",",
|
11
|
+
escape: '"',
|
12
|
+
quote: '"',
|
13
|
+
encoding: "UTF-8" }.each do |attribute, default_value|
|
14
|
+
|
15
|
+
describe "#attribute" do
|
16
|
+
context "if called without a parameter" do
|
17
|
+
it "should return the (default) value of the attribute" do
|
18
|
+
expect(source.send(attribute)).to eq(default_value)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context "if called with a parameter" do
|
23
|
+
it "should set the attribute to the specified value" do
|
24
|
+
source.send(attribute, "new value")
|
25
|
+
|
26
|
+
expect(source.send(attribute)).to eq("new value")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
describe "#fields" do
|
35
|
+
it "should return an empty Hash if no fields are set" do
|
36
|
+
expect(source.fields).to eq({})
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should return the fields and types that were set" do
|
40
|
+
source.field :field1, String
|
41
|
+
source.field :field2, Fixnum
|
42
|
+
|
43
|
+
expect(source.fields).to eq({
|
44
|
+
field1: String,
|
45
|
+
field2: Fixnum
|
46
|
+
})
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
describe "#file_name_overriden?" do
|
52
|
+
it "should signal if the file name parameter of the source definition has been set to something other than the default" do
|
53
|
+
expect(source.file_name_overriden?).to be_falsey
|
54
|
+
|
55
|
+
source.file "overriden.csv"
|
56
|
+
expect(source.file_name_overriden?).to be_truthy
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
describe "#files" do
|
62
|
+
it "should return nil by default" do
|
63
|
+
expect(source.files).to be_nil
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
describe "#resolve_files" do
|
69
|
+
it "should store the file names of all files matching the file pattern" do
|
70
|
+
allow(Cranium).to receive_messages configuration: (Cranium::Configuration.new.tap do |config|
|
71
|
+
config.gpfdist_home_directory = "/home/gpfdist"
|
72
|
+
config.upload_directory = "customer"
|
73
|
+
end)
|
74
|
+
source.file "product*.csv"
|
75
|
+
allow(Dir).to receive(:[]).with("/home/gpfdist/customer/product*.csv").and_return(["/home/gpfdist/customer/product2.csv",
|
76
|
+
"/home/gpfdist/customer/product1.csv"])
|
77
|
+
|
78
|
+
source.resolve_files
|
79
|
+
|
80
|
+
expect(source.files).to eq(["product1.csv", "product2.csv"])
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe Cranium::DSL do
|
4
|
+
|
5
|
+
let(:dsl_object) { Object.new.tap { |object| object.extend Cranium::DSL } }
|
6
|
+
|
7
|
+
describe "#database" do
|
8
|
+
it "should register a database connection in the application" do
|
9
|
+
block = lambda {}
|
10
|
+
|
11
|
+
expect(Cranium::Database).to receive(:register_database) do |arg, &blk|
|
12
|
+
expect(arg).to eq :name
|
13
|
+
expect(blk).to be block
|
14
|
+
end
|
15
|
+
|
16
|
+
dsl_object.database(:name, &block)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
describe "#source" do
|
22
|
+
it "should register a source in the application" do
|
23
|
+
expect(Cranium.application).to receive(:register_source).with(:name)
|
24
|
+
|
25
|
+
dsl_object.source(:name)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
describe "#extract" do
|
31
|
+
it "should create an extract definition and execute it" do
|
32
|
+
extract_definition = double "ExtractDefinition"
|
33
|
+
block = lambda {}
|
34
|
+
|
35
|
+
allow(Cranium::DSL::ExtractDefinition).to receive(:new).with(:contacts).and_return(extract_definition)
|
36
|
+
expect(extract_definition).to receive(:instance_eval) { |&blk| expect(blk).to be block }
|
37
|
+
|
38
|
+
extractor = double "DataExtractor"
|
39
|
+
allow(Cranium::Extract::DataExtractor).to receive_messages new: extractor
|
40
|
+
expect(extractor).to receive(:execute).with(extract_definition)
|
41
|
+
|
42
|
+
dsl_object.extract :contacts, &block
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
describe "#deduplicate" do
|
48
|
+
it "should call transform with correct source and target arguments" do
|
49
|
+
expect(dsl_object).to receive(:transform).with(:sales_items => :products)
|
50
|
+
dsl_object.deduplicate :sales_items, into: :products, by: [:item]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
describe "#import" do
|
56
|
+
it "should create an import definition and execute it" do
|
57
|
+
import_definition = double "ImportDefinition"
|
58
|
+
block = lambda {}
|
59
|
+
|
60
|
+
allow(Cranium::DSL::ImportDefinition).to receive(:new).with(:contacts).and_return(import_definition)
|
61
|
+
expect(import_definition).to receive(:instance_eval) { |&blk| expect(blk).to be block }
|
62
|
+
|
63
|
+
importer = double "DataImporter"
|
64
|
+
allow(Cranium::DataImporter).to receive_messages new: importer
|
65
|
+
expect(importer).to receive(:import).with(import_definition)
|
66
|
+
|
67
|
+
dsl_object.import :contacts, &block
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
describe "#archive" do
|
73
|
+
it "should archive files for the specified sources" do
|
74
|
+
allow(Cranium.application).to receive_messages sources: {first_source: double(files: ["file1", "file2"]),
|
75
|
+
second_source: double(files: ["file3"]),
|
76
|
+
third_source: double(files: ["file4"])}
|
77
|
+
|
78
|
+
expect(Cranium::Archiver).to receive(:archive).with "file1", "file2"
|
79
|
+
expect(Cranium::Archiver).to receive(:archive).with "file3"
|
80
|
+
|
81
|
+
dsl_object.archive :first_source, :second_source
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
describe "#remove" do
|
87
|
+
it "should remove files for the specified sources" do
|
88
|
+
allow(Cranium.application).to receive_messages sources: {first_source: double(files: ["file1", "file2"]),
|
89
|
+
second_source: double(files: ["file3"]),
|
90
|
+
third_source: double(files: ["file4"])}
|
91
|
+
|
92
|
+
expect(Cranium::Archiver).to receive(:remove).with "file1", "file2"
|
93
|
+
expect(Cranium::Archiver).to receive(:remove).with "file3"
|
94
|
+
|
95
|
+
dsl_object.remove :first_source, :second_source
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
describe "#sequence" do
|
101
|
+
it "should return a sequence with the specified name" do
|
102
|
+
result = dsl_object.sequence "test_sequence"
|
103
|
+
|
104
|
+
expect(result).to be_a Cranium::Transformation::Sequence
|
105
|
+
expect(result.name).to eq("test_sequence")
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
describe "#after" do
|
111
|
+
it "should register a new after hook for the application" do
|
112
|
+
block = -> {}
|
113
|
+
|
114
|
+
expect(Cranium.application).to receive(:register_hook).with(:after) { |&blk| expect(blk).to be block }
|
115
|
+
|
116
|
+
dsl_object.after &block
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|