csv_madness 0.0.6 → 0.0.10
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.
Potentially problematic release.
This version of csv_madness might be problematic. Click here for more details.
- checksums.yaml +5 -13
- data/.document +5 -0
- data/CHANGELOG.markdown +4 -0
- data/Gemfile +2 -8
- data/README.rdoc +55 -47
- data/Rakefile +10 -49
- data/VERSION +1 -1
- data/lib/csv_madness/builder.rb +95 -10
- data/lib/csv_madness/column.rb +30 -0
- data/lib/csv_madness/column_methods/core_methods.rb +6 -0
- data/lib/csv_madness/column_methods/default_value_methods.rb +7 -0
- data/lib/csv_madness/column_methods/error_methods.rb +19 -0
- data/lib/csv_madness/column_methods/fetch_methods.rb +11 -0
- data/lib/csv_madness/column_methods/typing_methods.rb +24 -0
- data/lib/csv_madness/column_set.rb +5 -0
- data/lib/csv_madness/column_type.rb +74 -0
- data/lib/csv_madness/column_types/date.rb +14 -0
- data/lib/csv_madness/column_types/float.rb +25 -0
- data/lib/csv_madness/column_types/integer.rb +17 -0
- data/lib/csv_madness/column_types/string.rb +9 -0
- data/lib/csv_madness/data_accessor_module.rb +79 -44
- data/lib/csv_madness/exceptions.rb +16 -0
- data/lib/csv_madness/gem_api.rb +11 -2
- data/lib/csv_madness/index_set.rb +36 -0
- data/lib/csv_madness/indexer.rb +105 -0
- data/lib/csv_madness/record.rb +68 -40
- data/lib/csv_madness/record_filter.rb +68 -0
- data/lib/csv_madness/record_methods/method_methods.rb +57 -0
- data/lib/csv_madness/sheet.rb +172 -442
- data/lib/csv_madness/sheet_methods/base.rb +6 -0
- data/lib/csv_madness/sheet_methods/class_methods.rb +84 -0
- data/lib/csv_madness/sheet_methods/column_methods.rb +175 -0
- data/lib/csv_madness/sheet_methods/file_methods.rb +44 -0
- data/lib/csv_madness/sheet_methods/indexing.rb +133 -0
- data/lib/csv_madness/sheet_methods/record_methods.rb +175 -0
- data/lib/csv_madness/sheet_methods/sorting_methods.rb +10 -0
- data/lib/csv_madness/utils/const_proc.rb +19 -0
- data/lib/csv_madness/utils/counter.rb +24 -0
- data/lib/csv_madness/utils/counter_proc.rb +31 -0
- data/lib/csv_madness/utils/looker_upper.rb +36 -0
- data/lib/csv_madness.rb +2 -2
- data/test/csv/nil_headers.csv +5 -0
- data/test/csv/out/.gitkeep +0 -0
- data/test/csv/pokemon.csv +9 -0
- data/test/csv/test_column_types.csv +2 -2
- data/test/helper.rb +13 -5
- data/test/test_builder.rb +43 -3
- data/test/test_csv_madness.rb +34 -25
- data/test/test_index_set.rb +30 -0
- data/test/test_indexer.rb +50 -0
- data/test/test_reloading_spreadsheet.rb +5 -2
- data/test/test_sheet.rb +184 -37
- data/test/test_utils.rb +99 -0
- metadata +82 -36
checksums.yaml
CHANGED
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
|
|
5
|
-
data.tar.gz: !binary |-
|
|
6
|
-
ZTRkOWNjMjYyNTNmMjA3YzMwZThlMzAyOWRjMTM0ZWYwMTQ3NWE0MA==
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 63380347571236804849e35c32c5463efc6e71151e992a3e3f18f8d2673fc110
|
|
4
|
+
data.tar.gz: 8c58382266af7f1091a80dc89eb6900a4efb8a5f956fe5801aa77ea5d5e4f05f
|
|
7
5
|
SHA512:
|
|
8
|
-
metadata.gz:
|
|
9
|
-
|
|
10
|
-
Zjg2ZjA5ZGZlZTEyNjg1NjFmNzE5NzY3NTFhNzY3ZTIyNDFhZGIzZTEzOGI5
|
|
11
|
-
ZDUzMjZjYjJkODdiM2M1M2JiMjZiMGNlMTViNWE2Yzk1ZjU1YjI=
|
|
12
|
-
data.tar.gz: !binary |-
|
|
13
|
-
MjJlYzI1YTQxMzU4NzA0MmVlNzFkYzVmNDE3NjYyMDM5YmI3Y2JmMmQyOWZk
|
|
14
|
-
OGQ0NzFhODUyMzRjNWI3ZWRlZDQ3YmQ0NGU0Zjc4ZDFmNWRkMWY4MDc4OTdi
|
|
15
|
-
YjkwY2U1MGY3MmVhNTY2NTYzYzc1ZDViZWViMTg0OTE5OGU1ZTE=
|
|
6
|
+
metadata.gz: 3b8ca366e680bf629fbb081145a8b254e5a121102b0772a40095893e06bc1f46f1f081c64cfa60efb1cbe69e633685c65b406795601c237157a8af63a5ce4d3c
|
|
7
|
+
data.tar.gz: 298b6b68d6bed7254ae7177b6fd24f8bfccb765435df5fbd5e9bfabb618db3bf386c276c741ca5fbab2bd9d8168c011010579ccca3bbfe7af0a19a2cffe163ad
|
data/.document
ADDED
data/CHANGELOG.markdown
CHANGED
data/Gemfile
CHANGED
|
@@ -7,14 +7,8 @@ source "http://rubygems.org"
|
|
|
7
7
|
# Include everything needed to run rake, tests, features, etc.
|
|
8
8
|
|
|
9
9
|
group :development do
|
|
10
|
-
# gem "shoulda", ">= 3.5"
|
|
11
|
-
# gem "rdoc", "~> 3.12"
|
|
12
|
-
# gem "bundler", "~> 1.5"
|
|
13
|
-
# gem "jeweler", "~> 2"
|
|
14
10
|
gem "fun_with_testing"
|
|
15
|
-
# gem "debugger"
|
|
16
11
|
end
|
|
17
12
|
|
|
18
|
-
|
|
19
|
-
#
|
|
20
|
-
gem 'fun_with_gems', '~> 0.0', ">= 0.0.2"
|
|
13
|
+
gem 'fun_with_gems', '~> 0.0', ">= 0.0.6"
|
|
14
|
+
gem 'csv' # removed from core
|
data/README.rdoc
CHANGED
|
@@ -7,62 +7,62 @@ CSV Madness tries to remove what little pain is left from Ruby's CSV class. Loa
|
|
|
7
7
|
|
|
8
8
|
== Why should I use it?
|
|
9
9
|
|
|
10
|
-
I like brief code and I cannot lie.
|
|
10
|
+
I like brief code and I cannot lie.
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
== Examples
|
|
14
14
|
|
|
15
15
|
CsvMadness makes some assumptions about your CSV file. It does assume headers, for example.
|
|
16
16
|
|
|
17
|
-
The simplest case is when your columns are nicely named. For example, if you have a csv file named
|
|
17
|
+
The simplest case is when your columns are nicely named. For example, if you have a csv file named `~/data/people.csv`:
|
|
18
|
+
|
|
19
|
+
"id","fname","lname","age","born"
|
|
20
|
+
"1","Mary","Moore","27","1986-04-08 15:06:10"
|
|
21
|
+
"2","Bill","Paxton","39","1974-02-22"
|
|
22
|
+
"3","Charles","Darwin","72",""
|
|
23
|
+
"4","Chuck","Norris","57","1901-03-02"
|
|
18
24
|
|
|
19
|
-
<code>
|
|
20
|
-
"id","fname","lname","age","born"
|
|
21
|
-
"1","Mary","Moore","27","1986-04-08 15:06:10"
|
|
22
|
-
"2","Bill","Paxton","39","1974-02-22"
|
|
23
|
-
"3","Charles","Darwin","72",""
|
|
24
|
-
"4","Chuck","Norris","57","1901-03-02"
|
|
25
|
-
</code>
|
|
26
25
|
|
|
27
26
|
... then you can write code like so:
|
|
28
27
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
sheet
|
|
32
|
-
sheet.
|
|
33
|
-
sheet.
|
|
34
|
-
sheet.
|
|
35
|
-
sheet.
|
|
36
|
-
|
|
37
|
-
|
|
28
|
+
require 'csv_madness'
|
|
29
|
+
|
|
30
|
+
sheet = CsvMadness.load( "~/data/people.csv" )
|
|
31
|
+
sheet.columns # => [:id, :fname, :lname, :age, :born]
|
|
32
|
+
sheet.records.map(&:id) # => ["1", "2", "3", "4"]
|
|
33
|
+
sheet.set_column_type(:id, :float)
|
|
34
|
+
sheet.records.map(&:id) # => [1.0, 2.0, 3.0, 4.0]
|
|
35
|
+
sheet.alter_column(:id) do |id|
|
|
36
|
+
id + rand() - 0.5
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
sheet.records.map(&:id) # => [0.7186, 2.30134, 2.90132, 4.30124] (your results may vary)
|
|
40
|
+
mary = sheet[0]
|
|
38
41
|
|
|
39
|
-
|
|
40
|
-
mary = sheet[0]
|
|
42
|
+
"#{mary.lname}, #{mary.fname} (#{mary.id})" # => "Moore, Mary (1)"
|
|
41
43
|
|
|
42
|
-
"#{mary.lname}, #{mary.fname} (#{mary.id})" # => "Moore, Mary (1)"
|
|
43
|
-
'''
|
|
44
44
|
|
|
45
45
|
If you're not satisfied with your column names, you can send your own, in the column order. An index can also be provided, which will allow you to use <tt>.fetch()</tt> to find specific records quickly:
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
sheet = CsvMadness.load( "~/data/people.csv",
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
require 'csv_madness'
|
|
48
|
+
sheet = CsvMadness.load( "~/data/people.csv",
|
|
49
|
+
columns: [:uid, :first_name, :last_name, :years_on_planet, :birthday],
|
|
50
|
+
index: :uid )
|
|
51
|
+
|
|
52
|
+
sheet.fetch("2").years_on_planet # => "39"
|
|
51
53
|
|
|
52
|
-
sheet.fetch("2").years_on_planet # => "39"
|
|
53
|
-
'''
|
|
54
54
|
|
|
55
55
|
**Note:** you can provide multiple indexes as an array. However many columns you index, you'll run into trouble if the index isn't unique for each record. (that may change in the future)
|
|
56
56
|
|
|
57
57
|
|
|
58
58
|
It's useful to clean up your files. Say you have:
|
|
59
59
|
|
|
60
|
-
"
|
|
61
|
-
"1 ","Mary ","Moore","27","1986-04-08 15:06:10"
|
|
62
|
-
""," Bill ","Paxton",," Feb. 22, 1974 "
|
|
63
|
-
,"Charles "," Darwin","72 "
|
|
64
|
-
"4","Chuck","Norris",," 2 March 1901 "
|
|
65
|
-
|
|
60
|
+
"id","fname","lname","age"," born "
|
|
61
|
+
"1 ","Mary ","Moore","27","1986-04-08 15:06:10"
|
|
62
|
+
""," Bill ","Paxton",," Feb. 22, 1974 "
|
|
63
|
+
"3","Charles "," Darwin","72 "
|
|
64
|
+
"4","Chuck","Norris",," 2 March 1901 "
|
|
65
|
+
|
|
66
66
|
|
|
67
67
|
Ick. Missing IDs, inconsistent date formats, leading and trailing whitespace... We can fix this.
|
|
68
68
|
|
|
@@ -163,27 +163,35 @@ You could do something similar to clean and standardize phone numbers, detect an
|
|
|
163
163
|
|
|
164
164
|
You have an array of objects. You want to write them to a spreadsheet.
|
|
165
165
|
|
|
166
|
-
```ruby
|
|
167
166
|
sb = CsvMadness::Builder.new do |sb|
|
|
168
167
|
sb.column( :id )
|
|
169
|
-
sb.column( :addressee,
|
|
170
|
-
sb.column( :
|
|
171
|
-
sb.column( :
|
|
172
|
-
sb.column( :city,
|
|
173
|
-
sb.column( :state_code,
|
|
174
|
-
sb.column( :formatted_zip,
|
|
175
|
-
sb.column( :phone,
|
|
176
|
-
sb.column( :
|
|
177
|
-
sb.column( :
|
|
178
|
-
sb.column( :
|
|
168
|
+
sb.column( :addressee, "addressee_custom" )
|
|
169
|
+
sb.column( :street, "primary_address.street_address" )
|
|
170
|
+
sb.column( :street2, "primary_address.supplemental_address_1" )
|
|
171
|
+
sb.column( :city, "primary_address.city" )
|
|
172
|
+
sb.column( :state_code, "primary_address.state_code" )
|
|
173
|
+
sb.column( :formatted_zip, "primary_address.formatted_zip" )
|
|
174
|
+
sb.column( :phone, "phones.first.phone" )
|
|
175
|
+
sb.column( :contact, "congregation_fieldset.congregation_leader" )
|
|
176
|
+
sb.column( :email, "emails.first.email" )
|
|
177
|
+
sb.column( :greeting ) do |contact|
|
|
178
|
+
if contact.business?
|
|
179
|
+
"To our friends at #{contact.organization_name},"
|
|
180
|
+
elsif contact.person?
|
|
181
|
+
"Dear #{ contact.title } #{ contact.last_name }"
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
179
185
|
end
|
|
180
186
|
|
|
181
187
|
sheet = sb.build( [address1, address2, address3...] )
|
|
182
|
-
|
|
188
|
+
|
|
189
|
+
outfile = FunWith::Files::FilePath.home.join( "Documents", "addresses", "address_records.csv" )
|
|
190
|
+
sheet.write_to_file( outfile )
|
|
183
191
|
|
|
184
192
|
=== Documentation is incomplete ===
|
|
185
193
|
|
|
186
|
-
There are
|
|
194
|
+
There are other features, but they'll take time to test and document.
|
|
187
195
|
|
|
188
196
|
|
|
189
197
|
|
data/Rakefile
CHANGED
|
@@ -1,22 +1,13 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
|
+
require 'fun_with_gems'
|
|
3
|
+
require_relative File.join( "lib", "csv_madness" )
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
+
self.extend( FunWith::Gems::Rakefile )
|
|
6
|
+
rakefile_setup CsvMadness
|
|
5
7
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
$stderr.puts e.message
|
|
10
|
-
$stderr.puts "Run `bundle install` to install missing gems"
|
|
11
|
-
exit e.status_code
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
require 'rake'
|
|
15
|
-
|
|
16
|
-
require 'jeweler'
|
|
17
|
-
|
|
18
|
-
Jeweler::Tasks.new do |gem|
|
|
19
|
-
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
|
8
|
+
gem_specification do |gem|
|
|
9
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
|
10
|
+
# dependencies defined in Gemfile
|
|
20
11
|
gem.name = "csv_madness"
|
|
21
12
|
gem.homepage = "http://github.com/darthschmoo/csv_madness"
|
|
22
13
|
gem.license = "MIT"
|
|
@@ -24,39 +15,9 @@ Jeweler::Tasks.new do |gem|
|
|
|
24
15
|
gem.description = "CSV Madness removes what little pain is left from Ruby's CSV class. Load a CSV file, and get back an array of objects with customizable getter/setter methods."
|
|
25
16
|
gem.email = "keeputahweird@gmail.com"
|
|
26
17
|
gem.authors = ["Bryce Anderson"]
|
|
27
|
-
|
|
28
|
-
# dependencies defined in Gemfile
|
|
29
|
-
gem.files = Dir.glob( File.join( ".", "lib", "**", "*.rb" ) ) +
|
|
30
|
-
Dir.glob( File.join( ".", "test", "**", "*" ) ) +
|
|
31
|
-
%w( Gemfile Rakefile LICENSE.txt README.rdoc VERSION CHANGELOG.markdown )
|
|
32
|
-
|
|
33
|
-
end
|
|
34
18
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
require 'rake/testtask'
|
|
38
|
-
Rake::TestTask.new(:test) do |test|
|
|
39
|
-
test.libs << 'lib' << 'test'
|
|
40
|
-
test.pattern = 'test/**/test_*.rb'
|
|
41
|
-
test.verbose = true
|
|
19
|
+
# dependencies defined in Gemfile
|
|
20
|
+
add_specification_files( gem, :default )
|
|
42
21
|
end
|
|
43
22
|
|
|
44
|
-
|
|
45
|
-
# Rcov::RcovTask.new do |test|
|
|
46
|
-
# test.libs << 'test'
|
|
47
|
-
# test.pattern = 'test/**/test_*.rb'
|
|
48
|
-
# test.verbose = true
|
|
49
|
-
# test.rcov_opts << '--exclude "gems/*"'
|
|
50
|
-
# end
|
|
51
|
-
|
|
52
|
-
task :default => :test
|
|
53
|
-
|
|
54
|
-
require 'rdoc/task'
|
|
55
|
-
Rake::RDocTask.new do |rdoc|
|
|
56
|
-
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
|
57
|
-
|
|
58
|
-
rdoc.rdoc_dir = 'rdoc'
|
|
59
|
-
rdoc.title = "csv_madness #{version}"
|
|
60
|
-
rdoc.rdoc_files.include('README*')
|
|
61
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
62
|
-
end
|
|
23
|
+
setup_gem_boilerplate
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.0.
|
|
1
|
+
0.0.10
|
data/lib/csv_madness/builder.rb
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
module CsvMadness
|
|
2
2
|
class Builder
|
|
3
|
+
attr_accessor :label
|
|
4
|
+
|
|
3
5
|
def initialize( &block )
|
|
4
6
|
@columns = {}
|
|
5
7
|
@column_syms = []
|
|
8
|
+
@column_counters = {}
|
|
9
|
+
|
|
6
10
|
@module = Module.new # for extending
|
|
7
11
|
self.extend( @module )
|
|
8
|
-
yield self
|
|
12
|
+
yield self if block_given?
|
|
9
13
|
end
|
|
10
14
|
|
|
15
|
+
|
|
11
16
|
def column( sym, method_path = nil, &block )
|
|
12
17
|
warn( "#{sym} already defined. Overwriting." ) if @column_syms.include?( sym )
|
|
13
18
|
|
|
@@ -20,6 +25,24 @@ module CsvMadness
|
|
|
20
25
|
Proc.new(&sym)
|
|
21
26
|
end
|
|
22
27
|
end
|
|
28
|
+
|
|
29
|
+
def column_names
|
|
30
|
+
@column_syms
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def constant_column( sym, const )
|
|
34
|
+
cproc = Utils::ConstProc.proc_for( const )
|
|
35
|
+
column( sym, &cproc )
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def blank_column( sym )
|
|
39
|
+
constant_column( sym, nil )
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def counter_column( sym, initial_value = 0 )
|
|
43
|
+
column( sym, &Utils::CounterProc.counter( initial_value ) )
|
|
44
|
+
end
|
|
45
|
+
|
|
23
46
|
|
|
24
47
|
# Three :on_error values:
|
|
25
48
|
# :print => Put an error message in the cell instead of a value
|
|
@@ -27,23 +50,34 @@ module CsvMadness
|
|
|
27
50
|
# :ignore => Hand back an empty cell
|
|
28
51
|
#
|
|
29
52
|
# Although ideally it should be configurable by column...
|
|
30
|
-
def build( objects, opts = {
|
|
53
|
+
def build( objects, opts = {} )
|
|
54
|
+
@progress_mode = choose_progress_mode( opts[:progress] )
|
|
55
|
+
debugger unless defined?( @show_progress )
|
|
56
|
+
|
|
57
|
+
@on_error = opts.fetch(:on_error){ :print }
|
|
58
|
+
@verbose = opts.fetch(:verbose){ false }
|
|
59
|
+
|
|
31
60
|
spreadsheet = CsvMadness::Sheet.new( @column_syms )
|
|
61
|
+
|
|
62
|
+
@objects = objects
|
|
63
|
+
@objects = @objects.records if @objects.is_a?( CsvMadness::Sheet )
|
|
32
64
|
|
|
33
|
-
for object in objects
|
|
34
|
-
STDOUT << "."
|
|
65
|
+
for object in @objects
|
|
35
66
|
record = {}
|
|
36
67
|
for sym in @column_syms
|
|
37
68
|
record[sym] = build_cell( object, sym, opts )
|
|
38
69
|
end
|
|
39
|
-
|
|
70
|
+
|
|
40
71
|
spreadsheet.add_record( record ) # hash form
|
|
72
|
+
show_progress
|
|
41
73
|
end
|
|
74
|
+
|
|
75
|
+
_reset_counter_columns
|
|
42
76
|
|
|
43
77
|
spreadsheet
|
|
44
78
|
end
|
|
45
79
|
|
|
46
|
-
def build_cell( object, sym, opts = {
|
|
80
|
+
def build_cell( object, sym, opts = {} )
|
|
47
81
|
column = @columns[sym]
|
|
48
82
|
|
|
49
83
|
case column
|
|
@@ -52,10 +86,12 @@ module CsvMadness
|
|
|
52
86
|
when Proc
|
|
53
87
|
build_cell_by_proc( object, column, opts )
|
|
54
88
|
else
|
|
55
|
-
|
|
89
|
+
debugger
|
|
90
|
+
"no idea what to do with #{column.inspect} (#{column.class})"
|
|
56
91
|
end
|
|
57
92
|
end
|
|
58
93
|
|
|
94
|
+
# ?????
|
|
59
95
|
def def( method_name, &block )
|
|
60
96
|
@module.send( :define_method, method_name, &block )
|
|
61
97
|
end
|
|
@@ -63,7 +99,7 @@ module CsvMadness
|
|
|
63
99
|
protected
|
|
64
100
|
def build_cell_by_pathstring( object, str, opts )
|
|
65
101
|
handle_cell_build_error( :build_cell_by_pathstring, opts ) do
|
|
66
|
-
for method in str.split(".")
|
|
102
|
+
for method in str.split(".")
|
|
67
103
|
object = object.send(method)
|
|
68
104
|
end
|
|
69
105
|
|
|
@@ -82,9 +118,9 @@ module CsvMadness
|
|
|
82
118
|
yield
|
|
83
119
|
end
|
|
84
120
|
rescue Exception => e
|
|
85
|
-
case
|
|
121
|
+
case @on_error
|
|
86
122
|
when nil, :print
|
|
87
|
-
puts "error #{e.message} #{caller}" if
|
|
123
|
+
puts "error #{e.message} #{caller}" if @verbose
|
|
88
124
|
"ERROR: #{e.message} (#{caller}())"
|
|
89
125
|
when :raise
|
|
90
126
|
puts "Re-raisinge error #{e.message}. Set opts[:on_error] to :print or :ignore if you want Builder to continue on errors."
|
|
@@ -93,5 +129,54 @@ module CsvMadness
|
|
|
93
129
|
""
|
|
94
130
|
end
|
|
95
131
|
end
|
|
132
|
+
|
|
133
|
+
def _reset_counter_columns
|
|
134
|
+
for sym, column in @columns
|
|
135
|
+
column.reset if column.is_a?( Utils::CounterProc )
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
#
|
|
140
|
+
def choose_progress_mode( m )
|
|
141
|
+
case m
|
|
142
|
+
when true
|
|
143
|
+
@show_progress = true
|
|
144
|
+
@progress_mode = progress_bar_available? ? :tty : :stdout
|
|
145
|
+
when false, nil
|
|
146
|
+
@show_progress = false
|
|
147
|
+
@progress_mode = :none
|
|
148
|
+
when Symbol
|
|
149
|
+
@show_progress = (m == :none) ? false : true
|
|
150
|
+
@progress_mode = m
|
|
151
|
+
else
|
|
152
|
+
warn "unknown progress mode #{m.inspect}"
|
|
153
|
+
choose_progress_mode( false )
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def show_progress
|
|
158
|
+
if @show_progress
|
|
159
|
+
@progress_mode ||= progress_bar_available? ? :tty : :stdout
|
|
160
|
+
|
|
161
|
+
case @progress_mode
|
|
162
|
+
when :tty
|
|
163
|
+
@tty_progress_bar ||= TTY::ProgressBar.new( "Building spreadsheet :current/:total (:percent) [:bar]", total: @objects.length )
|
|
164
|
+
@tty_progress_bar.advance(1)
|
|
165
|
+
when :stdout
|
|
166
|
+
STDOUT << "."
|
|
167
|
+
when :counter
|
|
168
|
+
unless defined?( @tty_progress_count )
|
|
169
|
+
puts ""
|
|
170
|
+
@tty_progress_count = 1
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
print "\r#{@tty_progress_count}/#{@objects.length} records written "
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def progress_bar_available?
|
|
179
|
+
defined?(TTY) && defined?(TTY::ProgressBar)
|
|
180
|
+
end
|
|
96
181
|
end
|
|
97
182
|
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module CsvMadness
|
|
2
|
+
class Column
|
|
3
|
+
include ColumnMethods::CoreMethods
|
|
4
|
+
include ColumnMethods::FetchMethods
|
|
5
|
+
include ColumnMethods::TypingMethods
|
|
6
|
+
include ColumnMethods::ErrorMethods
|
|
7
|
+
include ColumnMethods::DefaultValueMethods
|
|
8
|
+
|
|
9
|
+
attr_accessor :spreadsheet,
|
|
10
|
+
:index,
|
|
11
|
+
:name,
|
|
12
|
+
:type_conversion
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def initialize( _sheet, _name, _index, _type )
|
|
16
|
+
self.name = convert_column_name( nm )
|
|
17
|
+
self.index = index
|
|
18
|
+
self.aliases = @opts[:aliases]
|
|
19
|
+
self.set_type( opts[:type] )
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def get_cell( record )
|
|
23
|
+
record.data[self.index]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def set_cell( record, value )
|
|
27
|
+
record.data[self.index] = self.type_convert( value )
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module CsvMadness
|
|
2
|
+
module ColumnMethods
|
|
3
|
+
module ErrorMethods
|
|
4
|
+
# When a conversion fails, what happens?
|
|
5
|
+
#
|
|
6
|
+
# debug : enters debugger (test mode)
|
|
7
|
+
# ignore : no message is given and the default value is returned ("" for string, 0 for integer, 0.0 for float)?
|
|
8
|
+
# stdout : message printed to stdout
|
|
9
|
+
# stderr : message printed to stderr
|
|
10
|
+
# cell : message appears in the cell
|
|
11
|
+
attr_accessor :warning_style
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
attr_accessor :error_message
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module CsvMadness
|
|
2
|
+
module ColumnMethods
|
|
3
|
+
module TypingMethods
|
|
4
|
+
def type_convert( value )
|
|
5
|
+
if self.type_conversion
|
|
6
|
+
self.type_conversion.call( value )
|
|
7
|
+
else
|
|
8
|
+
value
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def set_type( type )
|
|
13
|
+
self.type_conversion = TypeConversion[@opts[:type]]
|
|
14
|
+
self.convert_column_to_type
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def convert_column_to_type
|
|
18
|
+
for record in self.spreadsheet.records
|
|
19
|
+
set_cell( record, get_cell( record ) )
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
module CsvMadness
|
|
2
|
+
class ColumnType
|
|
3
|
+
COLUMN_TYPE_LOOKUP = {
|
|
4
|
+
:integer => ColumnTypes::IntegerType,
|
|
5
|
+
:date => ColumnTypes::DateType,
|
|
6
|
+
:string => ColumnTypes::StringType,
|
|
7
|
+
# :number => ColumnTypes::NumberType,
|
|
8
|
+
:float => ColumnTypes::FloatType
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
DEFAULT_ERROR_MESSAGES = {
|
|
12
|
+
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# What is returned when a conversion fails?
|
|
17
|
+
#
|
|
18
|
+
# :default : the type conversion returns
|
|
19
|
+
# :blank : the cell becomes a nil
|
|
20
|
+
# :stderr : send warning message to stderr and return a nil
|
|
21
|
+
# :stdout : send warning message to stdout and return a nil
|
|
22
|
+
# :sheet : write the warning message into the sheet itself
|
|
23
|
+
attr_accessor :conversion_error_value
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def self.[]( t )
|
|
27
|
+
COLUMN_TYPE_LOOKUP[t]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def initialize( opts = {} )
|
|
31
|
+
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# default_hierarchy
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def standard_opts( overrides )
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def conversion_error_message( input )
|
|
45
|
+
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def handle_conversion_errors( input, &block )
|
|
49
|
+
begin
|
|
50
|
+
yield if block_given?
|
|
51
|
+
rescue ArgumentError
|
|
52
|
+
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def self.default_value
|
|
57
|
+
nil
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def default
|
|
61
|
+
self.default_value || self.class.default_value || nil
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
protected
|
|
65
|
+
def build_error_message( input, e )
|
|
66
|
+
if input =~ /CsvMadness::TypeError/
|
|
67
|
+
input
|
|
68
|
+
else
|
|
69
|
+
"CsvMadness::TypeError::#{ }"
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module CsvMadness
|
|
2
|
+
module ColumnTypes
|
|
3
|
+
class DateType < CsvMadness::ColumnType
|
|
4
|
+
# Blank inputs return nil. Other inputs may raise a date error
|
|
5
|
+
def self.convert( input )
|
|
6
|
+
if input.fwf_blank?
|
|
7
|
+
self.default_value
|
|
8
|
+
elsif input.is_a?(String)
|
|
9
|
+
Date.parse( input )
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|