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