csv2hash 0.6.8 → 0.7.0

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: 7c09804fb3f85eacdc90cbfa95ccee0acc8a4fb8
4
- data.tar.gz: d29ac384ef4275b13e087fd0eb504eba2f93d337
3
+ metadata.gz: d0017cb5da55df2b12a7a2a859cb6735dcfe2e17
4
+ data.tar.gz: a720666766679ea852bd1337f6d0795073d30f3f
5
5
  SHA512:
6
- metadata.gz: f2b07b5cfcb24197dec4195873a285f353974960ba29deee2a805da2ee45ce5c8a09838ec04b49fab4e6683672226b46587808ffdaacf066b3c6770f139451e1
7
- data.tar.gz: 5f2fda3e1d08e971ad39ad05bcfbf094f29d1d2e5ca9a86a42cf413d5c7eb502a2ce08bdcfca25c0f1afa5ccd31fe4410bea0cc190ff53fec83e1b7bc319dc54
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
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- csv2hash (0.6.8)
4
+ csv2hash (0.7.0)
5
5
  activesupport (~> 4.1)
6
6
 
7
7
  GEM
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
@@ -1,5 +1,9 @@
1
1
  # Upgrading
2
2
 
3
+ # Upgrading from 0.6.8 to 0.7.0
4
+
5
+ nothing
6
+
3
7
  # Upgrading from 0.6.7 to 0.6.8
4
8
 
5
9
  nothing
@@ -1,22 +1,42 @@
1
1
  module Csv2hash
2
2
  module Discover
3
3
 
4
- def find_dynamic_position cell
5
- y, x = cell.rules.fetch :position
6
- column, matcher = y
7
- dynamic_y_axe = data_source.index { |entries| entries[column] =~ matcher }
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
- if dynamic_y_axe.nil?
10
- if cell.rules.fetch(:allow_blank)
11
- return nil
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
- raise "Y doesn't found for #{cell.rules[:position]} on :#{cell.rules.fetch(:key)}"
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
@@ -5,8 +5,7 @@ module Csv2hash
5
5
  include Discover
6
6
 
7
7
  def validate_rules y=nil
8
- find_or_remove_dynamic_fields! if definition.type == Definition::MAPPING
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 find_or_remove_dynamic_fields!
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 dynamic_field?(_y)
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 dynamic_field? field
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
@@ -1,3 +1,3 @@
1
1
  module Csv2hash
2
- VERSION = '0.6.8'
2
+ VERSION = '0.7.0'
3
3
  end
@@ -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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: csv2hash
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.8
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joel AZEMAR