Permalink
Browse files

0.5.0

  • Loading branch information...
1 parent 4fef5c3 commit 33e7c2ab098fb35d774214f2b1e64f8b81cae4a0 thoran committed Nov 18, 2011
Showing with 86 additions and 128 deletions.
  1. +29 −45 bin/csv_checker.rb
  2. +3 −3 config/checks.rb
  3. +0 −13 lib/Array/not_emptyQ.rb
  4. +0 −18 lib/Object/inQ.rb
  5. +0 −14 lib/Object/not_nilQ.rb
  6. +54 −16 lib/SimpleCSV.rbd/SimpleCSV.rb
  7. +0 −19 lib/String/integerQ.rb
View
@@ -1,76 +1,60 @@
#!/usr/bin/env ruby
# csv_checker.rb
-# 20111112
-# 0.4.0
+# 20111117, 18
+# 0.5.0
-$LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')))
+# Changes since 0.4:
+# 1. Removed all of the core extensions used here or in config/checks.rb.
+# 2. Moved @warnings and @errors from parse() into check(), where they should have been, and made them locals, thereby removing two effectively global instance variables.
+# 3. Made use of a new method on SimpleCSV's eigenclass: collect(), which shortens the required amount of code quite a bit.
-require 'Array/not_emptyQ'
-require 'Object/inQ'
-require 'Object/not_nilQ'
-require 'SimpleCSV.rbd/SimpleCSV'
-require 'String/integerQ'
-require 'Switches'
+require '../lib/SimpleCSV.rbd/SimpleCSV'
+require '../lib/Switches'
def switches
- @switches ||= (
- Switches.new do |s|
- s.set :d, :data_file, :default => '../data/data.csv'
- s.set :s, :states_file, :default => '../data/states.csv'
- s.set :f, :filter_type, :default => 'everything' # clean, unclean, warnings, no_warnings, errors, no_errors, everything
- s.set :c, :checks_file, :default => '../config/checks.rb'
- end
- )
+ @switches ||= Switches.new do |s|
+ s.set :d, :data_file, :default => '../data/data.csv'
+ s.set :s, :states_file, :default => '../data/states.csv'
+ s.set :f, :filter_type, :default => 'everything' # clean, unclean, warnings, no_warnings, errors, no_errors, everything
+ s.set :c, :checks_file, :default => '../config/checks.rb'
+ end
end
def state_codes
- @state_codes ||= (
- csv_file = CSVFile.new(switches.states_file, :row_separator => "\r\r\n")
- csv_file.columns = ['code', 'name']
- csv_file.read.collect{|e| e['code']}
- )
+ @state_codes ||= CSVFile.collect(switches.states_file, :row_separator => "\r\r\n", :columns => ['code', 'name']){|row| row['code']}
end
def checks
@checks ||= instance_eval(File.read(switches.checks_file))
end
def check(parsed_row)
+ warnings, errors = [], []
checks.each do |check|
if check[:warning] && !check[:test].call(parsed_row)
- @warnings << check[:warning].call(parsed_row)
+ warnings << check[:warning].call(parsed_row)
elsif check[:error] && !check[:test].call(parsed_row)
- @errors << check[:error].call(parsed_row)
+ errors << check[:error].call(parsed_row)
end
end
+ {:warnings => warnings, :errors => errors}
end
def parse(data_file)
- SimpleCSV.parse(data_file, :headers => true, :row_separator => "\r\r\n").collect do |row|
- @warnings, @errors = [], []
- check(row)
- row.merge(:warnings => @warnings, :errors => @errors)
- end
+ CSVFile.collect(data_file, :headers => true, :row_separator => "\r\r\n"){|row| row.merge(check(row))}
end
def filter(parsed_data)
- if switches.filter_type == 'clean'
- parsed_data.select{|e| e[:warnings].empty? && e[:errors].empty?}
- elsif switches.filter_type == 'unclean'
- parsed_data.select{|e| e[:warnings].not_empty? || e[:errors].not_empty?}
- elsif switches.filter_type == 'warnings'
- parsed_data.select{|e| e[:warnings].not_empty?}
- elsif switches.filter_type == 'no_warnings'
- parsed_data.select{|e| e[:warnings].empty?}
- elsif switches.filter_type == 'errors'
- parsed_data.select{|e| e[:errors].not_empty?}
- elsif switches.filter_type == 'no_errors'
- parsed_data.select{|e| e[:errors].empty?}
- elsif switches.filter_type == 'everything'
- parsed_data
- else
- raise 'filter unrecognized'
+ case switches.filter_type
+ when 'clean'; parsed_data.select{|e| e[:warnings].empty? && e[:errors].empty?}
+ when 'unclean'; parsed_data.select{|e| !e[:warnings].empty? || !e[:errors].empty?}
+ when 'warnings'; parsed_data.select{|e| !e[:warnings].empty?}
+ when 'no_warnings'; parsed_data.select{|e| e[:warnings].empty?}
+ when 'errors'; parsed_data.select{|e| !e[:errors].empty?}
+ when 'no_errors'; parsed_data.select{|e| e[:errors].empty?}
+ when 'everything'; parsed_data
+ else; raise 'filter unrecognized'
end
end
View
@@ -5,14 +5,14 @@
},
{
:warning => Proc.new{|parsed_row| "The code denoting state must be in the file states.csv (ie. in the range 1..8)... #{parsed_row['State']} is outside this range."},
- :test => Proc.new{|parsed_row| parsed_row['State'].in?(state_codes)}
+ :test => Proc.new{|parsed_row| state_codes.include?(parsed_row['State'])}
},
{
:warning => Proc.new{|parsed_row| "The salary must be an integer and not a float... #{parsed_row['Salary']} is not an integer."},
- :test => Proc.new{|parsed_row| parsed_row['Salary'] && parsed_row['Salary'].integer?}
+ :test => Proc.new{|parsed_row| parsed_row['Salary'] && parsed_row['Salary'] =~ /^\d+$/}
},
{
:error => Proc.new{|parsed_row| "The postcode must exist... The postcode is missing."},
- :test => Proc.new{|parsed_row| parsed_row['Postcode'].not_nil?}
+ :test => Proc.new{|parsed_row| !parsed_row['Postcode'].nil?}
}
]
View
@@ -1,13 +0,0 @@
-# Array#not_empty?
-# Array#not_emptyQ
-
-# 20071228
-# 0.0.0
-
-class Array
-
- def not_empty?
- !self.empty?
- end
-
-end
View
@@ -1,18 +0,0 @@
-# Object/self.inQ
-# Object.in?
-
-# 20111114
-# 0.5.0
-
-# Description: This is essentially the reverse of Enumerable.include?. It can be pointed at any object and takes a collection as an argument.
-
-# Changes:
-# 1. Using Enumerable#flatten instead of testing the first argument in the argument list, since ths makes the code much shorter.
-
-class Object
-
- def in?(*a)
- a.flatten.include?(self)
- end
-
-end
View
@@ -1,14 +0,0 @@
-# Object#not_nil?
-
-# 20070301
-# 0.0.0
-
-# Description: A different way of doing !object.nil?.
-
-class Object
-
- def not_nil?
- !self.nil?
- end
-
-end
@@ -1,7 +1,7 @@
# SimpleCSV
-# 20111110, 11
-# 0.9.8
+# 20111111, 17, 18
+# 0.9.9 (in progress)
# Description: A CSV object for reading and writing CSV (and similar) text files with tabulated data to and from files and strings.
@@ -105,6 +105,19 @@
# 67. ~ SimpleCSV.first_row, so as arguments can be supplied to the constructor.
# 68. ~ SimpleCSV.attributes, so as arguments can be supplied to the constructor.
# 69. ~ SimpleCSV.columns, so as arguments can be supplied to the constructor.
+# 8/9
+# 70. ~ SimpleCSV.parse, back to the way it was at 0.9.7, since the call to read will never require the block argument as it never makes it there.
+# 71. Simplified the SimpleCSV eigenclass methods which were using open() by using new() instead, since this is more correct, more succinct, and more efficient.
+# 72. Added SimpleCSV eigenclass collection methods: collect, select, reject, detect. I couldn't simply mixin Enumerable as I had with the instance methods, because I needed to be able to supply arguments other than a block.
+# 73. + in SimpleCSV, alias_method :read_csv_header, :read_header in SimpleCSV.
+# 74. + in SimpleCSV, alias_method :write_csv_header, :write_header.
+# 75. + in SimpleCSV, alias_method :write_csv_row, :write_row.
+# 76. + in SimpleCSV, alias_method :each_row, :each.
+# 77. - CSVFile, attr_reader :filename, :args.
+# 78. ~ CSVFile#mode, so as it makes use of the instance variable rather than the removed reader method args.
+# 79. ~ CSVFile#mode, so it may accept hyphenated options for the mode aliases: read-only, read-write, write-only.
+# 80. ~ CSVFile#permissions, so as it makes use of the instance variable rather than the removed reader method args.
+# 81. ~ CSVFile#filename, by memoizing it.
require 'stringio'
@@ -130,7 +143,7 @@ def source_type(source)
end
def open(source, *args, &block)
- @csv_file ||= source_type(source).new(source, *args)
+ @csv_file = new(source, *args)
if block
begin
yield @csv_file
@@ -144,15 +157,38 @@ def open(source, *args, &block)
end
def each(source, *args, &block)
- open(source, *args){|csv_file| csv_file.each(&block)}
+ new(source, *args).each(&block)
end
alias_method :foreach, :each
+ def collect(source, *args, &block)
+ new_collection = []
+ each(source, *args){|row| new_collection << block.call(row)}
+ new_collection
+ end
+ alias_method :map, :collect
+
+ def select(source, *args, &block)
+ new_collection = []
+ each(source, *args){|row| new_collection << row if block.call(row)}
+ new_collection
+ end
+
+ def reject(source, *args, &block)
+ new_collection = []
+ each(source, *args){|row| new_collection << row unless block.call(row)}
+ new_collection
+ end
+
+ def detect(source, *args, &block)
+ each(source, *args){|row| return row if block.call(row)}
+ end
+
def read(source, *args, &block)
if block
parse(source, *args, &block)
else
- open(source, *args){|csv_file| csv_file.read_csv}
+ new(source, *args).read_csv
end
end
alias_method :read_csv, :read
@@ -161,13 +197,13 @@ def parse(source, *args, &block)
if block
each(source, *args, &block)
else
- read(source, *args, &block)
+ read(source, *args)
end
end
alias_method :parse_csv, :parse
def write(source, *args)
- open(source, *args){|csv_file| csv_file.write_csv}
+ new(source, *args).write_csv
end
alias_method :write_csv, :write
@@ -249,6 +285,7 @@ def read_header
@source.rewind
end
end
+ alias_method :read_csv_header, :read_header
def parse(*selected_columns, &block)
if block
@@ -331,6 +368,7 @@ def write_header(*selected_columns)
write_row(columns.to_csv)
end
end
+ alias_method :write_csv_header, :write_header
def write_row(row, *selected_columns)
collector = []
@@ -344,6 +382,7 @@ def write_row(row, *selected_columns)
@source.puts(collector.to_csv(@quote))
end
end
+ alias_method :write_csv_row, :write_row
def each(*selected_columns)
selected_columns.flatten!
@@ -365,6 +404,7 @@ def each(*selected_columns)
end
end
end
+ alias_method :each_row, :each
def attributes
@attributes ||= (
@@ -429,8 +469,6 @@ def open(source, *args, &block)
end # class << self
- attr_reader :filename, :args
-
def initialize(filename, *args)
@filename = filename
@args = args
@@ -443,23 +481,23 @@ def source
def mode
@mode ||= (
- case args.peek_options[:mode].to_s
- when 'r', 'r+', 'w', 'w+', 'a', 'a+'; args.peek_options[:mode].to_s
- when 'read_only', 'readonly'; 'r'
- when 'rw', 'read_write', 'readwrite'; 'r+'
- when 'write_only', 'writeonly'; 'w'
+ case @args.peek_options[:mode].to_s
+ when 'r', 'r+', 'w', 'w+', 'a', 'a+'; @args.peek_options[:mode].to_s
+ when 'read_only', 'read-only', 'readonly'; 'r'
+ when 'rw', 'read_write', 'read-write', 'readwrite'; 'r+'
+ when 'write_only', 'write-only', 'writeonly'; 'w'
when 'append'; 'a'
else 'r'
end
)
end
def permissions
- @permissions ||= args.peek_options[:permissions]
+ @permissions ||= @args.peek_options[:permissions]
end
def filename
- @filename = File.expand_path(@filename)
+ @filename ||= File.expand_path(@filename)
end
end # class CSVFile
View
@@ -1,19 +0,0 @@
-# String#integer?
-
-# 20070622
-# 0.0.0
-
-class String
-
- def integer?
- self =~ /^\d+$/ ? true : false
- end
-
-end
-
-if $0 == __FILE__
- 'string'.integer?
- '9.0'.integer?
- '9'.integer?
- '99'.integer?
-end

0 comments on commit 33e7c2a

Please sign in to comment.