csv_party 0.0.1.pre3 → 0.0.1.pre7

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 +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
  - - ">"