csv_party 0.0.1.pre → 0.0.1.pre2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/csv_party.rb +85 -38
  3. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 475350af4d86e4c6586ea1752a731a888ddb336d
4
- data.tar.gz: 679da67bcacd832eb394f0bd93f483babcf44ecc
3
+ metadata.gz: c0ddb8c3205a36c4ed2b5dd97baf32e60d1da2bb
4
+ data.tar.gz: f7bd204ac0a1f74b0f7cd5e90df88fa36edc8e7c
5
5
  SHA512:
6
- metadata.gz: 9c91fdd4277ba7e80b9561333879386c115dbaa58df621705cdbfae8d517ab7a74b2298efdc8f45e32be014f2718d1c2fa8fcaba3adbb17db13b0e38ffda934b
7
- data.tar.gz: 58597567ec2ab2e1950bbc32a7526fe57ebe632437074661dcfc7c3390a90f570ce90ce203acfd622f84dea4aedea5e9d429206953bd21eb59621e986cfd596e
6
+ metadata.gz: d00c4e83bf4f3564f15c9abf6159e209652a1f04b99df349211a7040b47d8670804a6b4c38c77594e2d75fa1735581b706baa0fa45a1ff449180d5112881e474
7
+ data.tar.gz: 7778782ffea56a91250478b23fb8ad5634c5b17f1c94b0ef94910c6544e25d242f6c457cbc0f644316eaa1fc28d0827cfeb99d732ba72ea5b27182c8507f456f
data/lib/csv_party.rb CHANGED
@@ -3,66 +3,76 @@ require 'bigdecimal'
3
3
  require 'ostruct'
4
4
 
5
5
  class CSVParty
6
- def initialize(csv_path)
6
+ def initialize(csv_path, options = {})
7
+ options[:headers] = true
7
8
  @headers = CSV.new(File.open(csv_path)).shift
8
- @csv = CSV.new(File.open(csv_path), headers: true)
9
+ @csv = CSV.new(File.open(csv_path), options)
10
+
9
11
  raise_unless_named_parsers_are_valid
10
12
  raise_unless_csv_has_all_headers
11
13
  end
12
14
 
13
15
  def import!
14
- @csv.each do |row|
15
- parsed_row = parse_row(row)
16
- import_row(parsed_row)
16
+ loop do
17
+ begin
18
+ row = @csv.shift
19
+ break unless row
20
+ import_row(row)
21
+ rescue StandardError => error
22
+ process_error(error, @csv.lineno + 1)
23
+ next
24
+ end
17
25
  end
18
26
  end
19
27
 
20
28
  def parse_row(row)
29
+ unparsed_row = OpenStruct.new
21
30
  parsed_row = OpenStruct.new
22
- parsed_row[:values] = OpenStruct.new
23
31
 
24
32
  columns.each do |name, options|
25
33
  header = options[:header]
34
+ unparsed_value = row[header]
26
35
  parser = options[:parser]
27
- parsed_row[name] = instance_exec(row[header], &parser)
28
- parsed_row[:values][name] = row[header]
36
+
37
+ unparsed_row[name] = unparsed_value
38
+ parsed_row[name] = instance_exec(unparsed_value, &parser)
29
39
  end
30
40
 
41
+ parsed_row['unparsed'] = unparsed_row
42
+ parsed_row['csv_string'] = row.to_csv
43
+
31
44
  return parsed_row
32
45
  end
33
46
 
34
- def import_row(parsed_row)
35
- importer.call(parsed_row)
47
+ def import_row(row)
48
+ parsed_row = parse_row(row)
49
+ instance_exec(parsed_row, &importer)
50
+ end
51
+
52
+ def process_error(error, line_number)
53
+ instance_exec(error, line_number, &error_handler)
36
54
  end
37
55
 
38
56
  def self.column(name, options, &block)
39
- if columns.has_key?(name)
40
- raise DuplicateColumnError, "A column named :#{name} has already been defined, choose a different name"
41
- end
42
- unless options.has_key?(:header)
43
- raise MissingHeaderError, "A header must be specified for #{name}"
44
- end
57
+ raise_if_duplicate_column(name)
58
+ raise_if_missing_header(name, options)
45
59
 
46
60
  if block_given?
47
61
  columns[name] = { header: options[:header], parser: block }
48
62
  else
49
- if options.has_key?(:as)
50
- parser_method = "#{options[:as]}_parser".to_sym
51
- else
52
- parser_method = :string_parser
53
- end
63
+ parser_method = if options.has_key?(:as)
64
+ "#{options[:as]}_parser".to_sym
65
+ else
66
+ :string_parser
67
+ end
54
68
  columns[name] = {
55
69
  header: options[:header],
56
- parser: Proc.new { |value| send(parser_method, value) },
70
+ parser: proc { |value| send(parser_method, value) },
57
71
  parser_method: parser_method
58
72
  }
59
73
  end
60
74
  end
61
75
 
62
- def self.import(&block)
63
- @importer = block
64
- end
65
-
66
76
  def self.columns
67
77
  @columns ||= {}
68
78
  end
@@ -71,6 +81,10 @@ class CSVParty
71
81
  self.class.columns
72
82
  end
73
83
 
84
+ def self.import(&block)
85
+ @importer = block
86
+ end
87
+
74
88
  def self.importer
75
89
  @importer
76
90
  end
@@ -79,8 +93,32 @@ class CSVParty
79
93
  self.class.importer
80
94
  end
81
95
 
82
- private
96
+ def self.error(&block)
97
+ @error = block
98
+ end
99
+
100
+ def self.error_handler
101
+ @error
102
+ end
103
+
104
+ def error_handler
105
+ self.class.error_handler
106
+ end
107
+
108
+ def self.raise_if_duplicate_column(name)
109
+ return unless columns.has_key?(name)
110
+
111
+ raise DuplicateColumnError, "A column named :#{name} has already been \
112
+ defined, choose a different name"
113
+ end
114
+
115
+ def self.raise_if_missing_header(name, options)
116
+ return if options.has_key?(:header)
117
+
118
+ raise MissingHeaderError, "A header must be specified for #{name}"
119
+ end
83
120
 
121
+ private
84
122
 
85
123
  def raw_parser(value)
86
124
  value
@@ -91,15 +129,16 @@ class CSVParty
91
129
  end
92
130
 
93
131
  def boolean_parser(value)
94
- ['1', 't', 'true'].include? value.to_s.strip.downcase
132
+ %w[1 t true].include? value.to_s.strip.downcase
95
133
  end
96
134
 
97
135
  def integer_parser(value)
136
+ return nil if value.nil? || value.strip.empty?
98
137
  value.to_i
99
138
  end
100
139
 
101
140
  def decimal_parser(value)
102
- cleaned_value = value.to_s.strip.gsub(/[^0-9.]/, "")
141
+ cleaned_value = value.to_s.strip.gsub(/[^0-9.]/, '')
103
142
  BigDecimal.new(cleaned_value)
104
143
  end
105
144
 
@@ -108,7 +147,7 @@ class CSVParty
108
147
  end
109
148
 
110
149
  def columns_with_named_parsers
111
- columns.select { |name, options| options.has_key?(:parser_method) }
150
+ columns.select { |_name, options| options.has_key?(:parser_method) }
112
151
  end
113
152
 
114
153
  # This error has to be raised at runtime because, when the class body
@@ -117,22 +156,30 @@ class CSVParty
117
156
  def raise_unless_named_parsers_are_valid
118
157
  columns_with_named_parsers.each do |name, options|
119
158
  parser = options[:parser_method]
120
- unless named_parsers.include? parser
121
- raise UnknownParserError,
122
- "You're trying to use the :#{parser.to_s.gsub('_parser', '')} parser for the :#{name} column, but it doesn't exist. Available parsers are: :#{named_parsers.map { |p| p.to_s.gsub('_parser', '') }.join(', :')}."
123
- end
159
+ next if named_parsers.include? parser
160
+
161
+ parser = parser.to_s.gsub('_parser', '')
162
+ parsers = named_parsers
163
+ .map { |p| p.to_s.gsub('_parser', '') }
164
+ .join(', :')
165
+ raise UnknownParserError,
166
+ "You're trying to use the :#{parser} parser for the :#{name} \
167
+ column, but it doesn't exist. Available parsers are: :#{parsers}."
124
168
  end
125
169
  end
126
170
 
127
171
  def defined_headers
128
- columns.map { |name, options| options[:header] }
172
+ columns.map { |_name, options| options[:header] }
129
173
  end
130
174
 
131
175
  def raise_unless_csv_has_all_headers
132
176
  missing_columns = defined_headers - @headers
133
- unless missing_columns.empty?
134
- raise MissingColumnError, "CSV file is missing column(s) with header(s) '#{missing_columns.join("', '")}'."
135
- end
177
+ return if missing_columns.empty?
178
+
179
+ columns = missing_columns.join("', '")
180
+ raise MissingColumnError,
181
+ "CSV file is missing column(s) with header(s) '#{columns}'. \
182
+ File has these headers: #{@headers.join(', ')}."
136
183
  end
137
184
  end
138
185
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: csv_party
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1.pre
4
+ version: 0.0.1.pre2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rico Jones
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-03-30 00:00:00.000000000 Z
11
+ date: 2017-04-27 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A gem for making CSV imports a little more fun.
14
14
  email: rico@toasterlovin.com