csv_party 1.0.0.rc4 → 1.0.0.rc5

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 67d5895445a9fe397df95275260491ed6d9f6ce5
4
- data.tar.gz: 542a1442466867afa33cf0ef883a32443777cfd5
2
+ SHA256:
3
+ metadata.gz: 174fc0ba77e52795b1763e2ff29eae5e5c3cfa3bcd181973aaba886a54a79eeb
4
+ data.tar.gz: 7676780e13435e10bf35b7c34c849484ea0efe0a86d7889ff2a4697f312a99c6
5
5
  SHA512:
6
- metadata.gz: 1035dac76f5ec71d97015a5d9c2874074c28ad4b0ff7994ab565735fd6d49e539e4292a42ee15f44d8d2de2fb356e81a06f61685019fa3ad250d12ab9c160ba8
7
- data.tar.gz: 93c748026adc2fa907f3e08825c0fef8b41fed5b2945e3066aff70978a86ed4c9d47df3a98abc30e25b2fb44a597cddb91efbb519699d2a9e3b4c580b4ef1100
6
+ metadata.gz: ab4bc476717516d1fa2a9d674f47e60c95ef0b24541cb7cf6cf76948ca5b9ce9dad96b9b89d10edfd99f338d4a04b612fd241c38d37d746d049f6eedb8868120
7
+ data.tar.gz: 4041355ba68669a3cf3e6275bf099da83d3d92d759bfc328269080a41dcbee02fa9a77b9c95c3549eaf5330cfc9fbbdcfdea05f83d320d2f29b55e89553d3fea
data/ROADMAP.md CHANGED
@@ -8,9 +8,13 @@ Roadmap
8
8
  - [1.5 Runtime Configuration](#15-runtime-configuration)
9
9
  - [1.6 CSV Parse Error Handling](#16-csv-parse-error-handling)
10
10
  - [Someday Features](#someday-features)
11
+ - [Parse Row Access](#parse-row-access)
12
+ - [Deferred Parsing](#deferred-parsing)
13
+ - [Columns Macro](#columns-macro)
11
14
  - [Column Numbers](#column-numbers)
12
15
  - [Multi-column Parsing](#multi-column-parsing)
13
16
  - [Parse Dependencies](#parse-dependencies)
17
+ - [Rails Generator](#rails-generator0
14
18
 
15
19
  #### 1.1 Early Return While Parsing
16
20
 
@@ -230,6 +234,68 @@ error handling API for non-parse errors. So:
230
234
 
231
235
  ## Someday Features
232
236
 
237
+ #### Parse Row Access
238
+
239
+ This feature would allow access to the `CSV::Row` object when parsing a column.
240
+ It could work something like this:
241
+
242
+ column product do |value, row|
243
+ Product.find_by(name: row['Product'])
244
+ end
245
+
246
+ In theory, the CSVParty API would cover all use cases where somebody would need
247
+ to access the raw row data, but perhaps not. Sometimes it's nice to be able to
248
+ cut through the stuff in your way and just get at the raw internals.
249
+
250
+ Additionally, perhaps deferred parsing would allow access to parsed row values,
251
+ which would possibly enable some of the features below, Multi Column Parsing and
252
+ Parse Dependencies, without requiring additional code. That might look like:
253
+
254
+ column product do |value, row|
255
+ Product.find_by(name: row.unparsed.row)
256
+ end
257
+
258
+ #### Deferred Parsing
259
+
260
+ Currently, CSVParty parses all columns before the row import logic is executed.
261
+ There are situations where parsing columns is expensive and you may want to
262
+ defer parsing columns until and unless you actually need them for that a given
263
+ row. For example, say you have an import where you are either:
264
+
265
+ 1. Updating a value on existing records, or
266
+ 2. Setting a value on and creating new records
267
+
268
+ And when you create new records, you have to also set a bunch of other values
269
+ and some of those values require database queries.
270
+
271
+ In a situation like this, you would want to defer all of the database queries
272
+ that need to run when creating a new record so that they aren't done in cases
273
+ where you are updating an existing record.
274
+
275
+ #### Columns Macro
276
+
277
+ This feature would allow you to declare multiple columns in a single line. So,
278
+ rather than:
279
+
280
+ column :product
281
+ column :price
282
+ column :color
283
+
284
+ You could do:
285
+
286
+ columns :product, :price, :color
287
+
288
+ This is probably most useful when there are a bunch of columns that should all
289
+ be parsed as text. Though it might make sense to allow specifying parsers and
290
+ other options:
291
+
292
+ columns product: { as: :raw }, price: { as: :decimal }
293
+
294
+ It should probably also be possible to combine `columns` and `column` macros:
295
+
296
+ columns :product, :price, :color
297
+ column :size
298
+
233
299
  #### Column Numbers
234
300
 
235
301
  CSVParty is entirely oriented around a CSV file having a header. This is not
@@ -269,3 +335,21 @@ that might look like:
269
335
  customer.orders.find(order_id)
270
336
  end
271
337
  end
338
+
339
+ #### Rails Generator
340
+
341
+ This feature would add a generator for Rails which creates an importer file. So
342
+ doing:
343
+
344
+ rails generate importer Product
345
+
346
+ Would generate the following file at `app/importers/product_importer.rb`:
347
+
348
+ class ProductImporter
349
+ include CSVParty
350
+
351
+ import do |row|
352
+ # import logic goes here
353
+ end
354
+ end
355
+
@@ -1,3 +1,6 @@
1
+ require 'csv'
2
+ require 'csv_party/errors'
3
+
1
4
  module CSVParty
2
5
  class DataPreparer
3
6
  def initialize(options)
@@ -15,7 +18,7 @@ module CSVParty
15
18
  @options[:content]
16
19
  end
17
20
  options = extract_csv_options.merge(headers: true)
18
- CSV.new(data, options)
21
+ CSV.new(data, **options)
19
22
  end
20
23
 
21
24
  private
data/lib/csv_party/dsl.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require 'csv_party/configuration'
2
+
1
3
  module CSVParty
2
4
  module DSL
3
5
  def column(column, options = {}, &block)
@@ -1,3 +1,5 @@
1
+ require 'bigdecimal'
2
+
1
3
  module CSVParty
2
4
  module Parsers
3
5
  def parse_raw(value)
@@ -25,7 +27,7 @@ module CSVParty
25
27
  end
26
28
 
27
29
  def parse_decimal(value)
28
- BigDecimal.new(prepare_numeric_value(value))
30
+ BigDecimal(prepare_numeric_value(value))
29
31
  end
30
32
 
31
33
  def parse_date(value, format = nil)
data/lib/csv_party/row.rb CHANGED
@@ -1,10 +1,11 @@
1
+ require 'ostruct'
2
+
1
3
  module CSVParty
2
4
  class Row
3
5
  attr_accessor :row_number, :csv_string, :unparsed
4
6
 
5
- def initialize(csv_row, config, runner)
7
+ def initialize(csv_row, runner)
6
8
  @csv_row = csv_row
7
- @config = config
8
9
  @runner = runner
9
10
  @attributes = OpenStruct.new
10
11
  parse_row!(csv_row)
@@ -16,7 +17,7 @@ module CSVParty
16
17
  self.csv_string = csv_row.to_csv
17
18
  self.unparsed = extract_unparsed_values(csv_row)
18
19
 
19
- @config.columns.each do |column, options|
20
+ @runner.config.columns.each do |column, options|
20
21
  header = options[:header]
21
22
  value = csv_row[header]
22
23
  @attributes[column] = parse_value(value, options)
@@ -25,7 +26,7 @@ module CSVParty
25
26
 
26
27
  def extract_unparsed_values(csv_row)
27
28
  unparsed_row = OpenStruct.new
28
- config.columns.each do |column, options|
29
+ @runner.config.columns.each do |column, options|
29
30
  header = options[:header]
30
31
  unparsed_row[column] = csv_row[header]
31
32
  end
@@ -1,8 +1,12 @@
1
1
  require 'csv_party/parsers'
2
+ require 'csv_party/validations'
3
+ require 'csv_party/errors'
4
+ require 'csv_party/row'
2
5
 
3
6
  module CSVParty
4
7
  class Runner
5
8
  include Parsers
9
+ include Validations
6
10
 
7
11
  attr_accessor :csv, :config, :importer
8
12
 
@@ -52,24 +56,23 @@ module CSVParty
52
56
  csv.rewind
53
57
  end
54
58
 
59
+ def initialize_regex_headers!
60
+ config.columns_with_regex_headers.each do |name, options|
61
+ found_header = @_headers.find do |header|
62
+ options[:header].match(header)
63
+ end
64
+ options[:header] = found_header || name.to_s
65
+ end
66
+ end
67
+
55
68
  def import_rows!
56
69
  loop do
57
70
  begin
58
- row = csv.shift
59
- break unless row
60
- import_row!(row)
61
- rescue NextRowError
62
- next
63
- rescue SkippedRowError => error
64
- handle_skipped_row(error)
65
- rescue AbortedRowError => error
66
- handle_aborted_row(error)
67
- rescue AbortedImportError
68
- raise
71
+ csv_row = csv.shift
72
+ break unless csv_row
73
+ import_row!(csv_row)
69
74
  rescue CSV::MalformedCSVError
70
75
  raise
71
- rescue StandardError => error
72
- handle_error(error, @_current_row_number, row.to_csv)
73
76
  end
74
77
  end
75
78
 
@@ -78,9 +81,20 @@ module CSVParty
78
81
 
79
82
  def import_row!(csv_row)
80
83
  @_current_row_number += 1
81
- @_current_parsed_row = Row.new(csv_row, config, self)
84
+ @_current_parsed_row = Row.new(csv_row, self)
82
85
  @_current_parsed_row.row_number = @_current_row_number
86
+
83
87
  instance_exec(@_current_parsed_row, &config.row_importer)
88
+ rescue NextRowError
89
+ return
90
+ rescue SkippedRowError => error
91
+ handle_skipped_row(error)
92
+ rescue AbortedRowError => error
93
+ handle_aborted_row(error)
94
+ rescue AbortedImportError
95
+ raise
96
+ rescue StandardError => error
97
+ handle_error(error, @_current_row_number, csv_row.to_csv)
84
98
  end
85
99
 
86
100
  def next_row!
@@ -138,38 +152,6 @@ module CSVParty
138
152
  .new(error, line_number, csv_string)
139
153
  end
140
154
 
141
- def raise_unless_row_processor_is_defined!
142
- return if config.row_importer
143
-
144
- raise UndefinedRowProcessorError.new
145
- end
146
-
147
- def raise_unless_rows_have_been_imported!
148
- return if @_rows_have_been_imported
149
-
150
- raise UnimportedRowsError.new
151
- end
152
-
153
- def raise_unless_all_dependencies_are_present!
154
- config.dependencies.each do |dependency|
155
- next unless importer.send(dependency).nil?
156
-
157
- raise MissingDependencyError.new(self, dependency)
158
- end
159
- end
160
-
161
- # This error has to be raised at runtime because, when the class body
162
- # is being executed, the parser methods won't be available unless
163
- # they are defined above the column definitions in the class body
164
- def raise_unless_all_named_parsers_exist!
165
- config.columns_with_named_parsers.each do |name, options|
166
- parser = options[:parser]
167
- next if named_parsers.include? parser
168
-
169
- raise UnknownParserError.new(name, parser, named_parsers)
170
- end
171
- end
172
-
173
155
  def named_parsers
174
156
  (methods +
175
157
  private_methods +
@@ -177,21 +159,6 @@ module CSVParty
177
159
  importer.private_methods).grep(/^parse_/)
178
160
  end
179
161
 
180
- def raise_unless_csv_has_all_columns!
181
- return if missing_columns.empty?
182
-
183
- raise MissingColumnError.new(present_columns, missing_columns)
184
- end
185
-
186
- def initialize_regex_headers!
187
- config.columns_with_regex_headers.each do |name, options|
188
- found_header = @_headers.find do |header|
189
- options[:header].match(header)
190
- end
191
- options[:header] = found_header || name.to_s
192
- end
193
- end
194
-
195
162
  def respond_to_missing?(method, _include_private)
196
163
  importer.respond_to?(method, true)
197
164
  end
@@ -0,0 +1,41 @@
1
+ module CSVParty
2
+ module Validations
3
+ def raise_unless_row_processor_is_defined!
4
+ return if config.row_importer
5
+
6
+ raise UndefinedRowProcessorError.new
7
+ end
8
+
9
+ def raise_unless_rows_have_been_imported!
10
+ return if @_rows_have_been_imported
11
+
12
+ raise UnimportedRowsError.new
13
+ end
14
+
15
+ def raise_unless_all_dependencies_are_present!
16
+ config.dependencies.each do |dependency|
17
+ next unless importer.send(dependency).nil?
18
+
19
+ raise MissingDependencyError.new(self, dependency)
20
+ end
21
+ end
22
+
23
+ # This error has to be raised at runtime because, when the class body
24
+ # is being executed, the parser methods won't be available unless
25
+ # they are defined above the column definitions in the class body
26
+ def raise_unless_all_named_parsers_exist!
27
+ config.columns_with_named_parsers.each do |name, options|
28
+ parser = options[:parser]
29
+ next if named_parsers.include? parser
30
+
31
+ raise UnknownParserError.new(name, parser, named_parsers)
32
+ end
33
+ end
34
+
35
+ def raise_unless_csv_has_all_columns!
36
+ return if missing_columns.empty?
37
+
38
+ raise MissingColumnError.new(present_columns, missing_columns)
39
+ end
40
+ end
41
+ end
data/lib/csv_party.rb CHANGED
@@ -1,11 +1,5 @@
1
- require 'bigdecimal'
2
- require 'csv'
3
- require 'ostruct'
4
- require 'csv_party/configuration'
5
1
  require 'csv_party/dsl'
6
2
  require 'csv_party/data_preparer'
7
- require 'csv_party/errors'
8
- require 'csv_party/row'
9
3
  require 'csv_party/runner'
10
4
 
11
5
  module CSVParty
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: csv_party
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.rc4
4
+ version: 1.0.0.rc5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rico Jones
@@ -28,6 +28,7 @@ files:
28
28
  - lib/csv_party/row.rb
29
29
  - lib/csv_party/runner.rb
30
30
  - lib/csv_party/testing.rb
31
+ - lib/csv_party/validations.rb
31
32
  homepage: https://github.com/toasterlovin/csv_party
32
33
  licenses:
33
34
  - MIT
@@ -40,15 +41,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
40
41
  requirements:
41
42
  - - ">="
42
43
  - !ruby/object:Gem::Version
43
- version: '2.0'
44
+ version: '2.6'
44
45
  required_rubygems_version: !ruby/object:Gem::Requirement
45
46
  requirements:
46
47
  - - ">"
47
48
  - !ruby/object:Gem::Version
48
49
  version: 1.3.1
49
50
  requirements: []
50
- rubyforge_project:
51
- rubygems_version: 2.6.11
51
+ rubygems_version: 3.0.3.1
52
52
  signing_key:
53
53
  specification_version: 4
54
54
  summary: CSV Party