csv_party 0.0.1.pre → 0.0.1.pre2

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.
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