iron-import 0.8.2 → 0.8.3
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 +10 -0
- data/Version.txt +1 -1
- data/lib/iron/import/column.rb +8 -5
- data/lib/iron/import/data_reader.rb +11 -1
- data/lib/iron/import/importer.rb +18 -8
- data/lib/iron/import/row.rb +4 -0
- data/spec/importer/csv_reader_spec.rb +71 -0
- data/spec/importer/data_reader_spec.rb +28 -2
- data/spec/samples/wsm-data.csv +3 -0
- data/spec/spec_helper.rb +5 -0
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2d9d43cccc1da84c981d29256f46c9c225241ba9
|
4
|
+
data.tar.gz: df64d0839daf658b286d0513c404271899303a40
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 23e16558e41b48c89d45c11e2fc38f1efebdd56dece3866a691c96c98d92371c7cae8e1ad0e874217d81ec649d906b71fac057208cd2c09e49cd47312a83b3ee
|
7
|
+
data.tar.gz: 9601f72f3b8c651b68c60440ee4ef5838c075daaa6a84146600ff876da2eeb2e1c5eb8bd4e9ce4e36aca689a8068afe486b941c0f807d7300e0d21e164ad62b0
|
data/History.txt
CHANGED
@@ -1,16 +1,26 @@
|
|
1
|
+
== 0.8.3 / 2017-08-22
|
2
|
+
|
3
|
+
* Add :bool column type (supports 'yes'/'no', 'Y'/'N', 0/1, 'true'/'false', 'T'/'F')
|
4
|
+
* Add error when failing to find valid header due to Importer#validate_columns failure
|
5
|
+
* Utilize new Class#inspect_only feature to make #inspect worth a damn on Column & Row during debugging
|
6
|
+
* Execute Importer#validate_columns in importer context to allow explicit #add_error calls
|
7
|
+
|
1
8
|
== 0.8.2 / 2017-08-01
|
9
|
+
|
2
10
|
* Add Importer#on_success to enable conditional processing once an import has been successfully completed
|
3
11
|
* Pre-parse values with Column#type set when using Column#parse (instead of ignoring it)
|
4
12
|
* Make importer scope available in Importer#process and the block form of Importer#import
|
5
13
|
* Add backtrace info to error logging for exceptions to help during debugging
|
6
14
|
|
7
15
|
== 0.8.1 / 2017-07-18
|
16
|
+
|
8
17
|
* Do not include optional headers in #missing_headers
|
9
18
|
* Improve string parsing to strip trailing '.0' from incoming float values
|
10
19
|
* Add #to_h to Row for consistency with Column
|
11
20
|
* Bugfix for calls to #add_error with invalid calling signature
|
12
21
|
|
13
22
|
== 0.8.0 / 2017-06-29
|
23
|
+
|
14
24
|
* Breaking Change: change signature of Importer#add_error to support new features
|
15
25
|
* Breaking Change: Importer.missing_headers will be [] instead of nil on all headers found
|
16
26
|
* Breaking Change: remove deprecated method Column#required!
|
data/Version.txt
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.8.
|
1
|
+
0.8.3
|
data/lib/iron/import/column.rb
CHANGED
@@ -22,7 +22,7 @@ class Importer
|
|
22
22
|
# header /(price|cost)/i
|
23
23
|
#
|
24
24
|
# # Tells the data parser what type of data this column contains, one
|
25
|
-
# # of :integer, :string, :date, :float, or :cents. Defaults to :string.
|
25
|
+
# # of :integer, :string, :date, :float, :bool or :cents. Defaults to :string.
|
26
26
|
# type :cents
|
27
27
|
#
|
28
28
|
# # Instead of a type, you can set an explicit parse block. Be aware
|
@@ -30,8 +30,8 @@ class Importer
|
|
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
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-
|
34
|
-
# # of
|
33
|
+
# # value of the row, but if used with #type, you can have the pre-processed
|
34
|
+
# # output of that type as your input.
|
35
35
|
# parse do |raw_value|
|
36
36
|
# val = raw_value.to_i + 1000
|
37
37
|
# # NOTE: we're in a block, so don't do this:
|
@@ -53,9 +53,9 @@ class Importer
|
|
53
53
|
# virtual!
|
54
54
|
#
|
55
55
|
# # When #virtual! is set, gets called to calculate each row's value for this
|
56
|
-
# # column using the row's parsed values.
|
56
|
+
# # column using the row's parsed values from other columns.
|
57
57
|
# calculate do |row|
|
58
|
-
# row[:
|
58
|
+
# row[:other_col_key] + 5
|
59
59
|
# end
|
60
60
|
# end
|
61
61
|
# end
|
@@ -83,6 +83,9 @@ class Importer
|
|
83
83
|
dsl_accessor :header, :position, :type
|
84
84
|
dsl_accessor :parse, :validate, :calculate
|
85
85
|
dsl_flag :optional, :virtual
|
86
|
+
|
87
|
+
# Limit our inspect to avoid dumping whole importer
|
88
|
+
inspect_only :key, :type, :optional, :virtual, :position, :index
|
86
89
|
|
87
90
|
def self.pos_to_index(pos)
|
88
91
|
raise 'Invalid column position: ' + pos.inspect unless pos.is_a?(String) && pos.match(/\A[a-z]{1,3}\z/i)
|
@@ -283,7 +283,17 @@ class Importer
|
|
283
283
|
# Pull out the date part of the string and convert
|
284
284
|
date_str = val.to_s.extract(/[0-9]+[\-\/][0-9]+[\-\/][0-9]+/)
|
285
285
|
date_str.to_date rescue nil
|
286
|
-
|
286
|
+
|
287
|
+
when :bool then
|
288
|
+
val_str = parse_value(val, :string).to_s.downcase
|
289
|
+
if ['true','yes','y','t','1'].include?(val_str)
|
290
|
+
return true
|
291
|
+
elsif ['false','no','n','f','0'].include?(val_str)
|
292
|
+
return false
|
293
|
+
else
|
294
|
+
nil
|
295
|
+
end
|
296
|
+
|
287
297
|
else
|
288
298
|
raise "Unknown column type #{type.inspect} - unimplemented?"
|
289
299
|
end
|
data/lib/iron/import/importer.rb
CHANGED
@@ -109,7 +109,7 @@ class Importer
|
|
109
109
|
dsl_flag :headerless
|
110
110
|
# Explicitly sets the row number (1-indexed) where data rows begin,
|
111
111
|
# usually left defaulted to nil to automatically start after the header
|
112
|
-
# row.
|
112
|
+
# row, or on the first row if #headerless! is set.
|
113
113
|
dsl_accessor :start_row
|
114
114
|
# Set to a block/lambda taking a parsed but unvalidated row as a hash,
|
115
115
|
# return true to keep, false to skip.
|
@@ -363,6 +363,7 @@ class Importer
|
|
363
363
|
end
|
364
364
|
|
365
365
|
# Read in the data!
|
366
|
+
loaded = false
|
366
367
|
@reader.load(path_or_stream, scopes) do |raw_rows|
|
367
368
|
# Find our column layout, start of data, etc
|
368
369
|
if find_header(raw_rows)
|
@@ -374,6 +375,7 @@ class Importer
|
|
374
375
|
end
|
375
376
|
end
|
376
377
|
# We've found a workable sheet/table/whatever, stop looking
|
378
|
+
loaded = true
|
377
379
|
true
|
378
380
|
|
379
381
|
else
|
@@ -383,9 +385,14 @@ class Importer
|
|
383
385
|
end
|
384
386
|
end
|
385
387
|
|
386
|
-
#
|
387
|
-
|
388
|
-
|
388
|
+
# Verify that we found a working set of rows
|
389
|
+
unless loaded
|
390
|
+
# If we have any missing headers, note that fact
|
391
|
+
if @missing_headers && @missing_headers.count > 0
|
392
|
+
add_error("Unable to locate required column header for column(s): " + @missing_headers.collect{|c| ":#{c}"}.list_join(', '))
|
393
|
+
else
|
394
|
+
add_error("Unable to locate required column headers!")
|
395
|
+
end
|
389
396
|
end
|
390
397
|
|
391
398
|
# If we're here with no errors, we rule!
|
@@ -533,15 +540,18 @@ class Importer
|
|
533
540
|
col.data.index = nil
|
534
541
|
col.data.header_text = nil
|
535
542
|
end
|
536
|
-
|
543
|
+
|
537
544
|
# Have we found them all, or at least a valid sub-set?
|
538
545
|
header_found = remaining.empty?
|
539
546
|
unless header_found
|
540
|
-
if remaining.all?(&:optional?)
|
547
|
+
if found_columns.any? && remaining.all?(&:optional?)
|
541
548
|
if @column_validator
|
542
549
|
# Run custom column validator
|
543
|
-
|
544
|
-
|
550
|
+
valid = false
|
551
|
+
had_error = Error.with_context(@importer, nil, nil, nil) do
|
552
|
+
valid = DslProxy.exec(self, found_columns, &@column_validator)
|
553
|
+
end
|
554
|
+
header_found = !had_error && valid
|
545
555
|
else
|
546
556
|
# No validator... do we have any found columns at all???
|
547
557
|
header_found = @columns.any?(&:present?)
|
data/lib/iron/import/row.rb
CHANGED
@@ -2,8 +2,12 @@ class Importer
|
|
2
2
|
|
3
3
|
class Row
|
4
4
|
|
5
|
+
# Attributes
|
5
6
|
attr_reader :line, :values, :errors
|
6
7
|
|
8
|
+
# Limit our inspect to avoid dumping whole importer
|
9
|
+
inspect_only :line, :values
|
10
|
+
|
7
11
|
def initialize(importer, line, value_hash = nil)
|
8
12
|
@importer = importer
|
9
13
|
@line = line
|
@@ -28,4 +28,75 @@ describe Importer::CsvReader do
|
|
28
28
|
]
|
29
29
|
end
|
30
30
|
|
31
|
+
it 'should fail on WSM sample data' do
|
32
|
+
importer = Importer.build do
|
33
|
+
column :company_name do
|
34
|
+
optional!
|
35
|
+
end
|
36
|
+
virtual_column :company do
|
37
|
+
calculate do |row|
|
38
|
+
if column(:company_name).present?
|
39
|
+
row[:company_name]
|
40
|
+
else
|
41
|
+
[row[:store_code], row[:store_num]].list_join(', ')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
column :store_code do
|
46
|
+
header /code$/i
|
47
|
+
optional!
|
48
|
+
end
|
49
|
+
column :store_num do
|
50
|
+
optional!
|
51
|
+
header /num(ber)?$/i
|
52
|
+
end
|
53
|
+
virtual_column :store do
|
54
|
+
calculate do |row|
|
55
|
+
Store.find_by_upc(row[:upc])
|
56
|
+
end
|
57
|
+
end
|
58
|
+
column :buyer_name do
|
59
|
+
optional!
|
60
|
+
end
|
61
|
+
column :buyer_email do
|
62
|
+
optional!
|
63
|
+
header /buyer\s*email/i
|
64
|
+
validate do |val|
|
65
|
+
val.match? /^\s*([a-z0-9_\-\+\.]+@[a-z0-9\.\-]+\.[a-z]+)(,\s*[a-z0-9_\-\+\.]+@[a-z0-9\.\-]+\.[a-z]+)*\s*$/i
|
66
|
+
end
|
67
|
+
end
|
68
|
+
column :rep_email do
|
69
|
+
optional!
|
70
|
+
header /^(sales\s*)?rep\s*email/i
|
71
|
+
validate do |val|
|
72
|
+
val.match? /^\s*([a-z0-9_\-\+\.]+@[a-z0-9\.\-]+\.[a-z]+)(,\s*[a-z0-9_\-\+\.]+@[a-z0-9\.\-]+\.[a-z]+)*\s*$/i
|
73
|
+
end
|
74
|
+
end
|
75
|
+
column :regional do
|
76
|
+
optional!
|
77
|
+
type :bool
|
78
|
+
end
|
79
|
+
|
80
|
+
# We need a company name column if none passed in
|
81
|
+
validate_columns do |cols|
|
82
|
+
keys = cols.collect(&:key)
|
83
|
+
has_company = keys.include?(:company_name)
|
84
|
+
has_company && (keys.include?(:store_num) || keys.include?(:store_code))
|
85
|
+
end
|
86
|
+
|
87
|
+
# Only pay attention to rows with a store num or code
|
88
|
+
# filter_rows do |row|
|
89
|
+
# row[:store_num].present? || row[:store_code].present?
|
90
|
+
# end
|
91
|
+
|
92
|
+
# Make sure rows are valid
|
93
|
+
validate_rows do |row|
|
94
|
+
add_error("Unable to locate specified company") unless row[:company].present?
|
95
|
+
add_error("Unable to locate specified store") unless row[:store].present?
|
96
|
+
end
|
97
|
+
end
|
98
|
+
importer.import(SpecHelper.sample_path('wsm-data.csv')).should be_false
|
99
|
+
importer.errors.first.to_s.should == "Unable to locate required column headers!"
|
100
|
+
end
|
101
|
+
|
31
102
|
end
|
@@ -44,7 +44,8 @@ describe Importer::DataReader do
|
|
44
44
|
'' => nil,
|
45
45
|
255 => '255',
|
46
46
|
-1.5 => '-1.5',
|
47
|
-
10.0 => '10'
|
47
|
+
10.0 => '10',
|
48
|
+
'10.0' => '10.0'
|
48
49
|
}.each_pair do |val, res|
|
49
50
|
@reader.parse_value(val, :string).should === res
|
50
51
|
end
|
@@ -60,7 +61,9 @@ describe Importer::DataReader do
|
|
60
61
|
'-95' => -9500,
|
61
62
|
52 => 5200,
|
62
63
|
1.0 => 100,
|
63
|
-
1.25 => 125
|
64
|
+
1.25 => 125,
|
65
|
+
-2.001 => -200,
|
66
|
+
'bob' => nil
|
64
67
|
}.each_pair do |val, res|
|
65
68
|
@reader.parse_value(val, :cents).should === res
|
66
69
|
end
|
@@ -79,6 +82,29 @@ describe Importer::DataReader do
|
|
79
82
|
end
|
80
83
|
end
|
81
84
|
|
85
|
+
it 'should parse bools' do
|
86
|
+
{
|
87
|
+
'tRue' => true,
|
88
|
+
'yes ' => true,
|
89
|
+
'T' => true,
|
90
|
+
'y' => true,
|
91
|
+
'1' => true,
|
92
|
+
1.0 => true,
|
93
|
+
'FALSE' => false,
|
94
|
+
'no' => false,
|
95
|
+
'F' => false,
|
96
|
+
'n' => false,
|
97
|
+
'0' => false,
|
98
|
+
0 => false,
|
99
|
+
'tim' => nil,
|
100
|
+
nil => nil,
|
101
|
+
'' => nil,
|
102
|
+
'xyz' => nil
|
103
|
+
}.each_pair do |val, res|
|
104
|
+
@reader.parse_value(val, :bool).should === res
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
82
108
|
it 'should build an instance based on format' do
|
83
109
|
Importer::DataReader.for_format(@importer, :csv).should be_a(Importer::CsvReader)
|
84
110
|
Importer::DataReader.for_format(@importer, :xls).should be_a(Importer::XlsReader)
|
data/spec/spec_helper.rb
CHANGED
@@ -10,6 +10,11 @@ RSpec.configure do |config|
|
|
10
10
|
config.color = true
|
11
11
|
config.add_formatter 'documentation'
|
12
12
|
config.backtrace_exclusion_patterns = [/rspec/]
|
13
|
+
# Allow us to use: it '...', :focus do ... end
|
14
|
+
# rather than needing: it '...', :focus => true do ... end
|
15
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
16
|
+
# If everything is filtered, run everything - used if no :focus element is present
|
17
|
+
config.run_all_when_everything_filtered = true
|
13
18
|
end
|
14
19
|
|
15
20
|
module SpecHelper
|
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.3
|
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-08-
|
11
|
+
date: 2017-08-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: iron-extensions
|
@@ -19,7 +19,7 @@ dependencies:
|
|
19
19
|
version: '1.2'
|
20
20
|
- - ">="
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: 1.2.
|
22
|
+
version: 1.2.2
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -29,7 +29,7 @@ dependencies:
|
|
29
29
|
version: '1.2'
|
30
30
|
- - ">="
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: 1.2.
|
32
|
+
version: 1.2.2
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: iron-dsl
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -133,6 +133,7 @@ files:
|
|
133
133
|
- spec/samples/simple.csv
|
134
134
|
- spec/samples/simple.html
|
135
135
|
- spec/samples/test-products.xls
|
136
|
+
- spec/samples/wsm-data.csv
|
136
137
|
- spec/spec_helper.rb
|
137
138
|
homepage: http://irongaze.com
|
138
139
|
licenses:
|