iron-import 0.8.1 → 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: af5ddfc35604cb25d28377798c3ea609ca7e575e
4
- data.tar.gz: 76a6ed3cc099d49e6a5fa888b3a37dff26e2bee8
3
+ metadata.gz: ce84c9899f23abe7fd1f3ed06dc3f50e8986a585
4
+ data.tar.gz: e1cd18c73aa8663bd0b98305dd18b0ddaee9f75e
5
5
  SHA512:
6
- metadata.gz: d8d0f3a7cd1a6a2503df394b94dd74630d1226e4c1b0ed43fe9d76d0f264df358641ce7be2d51f30b7c0d870118af109f7e183de37e7a8d3a0c4d3aea8cd68b7
7
- data.tar.gz: 3d6d629a0c09ce84b526025d945b55d0f4c6b8e250b3053b43ddb5fdba91e62e7c13a623f66c65a2e39700dd632464471309a6c3357b04c5e7e45c4b757a69ba
6
+ metadata.gz: ca04f44c1434825045fb27b1d2aa0a2b16b70174dc43bf5f2aa89212331dc06e06ef41d1e4f0070971325f9014d0b6ed4e17a20cf47cb137c2312334255690ee
7
+ data.tar.gz: 824b6842a4a2c60c1d1ee8737b490fe96e8029e3f8042fa548b85927d8735d2c25c551b73fbc769e407294cdbcc663ec888477746833cb8296c9ffa5ffcaf928
data/History.txt CHANGED
@@ -1,3 +1,9 @@
1
+ == 0.8.2 / 2017-08-01
2
+ * Add Importer#on_success to enable conditional processing once an import has been successfully completed
3
+ * Pre-parse values with Column#type set when using Column#parse (instead of ignoring it)
4
+ * Make importer scope available in Importer#process and the block form of Importer#import
5
+ * Add backtrace info to error logging for exceptions to help during debugging
6
+
1
7
  == 0.8.1 / 2017-07-18
2
8
  * Do not include optional headers in #missing_headers
3
9
  * Improve string parsing to strip trailing '.0' from incoming float values
data/Version.txt CHANGED
@@ -1 +1 @@
1
- 0.8.1
1
+ 0.8.2
@@ -29,7 +29,9 @@ class Importer
29
29
  # # that different source types may give you different raw values for what
30
30
  # # seems like the "same" source value, for example an Excel source file
31
31
  # # will give you a float value for all numeric types, even "integers", while
32
- # # CSV and HTML values are always strings.
32
+ # # CSV and HTML values are always strings. By default, will take the raw
33
+ # # value of the row, but if used with #type, you can have the pre-processing
34
+ # # of the type as your input.
33
35
  # parse do |raw_value|
34
36
  # val = raw_value.to_i + 1000
35
37
  # # NOTE: we're in a block, so don't do this:
@@ -125,7 +127,7 @@ class Importer
125
127
  @virtual = options_hash.delete(:virtual) { false }
126
128
 
127
129
  # Return it as a string, by default
128
- @type = options_hash.delete(:type) { :string }
130
+ @type = options_hash.delete(:type)
129
131
 
130
132
  # Position can be explicitly set
131
133
  @position = options_hash.delete(:position)
@@ -184,6 +186,24 @@ class Importer
184
186
  end
185
187
  end
186
188
 
189
+ # Override normal dsl_accessor behavior to return our default type
190
+ # which will be :raw if a #parse handler has been set, else :string
191
+ alias_method :internal_type, :type
192
+ def type(*args)
193
+ if args.count > 0
194
+ internal_type(*args)
195
+ else
196
+ if @type
197
+ # Explicitly set type
198
+ @type
199
+ else
200
+ # Our default is generally :string, but if we have a parser,
201
+ # default to the :raw value
202
+ parses? ? :raw : :string
203
+ end
204
+ end
205
+ end
206
+
187
207
  # Applies any custom parser defined to process the given value, capturing
188
208
  # errors as needed
189
209
  def parse_value(row, raw_val)
@@ -42,7 +42,7 @@ class Importer
42
42
 
43
43
  rescue Exception => e
44
44
  # Catch any exceptions thrown and note them with helpful stacktrace info for debugging custom readers
45
- add_error("Error in custom reader: #{e} @ #{e.backtrace.first}")
45
+ add_exception(e)
46
46
  end
47
47
 
48
48
  end
@@ -219,10 +219,16 @@ class Importer
219
219
  return nil if val.nil? || val.to_s.strip == ''
220
220
 
221
221
  case type
222
+ when :raw then
223
+ val
224
+
222
225
  when :string then
223
226
  if val.is_a?(Float)
224
- val.to_s.strip.gsub(/\.0+$/, '')
227
+ # Sometimes float values come in for "integer" columns from Excel,
228
+ # so if the user asks for a string, strip off that ".0" if present
229
+ val.to_s.gsub(/\.0+$/, '')
225
230
  else
231
+ # Strip whitespace and we're good to go
226
232
  val.to_s.strip
227
233
  end
228
234
 
@@ -248,7 +254,7 @@ class Importer
248
254
  if val.class < Numeric
249
255
  val.to_f
250
256
  else
251
- # Convert to string, strip off trailing decimal zeros
257
+ # Clean up then verify it matches a valid float format & convert
252
258
  val = val.to_s.strip
253
259
  if val.match(/\A-?[0-9]+(?:\.[0-9]+)?\z/)
254
260
  val.to_f
@@ -287,6 +293,10 @@ class Importer
287
293
  @importer.add_error(*args)
288
294
  end
289
295
 
296
+ def add_exception(*args)
297
+ @importer.add_exception(*args)
298
+ end
299
+
290
300
  end
291
301
 
292
302
  end
@@ -21,7 +21,7 @@ class Importer
21
21
  yield
22
22
  rescue RuntimeError => e
23
23
  # Old-style way of registering errors was to just raise 'foo'
24
- importer.add_error(e.to_s)
24
+ importer.add_exception(e)
25
25
  end
26
26
  had_error = @error_occurred
27
27
 
@@ -25,7 +25,7 @@ class Importer
25
25
  false
26
26
  end
27
27
  rescue Exception => e
28
- add_error("Error reading #{source}: #{e}")
28
+ add_exception(e)
29
29
  false
30
30
  end
31
31
 
@@ -47,7 +47,7 @@ class Importer
47
47
 
48
48
  rescue Exception => e
49
49
  # Not sure why we'd get here, but we strive for error-freedom here, yessir.
50
- @importer.add_error("Error loading Excel data: #{e}")
50
+ add_exception(e)
51
51
  end
52
52
 
53
53
  # When true, the given sheet name or zero-based index
@@ -27,7 +27,7 @@ class Importer
27
27
  end
28
28
 
29
29
  rescue Exception => e
30
- add_error("Error reading HTML source #{source}: #{e}")
30
+ add_exception(e)
31
31
  false
32
32
  end
33
33
 
@@ -65,7 +65,7 @@ class Importer
65
65
 
66
66
  rescue Exception => e
67
67
  # Not sure why we'd get here, but we strive for error-freedom here, yessir.
68
- add_error("Error loading tables #{scopes.list_join(', ')}: #{e}")
68
+ add_exception(e)
69
69
  end
70
70
 
71
71
  end
@@ -76,6 +76,10 @@
76
76
  # end.on_error do
77
77
  # # If we have any errors, do something
78
78
  # raise error_summary
79
+ #
80
+ # end.on_success do
81
+ # # No errors, do this
82
+ # puts "Imported successfully!"
79
83
  # end
80
84
  #
81
85
  class Importer
@@ -418,12 +422,12 @@ class Importer
418
422
  # will not be present. If you want to register an error, simply
419
423
  # raise "some text" or call #add_error and it will be added to the importer's
420
424
  # error list for display to the user, logging, or whatever.
421
- def process
425
+ def process(&block)
422
426
  @data.rows.each do |row|
423
427
  begin
424
- yield row
428
+ DslProxy.exec(self, row, &block)
425
429
  rescue Exception => e
426
- add_error(e.to_s, :row => row)
430
+ add_exception(e, :row => row)
427
431
  end
428
432
  end
429
433
  end
@@ -448,6 +452,24 @@ class Importer
448
452
  self
449
453
  end
450
454
 
455
+ # Call with a block to process any post-import tasks. Block will only execute
456
+ # if an import has been run with no errors.
457
+ #
458
+ # Your block can access the #rows to do whatever
459
+ # logging, reporting etc. is desired.
460
+ def on_success(&block)
461
+ raise 'Invalid block passed to Importer#on_succes: block may accept 0 or 1arguments' if block.arity > 1
462
+
463
+ if @data.rows.any? && !has_errors?
464
+ case block.arity
465
+ when 0 then DslProxy.exec(self, &block)
466
+ when 1 then DslProxy.exec(self, @data.rows, &block)
467
+ end
468
+ end
469
+
470
+ self
471
+ end
472
+
451
473
  # Call with a block accepting an array of Column objects and returning
452
474
  # true if the columns in the array should constitute a valid header row. Intended
453
475
  # for use with optional columns to define multiple supported column sets, or
@@ -557,12 +579,11 @@ class Importer
557
579
  if col.present? && !col.virtual?
558
580
  index = col.data.index
559
581
  raw_val = raw_data[index]
582
+ # Otherwise use our standard parser
583
+ val = @reader.parse_value(raw_val, col.type)
560
584
  if col.parses?
561
585
  # Use custom parser if this row has one
562
- val = col.parse_value(row, raw_val)
563
- else
564
- # Otherwise use our standard parser
565
- val = @reader.parse_value(raw_val, col.type)
586
+ val = col.parse_value(row, val)
566
587
  end
567
588
  values[col.key] = val
568
589
  end
@@ -631,6 +652,12 @@ class Importer
631
652
  @data.errors << Error.new(msg, context)
632
653
  end
633
654
 
655
+ def add_exception(ex, context = {})
656
+ line = ex.backtrace.first.extract(/[a-z0-9_\-\.]+\:[0-9]+\:/i)
657
+ msg = [line, ex.to_s].list_join(' ')
658
+ @data.errors << Error.new(msg, context)
659
+ end
660
+
634
661
  # Returns a human-readable summary of the errors present on the importer, or
635
662
  # nil if no errors are present
636
663
  def error_summary
@@ -647,10 +674,11 @@ class Importer
647
674
 
648
675
  # Build summary & return
649
676
  list.values.collect do |errs|
650
- summary = errs.first.summary
677
+ err = errs.first
651
678
  if errs.count == 1
652
- summary
679
+ err.summary
653
680
  else
681
+ summary = [err.column.to_s, err.text].list_join(': ')
654
682
  errs.count.to_s + ' x ' + summary
655
683
  end
656
684
  end.list_join(', ')
@@ -23,21 +23,6 @@ describe Importer do
23
23
  importer.scopes.should == { :xls => [1, 'Sheet 2'], :html => ['table.funny'] }
24
24
  end
25
25
 
26
- it 'should calculate virtual columns' do
27
- importer = Importer.build do
28
- column :num, :type => :int
29
- virtual_column :summary do
30
- calculate do |row|
31
- "Value = #{row[:num]}"
32
- end
33
- end
34
- end
35
-
36
- importer.import_string("num\n1\n2")
37
- importer.error_summary.should be_nil
38
- importer.column(:summary).to_a.should == ['Value = 1', 'Value = 2']
39
- end
40
-
41
26
  it 'should find headers automatically' do
42
27
  # Define a few sample columns
43
28
  importer = Importer.new
@@ -88,6 +73,37 @@ describe Importer do
88
73
  importer.find_header(rows).should be_true
89
74
  importer.missing_headers.should be_empty
90
75
  end
76
+
77
+ it 'should calculate virtual columns' do
78
+ importer = Importer.build do
79
+ column :num, :type => :int
80
+ virtual_column :summary do
81
+ calculate do |row|
82
+ "Value = #{row[:num]}"
83
+ end
84
+ end
85
+ end
86
+
87
+ importer.import_string("num\n1\n2")
88
+ importer.error_summary.should be_nil
89
+ importer.column(:summary).to_a.should == ['Value = 1', 'Value = 2']
90
+ end
91
+
92
+ it 'should honor type before applying custom parsers' do
93
+ importer = Importer.new
94
+ importer.column(:alpha) do
95
+ parse do |raw|
96
+ raw
97
+ end
98
+ end
99
+
100
+ importer.import_string("alpha\n1.0\n1.5")
101
+ importer.to_a.should == [{:alpha => '1.0'}, {:alpha => '1.5'}]
102
+
103
+ importer.column(:alpha).type :float
104
+ importer.import_string("alpha\n1.0\n1.5")
105
+ importer.to_a.should == [{:alpha => 1.0}, {:alpha => 1.5}]
106
+ end
91
107
 
92
108
  it 'should support row-based validation' do
93
109
  importer = Importer.build do
@@ -153,6 +169,25 @@ describe Importer do
153
169
  was_run.should be_true
154
170
  end
155
171
 
172
+ it 'should run conditional code when successful' do
173
+ importer = Importer.build do
174
+ column :foo
175
+ end
176
+
177
+ was_run = false
178
+ importer.on_success do
179
+ was_run = true
180
+ end
181
+ was_run.should be_false
182
+
183
+ importer.import_string("foo\n1")
184
+ importer.has_errors?.should be_false
185
+ importer.on_success do
186
+ was_run = true
187
+ end
188
+ was_run.should be_true
189
+ end
190
+
156
191
  it 'should import a test csv file' do
157
192
  importer = Importer.build do
158
193
  column :number
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iron-import
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1
4
+ version: 0.8.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rob Morris
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-07-18 00:00:00.000000000 Z
11
+ date: 2017-08-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: iron-extensions