data_forge 0.1
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.
- data/.gitignore +19 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +2 -0
- data/LICENSE +20 -0
- data/README.md +49 -0
- data/Rakefile +16 -0
- data/bin/forge +4 -0
- data/config/cucumber.yml +8 -0
- data/data_forge.gemspec +26 -0
- data/features/accessing_command_line_parameters.feature +52 -0
- data/features/deduplication.feature +49 -0
- data/features/file/file_format_options.feature +146 -0
- data/features/file/has_header_row.feature +62 -0
- data/features/step_definitions/file_steps.rb +8 -0
- data/features/support/env.rb +8 -0
- data/features/transform/output_command.feature +123 -0
- data/features/transform/outputting_to_multiple_files.feature +57 -0
- data/features/transform/overwrite_original_file.feature +37 -0
- data/features/transform/record_transformation.feature +47 -0
- data/lib/data_forge/cli/main.rb +21 -0
- data/lib/data_forge/cli/options.rb +62 -0
- data/lib/data_forge/cli.rb +24 -0
- data/lib/data_forge/dsl/attributes.rb +15 -0
- data/lib/data_forge/dsl/commands.rb +23 -0
- data/lib/data_forge/dsl/helpers.rb +22 -0
- data/lib/data_forge/dsl.rb +9 -0
- data/lib/data_forge/file/csv/csv_record_file_definition.rb +46 -0
- data/lib/data_forge/file/csv/csv_record_file_reader.rb +42 -0
- data/lib/data_forge/file/csv/csv_record_file_writer.rb +62 -0
- data/lib/data_forge/file/csv.rb +13 -0
- data/lib/data_forge/file/record_file_definition.rb +17 -0
- data/lib/data_forge/file/record_file_reader.rb +22 -0
- data/lib/data_forge/file/record_file_writer.rb +32 -0
- data/lib/data_forge/file.rb +36 -0
- data/lib/data_forge/transformation/deduplication.rb +38 -0
- data/lib/data_forge/transformation/ruby_transformation.rb +33 -0
- data/lib/data_forge/transformation/ruby_transformation_context.rb +27 -0
- data/lib/data_forge/transformation/transformation_base.rb +29 -0
- data/lib/data_forge/transformation.rb +10 -0
- data/lib/data_forge/version.rb +3 -0
- data/lib/data_forge.rb +13 -0
- data/spec/data_forge/cli/main_spec.rb +45 -0
- data/spec/data_forge/cli/options_spec.rb +64 -0
- data/spec/data_forge/cli_spec.rb +54 -0
- data/spec/data_forge/dsl/commands_spec.rb +42 -0
- data/spec/data_forge/dsl/helpers_spec.rb +24 -0
- data/spec/data_forge/file/csv/csv_record_file_definition_spec.rb +97 -0
- data/spec/data_forge/file/csv/csv_record_file_reader_spec.rb +78 -0
- data/spec/data_forge/file/csv/csv_record_file_writer_spec.rb +100 -0
- data/spec/data_forge/file/record_file_definition_spec.rb +17 -0
- data/spec/data_forge/file/record_file_reader_spec.rb +15 -0
- data/spec/data_forge/file/record_file_writer_spec.rb +15 -0
- data/spec/data_forge/file_spec.rb +49 -0
- data/spec/data_forge/transformation/deduplication_spec.rb +77 -0
- data/spec/data_forge/transformation/ruby_transformation_context_spec.rb +49 -0
- data/spec/data_forge/transformation/ruby_transformation_spec.rb +71 -0
- data/spec/data_forge_spec.rb +9 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/support/helpers/record_reader_helper.rb +17 -0
- data/spec/support/helpers/record_writer_helper.rb +16 -0
- metadata +218 -0
data/lib/data_forge.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'data_forge/version'
|
2
|
+
|
3
|
+
module DataForge
|
4
|
+
|
5
|
+
autoload :CLI, 'data_forge/cli'
|
6
|
+
autoload :DSL, 'data_forge/dsl'
|
7
|
+
autoload :File, 'data_forge/file'
|
8
|
+
autoload :Transformation, 'data_forge/transformation'
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
self.extend DataForge::DSL::Commands,
|
13
|
+
DataForge::DSL::Helpers
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe DataForge::CLI::Main do
|
4
|
+
|
5
|
+
let(:options) { DataForge::CLI::Options.new }
|
6
|
+
let(:args) { double "ARGV" }
|
7
|
+
let(:stdout) { double "STDOUT" }
|
8
|
+
let(:stderr) { double "STDERR" }
|
9
|
+
let(:kernel) { double "Kernel" }
|
10
|
+
|
11
|
+
subject { described_class.new args, STDIN, stdout, stderr, kernel }
|
12
|
+
|
13
|
+
before do
|
14
|
+
allow(DataForge::CLI).to receive(:parse_options).with(args, stdout).and_return options
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
describe "#execute!" do
|
19
|
+
it "should execute the command script specified in the options" do
|
20
|
+
options.command_script = "command_script.rb"
|
21
|
+
|
22
|
+
expect(subject).to receive(:load).with("command_script.rb")
|
23
|
+
|
24
|
+
subject.execute!
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should not execute the command script if the options direct to stop execution" do
|
28
|
+
options.execute = false
|
29
|
+
|
30
|
+
expect(subject).not_to receive(:load)
|
31
|
+
|
32
|
+
subject.execute!
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should output an error message in case of an error" do
|
36
|
+
allow(subject).to receive(:load).and_raise "Error message"
|
37
|
+
|
38
|
+
expect(stderr).to receive(:puts).with "ERROR: Error message"
|
39
|
+
expect(kernel).to receive(:exit).with 1
|
40
|
+
|
41
|
+
subject.execute!
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe DataForge::CLI::Options do
|
4
|
+
|
5
|
+
let(:stdout) { StringIO.new }
|
6
|
+
subject { DataForge::CLI::Options }
|
7
|
+
|
8
|
+
describe ".parse" do
|
9
|
+
context "when parsing the command script parameter" do
|
10
|
+
it "should accept a single command script parameter" do
|
11
|
+
options = subject.parse(%w[command_script.rb])
|
12
|
+
|
13
|
+
expect(options.command_script).to eq "command_script.rb"
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should raise an error if no command script is specified" do
|
17
|
+
expect { subject.parse(%w[]) }.to raise_error "No command script specified"
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should raise an error if there is more than one command script specified" do
|
21
|
+
expect { subject.parse(%w[command_script1.rb command_script2.rb]) }.to raise_error "More than one command script specified"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
context "when parsing the --help switch" do
|
27
|
+
it "should print the help information" do
|
28
|
+
subject.parse(%w[--help], stdout)
|
29
|
+
|
30
|
+
expect(stdout.string).to include "Usage: [bundle exec] forge [options] command_script.rb"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
context "when parsing the --version switch" do
|
36
|
+
it "should print the version" do
|
37
|
+
subject.parse(%w[--version], stdout)
|
38
|
+
|
39
|
+
expect(stdout.string).to match /DataForge, version \d+(\.\d+)*/
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
context "when parsing user-defined parameters" do
|
45
|
+
it "should accept a name-value pair as a parameter with the -U switch" do
|
46
|
+
options = subject.parse(%w[-Ucustomer=test command_script.rb])
|
47
|
+
|
48
|
+
expect(options.user_params).to eq(customer: "test")
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should accept multiple user-defined parameters" do
|
52
|
+
options = subject.parse(%w[-Ucustomer=test -Udata_file=items.csv command_script.rb])
|
53
|
+
|
54
|
+
expect(options.user_params).to eq(customer: "test", data_file: "items.csv")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
it "should raise an error if an unknown option is specified" do
|
60
|
+
expect { subject.parse(%w[--unknown]) }.to raise_error OptionParser::InvalidOption
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe DataForge::CLI do
|
4
|
+
let(:options) { DataForge::CLI::Options.new }
|
5
|
+
let(:args) { double "ARGV" }
|
6
|
+
let(:stdout) { double "STDOUT" }
|
7
|
+
|
8
|
+
before do
|
9
|
+
allow(DataForge::CLI::Options).to receive(:parse).with(args, stdout).and_return options
|
10
|
+
end
|
11
|
+
|
12
|
+
after do
|
13
|
+
subject.instance_variable_set :@command_script, nil
|
14
|
+
subject.instance_variable_set :@user_params, nil
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
describe ".parse_options" do
|
19
|
+
it "should return the command line options parsed into an Options object" do
|
20
|
+
expect(subject.parse_options args, stdout).to eq options
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
describe ".command_script" do
|
26
|
+
it "should be nil by default" do
|
27
|
+
expect(subject.command_script).to be_nil
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should return the command script specified in options that were parsed" do
|
31
|
+
options.command_script = "command_script.rb"
|
32
|
+
|
33
|
+
subject.parse_options args, stdout
|
34
|
+
|
35
|
+
expect(subject.command_script).to eq "command_script.rb"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
describe ".user_params" do
|
41
|
+
it "should be nil by default" do
|
42
|
+
expect(subject.user_params).to be_nil
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should return the user-defined parameters specified in options that were parsed" do
|
46
|
+
options.user_params = {p1: "v1", p2: "v2"}
|
47
|
+
|
48
|
+
subject.parse_options args, stdout
|
49
|
+
|
50
|
+
expect(subject.user_params).to eq(p1: "v1", p2: "v2")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe DataForge::DSL::Commands do
|
4
|
+
|
5
|
+
let(:dsl_object) { Object.new.tap { |object| object.extend DataForge::DSL::Commands } }
|
6
|
+
let(:block) { lambda {} }
|
7
|
+
|
8
|
+
describe "#file" do
|
9
|
+
it "should register a file descriptor" do
|
10
|
+
expect(DataForge::File).to receive(:register_file_definition).with(:name) { |&blk| expect(blk).to be block }
|
11
|
+
|
12
|
+
dsl_object.file :name, &block
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
describe "#transform" do
|
18
|
+
it "should create a file transformation and execute it" do
|
19
|
+
transformation = instance_double "DataForge::Transformation::RubyTransformation"
|
20
|
+
|
21
|
+
allow(DataForge::Transformation::RubyTransformation).to receive(:from_input)
|
22
|
+
.with(:source, into: :target) { |&blk| expect(blk).to be block }
|
23
|
+
.and_return(transformation)
|
24
|
+
expect(transformation).to receive(:execute)
|
25
|
+
|
26
|
+
dsl_object.transform :source, into: :target, &block
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
describe "#deduplicate" do
|
32
|
+
it "should create a deduplication transformation and execute it" do
|
33
|
+
deduplication = instance_double "DataForge::Transformation::Deduplication"
|
34
|
+
allow(DataForge::Transformation::Deduplication).to receive(:from_input).with(:items, into: :unique_items, using: :item_id).and_return(deduplication)
|
35
|
+
|
36
|
+
expect(deduplication).to receive(:execute)
|
37
|
+
|
38
|
+
dsl_object.deduplicate :items, into: :unique_items, using: :item_id
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe DataForge::DSL::Helpers do
|
4
|
+
|
5
|
+
subject { Object.new.extend described_class }
|
6
|
+
|
7
|
+
describe "PARAMS" do
|
8
|
+
it "should return the user parameters passed in through the CLI" do
|
9
|
+
expect(DataForge::CLI).to receive(:user_params).and_return "user defined parameters"
|
10
|
+
|
11
|
+
expect(subject.instance_eval { PARAMS }).to eq "user defined parameters"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
describe "COMMAND_SCRIPT" do
|
17
|
+
it "should return the command script that is currently executing" do
|
18
|
+
expect(DataForge::CLI).to receive(:command_script).and_return "command_script.rb"
|
19
|
+
|
20
|
+
expect(subject.instance_eval { COMMAND_SCRIPT }).to eq "command_script.rb"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe DataForge::File::CSV::CSVRecordFileDefinition do
|
4
|
+
|
5
|
+
subject { described_class.new :definition_name }
|
6
|
+
|
7
|
+
describe "#name" do
|
8
|
+
it "should return the name of the descriptor" do
|
9
|
+
expect(subject.name).to eq :definition_name
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
describe "#field" do
|
15
|
+
it "should define a field with a type" do
|
16
|
+
subject.field :field1, String
|
17
|
+
|
18
|
+
expect(subject.fields).to eq(field1: String)
|
19
|
+
end
|
20
|
+
|
21
|
+
context "when called without a type" do
|
22
|
+
it "should define a String field" do
|
23
|
+
subject.field :field1
|
24
|
+
|
25
|
+
expect(subject.fields).to eq(field1: String)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
describe "#fields" do
|
32
|
+
it "should return an empty Hash if no fields are defined" do
|
33
|
+
expect(subject.fields).to eq({})
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should return the fields and types (as a Hash) that are defined" do
|
37
|
+
subject.field :field1, String
|
38
|
+
subject.field :field2, Fixnum
|
39
|
+
|
40
|
+
expect(subject.fields).to eq(field1: String, field2: Fixnum)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
describe "#field_names" do
|
46
|
+
it "should return an empty array if no fields are defined" do
|
47
|
+
expect(subject.field_names).to eq []
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should return the names of the fields that are defined" do
|
51
|
+
subject.field :field1, String
|
52
|
+
subject.field :field2, Fixnum
|
53
|
+
|
54
|
+
expect(subject.field_names).to eq [:field1, :field2]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
describe "attributes" do
|
60
|
+
{file_name: "definition_name.csv",
|
61
|
+
delimiter: ",",
|
62
|
+
quote: '"',
|
63
|
+
encoding: "UTF-8",
|
64
|
+
has_header_row: true}
|
65
|
+
.each do |attribute_name, default_value|
|
66
|
+
|
67
|
+
describe "#{attribute_name}" do
|
68
|
+
it "should return or set the attribute value" do
|
69
|
+
subject.public_send attribute_name, "new value"
|
70
|
+
|
71
|
+
expect(subject.public_send(attribute_name)).to eq "new value"
|
72
|
+
end
|
73
|
+
|
74
|
+
context "when not overridden" do
|
75
|
+
it "should return the default value of the attribute" do
|
76
|
+
expect(subject.send(attribute_name)).to eq default_value
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
describe "#separator" do
|
86
|
+
it "should be an alias for #delimiter" do
|
87
|
+
expect(subject.separator).to eq ","
|
88
|
+
|
89
|
+
subject.delimiter ";"
|
90
|
+
expect(subject.separator).to eq ";"
|
91
|
+
|
92
|
+
subject.separator "|"
|
93
|
+
expect(subject.delimiter).to eq "|"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe DataForge::File::CSV::CSVRecordFileReader do
|
4
|
+
|
5
|
+
let(:csv_file) { instance_double "CSV" }
|
6
|
+
let(:definition) { instance_double "DataForge::File::CSV::CSVRecordFileDefinition",
|
7
|
+
name: :test,
|
8
|
+
file_name: "test.csv",
|
9
|
+
field_names: [:field1, :field2],
|
10
|
+
delimiter: "delimiter",
|
11
|
+
quote: "quote",
|
12
|
+
encoding: "encoding",
|
13
|
+
has_header_row: true }
|
14
|
+
|
15
|
+
subject { described_class.new definition }
|
16
|
+
|
17
|
+
|
18
|
+
describe "#definition" do
|
19
|
+
it "should return the file definition the writer was created for" do
|
20
|
+
expect(subject.definition).to eq definition
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
describe "#name" do
|
26
|
+
it "should return the file definition's name" do
|
27
|
+
expect(subject.name).to eq :test
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
describe "#fields" do
|
33
|
+
it "should return the file definition's fields" do
|
34
|
+
expect(subject.fields).to eq [:field1, :field2]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
describe "#each_record" do
|
40
|
+
context "when the CSV file has a header row" do
|
41
|
+
it "should skip the header row and iterate through all records in the CSV file" do
|
42
|
+
expect(CSV).to receive(:open).with("test.csv", {col_sep: "delimiter",
|
43
|
+
quote_char: "quote",
|
44
|
+
encoding: "encoding",
|
45
|
+
return_headers: false}).and_yield csv_file
|
46
|
+
allow(csv_file).to receive(:shift).and_return(["field1", "field2"], [1, 2], [3, 4], nil)
|
47
|
+
|
48
|
+
records = []
|
49
|
+
subject.each_record { |record| records << record }
|
50
|
+
|
51
|
+
expect(records).to eq [{field1: 1, field2: 2}, {field1: 3, field2: 4}]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
context "when the CSV file has no header row" do
|
57
|
+
let(:definition) { instance_double "DataForge::File::CSV::CSVRecordFileDefinition",
|
58
|
+
name: :test,
|
59
|
+
file_name: "test.csv",
|
60
|
+
field_names: [:field1, :field2],
|
61
|
+
delimiter: "delimiter",
|
62
|
+
quote: "quote",
|
63
|
+
encoding: "encoding",
|
64
|
+
has_header_row: false }
|
65
|
+
|
66
|
+
it "should iterate through all records in the CSV file" do
|
67
|
+
allow(CSV).to receive(:open).and_yield csv_file
|
68
|
+
allow(csv_file).to receive(:shift).and_return([1, 2], [3, 4], nil)
|
69
|
+
|
70
|
+
records = []
|
71
|
+
subject.each_record { |record| records << record }
|
72
|
+
|
73
|
+
expect(records).to eq [{field1: 1, field2: 2}, {field1: 3, field2: 4}]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe DataForge::File::CSV::CSVRecordFileWriter do
|
4
|
+
|
5
|
+
let(:csv_file) { instance_double "CSV" }
|
6
|
+
let(:definition) { instance_double "DataForge::File::CSV::CSVRecordFileDefinition",
|
7
|
+
name: :test,
|
8
|
+
file_name: "test.csv",
|
9
|
+
field_names: [:field1, :field2, :field3],
|
10
|
+
delimiter: "delimiter",
|
11
|
+
quote: "quote",
|
12
|
+
encoding: "encoding",
|
13
|
+
has_header_row: true }
|
14
|
+
|
15
|
+
subject { described_class.new definition }
|
16
|
+
|
17
|
+
before do
|
18
|
+
allow(Dir::Tmpname).to receive(:make_tmpname).with(["test", ".csv"], 1).and_return("generated_tempname.csv")
|
19
|
+
allow(CSV).to receive(:open).and_return csv_file
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
describe "#definition" do
|
24
|
+
it "should return the file definition the writer was created for" do
|
25
|
+
expect(subject.definition).to eq definition
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
describe "#name" do
|
31
|
+
it "should return the file definition's name" do
|
32
|
+
expect(subject.name).to eq :test
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
describe "#fields" do
|
38
|
+
it "should return the file definition's fields" do
|
39
|
+
expect(subject.fields).to eq [:field1, :field2, :field3]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
describe "#open" do
|
45
|
+
it "should open a CSV file for writing with a temporary filename" do
|
46
|
+
expect(CSV).to receive(:open).with("generated_tempname.csv", "w", anything)
|
47
|
+
|
48
|
+
subject.open
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
it "should use the file definition's settings as CSV options" do
|
53
|
+
expect(CSV).to receive(:open).with(anything, "w", {col_sep: "delimiter",
|
54
|
+
quote_char: "quote",
|
55
|
+
encoding: "encoding",
|
56
|
+
write_headers: true,
|
57
|
+
headers: [:field1, :field2, :field3]})
|
58
|
+
|
59
|
+
subject.open
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
context "when a file has no header" do
|
64
|
+
it "should open a CSV file with no header row" do
|
65
|
+
allow(definition).to receive(:has_header_row).and_return false
|
66
|
+
|
67
|
+
expect(CSV).to receive(:open).with(anything, "w", {col_sep: "delimiter",
|
68
|
+
quote_char: "quote",
|
69
|
+
encoding: "encoding",
|
70
|
+
write_headers: false})
|
71
|
+
|
72
|
+
subject.open
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
describe "#close" do
|
79
|
+
it "should close and rename the open file" do
|
80
|
+
subject.open
|
81
|
+
|
82
|
+
expect(csv_file).to receive :close
|
83
|
+
expect(FileUtils).to receive(:move).with("generated_tempname.csv", "test.csv")
|
84
|
+
|
85
|
+
subject.close
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
describe "#output_record" do
|
91
|
+
it "should write the specified fields of a hash (in the specified order) as a row into the CSV file" do
|
92
|
+
subject.open
|
93
|
+
|
94
|
+
expect(csv_file).to receive(:<<).with ["a", "b", "c"]
|
95
|
+
|
96
|
+
subject.write(field3: "c", field1: "a", field4: "d", field2: "b")
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe DataForge::File::RecordFileDefinition do
|
4
|
+
|
5
|
+
describe ".from_input" do
|
6
|
+
it "should instantiate a CSV file definition and initalize it with the initializer block" do
|
7
|
+
definition = instance_double "DataForge::File::CSV::CSVRecordFileDefinition"
|
8
|
+
initializer_block = lambda {}
|
9
|
+
|
10
|
+
expect(DataForge::File::CSV::CSVRecordFileDefinition).to receive(:new).with(:test).and_return definition
|
11
|
+
expect(definition).to receive(:instance_eval) { |&block| expect(block).to be initializer_block }
|
12
|
+
|
13
|
+
expect(subject.from_input :test, &initializer_block).to eq definition
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe DataForge::File::RecordFileReader do
|
4
|
+
|
5
|
+
describe ".for" do
|
6
|
+
it "should return a record reader for the specified file definition" do
|
7
|
+
definition = instance_double DataForge::File::CSV::CSVRecordFileDefinition
|
8
|
+
|
9
|
+
expect(DataForge::File::CSV::CSVRecordFileReader).to receive(:new).with(definition).and_return "record reader"
|
10
|
+
|
11
|
+
expect(described_class.for definition).to eq "record reader"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe DataForge::File::RecordFileWriter do
|
4
|
+
|
5
|
+
describe ".for" do
|
6
|
+
it "should return a record writer for the specified file definition" do
|
7
|
+
definition = instance_double DataForge::File::CSV::CSVRecordFileDefinition
|
8
|
+
|
9
|
+
expect(DataForge::File::CSV::CSVRecordFileWriter).to receive(:new).with(definition).and_return "record writer"
|
10
|
+
|
11
|
+
expect(described_class.for definition).to eq "record writer"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe DataForge::File do
|
4
|
+
|
5
|
+
let(:definition) { instance_double "DataForge::File::RecordFileDefinition" }
|
6
|
+
|
7
|
+
before do
|
8
|
+
allow(DataForge::File::RecordFileDefinition).to receive(:from_input).with(:definition_name).and_return definition
|
9
|
+
end
|
10
|
+
|
11
|
+
after do
|
12
|
+
subject.instance_variable_set :@file_definitions, {}
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
describe ".reader_for" do
|
17
|
+
let(:reader) { double "Reader" }
|
18
|
+
|
19
|
+
it "should return a record reader for the file with the specified name" do
|
20
|
+
subject.register_file_definition :definition_name
|
21
|
+
|
22
|
+
expect(DataForge::File::RecordFileReader).to receive(:for).with(definition).and_return reader
|
23
|
+
|
24
|
+
expect(subject.reader_for :definition_name).to eq reader
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should raise an error if there is no file registered by the specified name" do
|
28
|
+
expect { subject.reader_for :definition_name }.to raise_error "Unknown file reference 'definition_name'"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
describe ".writer_for" do
|
34
|
+
let(:writer) { double "Writer" }
|
35
|
+
|
36
|
+
it "should return a record writer for the file with the specified name" do
|
37
|
+
subject.register_file_definition :definition_name
|
38
|
+
|
39
|
+
expect(DataForge::File::RecordFileWriter).to receive(:for).with(definition).and_return writer
|
40
|
+
|
41
|
+
expect(subject.writer_for :definition_name).to eq writer
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should raise an error if there is no file registered by the specified name" do
|
45
|
+
expect { subject.writer_for :definition_name }.to raise_error "Unknown file reference 'definition_name'"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|