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 CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- ZGQ4MTA4MGJlYjA1MzIwOTU4NzEyODViMmFhYmYwMTA2YjQzMmY2MQ==
5
- data.tar.gz: !binary |-
6
- ZTRkOWNjMjYyNTNmMjA3YzMwZThlMzAyOWRjMTM0ZWYwMTQ3NWE0MA==
2
+ SHA256:
3
+ metadata.gz: 4f4f9b06af750b458f7eb43b10053dfce2b60a69cc55ea4410d4466c72680f0c
4
+ data.tar.gz: 23ddbf472f0b52973769e21eb72939622bee0e5b13e43e8c292ae28203106145
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- MzAzNTJmNjU5YmM2NjUxODY4YjRkMDkwMDc5NTQxOWIxZWRjMjRmYmU1YzFh
10
- Zjg2ZjA5ZGZlZTEyNjg1NjFmNzE5NzY3NTFhNzY3ZTIyNDFhZGIzZTEzOGI5
11
- ZDUzMjZjYjJkODdiM2M1M2JiMjZiMGNlMTViNWE2Yzk1ZjU1YjI=
12
- data.tar.gz: !binary |-
13
- MjJlYzI1YTQxMzU4NzA0MmVlNzFkYzVmNDE3NjYyMDM5YmI3Y2JmMmQyOWZk
14
- OGQ0NzFhODUyMzRjNWI3ZWRlZDQ3YmQ0NGU0Zjc4ZDFmNWRkMWY4MDc4OTdi
15
- YjkwY2U1MGY3MmVhNTY2NTYzYzc1ZDViZWViMTg0OTE5OGU1ZTE=
6
+ metadata.gz: 1e44f1666c4118cf68a16f254ea5ad8808f9a0036b346755d187c74baf24a65cff718323d81741cd5478b036fcea3518daa3b2b5f72f6de2bf89b3b435392de8
7
+ data.tar.gz: b44d0fa470ec59317f5a93b0e73d1ec6a032f300083734e2b0aef27b2077b51303dce1b504e77102e6cfb5f79cc2c229d136d4a5d7eca049402bb88e740e711a
data/Gemfile CHANGED
@@ -15,6 +15,5 @@ group :development do
15
15
  # gem "debugger"
16
16
  end
17
17
 
18
- # gem "fun_with_files", "~> 0.0", ">= 0.0.7"
19
- # gem "fun_with_version_strings", "~> 0.0"
20
- gem 'fun_with_gems', '~> 0.0', ">= 0.0.2"
18
+ gem "csv"
19
+ gem 'fun_with_gems', '~> 0.0', ">= 0.0.7"
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 <tt>~/data/people.csv</tt>:
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
- '''require 'csv_madness'
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
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
- sheet.records.map(&:id) # => [0.7186, 2.30134, 2.90132, 4.30124] (your results may vary)
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
- '''require 'csv_madness'
48
- sheet = CsvMadness.load( "~/data/people.csv",
49
- columns: [:uid, :first_name, :last_name, :years_on_planet, :birthday],
50
- index: :uid )
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
- """"id","fname","lname","age"," born "
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, "addressee_custom" )
170
- sb.column( :street_address, "primary_address.street_address" )
171
- sb.column( :supplemental_address_1, "primary_address.supplemental_address_1" )
172
- sb.column( :city, "primary_address.city" )
173
- sb.column( :state_code, "primary_address.state_code" )
174
- sb.column( :formatted_zip, "primary_address.formatted_zip" )
175
- sb.column( :phone, "phones.first.phone" )
176
- sb.column( :leader, "congregation_fieldset.congregation_leader" )
177
- sb.column( :denomination, "congregation_fieldset.denomination" )
178
- sb.column( :email, "emails.first.email" )
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 lots of other features, but they'll take time to test and document.
194
+ There are other features, but they'll take time to test and document.
187
195
 
188
196
 
189
197
 
data/Rakefile CHANGED
@@ -1,62 +1,89 @@
1
1
  # encoding: utf-8
2
+ require 'fun_with_gems'
3
+ require_relative File.join( "lib", "csv_madness" )
4
+ self.extend( FunWith::Gems::Rakefile)
2
5
 
3
- require 'rubygems'
4
- require 'bundler'
6
+ rakefile_setup CsvMadness
5
7
 
6
- begin
7
- Bundler.setup(:default, :development)
8
- rescue Bundler::BundlerError => e
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'
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
11
+ gem.name = "csv_madness"
12
+ gem.homepage = "http://github.com/darthschmoo/csv_madness"
13
+ gem.license = "MIT"
14
+ gem.summary = "CSV Madness turns your CSV rows into happycrazy objects."
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."
16
+ gem.email = "keeputahweird@gmail.com"
17
+ gem.authors = ["Bryce Anderson"]
17
18
 
18
- Jeweler::Tasks.new do |gem|
19
- # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
20
- gem.name = "csv_madness"
21
- gem.homepage = "http://github.com/darthschmoo/csv_madness"
22
- gem.license = "MIT"
23
- gem.summary = "CSV Madness turns your CSV rows into happycrazy objects."
24
- 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
- gem.email = "keeputahweird@gmail.com"
26
- 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
-
19
+ # dependencies defined in Gemfile
20
+ gem.files = Dir.glob( File.join( ".", "lib", "**", "*.rb" ) ) +
21
+ Dir.glob( File.join( ".", "test", "**", "*" ) ) +
22
+ %w( Gemfile Rakefile LICENSE.txt README.rdoc VERSION CHANGELOG.markdown )
23
+ # csv_madness.gemspec )
33
24
  end
34
25
 
35
- Jeweler::RubygemsDotOrgTasks.new
26
+ setup_gem_boilerplate
36
27
 
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
42
- end
43
-
44
- # require 'rcov/rcovtask'
45
- # Rcov::RcovTask.new do |test|
46
- # test.libs << 'test'
28
+ #
29
+ #
30
+ # require 'rubygems'
31
+ # require 'bundler'
32
+ #
33
+ # begin
34
+ # Bundler.setup(:default, :development)
35
+ # rescue Bundler::BundlerError => e
36
+ # $stderr.puts e.message
37
+ # $stderr.puts "Run `bundle install` to install missing gems"
38
+ # exit e.status_code
39
+ # end
40
+ #
41
+ # require 'rake'
42
+ #
43
+ # require 'jeweler'
44
+ #
45
+ # Jeweler::Tasks.new do |gem|
46
+ # # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
47
+ # gem.name = "csv_madness"
48
+ # gem.homepage = "http://github.com/darthschmoo/csv_madness"
49
+ # gem.license = "MIT"
50
+ # gem.summary = "CSV Madness turns your CSV rows into happycrazy objects."
51
+ # 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."
52
+ # gem.email = "keeputahweird@gmail.com"
53
+ # gem.authors = ["Bryce Anderson"]
54
+ #
55
+ # # dependencies defined in Gemfile
56
+ # gem.files = Dir.glob( File.join( ".", "lib", "**", "*.rb" ) ) +
57
+ # Dir.glob( File.join( ".", "test", "**", "*" ) ) +
58
+ # %w( Gemfile Rakefile LICENSE.txt README.rdoc VERSION CHANGELOG.markdown csv_madness.gemspec )
59
+ #
60
+ # end
61
+ #
62
+ # Jeweler::RubygemsDotOrgTasks.new
63
+ #
64
+ # require 'rake/testtask'
65
+ # Rake::TestTask.new(:test) do |test|
66
+ # test.libs << 'lib' << 'test'
47
67
  # test.pattern = 'test/**/test_*.rb'
48
68
  # test.verbose = true
49
- # test.rcov_opts << '--exclude "gems/*"'
50
69
  # 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
70
+ #
71
+ # # require 'rcov/rcovtask'
72
+ # # Rcov::RcovTask.new do |test|
73
+ # # test.libs << 'test'
74
+ # # test.pattern = 'test/**/test_*.rb'
75
+ # # test.verbose = true
76
+ # # test.rcov_opts << '--exclude "gems/*"'
77
+ # # end
78
+ #
79
+ # task :default => :test
80
+ #
81
+ # require 'rdoc/task'
82
+ # Rake::RDocTask.new do |rdoc|
83
+ # version = File.exist?('VERSION') ? File.read('VERSION') : ""
84
+ #
85
+ # rdoc.rdoc_dir = 'rdoc'
86
+ # rdoc.title = "csv_madness #{version}"
87
+ # rdoc.rdoc_files.include('README*')
88
+ # rdoc.rdoc_files.include('lib/**/*.rb')
89
+ # end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.6
1
+ 0.0.9
@@ -1,11 +1,14 @@
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
+
6
9
  @module = Module.new # for extending
7
10
  self.extend( @module )
8
- yield self
11
+ yield self if block_given?
9
12
  end
10
13
 
11
14
  def column( sym, method_path = nil, &block )
@@ -1,44 +1,79 @@
1
+ # Each record within the spreadsheet is extended by a module with accessor functions
2
+ # which allow the user to treat column names as methods. When columns are added, dropped, or re-ordered,
3
+ # the accessor module needs to be updated to reflect the change.
1
4
  class DataAccessorModule < Module
5
+ # mapping : keys = column symbol
6
+ # values = column index
7
+ attr_accessor :column_accessors_map
8
+
2
9
  def initialize( mapping )
3
- @column_accessors = []
4
- remap_accessors( mapping )
10
+ puts "Got mapping #{mapping.inspect}"
11
+ @column_accessors_map = mapping
12
+ remap_accessors
5
13
  end
6
14
 
7
- def install_column_accessors( column, index )
8
- @column_accessors << column
9
- eval <<-EOF
10
- self.send( :define_method, :#{column} ) do
11
- self.csv_data[#{index}]
15
+ def install_column_accessors( *columns )
16
+ for column in columns.flatten
17
+ unless is_valid_ruby_method?( column )
18
+ warn( "#{column.inspect} is not a valid ruby method" )
19
+ require "data_accessor_module" # TODO: WTF?
20
+ column = "csv_column_#{index}"
21
+ warn( " ----> renaming to #{column.inspect}" )
12
22
  end
23
+
24
+ if self.respond_to?( column )
25
+ puts "Column already has accessors"
26
+ else
27
+ eval <<-EOF
28
+ self.send( :define_method, :#{column} ) do
29
+ self.csv_data[ self.column_accessors_map[ :#{column} ] ]
30
+ end
13
31
 
14
- self.send( :define_method, :#{column}= ) do |val|
15
- self.csv_data[#{index}] = val
32
+ self.send( :define_method, :#{column}= ) do |val|
33
+ self.csv_data[ self.column_accessors_map[ :#{column} ] ] = val
34
+ end
35
+ EOF
16
36
  end
17
- EOF
37
+ end
18
38
  end
19
39
 
20
- def remove_column_accessors( column )
21
- self.send( :remove_method, column )
22
- self.send( :remove_method, :"#{column}=" )
40
+ # But if instead of hardcoding the index, the index was pulled from the spreadsheet mapping...
41
+ # a titch slower to look up? But a change to the mapping automatically updates the method
42
+ def remove_column_accessors( *columns )
43
+ for column in columns.flatten
44
+ for method_sym in [column, :"#{column}="]
45
+ self.send( :remove_method, method_sym ) if self.respond_to?( method_sym )
46
+ end
47
+ end
23
48
  end
24
49
 
25
50
  def remove_all_column_accessors
26
- @column_accessors ||= []
51
+ # 2016-11-24 : was this necessary for anything
52
+ # @column_accessors ||= []
53
+ remove_column_accessors( @column_accessors_map.keys )
54
+ end
55
+
56
+ # Partly obsoleted.
57
+ def remap_accessors( *args )
58
+ #
27
59
 
28
- for sym in @column_accessors
29
- remove_column_accessors( sym )
60
+
61
+ remove_all_column_accessors
62
+
63
+ if args.length == 1
64
+ debugger
65
+ @column_accessors_map = args.first
30
66
  end
31
67
 
32
- @column_accessors = []
68
+ install_column_accessors( @column_accessors_map.keys )
33
69
  end
34
70
 
35
- def remap_accessors( mapping )
36
- remove_all_column_accessors
37
-
38
- @mapping = mapping
71
+ # Symbol.inspect doesn't output " quotes when given a sym
72
+ # that expresses a valid method name or a valid identifier.
73
+ def is_valid_ruby_method?( str_or_sym )
74
+ sym = str_or_sym.to_sym
75
+ test_string = sym.inspect
39
76
 
40
- for column, index in @mapping
41
- install_column_accessors( column, index )
42
- end
77
+ test_string.match( /^:[@"]/ ).nil?
43
78
  end
44
79
  end
@@ -0,0 +1,16 @@
1
+ module CsvMadness
2
+ class LoadError < RuntimeError
3
+ end
4
+
5
+ class ColumnError < ArgumentError
6
+ end
7
+
8
+ class ForbiddenColumnNameError < ColumnError
9
+ end
10
+
11
+ class MissingColumnError < ColumnError
12
+ end
13
+
14
+ class ColumnExistsError < ColumnError
15
+ end
16
+ end
@@ -8,8 +8,10 @@ module CsvMadness
8
8
  # symbol.
9
9
  class Record
10
10
  attr_accessor :csv_data
11
+ attr_reader :column_accessors_map
11
12
 
12
- def initialize( data )
13
+ def initialize( data, mapping )
14
+ @column_accessors_map = mapping # holds a reference to its spreadsheet's overall mapping
13
15
  import_record_data( data )
14
16
  end
15
17