csv_party 0.0.1.pre3 → 0.0.1.pre7

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 +139 -57
  3. metadata +3 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1e5823d9eec07faf638ab898d7082be890c73373
4
- data.tar.gz: f3b115f8fd4055e141629fc3fc92ac39278dbb2f
3
+ metadata.gz: 4a03ef93214f6d5974e16e881c4ccab9b2543cfe
4
+ data.tar.gz: 3179789242dc149e98129bf06e6a18dafb1b25cf
5
5
  SHA512:
6
- metadata.gz: 3c89169637d692dec361b79b87e5e392050224e43869f0d7a6f66decb3ca1edaed8e7dab777a288ac17c5557ef2964bc5f51a80a841f73601158e22021bf44e5
7
- data.tar.gz: 4b677735ca562a25d30fd1a46b999d2803b4f73cf187db340f33f373c7acda071f96ffe1614dc6c51dbde7f2ceddc40c88a4738afb44f97ba40b3703f31accce
6
+ metadata.gz: 8fd9938a9dfe1fe7cf0fd2f52cb94c2bf210c4f3fab1e7789e14e0ad5a10b999895489193ba32d6df97327c475c6ce37b3f116f30990f91c8d0d27e3b271cd3e
7
+ data.tar.gz: 58b12e98902a2e676dfb32475922f6a36eb1b4535900b782d601d248cab785bbd5c8a4bd5fbd63eed527649870a4b5947b4dcbf06ac5fb2f96d2fe0fb3b1a919
data/lib/csv_party.rb CHANGED
@@ -3,65 +3,59 @@ require 'bigdecimal'
3
3
  require 'ostruct'
4
4
 
5
5
  class CSVParty
6
+ attr_accessor :columns, :row_importer, :importer, :error_processor
7
+
8
+ attr_reader :imported_rows, :skipped_rows, :aborted_rows,
9
+ :abort_message
10
+
6
11
  def initialize(csv_path, options = {})
12
+ initialize_import_settings
13
+ initialize_counters_and_statuses
14
+
7
15
  options[:headers] = true
16
+ dependencies = options.delete(:dependencies)
8
17
  @headers = CSV.new(File.open(csv_path)).shift
9
18
  @csv = CSV.new(File.open(csv_path), options)
10
19
 
20
+ setup_dependencies(dependencies)
11
21
  raise_unless_named_parsers_are_valid
12
22
  raise_unless_csv_has_all_headers
13
23
  end
14
24
 
15
25
  def import!
26
+ if importer
27
+ instance_exec(&importer)
28
+ else
29
+ import_rows!
30
+ end
31
+ rescue AbortedImportError => error
32
+ @aborted = true
33
+ @abort_message = error.message
34
+ end
35
+
36
+ def import_rows!
16
37
  loop do
17
38
  begin
18
39
  row = @csv.shift
19
40
  break unless row
20
- import_row(row)
41
+ import_row!(row)
42
+ imported_rows << @csv.lineno
43
+ rescue SkippedRowError
44
+ skipped_rows << @csv.lineno
45
+ next
46
+ rescue AbortedImportError => error
47
+ raise AbortedImportError, error.message
21
48
  rescue StandardError => error
22
49
  process_error(error, @csv.lineno + 1)
50
+ aborted_rows << @csv.lineno
23
51
  next
24
52
  end
25
53
  end
26
54
  end
27
55
 
28
- def parse_row(row)
29
- unparsed_row = OpenStruct.new
30
- parsed_row = OpenStruct.new
31
-
32
- columns.each do |name, options|
33
- header = options[:header]
34
- unparsed_value = row[header]
35
- parser = options[:parser]
36
-
37
- unparsed_row[name] = unparsed_value
38
- parsed_row[name] = if options[:blanks_as_nil] && is_blank?(unparsed_value)
39
- nil
40
- elsif parser.is_a? Symbol
41
- send(parser, unparsed_value)
42
- else
43
- instance_exec(unparsed_value, &parser)
44
- end
45
- end
46
-
47
- parsed_row['unparsed'] = unparsed_row
48
- parsed_row['csv_string'] = row.to_csv
49
-
50
- return parsed_row
51
- end
52
-
53
- def import_row(row)
54
- parsed_row = parse_row(row)
55
- instance_exec(parsed_row, &importer)
56
- end
57
-
58
- def process_error(error, line_number)
59
- instance_exec(error, line_number, &error_handler)
60
- end
61
-
62
- def self.column(name, options, &block)
63
- raise_if_duplicate_column(name)
64
- raise_if_missing_header(name, options)
56
+ def self.column(column, options, &block)
57
+ raise_if_duplicate_column(column)
58
+ raise_if_missing_header(column, options)
65
59
 
66
60
  options = {
67
61
  blanks_as_nil: (options[:as] == :raw ? false : true),
@@ -74,60 +68,116 @@ class CSVParty
74
68
  "#{options[:as]}_parser".to_sym
75
69
  end
76
70
 
77
- columns[name] = {
71
+ columns[column] = {
78
72
  header: options[:header],
79
73
  parser: parser,
80
74
  blanks_as_nil: options[:blanks_as_nil]
81
75
  }
82
76
  end
83
77
 
84
- def self.columns
85
- @columns ||= {}
86
- end
87
-
88
- def columns
89
- self.class.columns
78
+ def self.rows(&block)
79
+ @row_importer = block
90
80
  end
91
81
 
92
82
  def self.import(&block)
93
83
  @importer = block
94
84
  end
95
85
 
96
- def self.importer
97
- @importer
86
+ def self.errors(&block)
87
+ @error_processor = block
98
88
  end
99
89
 
100
- def importer
101
- self.class.importer
90
+ def self.columns
91
+ @columns ||= {}
102
92
  end
103
93
 
104
- def self.error(&block)
105
- @error = block
94
+ def self.row_importer
95
+ @row_importer ||= nil
106
96
  end
107
97
 
108
- def self.error_handler
109
- @error
98
+ def self.importer
99
+ @importer ||= nil
110
100
  end
111
101
 
112
- def error_handler
113
- self.class.error_handler
102
+ def self.error_processor
103
+ @error_processor ||= nil
104
+ end
105
+
106
+ def aborted?
107
+ @aborted
114
108
  end
115
109
 
116
110
  def self.raise_if_duplicate_column(name)
117
111
  return unless columns.has_key?(name)
118
112
 
119
113
  raise DuplicateColumnError, "A column named :#{name} has already been \
120
- defined, choose a different name"
114
+ defined, choose a different name"
121
115
  end
116
+ private_class_method :raise_if_duplicate_column
122
117
 
123
118
  def self.raise_if_missing_header(name, options)
124
119
  return if options.has_key?(:header)
125
120
 
126
121
  raise MissingHeaderError, "A header must be specified for #{name}"
127
122
  end
123
+ private_class_method :raise_if_missing_header
128
124
 
129
125
  private
130
126
 
127
+ def import_row!(row)
128
+ parsed_row = parse_row(row)
129
+ instance_exec(parsed_row, &row_importer)
130
+ end
131
+
132
+ def parse_row(row)
133
+ unparsed_row = OpenStruct.new
134
+ columns.each do |column, options|
135
+ header = options[:header]
136
+ unparsed_row[column] = row[header]
137
+ end
138
+
139
+ parsed_row = OpenStruct.new
140
+ columns.each do |column, options|
141
+ value = row[options[:header]]
142
+ parsed_row[column] = parse_column(
143
+ value,
144
+ options[:parser],
145
+ options[:blanks_as_nil]
146
+ )
147
+ end
148
+
149
+ parsed_row[:unparsed] = unparsed_row
150
+ parsed_row[:csv_string] = row.to_csv
151
+
152
+ return parsed_row
153
+ end
154
+
155
+ def parse_column(value, parser, blanks_as_nil)
156
+ if blanks_as_nil && is_blank?(value)
157
+ nil
158
+ elsif parser.is_a? Symbol
159
+ send(parser, value)
160
+ else
161
+ instance_exec(value, &parser)
162
+ end
163
+ end
164
+
165
+ def process_error(error, line_number)
166
+ instance_exec(error, line_number, &error_processor)
167
+ end
168
+
169
+ def skip_row
170
+ raise SkippedRowError
171
+ end
172
+
173
+ def abort_row(message)
174
+ raise AbortedRowError, message
175
+ end
176
+
177
+ def abort_import(message)
178
+ raise AbortedImportError, message
179
+ end
180
+
131
181
  def is_blank?(value)
132
182
  value.nil? || value.strip.empty?
133
183
  end
@@ -190,7 +240,30 @@ class CSVParty
190
240
  columns = missing_columns.join("', '")
191
241
  raise MissingColumnError,
192
242
  "CSV file is missing column(s) with header(s) '#{columns}'. \
193
- File has these headers: #{@headers.join(', ')}."
243
+ File has these headers: #{@headers.join(', ')}."
244
+ end
245
+
246
+ def setup_dependencies(dependencies)
247
+ return unless dependencies
248
+
249
+ dependencies.each do |dependency, value|
250
+ self.class.class_eval { attr_accessor dependency }
251
+ send("#{dependency}=", value)
252
+ end
253
+ end
254
+
255
+ def initialize_import_settings
256
+ @columns = self.class.columns
257
+ @row_importer = self.class.row_importer
258
+ @importer = self.class.importer
259
+ @error_processor = self.class.error_processor
260
+ end
261
+
262
+ def initialize_counters_and_statuses
263
+ @imported_rows = []
264
+ @skipped_rows = []
265
+ @aborted_rows = []
266
+ @aborted = false
194
267
  end
195
268
  end
196
269
 
@@ -205,3 +278,12 @@ end
205
278
 
206
279
  class MissingColumnError < ArgumentError
207
280
  end
281
+
282
+ class SkippedRowError < RuntimeError
283
+ end
284
+
285
+ class AbortedRowError < RuntimeError
286
+ end
287
+
288
+ class AbortedImportError < RuntimeError
289
+ end
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.pre3
4
+ version: 0.0.1.pre7
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-04-27 00:00:00.000000000 Z
11
+ date: 2017-05-01 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
@@ -29,7 +29,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
29
29
  requirements:
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: '0'
32
+ version: '2.0'
33
33
  required_rubygems_version: !ruby/object:Gem::Requirement
34
34
  requirements:
35
35
  - - ">"