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,141 @@
1
+ require 'csv'
2
+
3
+ class Cranium::Transformation::Join
4
+
5
+ attr_accessor :source_left, :source_right, :target, :match_fields, :type
6
+
7
+
8
+
9
+ def execute
10
+ validate_parameters
11
+ cache_commonly_used_values
12
+ join_sources
13
+ end
14
+
15
+
16
+
17
+ private
18
+
19
+ def validate_parameters
20
+ raise "Missing left source for join transformation" if source_left.nil?
21
+ raise "Missing right source for join transformation" if source_right.nil?
22
+ raise "Missing target for join transformation" if target.nil?
23
+ raise "Invalid match fields for join transformation" unless match_fields.nil? or match_fields.is_a? Hash
24
+ raise "Invalid type for join transformation" unless %i(inner left).include?(type)
25
+ end
26
+
27
+
28
+
29
+ def cache_commonly_used_values
30
+ @left_source_field_names = source_left.fields.keys
31
+ @right_source_field_names = source_right.fields.keys
32
+ @target_field_names = target.fields.keys
33
+ end
34
+
35
+
36
+
37
+ def join_sources
38
+ build_join_table_from_right_source_files
39
+ write_output_file
40
+ end
41
+
42
+
43
+
44
+ def build_join_table_from_right_source_files
45
+ @join_table = {}
46
+ source_right.files.each do |file|
47
+ build_join_table_from_file file
48
+ end
49
+ end
50
+
51
+
52
+
53
+ def build_join_table_from_file(file)
54
+ line_number = 0
55
+ CSV.foreach File.join(Cranium.configuration.upload_path, file), csv_read_options_for(source_right) do |row|
56
+ next if 1 == (line_number += 1)
57
+
58
+ record = Hash[@right_source_field_names.zip row]
59
+ index_key = right_index_key_for record
60
+ if @join_table.has_key? index_key
61
+ @join_table[index_key] << record
62
+ else
63
+ @join_table[index_key] = [record]
64
+ end
65
+ end
66
+ end
67
+
68
+
69
+
70
+ def write_output_file
71
+ CSV.open "#{Cranium.configuration.upload_path}/#{target.file}", "w:#{target.encoding}", csv_write_options_for(target) do |target_file|
72
+ @target_file = target_file
73
+ source_left.files.each do |file|
74
+ process_left_source_file File.join(Cranium.configuration.upload_path, file), target_file
75
+ end
76
+ end
77
+
78
+ @target.resolve_files
79
+ end
80
+
81
+
82
+
83
+ def process_left_source_file(input_file, output_file)
84
+ line_number = 0
85
+ CSV.foreach input_file, csv_read_options_for(source_left) do |row|
86
+ next if 1 == (line_number += 1)
87
+
88
+ record = Hash[@left_source_field_names.zip row]
89
+
90
+ joined_records = joined_records_for(record)
91
+ joined_records << record if joined_records.empty? && type == :left
92
+
93
+ joined_records.each do |record_to_output|
94
+ output_file << @target_field_names.map { |field| record_to_output[field] }
95
+ end
96
+ end
97
+ end
98
+
99
+
100
+
101
+ def joined_records_for(record)
102
+ index_key = left_index_key_for record
103
+ return [] unless @join_table.has_key? index_key
104
+ @join_table[index_key].map { |matching_record| record.merge matching_record }
105
+ end
106
+
107
+
108
+
109
+ def left_index_key_for(record)
110
+ record.select { |field, _| match_fields.values.include? field }.values
111
+ end
112
+
113
+
114
+
115
+ def right_index_key_for(record)
116
+ record.select { |field, _| match_fields.keys.include? field }.values
117
+ end
118
+
119
+
120
+
121
+ def csv_write_options_for(source_definition)
122
+ {
123
+ col_sep: source_definition.delimiter,
124
+ quote_char: source_definition.quote,
125
+ write_headers: true,
126
+ headers: source_definition.fields.keys
127
+ }
128
+ end
129
+
130
+
131
+
132
+ def csv_read_options_for(source_definition)
133
+ {
134
+ encoding: source_definition.encoding,
135
+ col_sep: source_definition.delimiter,
136
+ quote_char: source_definition.quote,
137
+ return_headers: false
138
+ }
139
+ end
140
+
141
+ end
@@ -0,0 +1,42 @@
1
+ class Cranium::Transformation::Sequence
2
+
3
+ attr_reader :name
4
+
5
+
6
+
7
+ def initialize(name)
8
+ @name = name
9
+ end
10
+
11
+
12
+
13
+ def next_value
14
+ if @current_value.nil?
15
+ @current_value = Cranium::Database.connection["SELECT nextval('#{@name}') AS next_value"].first[:next_value]
16
+ else
17
+ @current_value += 1
18
+ end
19
+ end
20
+
21
+
22
+
23
+ def flush
24
+ Cranium::Database.connection.run "SELECT setval('#{@name}', #{@current_value})" unless @current_value.nil?
25
+ end
26
+
27
+
28
+
29
+ class << self
30
+
31
+ def by_name(name)
32
+ @sequences ||= {}
33
+ if @sequences[name].nil?
34
+ @sequences[name] = new name
35
+ Cranium.application.after_import { @sequences[name].flush }
36
+ end
37
+ @sequences[name]
38
+ end
39
+
40
+ end
41
+
42
+ end
@@ -0,0 +1,8 @@
1
+ module Cranium::Transformation
2
+
3
+ autoload :DuplicationIndex, 'cranium/transformation/duplication_index'
4
+ autoload :Index, 'cranium/transformation/index'
5
+ autoload :Join, 'cranium/transformation/join'
6
+ autoload :Sequence, 'cranium/transformation/sequence'
7
+
8
+ end
@@ -0,0 +1,45 @@
1
+ class Cranium::TransformationRecord
2
+
3
+ attr_reader :data
4
+
5
+
6
+
7
+ def initialize(source_fields, target_fields)
8
+ @source_fields, @target_fields = source_fields, target_fields
9
+ end
10
+
11
+
12
+
13
+ def input_data=(values)
14
+ @data = Hash[@source_fields.zip values]
15
+ end
16
+
17
+
18
+
19
+ def [](field)
20
+ @data[field]
21
+ end
22
+
23
+
24
+
25
+ def []=(field, value)
26
+ @data[field] = value
27
+ end
28
+
29
+
30
+
31
+ def split_field(field, options)
32
+ values = @data[field].split(options[:by])
33
+
34
+ options[:into].each_with_index do |target_field, index|
35
+ @data[target_field] = values[index] || options[:default_value] || values.last
36
+ end
37
+ end
38
+
39
+
40
+
41
+ def has_key?(key)
42
+ @data.has_key? key
43
+ end
44
+
45
+ end
data/lib/cranium.rb ADDED
@@ -0,0 +1,57 @@
1
+ module Cranium
2
+
3
+ autoload :Application, 'cranium/application'
4
+ autoload :Archiver, 'cranium/archiver'
5
+ autoload :AttributeDSL, 'cranium/attribute_dsl'
6
+ autoload :CommandLineOptions, 'cranium/command_line_options'
7
+ autoload :Configuration, 'cranium/configuration'
8
+ autoload :Database, 'cranium/database'
9
+ autoload :DataImporter, 'cranium/data_importer'
10
+ autoload :DataReader, 'cranium/data_reader'
11
+ autoload :DataTransformer, 'cranium/data_transformer'
12
+ autoload :DefinitionRegistry, 'cranium/definition_registry'
13
+ autoload :DimensionManager, 'cranium/dimension_manager'
14
+ autoload :DSL, 'cranium/dsl'
15
+ autoload :ExternalTable, 'cranium/external_table'
16
+ autoload :Extract, 'cranium/extract'
17
+ autoload :ImportStrategy, 'cranium/import_strategy'
18
+ autoload :Logging, 'cranium/logging'
19
+ autoload :ProgressOutput, 'cranium/progress_output'
20
+ autoload :Sequel, 'cranium/sequel'
21
+ autoload :SourceRegistry, 'cranium/source_registry'
22
+ autoload :TestFramework, 'cranium/test_framework'
23
+ autoload :TransformationRecord, 'cranium/transformation_record'
24
+ autoload :Transformation, 'cranium/transformation'
25
+
26
+ class << self
27
+
28
+ def application(argv = [])
29
+ @application ||= Application.new(argv)
30
+ end
31
+
32
+
33
+
34
+ def configuration
35
+ @configuration ||= Configuration.new.freeze
36
+ end
37
+
38
+
39
+
40
+ def configure
41
+ mutable_configuration = configuration.dup
42
+ yield mutable_configuration
43
+ @configuration = mutable_configuration
44
+ @configuration.freeze
45
+ end
46
+
47
+
48
+
49
+ def load_arguments
50
+ application.load_arguments
51
+ end
52
+
53
+ end
54
+
55
+ end
56
+
57
+ self.extend Cranium::DSL
data/rake/test.rake ADDED
@@ -0,0 +1,31 @@
1
+ require 'cucumber'
2
+ require 'cucumber/rake/task'
3
+ require 'rspec/core/rake_task'
4
+
5
+ task :default => :test
6
+
7
+ desc "Run test suite (all RSpec examples and Cucumber features)"
8
+ task :test => [:'test:spec', :'test:features']
9
+
10
+ desc "Run RSpec code examples (options: RSPEC_SEED=seed)"
11
+ task :spec => :'test:spec'
12
+
13
+ desc "Run Cucumber features (options: CUCUMBER_SEED=seed)"
14
+ task :features => :'test:features'
15
+
16
+
17
+ namespace :test do
18
+
19
+ desc "Run RSpec code examples (options: RSPEC_SEED=seed)"
20
+ RSpec::Core::RakeTask.new :spec do |task|
21
+ task.verbose = false
22
+ task.rspec_opts = "--order random"
23
+ task.rspec_opts << " --seed #{ENV['RSPEC_SEED']}" if ENV['RSPEC_SEED']
24
+ end
25
+
26
+
27
+ Cucumber::Rake::Task.new(:features, "Run Cucumber features (options: CUCUMBER_SEED=seed)") do |task|
28
+ task.cucumber_opts = %w[--profile build]
29
+ end
30
+
31
+ end
@@ -0,0 +1,166 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe Cranium::Application do
4
+
5
+ let(:application) { Cranium::Application.new [] }
6
+
7
+
8
+ describe "#load_arguments" do
9
+ it "should return the provided load arguments" do
10
+ app = Cranium::Application.new ["--cranium-load", "load", "--customer_name", "my_customer"]
11
+ expect(app.load_arguments).to eq customer_name: "my_customer"
12
+ end
13
+ end
14
+
15
+ describe "#cranium_arguments" do
16
+ it "should return the provided load arguments" do
17
+ app = Cranium::Application.new ["--cranium-load", "loads/load_file", "--customer_name", "my_customer"]
18
+ expect(app.cranium_arguments).to eq load: "loads/load_file"
19
+ end
20
+ end
21
+
22
+
23
+ describe "Application" do
24
+ it "should include metrics logging capabilities" do
25
+ expect(application.respond_to?(:log)).to be_truthy
26
+ end
27
+ end
28
+
29
+
30
+ describe "#sources" do
31
+ it "should return a SourceRegistry" do
32
+ expect(application.sources).to be_a Cranium::SourceRegistry
33
+ end
34
+ end
35
+
36
+
37
+ describe "#register_source" do
38
+ it "should register a source in the source registry and resolve its files" do
39
+ source = double "SourceDefinition"
40
+
41
+ expect(application.sources).to receive(:register_source).with(:source1).and_return(source)
42
+ expect(source).to receive(:resolve_files)
43
+
44
+ application.register_source(:source1) { file "test*.csv" }
45
+ end
46
+ end
47
+
48
+
49
+ describe "#run" do
50
+ before(:each) do
51
+ @original_stderr = $stderr
52
+ $stderr = StringIO.new
53
+ end
54
+
55
+ after(:each) do
56
+ $stderr = @original_stderr
57
+ end
58
+
59
+ context "when no files are specified as an argument" do
60
+ it "should exit with an error" do
61
+ expect { application.run }.to raise_error(SystemExit) { |exit| expect(exit.status).to eq(1) }
62
+ end
63
+
64
+ it "should log an error to STDOUT" do
65
+ expect { application.run }.to raise_error(SystemExit)
66
+
67
+ expect($stderr.string.chomp).to eq "ERROR: No file specified"
68
+ end
69
+ end
70
+
71
+
72
+ context "when a non-existent file is specified as an argument" do
73
+ let(:application) { Cranium::Application.new ["--cranium-load", "no-such-file.exists"] }
74
+
75
+ it "should exit with an error" do
76
+ expect { application.run }.to raise_error(SystemExit) { |exit| expect(exit.status).to eq(1) }
77
+ end
78
+
79
+ it "should log an error to STDOUT" do
80
+ expect { application.run }.to raise_error(SystemExit)
81
+
82
+ expect($stderr.string.chomp).to eq "ERROR: File 'no-such-file.exists' does not exist"
83
+ end
84
+ end
85
+
86
+
87
+ context "when called with an existing file" do
88
+ let(:file) { "products.rb" }
89
+
90
+ let(:application) { Cranium::Application.new ["--cranium-load", file] }
91
+
92
+ before(:each) do
93
+ allow(File).to receive(:exists?).with(file).and_return(true)
94
+ end
95
+
96
+
97
+ it "should load the first file specified as a command line parameter" do
98
+ expect(application).to receive(:load).with(file)
99
+
100
+ application.run
101
+ end
102
+
103
+
104
+ it "should run any registered after hooks" do
105
+ allow(application).to receive :load
106
+
107
+ hook_ran = false
108
+ application.register_hook :after do
109
+ hook_ran = true
110
+ end
111
+
112
+ application.run
113
+
114
+ expect(hook_ran).to be_truthy
115
+ end
116
+
117
+
118
+ context "when the execution of the process raises an error" do
119
+ let(:error) { StandardError.new }
120
+ before(:each) { allow(application).to receive(:load).and_raise error }
121
+
122
+ it "should propagate the error" do
123
+ expect { application.run }.to raise_error
124
+ end
125
+
126
+ it "should log an error" do
127
+ expect(application).to receive(:log).with(:error, error)
128
+
129
+ expect { application.run }.to raise_error
130
+ end
131
+
132
+ it "should still run any registered after hooks" do
133
+ hook_ran = false
134
+ application.register_hook :after do
135
+ hook_ran = true
136
+ end
137
+
138
+ begin
139
+ application.run
140
+ rescue
141
+ end
142
+
143
+ expect(hook_ran).to be_truthy
144
+ end
145
+ end
146
+ end
147
+ end
148
+
149
+
150
+ describe "#after_import" do
151
+
152
+ it "should register the given block" do
153
+ block_called = false
154
+
155
+ application.after_import do
156
+ block_called = true
157
+ end
158
+
159
+ application.apply_hook(:after_import)
160
+
161
+ expect(block_called).to be_truthy
162
+ end
163
+
164
+ end
165
+
166
+ end
@@ -0,0 +1,44 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe Cranium::Archiver do
4
+
5
+ before(:each) do
6
+ allow(Cranium).to receive_messages(configuration: Cranium::Configuration.new.tap do |config|
7
+ config.gpfdist_home_directory = "gpfdist_home"
8
+ config.upload_directory = "upload_dir"
9
+ config.archive_directory = "path/to/archive"
10
+ end)
11
+ end
12
+
13
+
14
+ describe ".archive" do
15
+ it "should create the archive directory if it doesn't exist" do
16
+ allow(Dir).to receive(:exists?).with("path/to/archive").and_return(false)
17
+
18
+ expect(FileUtils).to receive(:mkpath).with "path/to/archive"
19
+
20
+ Cranium::Archiver.archive
21
+ end
22
+
23
+ it "should move files to the archive directory" do
24
+ allow(Dir).to receive(:exists?).with("path/to/archive").and_return(true)
25
+ allow(Time).to receive(:now).and_return Time.new(2000, 1, 1, 1, 2, 3)
26
+
27
+ expect(FileUtils).to receive(:mv).with "gpfdist_home/upload_dir/file.txt", "path/to/archive/2000-01-01_01h02m03s_file.txt"
28
+ expect(FileUtils).to receive(:mv).with "gpfdist_home/upload_dir/another_file.txt", "path/to/archive/2000-01-01_01h02m03s_another_file.txt"
29
+
30
+ Cranium::Archiver.archive "file.txt", "another_file.txt"
31
+ end
32
+ end
33
+
34
+
35
+ describe ".remove" do
36
+ it "should remove files from the upload directory" do
37
+ expect(FileUtils).to receive(:rm).with "gpfdist_home/upload_dir/file.txt"
38
+ expect(FileUtils).to receive(:rm).with "gpfdist_home/upload_dir/another_file.txt"
39
+
40
+ Cranium::Archiver.remove "file.txt", "another_file.txt"
41
+ end
42
+ end
43
+
44
+ end
@@ -0,0 +1,32 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe Cranium::CommandLineOptions do
4
+
5
+ let(:argv) {
6
+ %w[
7
+ --cranium-initializer my_initializer
8
+ --cranium-load my_load
9
+ --some-param some_value
10
+ --another-param another_value
11
+ ]
12
+ }
13
+
14
+
15
+ subject { Cranium::CommandLineOptions.new argv }
16
+
17
+ describe "#cranium_arguments" do
18
+
19
+ it "should return only arguments used by Cranium" do
20
+ expect(subject.cranium_arguments).to eq(initializer: "my_initializer", load: "my_load")
21
+ end
22
+
23
+ end
24
+
25
+
26
+ describe "#load_arguments" do
27
+ it "should return non-cranium arguments" do
28
+ expect(subject.load_arguments).to eq(:"some-param" => "some_value", :"another-param" => "another_value")
29
+ end
30
+ end
31
+
32
+ end
@@ -0,0 +1,31 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe Cranium::Configuration do
4
+
5
+ let(:config) { Cranium::Configuration.new }
6
+
7
+ describe "#upload_path" do
8
+ it "should return the full upload path" do
9
+ config.gpfdist_home_directory = "/gpfdist/home/dir"
10
+ config.upload_directory = "uploads/customer"
11
+
12
+ expect(config.upload_path).to eq "/gpfdist/home/dir/uploads/customer"
13
+ end
14
+ end
15
+
16
+
17
+ describe "#storage_directory" do
18
+ it "should return the previously set value" do
19
+ config.storage_directory = "/some/path"
20
+ expect(config.storage_directory).to eq "/some/path"
21
+ end
22
+
23
+ it "should return the default storage directory if one wasn't explicitly set" do
24
+ config.gpfdist_home_directory = "/gpfdist/home/dir"
25
+ config.upload_directory = "uploads/customer"
26
+
27
+ expect(config.storage_directory).to eq "/gpfdist/home/dir/uploads/customer/.cranium"
28
+ end
29
+ end
30
+
31
+ end
@@ -0,0 +1,55 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe Cranium::DataImporter do
4
+
5
+ before do
6
+ connection = double
7
+ allow(Cranium::Database).to receive(:connection).and_return connection
8
+ allow(connection).to receive(:transaction).and_yield
9
+ end
10
+
11
+ let(:importer) { Cranium::DataImporter.new }
12
+ let(:definition) { Cranium::DSL::ImportDefinition.new "definition_name" }
13
+
14
+ describe "#import" do
15
+
16
+ context "when called with both merge and delete_insert fields set" do
17
+ it "should raise an exception" do
18
+ definition.delete_insert_on :some_field
19
+ definition.merge_on :another_field
20
+
21
+ expect { importer.import(definition) }.to raise_error StandardError, "Import should not combine merge_on, delete_insert_on and truncate_insert settings"
22
+ end
23
+ end
24
+
25
+ context "when called with both merge and truncate_insert fields set" do
26
+ it "should raise an exception" do
27
+ definition.truncate_insert true
28
+ definition.merge_on :another_field
29
+
30
+ expect { importer.import(definition) }.to raise_error StandardError, "Import should not combine merge_on, delete_insert_on and truncate_insert settings"
31
+ end
32
+ end
33
+
34
+ context "when called with both delete_insert and truncate_insert fields set" do
35
+ it "should raise an exception" do
36
+ definition.delete_insert_on :some_field
37
+ definition.truncate_insert true
38
+
39
+ expect { importer.import(definition) }.to raise_error StandardError, "Import should not combine merge_on, delete_insert_on and truncate_insert settings"
40
+ end
41
+ end
42
+
43
+ context "when called with both merge, delete_insert and truncate_insert fields set" do
44
+ it "should raise an exception" do
45
+ definition.delete_insert_on :some_field
46
+ definition.merge_on :another_field
47
+ definition.truncate_insert true
48
+
49
+ expect { importer.import(definition) }.to raise_error StandardError, "Import should not combine merge_on, delete_insert_on and truncate_insert settings"
50
+ end
51
+ end
52
+
53
+ end
54
+
55
+ end
@@ -0,0 +1,16 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe Cranium::DataTransformer do
4
+
5
+ describe "#transform" do
6
+ it "should raise an error if the target definition's file name has been overriden" do
7
+ source = Cranium::DSL::SourceDefinition.new :source
8
+ target = Cranium::DSL::SourceDefinition.new :target
9
+
10
+ target.file "overriden filename"
11
+
12
+ expect { Cranium::DataTransformer.new(source, target).transform }.to raise_error StandardError, "Source definition 'target' cannot overrride the file name because it is a transformation target"
13
+ end
14
+ end
15
+
16
+ end