Skip to content

Commit

Permalink
CSVDocument.new may be used to create new csv file
Browse files Browse the repository at this point in the history
  • Loading branch information
Peter Lohmann committed Jan 12, 2017
1 parent 82a4d1e commit 310ab25
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 77 deletions.
60 changes: 23 additions & 37 deletions lib/atlas/csv_document.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,45 +42,37 @@ def self.curve(path)
# path - Path to the CSV file.
#
# Returns a CSVDocument.
def initialize(path)
def initialize(path, headers = nil)
@path = Pathname.new(path)

@table = CSV.table(@path.to_s, {
converters: [YEAR_NORMALIZER, :all],
header_converters: [KEY_NORMALIZER],
# Needed to retrieve the headers in case
# of an otherwise empty csv file
return_headers: true
})

@headers = table.headers

# Delete the header row for the internal representation -
# will be dynamically (re-)created when outputting
table.delete(0)

raise(BlankCSVHeaderError, path) if @headers.any?(&:nil?)
end
if headers
raise(ExistingCSVHeaderError, path) if @path.file?
@headers = headers.map(&KEY_NORMALIZER)
@table = CSV::Table.new([CSV::Row.new(@headers, @headers, true)])
else
@table = CSV.table(@path.to_s, {
converters: [YEAR_NORMALIZER, :all],
header_converters: [KEY_NORMALIZER],
# Needed to retrieve the headers in case
# of an otherwise empty csv file
return_headers: true
})

# Public: Creates a new csv file on disk and a corresponding
# new CSV document instance connected to that
#
# path - Path to the new CSV file
# headers - The column headers of the new CSV
#
# Returns a CSVDocument
def self.create(path, headers, normalizer = KEY_NORMALIZER)
headers = headers.map(&normalizer)
initial_table = CSV::Table.new([CSV::Row.new(headers, headers, true)])
@headers = table.headers

write(initial_table, Pathname.new(path))
# Delete the header row for the internal representation -
# will be dynamically (re-)created when outputting
table.delete(0)

new(path, normalizer)
raise(BlankCSVHeaderError, path) if @headers.any?(&:nil?)
end
end

# Public: Saves the CSV document to disk
def save
self.class.write(table, path)
def save!
FileUtils.mkdir_p(path.dirname)
File.write(path, table.to_csv)
self
end

# Public: Sets the value of a cell identified by its row and column.
Expand Down Expand Up @@ -117,12 +109,6 @@ def column_keys
private
#######

# Internal: Writes the content of a CSV table to disk
def self.write(table, path)
FileUtils.mkdir_p(path.dirname)
File.write(path, table.to_csv)
end

# Internal: Finds the value of a cell, raising an UnknownCSVRowError if no
# such row exists.
#
Expand Down
5 changes: 5 additions & 0 deletions lib/atlas/errors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,11 @@ def self.error_class(superclass = AtlasError, &block)
"of the cells in the first row must contain a non-blank value."
end

ExistingCSVHeaderError = error_class do |path|
"Column headers provided although CSV file #{path} already exists"
end


# Parser Errors ------------------------------------------------------------

CannotIdentifyError = error_class(ParserError) do |string|
Expand Down
4 changes: 2 additions & 2 deletions lib/atlas/scaler/time_curve_scaler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ def scale
row_keys = base_csv.row_keys
column_keys = base_csv.column_keys

scaled_csv = CSVDocument.create(@derived_dataset.time_curve_path(key), column_keys)
scaled_csv = CSVDocument.new(@derived_dataset.time_curve_path(key), column_keys)
row_keys.each do |row_key|
column_keys.each do |column_key|
base_value = base_csv.get(row_key, column_key)
scaled_csv.set(row_key, column_key, base_value * @scaling_factor)
end
end
scaled_csv.save
scaled_csv.save!
end
end
end # Scaler::TimeCurveScaler
Expand Down
82 changes: 46 additions & 36 deletions spec/atlas/csv_document_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,41 @@ module Atlas
CSVDocument.new(path.to_s)
end

it 'raises when the file does not exist' do
expect { CSVDocument.new('no') }.to raise_error(/no such file/i)
end
describe '.new' do
context 'without specified headers' do
it 'raises when the file does not exist' do
expect { CSVDocument.new('no') }.to raise_error(/no such file/i)
end

it 'raises when a header cell contains no value' do
path = Atlas.data_dir.join('blank.csv')
path.open('w') { |f| f.puts(",yes\nyes,1") }

expect { CSVDocument.new(path.to_s) }.
to raise_error(BlankCSVHeaderError)
end
end

context 'with specified headers' do
let(:headers) { %i( yes no maybe ) }
let(:path) { Atlas.data_dir.join('new.csv') }
let(:doc) { CSVDocument.new(path.to_s, headers) }

it 'raises when a header cell contains no value' do
path = Atlas.data_dir.join('blank.csv')
path.open('w') { |f| f.puts(",yes\nyes,1") }
it 'does not save the new file to disk' do
expect(File.exist?(doc.path)).to be_false
end

it 'raises when the file already exists' do
doc.save!
expect { CSVDocument.new(path.to_s, %i( hello world )) }.
to raise_error(ExistingCSVHeaderError)
end

it 'sets the headers / column_keys' do
expect(doc.column_keys).to eq(headers)
end
end

expect { CSVDocument.new(path.to_s) }.
to raise_error(BlankCSVHeaderError)
end

describe '#get' do
Expand Down Expand Up @@ -97,10 +122,10 @@ module Atlas
end
end # set

describe '#save' do
describe '#save!' do
it 'saves the CSVDocument content to disk' do
doc.set('yes', 'no', 42)
doc.save
doc.save!

expect(File.readlines(doc.path).map(&:strip)).to eq(
<<-EOF.lines.map(&:strip))
Expand All @@ -112,35 +137,20 @@ module Atlas
blank,,,
EOF
end
end

describe '.create' do
let(:doc_path) { Atlas.data_dir.join('new.csv') }
let(:headers) { %i(year hello\ world yes no) }
let(:normalized_headers) { %i(year hello_world yes no) }
let!(:doc) { CSVDocument.create(doc_path, headers) }

it 'creates a new csv file' do
expect(File.file?(doc_path)).to be_true
end

it 'creates a normalized header row in the csv file' do
expect(File.readlines(doc_path).first.strip).to eq(normalized_headers.map(&:to_s).join(','))
end

it 'returns a new CSVDocument' do
expect(doc).to_not be_blank
end

it 'sets the headers / column_keys for the CSVDocument' do
expect(doc.column_keys).to eq(normalized_headers)
end
context 'when the file did not exist before' do
let(:doc) { CSVDocument.new(Atlas.data_dir.join('doesnotexistbefore.csv'), %i( yes no maybe\ baby )) }
it 'creates a new csv file' do
doc.save!
expect(File.file?(doc.path)).to be_true
end

it 'allows setting (and retrieving) a value on the create CSVDocument' do
expect { doc.set(2018, :yes, 999) }.to_not raise_error
expect(doc.get(2018, 'yes')).to eq(999)
it 'creates a normalized header row in the csv file' do
doc.save!
expect(File.readlines(doc.path).first.strip).to eq('yes,no,maybe_baby')
end
end
end # .create
end
end # CSVDocument

describe CSVDocument::OneDimensional do
Expand Down
3 changes: 1 addition & 2 deletions spec/atlas/scaler_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,7 @@ module Atlas; describe Scaler do

describe Scaler::TimeCurveScaler do
let(:derived_dataset) do
Scaler.new('nl', 'rotterdam', scaling_value).create_scaled_dataset
Atlas::Dataset::DerivedDataset.find('rotterdam')
Atlas::Dataset::DerivedDataset.new(key: 'rotterdam', area: 'rotterdam')
end

before { Scaler::TimeCurveScaler.call(base_dataset, scaling_factor, derived_dataset) }
Expand Down

0 comments on commit 310ab25

Please sign in to comment.