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.
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