csv_madness 0.0.6 → 0.0.9
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.
- checksums.yaml +5 -13
- data/Gemfile +2 -3
- data/README.rdoc +55 -47
- data/Rakefile +79 -52
- data/VERSION +1 -1
- data/lib/csv_madness/builder.rb +4 -1
- data/lib/csv_madness/data_accessor_module.rb +59 -24
- data/lib/csv_madness/exceptions.rb +16 -0
- data/lib/csv_madness/record.rb +3 -1
- data/lib/csv_madness/sheet.rb +33 -387
- data/lib/csv_madness/sheet_methods/base.rb +6 -0
- data/lib/csv_madness/sheet_methods/class_methods.rb +77 -0
- data/lib/csv_madness/sheet_methods/column_methods.rb +187 -0
- data/lib/csv_madness/sheet_methods/file_methods.rb +41 -0
- data/lib/csv_madness/sheet_methods/indexing.rb +52 -0
- data/lib/csv_madness/sheet_methods/record_methods.rb +99 -0
- data/lib/csv_madness.rb +1 -2
- data/test/helper.rb +5 -0
- data/test/test_builder.rb +8 -4
- data/test/test_csv_madness.rb +6 -7
- data/test/test_reloading_spreadsheet.rb +2 -1
- data/test/test_sheet.rb +28 -18
- metadata +54 -36
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
module CsvMadness
|
|
2
|
+
module SheetMethods
|
|
3
|
+
module ColumnMethods
|
|
4
|
+
|
|
5
|
+
COLUMN_TYPES = {
|
|
6
|
+
number: Proc.new do |cell, record|
|
|
7
|
+
rval = cell
|
|
8
|
+
|
|
9
|
+
unless cell.nil? || (cell.is_a?(String) && cell.length == 0)
|
|
10
|
+
|
|
11
|
+
begin
|
|
12
|
+
rval = Integer(cell)
|
|
13
|
+
rescue
|
|
14
|
+
# do nothing
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
unless rval.is_a?(Integer)
|
|
18
|
+
begin
|
|
19
|
+
rval = Float(cell)
|
|
20
|
+
rescue
|
|
21
|
+
# do nothing
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
rval
|
|
27
|
+
end,
|
|
28
|
+
|
|
29
|
+
integer: Proc.new do |cell, record|
|
|
30
|
+
begin
|
|
31
|
+
Integer(cell)
|
|
32
|
+
rescue
|
|
33
|
+
cell
|
|
34
|
+
end
|
|
35
|
+
end,
|
|
36
|
+
|
|
37
|
+
float: Proc.new do |cell, record|
|
|
38
|
+
begin
|
|
39
|
+
Float(cell)
|
|
40
|
+
rescue
|
|
41
|
+
cell
|
|
42
|
+
end
|
|
43
|
+
end,
|
|
44
|
+
|
|
45
|
+
date: Proc.new do |cell, record|
|
|
46
|
+
begin
|
|
47
|
+
parse = Time.parse( cell || "" )
|
|
48
|
+
rescue ArgumentError
|
|
49
|
+
if cell =~ /^Invalid Time Format: /
|
|
50
|
+
parse = cell
|
|
51
|
+
else
|
|
52
|
+
parse = "Invalid Time Format: <#{cell}>"
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
parse
|
|
57
|
+
end
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
FORBIDDEN_COLUMN_NAMES = [:to_s] # breaks things hard when you use them. Probably not comprehensive, sadly.
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def column col
|
|
64
|
+
@records.map(&col)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def has_column?( col )
|
|
68
|
+
self.columns.include?( col.to_sym )
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# retrieve multiple columns. Returns an array of the form
|
|
72
|
+
# [ [record1:col1, record1:col2...], [record2:col1, record2:col2...] [...] ]
|
|
73
|
+
def multiple_columns(*args)
|
|
74
|
+
@records.inject([]){ |memo, record|
|
|
75
|
+
memo << args.map{ |arg| record.send(arg) }
|
|
76
|
+
memo
|
|
77
|
+
}
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def alter_column( column, blank = :undefined, &block )
|
|
81
|
+
raise_on_missing_columns( column )
|
|
82
|
+
|
|
83
|
+
if cindex = @columns.index( column )
|
|
84
|
+
for record in @records
|
|
85
|
+
if record.blank?(column) && blank != :undefined
|
|
86
|
+
record[cindex] = blank
|
|
87
|
+
else
|
|
88
|
+
record[cindex] = yield( record[cindex], record )
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# If no block given, adds an empty column.
|
|
95
|
+
# If block given, fills the new column cells when the block is run on each record
|
|
96
|
+
# TODO: Should be able to specify the index of the column
|
|
97
|
+
def add_column( column, &block )
|
|
98
|
+
raise_on_forbidden_column_names( column )
|
|
99
|
+
raise ColumnExistsError.new( "Column already exists: #{column}" ) if @columns.include?( column )
|
|
100
|
+
|
|
101
|
+
@columns << column
|
|
102
|
+
|
|
103
|
+
# add empty column to each row
|
|
104
|
+
@records.map{ |r|
|
|
105
|
+
r.csv_data << {column => ""}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
update_data_accessor_module
|
|
109
|
+
|
|
110
|
+
if block_given?
|
|
111
|
+
alter_column( column ) do |val, record|
|
|
112
|
+
yield val, record
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def drop_column( column )
|
|
118
|
+
raise_on_missing_columns( column )
|
|
119
|
+
|
|
120
|
+
@columns.delete( column )
|
|
121
|
+
|
|
122
|
+
key = column.to_s
|
|
123
|
+
|
|
124
|
+
@records.map{ |r|
|
|
125
|
+
r.csv_data.delete( key )
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
update_data_accessor_module
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def rename_column( column, new_name )
|
|
132
|
+
@columns[@columns.index(column)] = new_name
|
|
133
|
+
rename_index_column( column, new_name ) if @index_columns.include?( column )
|
|
134
|
+
update_data_accessor_module
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def set_column_type( column, type, blank = :undefined )
|
|
138
|
+
alter_column( column, blank, &COLUMN_TYPES[type] )
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
# If :reverse_merge is true, then the dest column is only overwritten for records where :dest is blank
|
|
143
|
+
def merge_columns( source, dest, opts = {} )
|
|
144
|
+
opts = { :drop_source => true, :reverse_merge => false, :default => "" }.merge( opts )
|
|
145
|
+
raise_on_missing_columns( source, dest )
|
|
146
|
+
|
|
147
|
+
self.records.each do |record|
|
|
148
|
+
if opts[:reverse_merge] == false || record.blank?( dest )
|
|
149
|
+
record[dest] = record.blank?(source) ? opts[:default] : record[source]
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
self.drop_column( source ) if opts[:drop_source]
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# By default, the
|
|
157
|
+
def concat_columns( col1, col2, opts = {} )
|
|
158
|
+
opts = {:separator => '', :out => col1}.merge( opts )
|
|
159
|
+
|
|
160
|
+
raise_on_missing_columns( col1, col2 )
|
|
161
|
+
self.add_column( opts[:out] ) unless self.columns.include?( opts[:out] )
|
|
162
|
+
|
|
163
|
+
for record in self.records
|
|
164
|
+
record[ opts[:out] ] = "#{record[col1]}#{opts[:separator]}#{record[col2]}"
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
alias :concatenate :concat_columns
|
|
169
|
+
|
|
170
|
+
protected
|
|
171
|
+
def raise_on_missing_columns( *cols )
|
|
172
|
+
missing_columns = cols.flatten.reject{ |col| self.has_column?( col ) }
|
|
173
|
+
|
|
174
|
+
raise MissingColumnError.new( "#{caller[0]}: required_column(s) :#{missing_columns.inspect} not found." ) unless missing_columns.fwf_blank?
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def forbidden_column_name?( col )
|
|
178
|
+
FORBIDDEN_COLUMN_NAMES.include?( col )
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def raise_on_forbidden_column_names( *cols )
|
|
182
|
+
forbidden_columns = cols.flatten.select{ |col| self.forbidden_column_name?( col ) }
|
|
183
|
+
raise ForbiddenColumnNameError.new( "forbidden names: #{forbidden_columns.inspect}" ) unless forbidden_columns.fwf_blank?
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module CsvMadness
|
|
2
|
+
module SheetMethods
|
|
3
|
+
module FileMethods
|
|
4
|
+
def write_to_file( file, opts = {} )
|
|
5
|
+
self.class.write_to_file( self, file, opts )
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
# pass an arbitrary filepath to save sheet to that filepath
|
|
9
|
+
# call save() with no arguments to overwrite the current spreadsheet file
|
|
10
|
+
# call save(:timestamp) to make a timestamped copy of the spreadsheet file
|
|
11
|
+
#
|
|
12
|
+
# Obviously, the last two require self.spreadsheet_file to exist and be writable
|
|
13
|
+
def save( file = self.spreadsheet_file )
|
|
14
|
+
if file == :timestamp
|
|
15
|
+
if self.spreadsheet_file
|
|
16
|
+
file = self.spreadsheet_file.fwf_filepath.timestamp
|
|
17
|
+
else
|
|
18
|
+
raise ArgumentError.new( "CsvMadness.save(:timestamp) - Spreadsheet must be specified by @spreadsheet_file" )
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
if file
|
|
23
|
+
self.write_to_file( self.spreadsheet_file, @opts || {} )
|
|
24
|
+
else
|
|
25
|
+
raise "CsvMadness.save(:timestamp) - Spreadsheet must be specified by spreadsheet_file"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def reload_spreadsheet( opts = @opts )
|
|
31
|
+
load_csv if @spreadsheet_file
|
|
32
|
+
set_initial_columns( opts[:columns] )
|
|
33
|
+
create_record_class
|
|
34
|
+
package
|
|
35
|
+
|
|
36
|
+
set_index_columns( opts[:index] )
|
|
37
|
+
reindex
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module CsvMadness
|
|
2
|
+
module SheetMethods
|
|
3
|
+
module Indexing
|
|
4
|
+
def set_index_columns( index_columns )
|
|
5
|
+
@index_columns = case index_columns
|
|
6
|
+
when NilClass
|
|
7
|
+
[]
|
|
8
|
+
when Symbol
|
|
9
|
+
[ index_columns ]
|
|
10
|
+
when Array
|
|
11
|
+
index_columns
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def add_to_index( col, key, record )
|
|
16
|
+
(@indexes[col] ||= {})[key] = record
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def add_to_indexes( records )
|
|
20
|
+
if records.is_a?( Array )
|
|
21
|
+
for record in records
|
|
22
|
+
add_to_indexes( record )
|
|
23
|
+
end
|
|
24
|
+
else
|
|
25
|
+
record = records
|
|
26
|
+
for col in @index_columns
|
|
27
|
+
add_to_index( col, record.send(col), record )
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def remove_from_index( record )
|
|
33
|
+
for col in @index_columns
|
|
34
|
+
@indexes[col].delete( record.send(col) )
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Reindexes the record lookup tables.
|
|
39
|
+
def reindex
|
|
40
|
+
@indexes = {}
|
|
41
|
+
add_to_indexes( @records )
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# shouldn't require reindex
|
|
45
|
+
def rename_index_column( column, new_name )
|
|
46
|
+
@index_columns[ @index_columns.index( column ) ] = new_name
|
|
47
|
+
@indexes[new_name] = @indexes[column]
|
|
48
|
+
@indexes.delete(column)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
module CsvMadness
|
|
2
|
+
module SheetMethods
|
|
3
|
+
module RecordMethods
|
|
4
|
+
def add_record( record )
|
|
5
|
+
case record
|
|
6
|
+
when Array
|
|
7
|
+
# CSV::Row.new( column names, column_entries ) (in same order as columns, natch)
|
|
8
|
+
record = CSV::Row.new( self.columns, record )
|
|
9
|
+
when Hash
|
|
10
|
+
header = []
|
|
11
|
+
fields = []
|
|
12
|
+
|
|
13
|
+
for col in self.columns
|
|
14
|
+
header << col
|
|
15
|
+
fields << record[col]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
record = CSV::Row.new( header, fields )
|
|
19
|
+
when CSV::Row
|
|
20
|
+
# do nothing
|
|
21
|
+
else
|
|
22
|
+
raise "sheet.add_record() doesn't take objects of type #{record.inspect}" unless record.respond_to?(:csv_data)
|
|
23
|
+
record = record.csv_data
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
record = @record_class.new( record, self.column_accessors_map )
|
|
27
|
+
@records << record
|
|
28
|
+
add_to_indexes( record )
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
alias :<< :add_record
|
|
32
|
+
|
|
33
|
+
def add_blank_record( &block )
|
|
34
|
+
self.add_record( {} )
|
|
35
|
+
if block_given?
|
|
36
|
+
yield self.records.last
|
|
37
|
+
else
|
|
38
|
+
self.records.last
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# record can be the row number (integer from 0...@records.length)
|
|
43
|
+
# record can be the record itself (anonymous class)
|
|
44
|
+
def remove_record( record )
|
|
45
|
+
record = @records[record] if record.is_a?(Integer)
|
|
46
|
+
return if record.nil?
|
|
47
|
+
|
|
48
|
+
self.remove_from_index( record )
|
|
49
|
+
@records.delete( record )
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Here's the deal: you hand us a block, and we'll remove the records for which
|
|
53
|
+
# it yields _true_.
|
|
54
|
+
def remove_records( records = nil, &block )
|
|
55
|
+
if block_given?
|
|
56
|
+
for record in @records
|
|
57
|
+
remove_record( record ) if yield( record ) == true
|
|
58
|
+
end
|
|
59
|
+
else # records should be an array
|
|
60
|
+
for record in records
|
|
61
|
+
self.remove_record( record )
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Fetches an indexed record based on the column indexed and the keying object.
|
|
67
|
+
# If key is an array of keying objects, returns an array of records in the
|
|
68
|
+
# same order that the keying objects appear.
|
|
69
|
+
# Index column should yield a different, unique value for each record.
|
|
70
|
+
def fetch( index_col, key )
|
|
71
|
+
if key.is_a?(Array)
|
|
72
|
+
key.map{ |k| @indexes[index_col][k] }
|
|
73
|
+
else
|
|
74
|
+
@indexes[index_col][key]
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# function should take an object, and return either true or false
|
|
79
|
+
# returns an array of objects that respond true when put through the
|
|
80
|
+
# meat grinder
|
|
81
|
+
def filter( &block )
|
|
82
|
+
rval = []
|
|
83
|
+
@records.each do |record|
|
|
84
|
+
rval << record if ( yield record )
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
rval
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# removes rows which fail the given test from the spreadsheet.
|
|
91
|
+
# TODO! Should be returning self rather than the records.a
|
|
92
|
+
def filter!( &block )
|
|
93
|
+
@records = self.filter( &block )
|
|
94
|
+
reindex
|
|
95
|
+
@records
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
data/lib/csv_madness.rb
CHANGED
|
@@ -3,6 +3,5 @@ require 'fun_with_gems'
|
|
|
3
3
|
# require 'fun_with_version_strings'
|
|
4
4
|
require 'time' # to use Time.parse to parse cells to get the date
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
FunWith::Gems.make_gem_fun( "CsvMadness", :require => lib_dir.join( "csv_madness" ) )
|
|
6
|
+
FunWith::Gems.make_gem_fun( "CsvMadness" )
|
|
8
7
|
|
data/test/helper.rb
CHANGED
|
@@ -33,6 +33,10 @@ class MadTestCase < FunWith::Testing::TestCase # Test::Unit::TestCase
|
|
|
33
33
|
set_spreadsheet_paths
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
+
def load_sheet( *args )
|
|
37
|
+
@sheet = CsvMadness.load( *args )
|
|
38
|
+
end
|
|
39
|
+
|
|
36
40
|
def load_mary
|
|
37
41
|
id = @simple.index_columns.first
|
|
38
42
|
@mary = @simple.fetch( id, MARY_ID )
|
|
@@ -61,6 +65,7 @@ class MadTestCase < FunWith::Testing::TestCase # Test::Unit::TestCase
|
|
|
61
65
|
end
|
|
62
66
|
|
|
63
67
|
def muck_up_spreadsheet
|
|
68
|
+
debugger
|
|
64
69
|
@simple.add_column(:scrambled_name) do |val, record|
|
|
65
70
|
record.fname.chars.map(&:to_s).zip( record.lname.chars.map(&:to_s) ).flatten.compact.join
|
|
66
71
|
end
|
data/test/test_builder.rb
CHANGED
|
@@ -4,15 +4,19 @@ class TestBuilder < MadTestCase
|
|
|
4
4
|
context "testing simple cases" do
|
|
5
5
|
should "spreadsheetize integers" do
|
|
6
6
|
integers = [65, 66, 67, 68, 69, 70]
|
|
7
|
+
|
|
7
8
|
sb = CsvMadness::Builder.new do |s|
|
|
8
|
-
s.column( :even, "even?" )
|
|
9
|
+
s.column( :even, "even?" ) # calls the .even?() method on each object
|
|
9
10
|
s.column( :odd, "odd?" )
|
|
10
11
|
s.column( :hashh, "hash" )
|
|
11
|
-
s.column( :hashhash, "hash.hash" )
|
|
12
|
+
s.column( :hashhash, "hash.hash" ) # calls .hash(), then calls .hash() on result
|
|
12
13
|
s.column( :chr )
|
|
13
14
|
s.column( :not_a_valid_method )
|
|
15
|
+
s.column( :square ) do |i| # Stores the block as a proc, runs on each object.
|
|
16
|
+
i * i
|
|
17
|
+
end
|
|
14
18
|
end
|
|
15
|
-
|
|
19
|
+
|
|
16
20
|
ss = sb.build( integers )
|
|
17
21
|
|
|
18
22
|
for record in ss.records
|
|
@@ -27,7 +31,7 @@ class TestBuilder < MadTestCase
|
|
|
27
31
|
ss = sb.build( integers, :on_error => :ignore )
|
|
28
32
|
|
|
29
33
|
assert_equal "", ss.records.first.not_a_valid_method
|
|
30
|
-
|
|
34
|
+
assert_equal 4225, ss.records.first.square
|
|
31
35
|
end
|
|
32
36
|
end
|
|
33
37
|
end
|
data/test/test_csv_madness.rb
CHANGED
|
@@ -110,7 +110,7 @@ class TestCsvMadness < MadTestCase
|
|
|
110
110
|
assert_in_delta Time.parse("1986-04-08"), born1, 3600 * 24
|
|
111
111
|
|
|
112
112
|
assert_kind_of String, born3
|
|
113
|
-
assert_match /Invalid Time Format/, born3
|
|
113
|
+
assert_match( /Invalid Time Format/, born3 )
|
|
114
114
|
end
|
|
115
115
|
|
|
116
116
|
should "successfully decorate record objects with new functionality" do
|
|
@@ -212,19 +212,18 @@ class TestCsvMadness < MadTestCase
|
|
|
212
212
|
|
|
213
213
|
should "to_csv properly" do
|
|
214
214
|
@to_csv = @nilsheet.to_csv( force_quotes: true )
|
|
215
|
-
assert_match /"Moore"/, @to_csv
|
|
216
|
-
assert_match /"age","born"/, @to_csv
|
|
215
|
+
assert_match( /"Moore"/, @to_csv )
|
|
216
|
+
assert_match( /"age","born"/, @to_csv )
|
|
217
217
|
end
|
|
218
218
|
|
|
219
219
|
should "write to an output file properly" do
|
|
220
|
-
# debugger
|
|
221
220
|
@outfile = @csv_output_path.join("output_nilfile.csv")
|
|
222
221
|
@nilsheet.write_to_file( @outfile, force_quotes: true )
|
|
223
222
|
|
|
224
223
|
assert File.exist?( @outfile )
|
|
225
224
|
@to_csv = File.read( @outfile )
|
|
226
|
-
assert_match /"Moore"/, @to_csv
|
|
227
|
-
assert_match /"age","born"/, @to_csv
|
|
225
|
+
assert_match( /"Moore"/, @to_csv )
|
|
226
|
+
assert_match( /"age","born"/, @to_csv )
|
|
228
227
|
end
|
|
229
228
|
end
|
|
230
229
|
|
|
@@ -236,7 +235,7 @@ class TestCsvMadness < MadTestCase
|
|
|
236
235
|
|
|
237
236
|
should "add column" do
|
|
238
237
|
@simple.add_column( :compound ) do |h, record|
|
|
239
|
-
|
|
238
|
+
"#{record.fname} #{record.lname} #{record.id}"
|
|
240
239
|
end
|
|
241
240
|
|
|
242
241
|
load_mary
|
data/test/test_sheet.rb
CHANGED
|
@@ -3,39 +3,46 @@ require 'helper'
|
|
|
3
3
|
class TestSheet < MadTestCase
|
|
4
4
|
context "testing getter_name()" do
|
|
5
5
|
should "return proper function names" do
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
test_data = [
|
|
7
|
+
[" heLLo __ world ", :hello_world],
|
|
8
|
+
["0 heLLo __ worlD!!! ", :_0_hello_world],
|
|
9
|
+
["9ess 🐙 bsv++=", :_9ess_bsv ]
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
for input, expected in test_data
|
|
13
|
+
assert_equal expected, CsvMadness::Sheet.getter_name( input )
|
|
14
|
+
end
|
|
8
15
|
end
|
|
9
16
|
end
|
|
10
17
|
|
|
11
18
|
context "testing default spreadsheet paths" do
|
|
12
|
-
should "
|
|
13
|
-
assert_raises(RuntimeError) do
|
|
19
|
+
should "raise error if a path does not exist" do
|
|
20
|
+
assert_raises( RuntimeError ) do
|
|
14
21
|
CsvMadness::Sheet.add_search_path( CsvMadness.root.join("rocaganthor") )
|
|
15
22
|
end
|
|
16
23
|
end
|
|
17
24
|
|
|
18
25
|
should "check a search path for files to load" do
|
|
19
|
-
|
|
20
|
-
assert sheet.is_a?(CsvMadness::Sheet)
|
|
26
|
+
load_sheet( "with_nils.csv" )
|
|
27
|
+
assert @sheet.is_a?( CsvMadness::Sheet )
|
|
21
28
|
end
|
|
22
29
|
end
|
|
23
30
|
|
|
24
31
|
context "testing column_types" do
|
|
25
32
|
should "gracefully handle empty strings for all column_types" do
|
|
26
|
-
|
|
33
|
+
load_sheet("test_column_types.csv")
|
|
27
34
|
|
|
28
35
|
for type, ignore_proc in CsvMadness::Sheet::COLUMN_TYPES
|
|
29
|
-
sheet.set_column_type( type, type )
|
|
36
|
+
@sheet.set_column_type( type, type )
|
|
30
37
|
end
|
|
31
38
|
|
|
32
|
-
record = sheet.records[0]
|
|
39
|
+
record = @sheet.records[0]
|
|
33
40
|
|
|
34
41
|
assert_kind_of String, record.date
|
|
35
42
|
assert_equal nil, record.number
|
|
36
43
|
assert_equal nil, record.float
|
|
37
44
|
|
|
38
|
-
record = sheet.records[1]
|
|
45
|
+
record = @sheet.records[1]
|
|
39
46
|
assert_equal "12", record.id
|
|
40
47
|
assert_equal 134.2, record.number
|
|
41
48
|
assert_equal 100, record.integer
|
|
@@ -45,7 +52,7 @@ class TestSheet < MadTestCase
|
|
|
45
52
|
|
|
46
53
|
context "testing add/remove records" do
|
|
47
54
|
setup do
|
|
48
|
-
|
|
55
|
+
load_sheet( "simple.csv", :index => :id )
|
|
49
56
|
end
|
|
50
57
|
|
|
51
58
|
should "add record" do
|
|
@@ -95,17 +102,20 @@ class TestSheet < MadTestCase
|
|
|
95
102
|
end
|
|
96
103
|
|
|
97
104
|
should "deliver me a properly blanked spreadsheet (index)" do
|
|
98
|
-
|
|
99
|
-
blank_sheet = sheet.blanked
|
|
105
|
+
load_sheet( "splitter.csv", :index => [:id] )
|
|
106
|
+
blank_sheet = @sheet.blanked
|
|
107
|
+
|
|
108
|
+
assert_zero blank_sheet.length
|
|
109
|
+
assert_includes blank_sheet.index_columns, :id
|
|
100
110
|
end
|
|
101
111
|
|
|
102
112
|
should "split splitter" do
|
|
103
|
-
|
|
113
|
+
load_sheet( "splitter.csv", :index => [:id, :party] )
|
|
104
114
|
|
|
105
|
-
sheets = sheet.split(&:party)
|
|
115
|
+
sheets = @sheet.split(&:party)
|
|
106
116
|
|
|
107
117
|
for party in %w(D I R)
|
|
108
|
-
assert_equal_length sheets[party], sheet.records.select{|r| r.party == party }
|
|
118
|
+
assert_equal_length sheets[party], @sheet.records.select{|r| r.party == party }
|
|
109
119
|
end
|
|
110
120
|
|
|
111
121
|
assert_includes sheets["D"].index_columns, :id
|
|
@@ -115,8 +125,8 @@ class TestSheet < MadTestCase
|
|
|
115
125
|
|
|
116
126
|
context "testing hilarious spreadsheet fails" do
|
|
117
127
|
should "raise error on forbidden column name" do
|
|
118
|
-
assert_raises(
|
|
119
|
-
|
|
128
|
+
assert_raises( CsvMadness::ForbiddenColumnNameError ) do
|
|
129
|
+
CsvMadness.load( "forbidden_column.csv" )
|
|
120
130
|
end
|
|
121
131
|
end
|
|
122
132
|
end
|