iron-import 0.8.1 → 0.8.2
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 +4 -4
- data/History.txt +6 -0
- data/Version.txt +1 -1
- data/lib/iron/import/column.rb +22 -2
- data/lib/iron/import/custom_reader.rb +1 -1
- data/lib/iron/import/data_reader.rb +12 -2
- data/lib/iron/import/error.rb +1 -1
- data/lib/iron/import/excel_reader.rb +2 -2
- data/lib/iron/import/html_reader.rb +2 -2
- data/lib/iron/import/importer.rb +37 -9
- data/spec/importer/importer_spec.rb +50 -15
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ce84c9899f23abe7fd1f3ed06dc3f50e8986a585
|
4
|
+
data.tar.gz: e1cd18c73aa8663bd0b98305dd18b0ddaee9f75e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
+
0.8.2
|
data/lib/iron/import/column.rb
CHANGED
@@ -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)
|
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)
|
@@ -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
|
-
|
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
|
-
#
|
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
|
data/lib/iron/import/error.rb
CHANGED
@@ -25,7 +25,7 @@ class Importer
|
|
25
25
|
false
|
26
26
|
end
|
27
27
|
rescue Exception => e
|
28
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
68
|
+
add_exception(e)
|
69
69
|
end
|
70
70
|
|
71
71
|
end
|
data/lib/iron/import/importer.rb
CHANGED
@@ -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
|
-
|
428
|
+
DslProxy.exec(self, row, &block)
|
425
429
|
rescue Exception => e
|
426
|
-
|
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,
|
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
|
-
|
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.
|
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-
|
11
|
+
date: 2017-08-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: iron-extensions
|