cranium 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (132) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -0
  3. data/.ruby-version +1 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +29 -0
  7. data/Rakefile +3 -0
  8. data/Vagrantfile +24 -0
  9. data/bin/cranium +9 -0
  10. data/config/cucumber.yml +9 -0
  11. data/cranium.gemspec +26 -0
  12. data/db/setup.sql +8 -0
  13. data/docker-compose.yml +8 -0
  14. data/examples/config.rb +14 -0
  15. data/examples/deduplication.rb +27 -0
  16. data/examples/import_csv_with_field_lookup_inserting_new_dimension_keys.rb +26 -0
  17. data/examples/incremental_extract.rb +17 -0
  18. data/examples/lookup_with_multiple_fields.rb +25 -0
  19. data/features/archive.feature +49 -0
  20. data/features/extract/incremental_extract.feature +56 -0
  21. data/features/extract/simple_extract.feature +85 -0
  22. data/features/import/import_csv_to_database_as_delta.feature +38 -0
  23. data/features/import/import_csv_to_database_with_delete_insert_merging.feature +51 -0
  24. data/features/import/import_csv_to_database_with_truncate_insert.feature +49 -0
  25. data/features/import/import_csv_to_database_with_update_merging.feature +46 -0
  26. data/features/import/import_csv_with_always_inserting_new_dimension_keys.feature +137 -0
  27. data/features/import/import_csv_with_field_lookup_inserting_new_dimension_keys.feature +62 -0
  28. data/features/import/import_csv_with_field_lookup_transformation.feature +125 -0
  29. data/features/import/import_csv_with_transformation.feature +55 -0
  30. data/features/import/import_multiple_csv_files_without_transformations.feature +44 -0
  31. data/features/import/import_with_load_id_from_sequence.feature +53 -0
  32. data/features/import/import_with_lookup_from_multiple_fields.feature +64 -0
  33. data/features/read.feature +56 -0
  34. data/features/remove.feature +44 -0
  35. data/features/restore_database_connection.feature +55 -0
  36. data/features/step_definitions/database_table_steps.rb +40 -0
  37. data/features/step_definitions/definition_steps.rb +3 -0
  38. data/features/step_definitions/execution_steps.rb +23 -0
  39. data/features/step_definitions/file_steps.rb +39 -0
  40. data/features/support/class_extensions.rb +24 -0
  41. data/features/support/env.rb +27 -0
  42. data/features/support/randomize.rb +22 -0
  43. data/features/support/stop_on_first_error.rb +5 -0
  44. data/features/transform/deduplication.feature +37 -0
  45. data/features/transform/empty_transformation.feature +72 -0
  46. data/features/transform/join.feature +180 -0
  47. data/features/transform/join_multiple_files_into_one_output_file.feature +46 -0
  48. data/features/transform/output_rows.feature +70 -0
  49. data/features/transform/projection.feature +34 -0
  50. data/features/transform/raw_ruby_transformation.feature +69 -0
  51. data/features/transform/split_field.feature +39 -0
  52. data/lib/cranium/application.rb +104 -0
  53. data/lib/cranium/archiver.rb +36 -0
  54. data/lib/cranium/attribute_dsl.rb +43 -0
  55. data/lib/cranium/command_line_options.rb +27 -0
  56. data/lib/cranium/configuration.rb +33 -0
  57. data/lib/cranium/data_importer.rb +35 -0
  58. data/lib/cranium/data_reader.rb +48 -0
  59. data/lib/cranium/data_transformer.rb +126 -0
  60. data/lib/cranium/database.rb +36 -0
  61. data/lib/cranium/definition_registry.rb +21 -0
  62. data/lib/cranium/dimension_manager.rb +65 -0
  63. data/lib/cranium/dsl/database_definition.rb +23 -0
  64. data/lib/cranium/dsl/extract_definition.rb +28 -0
  65. data/lib/cranium/dsl/import_definition.rb +50 -0
  66. data/lib/cranium/dsl/source_definition.rb +67 -0
  67. data/lib/cranium/dsl.rb +100 -0
  68. data/lib/cranium/extensions/file.rb +7 -0
  69. data/lib/cranium/extensions/sequel_greenplum.rb +30 -0
  70. data/lib/cranium/external_table.rb +75 -0
  71. data/lib/cranium/extract/data_extractor.rb +11 -0
  72. data/lib/cranium/extract/storage.rb +57 -0
  73. data/lib/cranium/extract/strategy/base.rb +27 -0
  74. data/lib/cranium/extract/strategy/incremental.rb +16 -0
  75. data/lib/cranium/extract/strategy/simple.rb +9 -0
  76. data/lib/cranium/extract/strategy.rb +7 -0
  77. data/lib/cranium/extract.rb +7 -0
  78. data/lib/cranium/import_strategy/base.rb +55 -0
  79. data/lib/cranium/import_strategy/delete_insert.rb +40 -0
  80. data/lib/cranium/import_strategy/delta.rb +8 -0
  81. data/lib/cranium/import_strategy/merge.rb +50 -0
  82. data/lib/cranium/import_strategy/truncate_insert.rb +19 -0
  83. data/lib/cranium/import_strategy.rb +9 -0
  84. data/lib/cranium/logging.rb +15 -0
  85. data/lib/cranium/profiling.rb +13 -0
  86. data/lib/cranium/progress_output.rb +37 -0
  87. data/lib/cranium/sequel/hash.rb +32 -0
  88. data/lib/cranium/sequel.rb +5 -0
  89. data/lib/cranium/source_registry.rb +21 -0
  90. data/lib/cranium/test_framework/cucumber_table.rb +140 -0
  91. data/lib/cranium/test_framework/database_entity.rb +29 -0
  92. data/lib/cranium/test_framework/database_sequence.rb +16 -0
  93. data/lib/cranium/test_framework/database_table.rb +33 -0
  94. data/lib/cranium/test_framework/upload_directory.rb +39 -0
  95. data/lib/cranium/test_framework/world.rb +66 -0
  96. data/lib/cranium/test_framework.rb +10 -0
  97. data/lib/cranium/transformation/duplication_index.rb +42 -0
  98. data/lib/cranium/transformation/index.rb +83 -0
  99. data/lib/cranium/transformation/join.rb +141 -0
  100. data/lib/cranium/transformation/sequence.rb +42 -0
  101. data/lib/cranium/transformation.rb +8 -0
  102. data/lib/cranium/transformation_record.rb +45 -0
  103. data/lib/cranium.rb +57 -0
  104. data/rake/test.rake +31 -0
  105. data/spec/cranium/application_spec.rb +166 -0
  106. data/spec/cranium/archiver_spec.rb +44 -0
  107. data/spec/cranium/command_line_options_spec.rb +32 -0
  108. data/spec/cranium/configuration_spec.rb +31 -0
  109. data/spec/cranium/data_importer_spec.rb +55 -0
  110. data/spec/cranium/data_transformer_spec.rb +16 -0
  111. data/spec/cranium/database_spec.rb +69 -0
  112. data/spec/cranium/definition_registry_spec.rb +45 -0
  113. data/spec/cranium/dimension_manager_spec.rb +63 -0
  114. data/spec/cranium/dsl/database_definition_spec.rb +23 -0
  115. data/spec/cranium/dsl/extract_definition_spec.rb +76 -0
  116. data/spec/cranium/dsl/import_definition_spec.rb +153 -0
  117. data/spec/cranium/dsl/source_definition_spec.rb +84 -0
  118. data/spec/cranium/dsl_spec.rb +119 -0
  119. data/spec/cranium/external_table_spec.rb +71 -0
  120. data/spec/cranium/extract/storage_spec.rb +125 -0
  121. data/spec/cranium/logging_spec.rb +37 -0
  122. data/spec/cranium/sequel/hash_spec.rb +56 -0
  123. data/spec/cranium/source_registry_spec.rb +31 -0
  124. data/spec/cranium/test_framework/cucumber_table_spec.rb +144 -0
  125. data/spec/cranium/transformation/duplication_index_spec.rb +75 -0
  126. data/spec/cranium/transformation/index_spec.rb +178 -0
  127. data/spec/cranium/transformation/join_spec.rb +43 -0
  128. data/spec/cranium/transformation/sequence_spec.rb +83 -0
  129. data/spec/cranium/transformation_record_spec.rb +78 -0
  130. data/spec/cranium_spec.rb +53 -0
  131. data/spec/spec_helper.rb +1 -0
  132. 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