csv2hash 0.6.8 → 0.7.0
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/CHANGELOG.md +9 -2
- data/Gemfile.lock +1 -1
- data/README.md +32 -2
- data/UPGRADE.md +4 -0
- data/lib/csv2hash/discover.rb +32 -12
- data/lib/csv2hash/validator.rb +30 -5
- data/lib/csv2hash/version.rb +1 -1
- data/spec/csv2hash/parser/collection_spec.rb +41 -0
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d0017cb5da55df2b12a7a2a859cb6735dcfe2e17
|
4
|
+
data.tar.gz: a720666766679ea852bd1337f6d0795073d30f3f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 686218a873857c290b1352f83313031cc29bfb6fb23871eb83c2d3f2b520a59d654c0bc30be9141ea43f83816bc36828b7a44fca86a7e071541dab7186ff6cab
|
7
|
+
data.tar.gz: 7d77cc8c42c5facd840c4b8aa34bb9c140822f0947ab54bb229c1866e98993eb9e0b48cc8b6242451538ef8bd27e239c06cf0447b1b77195c3306b786038aff5
|
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,15 @@
|
|
1
|
+
### VERSION 0.7.0
|
2
|
+
|
3
|
+
* feature
|
4
|
+
* Auto discover is also available for collection.
|
5
|
+
|
6
|
+
* [fullchanges](https://github.com/FinalCAD/csv2hash/pull/20)
|
7
|
+
|
1
8
|
### VERSION 0.6.8
|
2
9
|
|
3
10
|
* bug fix
|
4
11
|
* fix typo on generator
|
5
|
-
|
12
|
+
|
6
13
|
### VERSION 0.6.7
|
7
14
|
|
8
15
|
* enhancements
|
@@ -10,7 +17,7 @@
|
|
10
17
|
|
11
18
|
* refactoring
|
12
19
|
* following rails way for naming file
|
13
|
-
|
20
|
+
|
14
21
|
* [fullchanges](https://github.com/FinalCAD/csv2hash/pull/17)
|
15
22
|
|
16
23
|
### VERSION 0.6.6
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -26,8 +26,9 @@ It is a DSL to validate and map a CSV to a Ruby Hash.
|
|
26
26
|
* [define where your data are expected](#define-where-your-data-are-expected)
|
27
27
|
* [Samples](#samples)
|
28
28
|
* [[MAPPING] Validation of cells with defined precision](#mapping-validation-of-cells-with-defined-precision)
|
29
|
-
* [Auto discover position feature](#auto-discover-position-feature)
|
29
|
+
* [Auto discover position feature in Mapping](#auto-discover-position-feature-in-mapping)
|
30
30
|
* [[COLLECTION] Validation of a collection (Regular CSV)](#collection-validation-of-a-collection-regular-csv)
|
31
|
+
* [Auto discover position feature in Collection](#auto-discover-position-feature-in-collection)
|
31
32
|
* [Structure validation rules](#structure-validation-rules)
|
32
33
|
* [CSV Headers](#csv-headers)
|
33
34
|
* [Parser and configuration](#parser-and-configuration)
|
@@ -210,7 +211,7 @@ class MyParser
|
|
210
211
|
end
|
211
212
|
```
|
212
213
|
|
213
|
-
### Auto discover position feature
|
214
|
+
### Auto discover position feature in Mapping
|
214
215
|
|
215
216
|
This is a special feature for finding the Y index of row where you data start. For instance you have this following data :
|
216
217
|
|
@@ -242,6 +243,7 @@ became
|
|
242
243
|
cell position: [[0, /Employment/],1], key: 'employment'
|
243
244
|
```
|
244
245
|
|
246
|
+
|
245
247
|
### [COLLECTION] Validation of a collection (Regular CSV)
|
246
248
|
|
247
249
|
Consider the following CSV:
|
@@ -288,6 +290,34 @@ class MyParser
|
|
288
290
|
end
|
289
291
|
```
|
290
292
|
|
293
|
+
### Auto discover position feature in Collection
|
294
|
+
|
295
|
+
This is a special feature for finding a specific column index on header. For example you have the following data:
|
296
|
+
|
297
|
+
|
298
|
+
```
|
299
|
+
| Name | Age |
|
300
|
+
|---------------|---------------|
|
301
|
+
| John Doe | 23 |
|
302
|
+
| Jane Doe | 28 |
|
303
|
+
| | |
|
304
|
+
| | |
|
305
|
+
```
|
306
|
+
|
307
|
+
You want to extract `Name` and 'Age' for all rows but you want the order of the columns to be able to change.
|
308
|
+
You change the position to the regex of column index you are looking for. So this how the position
|
309
|
+
|
310
|
+
```
|
311
|
+
cell position: 0, key: 'name'
|
312
|
+
```
|
313
|
+
|
314
|
+
can be change to
|
315
|
+
|
316
|
+
```
|
317
|
+
cell position: /Name/ key: 'name'
|
318
|
+
```
|
319
|
+
|
320
|
+
|
291
321
|
### Structure validation rules
|
292
322
|
|
293
323
|
You may want to validate some structure, like min or max number of columns, definition accepts structure_rules as a key for the third parameter.
|
data/UPGRADE.md
CHANGED
data/lib/csv2hash/discover.rb
CHANGED
@@ -1,22 +1,42 @@
|
|
1
1
|
module Csv2hash
|
2
2
|
module Discover
|
3
3
|
|
4
|
-
def find_dynamic_position cell
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
def find_dynamic_position cell, header = nil
|
5
|
+
header.present? ? find_dynamic_position_x(cell, header) : find_dynamic_position_y(cell)
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
def find_dynamic_position_y cell
|
10
|
+
y, x = cell.rules.fetch :position
|
11
|
+
column, matcher = y
|
12
|
+
dynamic_y_axe = data_source.index { |entries| entries[column] =~ matcher }
|
8
13
|
|
9
|
-
|
10
|
-
|
11
|
-
|
14
|
+
if dynamic_y_axe.nil?
|
15
|
+
if cell.rules.fetch(:allow_blank)
|
16
|
+
return nil
|
17
|
+
else
|
18
|
+
raise "Y doesn't found for #{cell.rules[:position]} on :#{cell.rules.fetch(:key)}"
|
19
|
+
end
|
12
20
|
else
|
13
|
-
|
21
|
+
cell.rules[:position] = [dynamic_y_axe, x]
|
22
|
+
cell
|
14
23
|
end
|
15
|
-
else
|
16
|
-
cell.rules[:position] = [dynamic_y_axe, x]
|
17
|
-
cell
|
18
24
|
end
|
19
|
-
end
|
20
25
|
|
26
|
+
def find_dynamic_position_x cell, header
|
27
|
+
x = cell.rules.fetch :position
|
28
|
+
dynamic_x_axe = header.index { |column| column =~ x }
|
29
|
+
|
30
|
+
if dynamic_x_axe.nil?
|
31
|
+
if cell.rules.fetch(:allow_blank)
|
32
|
+
return nil
|
33
|
+
else
|
34
|
+
raise "Column doesn't found in #{definition.name}"
|
35
|
+
end
|
36
|
+
else
|
37
|
+
cell.rules[:position] = dynamic_x_axe
|
38
|
+
cell
|
39
|
+
end
|
40
|
+
end
|
21
41
|
end
|
22
42
|
end
|
data/lib/csv2hash/validator.rb
CHANGED
@@ -5,8 +5,7 @@ module Csv2hash
|
|
5
5
|
include Discover
|
6
6
|
|
7
7
|
def validate_rules y=nil
|
8
|
-
|
9
|
-
|
8
|
+
definition.type == Definition::MAPPING ? find_or_remove_dynamic_fields_on_mapping! : find_or_remove_dynamic_fields_on_collection!(y)
|
10
9
|
definition.cells.each do |cell|
|
11
10
|
_y, x = position cell.rules.fetch(:position)
|
12
11
|
begin
|
@@ -75,14 +74,14 @@ module Csv2hash
|
|
75
74
|
end
|
76
75
|
end
|
77
76
|
|
78
|
-
def
|
77
|
+
def find_or_remove_dynamic_fields_on_mapping!
|
79
78
|
cells = definition.cells.dup
|
80
79
|
# cells without optional and not found dynamic field
|
81
80
|
definition.cells = [].tap do |_cells|
|
82
81
|
while(!cells.empty?) do
|
83
82
|
cell = cells.pop
|
84
83
|
_y, x = cell.rules.fetch(:position)
|
85
|
-
if
|
84
|
+
if dynamic_field_for_mapping?(_y)
|
86
85
|
begin
|
87
86
|
_cell = find_dynamic_position cell
|
88
87
|
_cells << _cell
|
@@ -98,9 +97,35 @@ module Csv2hash
|
|
98
97
|
nil
|
99
98
|
end
|
100
99
|
|
101
|
-
def
|
100
|
+
def find_or_remove_dynamic_fields_on_collection! y
|
101
|
+
cells = definition.cells.dup
|
102
|
+
# cells without optional and not found dynamic field
|
103
|
+
definition.cells = [].tap do |_cells|
|
104
|
+
while(!cells.empty?) do
|
105
|
+
cell = cells.pop
|
106
|
+
x = cell.rules.fetch(:position)
|
107
|
+
if dynamic_field_for_collection?(x)
|
108
|
+
begin
|
109
|
+
_cell = find_dynamic_position cell, data_source.first
|
110
|
+
_cells << _cell
|
111
|
+
rescue => e
|
112
|
+
self.errors << { y: y, x: x, message: e.message, key: cell.rules.fetch(:key) }
|
113
|
+
raise if break_on_failure
|
114
|
+
end
|
115
|
+
else
|
116
|
+
_cells << cell
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end.compact
|
120
|
+
nil
|
121
|
+
end
|
122
|
+
|
123
|
+
def dynamic_field_for_mapping? field
|
102
124
|
field.is_a?(Array)
|
103
125
|
end
|
104
126
|
|
127
|
+
def dynamic_field_for_collection? field
|
128
|
+
field.is_a?(Regexp)
|
129
|
+
end
|
105
130
|
end
|
106
131
|
end
|
data/lib/csv2hash/version.rb
CHANGED
@@ -34,6 +34,47 @@ module Csv2hash
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
+
context 'discover' do
|
38
|
+
|
39
|
+
context 'when header keyword present' do
|
40
|
+
let(:data_source) { [ ['Name','Age'], [ 'John Doe',34 ], [ 'Jane Doe', 25] ] }
|
41
|
+
before do
|
42
|
+
subject.definition.header_size = 1
|
43
|
+
definition.cells << Cell.new({ position: /Age/, key: 'age' })
|
44
|
+
definition.cells << Cell.new({ position: /Name/, key: 'name' })
|
45
|
+
end
|
46
|
+
it {
|
47
|
+
expect(subject.tap { |c| c.parse! }.data).to eql(
|
48
|
+
{ data: [ { 'name' => 'John Doe', 'age' => 34 }, { 'name' => 'Jane Doe', 'age' => 25} ] }
|
49
|
+
)
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'when no header keyword found and no break on failure' do
|
54
|
+
let(:data_source) { [ [ 'John Doe',34 ], [ 'Jane Doe', 25] ] }
|
55
|
+
before do
|
56
|
+
subject.break_on_failure = false
|
57
|
+
definition.cells << Cell.new({ position: /Name/, key: 'name' })
|
58
|
+
subject.parse
|
59
|
+
end
|
60
|
+
it{ expect(subject.data).to eql(nil)}
|
61
|
+
it{ expect(subject.errors.first[:message]).to eql("Column doesn't found in foo")}
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'when no header keyword found and break on failure' do
|
65
|
+
let(:data_source) { [ [ 'John Doe',34 ], [ 'Jane Doe', 25] ] }
|
66
|
+
before do
|
67
|
+
subject.break_on_failure = true
|
68
|
+
definition.cells << Cell.new({ position: /Name/, key: 'name' })
|
69
|
+
end
|
70
|
+
it 'should throw exception' do
|
71
|
+
expect {
|
72
|
+
subject.parse
|
73
|
+
}.to raise_error("Column doesn't found in foo")
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
37
78
|
context 'with nested' do
|
38
79
|
let(:data_source) { [ [ 'John Doe', 22 ], [ 'Jane Doe', 19 ] ] }
|
39
80
|
before do
|