csv2hash 0.6.4 → 0.6.5
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 +11 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +1 -1
- data/README.md +60 -0
- data/UPGRADE.md +4 -0
- data/config/rules.erb.yml +2 -1
- data/config/rules.extra_validator.erb.yml +8 -0
- data/config/rules.yml +6 -2
- data/lib/csv2hash/cell.rb +10 -5
- data/lib/csv2hash/coercers/type_coercer.rb +25 -0
- data/lib/csv2hash/coercers/yaml_coercer.rb +43 -0
- data/lib/csv2hash/configuration.rb +17 -0
- data/lib/csv2hash/csv_array.rb +8 -6
- data/lib/csv2hash/definition.rb +1 -1
- data/lib/csv2hash/discover.rb +13 -7
- data/lib/csv2hash/railtie.rb +6 -0
- data/lib/csv2hash/registry.rb +11 -9
- data/lib/csv2hash/structure_validator.rb +1 -0
- data/lib/csv2hash/validator/collection.rb +1 -0
- data/lib/csv2hash/validator/mapping.rb +1 -0
- data/lib/csv2hash/validator.rb +39 -6
- data/lib/csv2hash/version.rb +1 -1
- data/lib/csv2hash.rb +25 -2
- data/lib/generators/csv2hash/install/install_generator.rb +15 -0
- data/lib/generators/csv2hash/install/templates/csh2hash.rb +6 -0
- data/spec/csv2hash/coniguration_spec.rb +18 -0
- data/spec/csv2hash/parser/mapping_spec.rb +36 -1
- data/spec/csv2hash/structure_validator_spec.rb +1 -4
- data/spec/csv2hash/type_coercer_spec.rb +16 -0
- data/spec/csv2hash/validator/collection_spec.rb +9 -13
- data/spec/csv2hash/validator/mapping_spec.rb +6 -5
- data/spec/csv2hash/validator_spec.rb +4 -4
- data/spec/csv2hash/yaml_loader_spec.rb +12 -0
- data/spec/generators/csv2hash/install_generator_spec.rb +32 -0
- data/spec/generators_helper.rb +10 -0
- data/spec/lib/extra_validators/downcase_validator.rb +9 -0
- metadata +18 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1abbff1cb3d760b2d6f7c762a69cd6dbdcbbd2b3
|
4
|
+
data.tar.gz: aa458b6a73cc2de62b4245431f5adf987aaa7517
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 535644564d7d73cf9a7c28baade14ea09e53443b2f98d1223794e1336f3a0bfa2849ba3ef42b5b0e82adf798aa4faf2a93f291439d5f505437ba40fe5fce8aa5
|
7
|
+
data.tar.gz: 5f12fe8e872273087f117885885618f4fd67c2a3e178b4d6192bdfd4c51188a572c52c32664246e6a5b5f2c1944bd41c385f92e66e41ad8eb3f54b23533fc860
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
### VERSION 0.6.5
|
2
|
+
|
3
|
+
* enhancements
|
4
|
+
* Add Coercer for deserialize field from Yaml like ExtraValidator and Regex
|
5
|
+
* Add Coercer for deserialize field from Csv value to ruby value like 'true' => true
|
6
|
+
* Add missing module prefix Csv2hash
|
7
|
+
* remove rule for dynmaic field with field is optional and not found
|
8
|
+
|
9
|
+
* feature
|
10
|
+
* Add generator for configure csv2hash on Rails app
|
11
|
+
|
1
12
|
### VERSION 0.6.4
|
2
13
|
|
3
14
|
* feature
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -426,8 +426,68 @@ rules:
|
|
426
426
|
- { position: [2,1], key: 'first_name' }
|
427
427
|
```
|
428
428
|
|
429
|
+
or for complex and verbose rule
|
430
|
+
|
431
|
+
```
|
432
|
+
name: 'example'
|
433
|
+
mapping: 'mapping'
|
434
|
+
header_size: 2
|
435
|
+
structure_rules: { max_columns: 20 }
|
436
|
+
rules:
|
437
|
+
- !rule
|
438
|
+
position: [1,1]
|
439
|
+
key: 'first_name'
|
440
|
+
- !rule
|
441
|
+
position: [2,1]
|
442
|
+
key: 'last_name'
|
443
|
+
```
|
444
|
+
|
445
|
+
Special attention, if you use `ExtraValidator` you must give only the String name of the classe, like that :
|
446
|
+
|
447
|
+
```
|
448
|
+
extra_validator: DowncaseValidator.new
|
449
|
+
```
|
450
|
+
|
451
|
+
should become
|
452
|
+
|
453
|
+
```
|
454
|
+
extra_validator: 'DowncaseValidator'
|
455
|
+
```
|
456
|
+
|
457
|
+
for autodiscover field you must give String without Regex like that :
|
458
|
+
|
459
|
+
```
|
460
|
+
position: [[0,/LastName/],1]
|
461
|
+
```
|
462
|
+
|
463
|
+
should become
|
464
|
+
|
465
|
+
```
|
466
|
+
position: [[0,'LastName'],1]
|
467
|
+
```
|
468
|
+
|
469
|
+
this change is due to Yaml conversion
|
470
|
+
|
429
471
|
You can write ERB file, should be named with following convention ```<file name>.erb.yml```
|
430
472
|
|
473
|
+
# Type conversion
|
474
|
+
|
475
|
+
By default Csv2hash doesn't convert basic value, but you can activate this on configuration, for Rails use :
|
476
|
+
|
477
|
+
add file `config/initializers/csv2hash.rb`
|
478
|
+
|
479
|
+
```
|
480
|
+
Csv2hash.configure do |conf|
|
481
|
+
conf.convert = true
|
482
|
+
end
|
483
|
+
```
|
484
|
+
|
485
|
+
```
|
486
|
+
rails generate csh2hash:install
|
487
|
+
```
|
488
|
+
|
489
|
+
If you want add your specific conversion
|
490
|
+
|
431
491
|
# Changes
|
432
492
|
|
433
493
|
please refere to [CHANGELOG.md](https://github.com/FinalCAD/csv2hash/blob/master/CHANGELOG.md) doc
|
data/UPGRADE.md
CHANGED
data/config/rules.erb.yml
CHANGED
@@ -0,0 +1,8 @@
|
|
1
|
+
name: 'example'
|
2
|
+
mapping: 'mapping'
|
3
|
+
header_size: 0
|
4
|
+
structure_rules: { max_columns: 20 }
|
5
|
+
rules:
|
6
|
+
- { position: [0,1], key: 'nick_name', extra_validator: 'Csv2hash::DowncaseValidator', message: 'your data should be writting in downcase only' }
|
7
|
+
- { position: [1,1], key: 'first_name', extra_validator: 'Csv2hash::DowncaseValidator', message: 'your data should be writting in downcase only' }
|
8
|
+
- { position: [[0,'LastName'],1], key: 'last_name', extra_validator: 'Csv2hash::DowncaseValidator', message: 'your data should be writting in downcase only' }
|
data/config/rules.yml
CHANGED
@@ -3,5 +3,9 @@ mapping: 'mapping'
|
|
3
3
|
header_size: 2
|
4
4
|
structure_rules: { max_columns: 20 }
|
5
5
|
rules:
|
6
|
-
-
|
7
|
-
|
6
|
+
- !rule
|
7
|
+
position: [1,1]
|
8
|
+
key: 'first_name'
|
9
|
+
- !rule
|
10
|
+
position: [2,1]
|
11
|
+
key: 'last_name'
|
data/lib/csv2hash/cell.rb
CHANGED
@@ -1,11 +1,16 @@
|
|
1
1
|
require 'active_support/core_ext/array/extract_options'
|
2
2
|
|
3
|
-
|
3
|
+
require_relative 'coercers/yaml_coercer'
|
4
4
|
|
5
|
-
|
5
|
+
module Csv2hash
|
6
|
+
class Cell
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
8
|
+
attr_accessor :rules
|
9
|
+
|
10
|
+
def initialize *args
|
11
|
+
self.rules = args.extract_options!
|
12
|
+
Csv2hash::Coercers::YamlCoercer.new(rules).deserialize!
|
13
|
+
end
|
10
14
|
|
15
|
+
end
|
11
16
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Csv2hash
|
2
|
+
class TypeCoercer < Struct.new(:data)
|
3
|
+
|
4
|
+
def deserialize!
|
5
|
+
data.each do |line|
|
6
|
+
line.each do |key, value|
|
7
|
+
_value = value.to_s.strip.downcase
|
8
|
+
change.each do |keys, v|
|
9
|
+
line[key] = v if keys.include?(_value)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def change
|
18
|
+
{
|
19
|
+
Csv2hash.configuration.true_values => true,
|
20
|
+
Csv2hash.configuration.false_values => false,
|
21
|
+
Csv2hash.configuration.nil_values => nil,
|
22
|
+
}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Csv2hash
|
2
|
+
module Coercers
|
3
|
+
class YamlCoercer < Struct.new(:rules)
|
4
|
+
|
5
|
+
def deserialize!
|
6
|
+
deserialize_validator!
|
7
|
+
deserialize_regex!
|
8
|
+
deserialize_range!
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def deserialize_validator!
|
14
|
+
begin
|
15
|
+
extra_validator = self.rules.fetch(:extra_validator)
|
16
|
+
self.rules[:extra_validator] = eval("::#{extra_validator}").new
|
17
|
+
rescue KeyError # Rules without ExtraValidator
|
18
|
+
rescue SyntaxError # When extra validator is a instance of Object
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def deserialize_regex!
|
23
|
+
y, x = self.rules.fetch :position
|
24
|
+
if y.is_a?(Array)
|
25
|
+
column, matcher_string = y
|
26
|
+
self.rules[:position] = [[column, Regexp.new(matcher_string)],x]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def deserialize_range!
|
31
|
+
begin
|
32
|
+
values = self.rules.fetch(:values)
|
33
|
+
if values.is_a?(String)
|
34
|
+
match_data = values.match(/^\((?<range>.*)\)$/)
|
35
|
+
self.rules[:values] = eval(match_data[:range])
|
36
|
+
end
|
37
|
+
rescue KeyError # Rules without ExtraValidator
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Csv2hash
|
2
|
+
class Configuration
|
3
|
+
attr_accessor :true_values
|
4
|
+
attr_accessor :false_values
|
5
|
+
attr_accessor :nil_values
|
6
|
+
# attr_accessor :extra_values
|
7
|
+
attr_accessor :convert
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
self.convert = false
|
11
|
+
self.true_values = ['yes','y','t']
|
12
|
+
self.false_values = ['no','n','f']
|
13
|
+
self.nil_values = ['nil','null']
|
14
|
+
# self.extra_values = {} # { [] => }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/csv2hash/csv_array.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
require 'csv'
|
2
|
-
class CsvArray < Array
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
module Csv2hash
|
4
|
+
class CsvArray < Array
|
5
|
+
|
6
|
+
def to_csv options = {}
|
7
|
+
CSV.generate(options) do |csv|
|
8
|
+
self.each do |element|
|
9
|
+
csv << [element[:value], element[:message]]
|
10
|
+
end
|
8
11
|
end
|
9
12
|
end
|
10
13
|
end
|
11
|
-
|
12
14
|
end
|
data/lib/csv2hash/definition.rb
CHANGED
@@ -52,7 +52,7 @@ module Csv2hash
|
|
52
52
|
default_position cell
|
53
53
|
unless cell.rules.has_key? :message
|
54
54
|
if cell.rules.has_key? :values
|
55
|
-
cell.rules.merge! message: ':
|
55
|
+
cell.rules.merge! message: 'value :found not supported for :key, please use one of :values'
|
56
56
|
else
|
57
57
|
cell.rules.merge! message: 'undefined :key on :position'
|
58
58
|
end
|
data/lib/csv2hash/discover.rb
CHANGED
@@ -1,14 +1,20 @@
|
|
1
1
|
module Csv2hash
|
2
2
|
module Discover
|
3
3
|
|
4
|
-
def
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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 }
|
8
|
+
|
9
|
+
if dynamic_y_axe.nil?
|
10
|
+
if cell.rules.fetch(:allow_blank)
|
11
|
+
return nil
|
12
|
+
else
|
13
|
+
raise "Y doesn't found for #{cell.rules[:position]} on :#{cell.rules.fetch(:key)}"
|
11
14
|
end
|
15
|
+
else
|
16
|
+
cell.rules[:position] = [dynamic_y_axe, x]
|
17
|
+
cell
|
12
18
|
end
|
13
19
|
end
|
14
20
|
|
data/lib/csv2hash/registry.rb
CHANGED
@@ -1,13 +1,15 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
module Csv2hash
|
2
|
+
class Registry
|
3
|
+
def initialize
|
4
|
+
@definitions = Hash.new
|
5
|
+
end
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
7
|
+
def [] name
|
8
|
+
@definitions[name]
|
9
|
+
end
|
9
10
|
|
10
|
-
|
11
|
-
|
11
|
+
def []= name, definition
|
12
|
+
@definitions[name] = definition
|
13
|
+
end
|
12
14
|
end
|
13
15
|
end
|
data/lib/csv2hash/validator.rb
CHANGED
@@ -5,7 +5,7 @@ module Csv2hash
|
|
5
5
|
include Discover
|
6
6
|
|
7
7
|
def validate_rules y=nil
|
8
|
-
|
8
|
+
find_or_remove_dynamic_fields! if definition.type == Definition::MAPPING
|
9
9
|
|
10
10
|
definition.cells.each do |cell|
|
11
11
|
_y, x = position cell.rules.fetch(:position)
|
@@ -38,15 +38,48 @@ module Csv2hash
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
rescue => e
|
41
|
-
raise message(cell, y, x)
|
41
|
+
raise message(cell, y, x, value)
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
-
def message cell, y, x
|
46
|
-
msg = cell.rules.fetch(:message)
|
47
|
-
|
45
|
+
def message cell, y, x, value
|
46
|
+
msg = cell.rules.fetch(:message)
|
47
|
+
msg = msg.gsub(':position', "[#{y}, #{x}]")
|
48
|
+
msg = msg.gsub(':key', ":#{cell.rules.fetch(:key, :no_key_given)}")
|
49
|
+
msg = msg.gsub(':found', "<#{value}>")
|
50
|
+
msg = msg.gsub(':values', "<#{cell.rules.fetch(:values, :no_values_given)}>")
|
51
|
+
cell.rules.each { |key, _value| msg.gsub!(":#{key.to_s}", _value.to_s) unless key == :position }
|
52
|
+
msg
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def find_or_remove_dynamic_fields!
|
58
|
+
cells = definition.cells.dup
|
59
|
+
_cells = []
|
60
|
+
|
61
|
+
while(!cells.empty?) do
|
62
|
+
cell = cells.pop
|
63
|
+
_y, x = cell.rules.fetch(:position)
|
64
|
+
if dynamic_field?(_y)
|
65
|
+
begin
|
66
|
+
_cell = find_dynamic_position cell
|
67
|
+
_cells << _cell if _cell
|
68
|
+
rescue => e
|
69
|
+
self.errors << { y: (_y||y), x: x, message: e.message, key: cell.rules.fetch(:key) }
|
70
|
+
raise if break_on_failure
|
71
|
+
end
|
72
|
+
else
|
73
|
+
_cells << cell
|
74
|
+
end
|
48
75
|
end
|
49
|
-
|
76
|
+
|
77
|
+
definition.cells = _cells # cells without optional and not found dynamic field
|
78
|
+
nil
|
79
|
+
end
|
80
|
+
|
81
|
+
def dynamic_field? field
|
82
|
+
field.is_a?(Array)
|
50
83
|
end
|
51
84
|
|
52
85
|
end
|
data/lib/csv2hash/version.rb
CHANGED
data/lib/csv2hash.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require_relative 'csv2hash/version'
|
2
|
+
require_relative 'csv2hash/configuration'
|
2
3
|
require_relative 'csv2hash/registry'
|
3
4
|
require_relative 'csv2hash/cell'
|
4
5
|
require_relative 'csv2hash/definition'
|
@@ -17,6 +18,9 @@ require_relative 'csv2hash/notifier'
|
|
17
18
|
require_relative 'csv2hash/extra_validator'
|
18
19
|
require_relative 'csv2hash/adapters/base'
|
19
20
|
require_relative 'csv2hash/yaml_loader'
|
21
|
+
require_relative 'csv2hash/coercers/type_coercer'
|
22
|
+
|
23
|
+
require 'csv2hash/railtie' if defined?(Rails)
|
20
24
|
|
21
25
|
require 'active_support/core_ext/array/extract_options'
|
22
26
|
|
@@ -26,8 +30,25 @@ rescue LoadError
|
|
26
30
|
end
|
27
31
|
|
28
32
|
module Csv2hash
|
33
|
+
|
34
|
+
class << self
|
35
|
+
attr_writer :configuration
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.configuration
|
39
|
+
@configuration ||= Configuration.new
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.reset
|
43
|
+
@configuration = Configuration.new
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.configure
|
47
|
+
yield(configuration)
|
48
|
+
end
|
49
|
+
|
29
50
|
class Main
|
30
|
-
include
|
51
|
+
include StructureValidator
|
31
52
|
|
32
53
|
class << self
|
33
54
|
|
@@ -89,9 +110,10 @@ module Csv2hash
|
|
89
110
|
validate_structure!
|
90
111
|
validate_data!
|
91
112
|
|
92
|
-
|
113
|
+
DataWrapper.new.tap do |response|
|
93
114
|
if valid?
|
94
115
|
fill!
|
116
|
+
TypeCoercer.new(data[:data]).deserialize! if Csv2hash.configuration.convert
|
95
117
|
response.data = data[:data]
|
96
118
|
else
|
97
119
|
response.valid = false
|
@@ -115,6 +137,7 @@ module Csv2hash
|
|
115
137
|
|
116
138
|
def data_source
|
117
139
|
@data_source ||= begin
|
140
|
+
self.file_path_or_data = Pathname(self.file_path_or_data) if self.file_path_or_data.is_a?(String)
|
118
141
|
adapter_name = self.file_path_or_data.respond_to?(:to_path) ? :csv : :memory
|
119
142
|
adapter = Adapter::Base.create(adapter_name, self.file_path_or_data)
|
120
143
|
adapter.source
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Csv2hash
|
2
|
+
module Generator
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
4
|
+
source_root File.expand_path('../templates', __FILE__)
|
5
|
+
|
6
|
+
desc <<DESC
|
7
|
+
description:
|
8
|
+
copy csv2hash configuration to an initializer.
|
9
|
+
DESC
|
10
|
+
def create_configuration
|
11
|
+
template 'csh2hash.rb', 'config/initializers/csh2hash.rb'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
Csv2hash.configure do |conf|
|
2
|
+
# conf.convert = false # default: false
|
3
|
+
# conf.true_values = ['yes','y','t'] # default: ['yes','y','t']
|
4
|
+
# conf.false_values = ['no','n','f'] # default: ['no','n','f']
|
5
|
+
# conf.nil_values = ['nil','null'] # default: ['nil','null']
|
6
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Csv2hash
|
4
|
+
describe Configuration do
|
5
|
+
let(:configuration) { Configuration.new }
|
6
|
+
|
7
|
+
describe '#default' do
|
8
|
+
let(:true_values) { ['yes','y','t'] }
|
9
|
+
let(:false_values) { ['no','n','f'] }
|
10
|
+
let(:nil_values) { ['nil','null'] }
|
11
|
+
|
12
|
+
it 'true values' do
|
13
|
+
expect(configuration.true_values).to eq(true_values)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -53,7 +53,7 @@ module Csv2hash
|
|
53
53
|
|
54
54
|
context 'discover' do
|
55
55
|
before do
|
56
|
-
definition.cells << Cell.new({ position: [[1,/Sex/],2], key: 'sex'})
|
56
|
+
definition.cells << Cell.new({ position: [[1,/Sex/],2], key: 'sex' })
|
57
57
|
end
|
58
58
|
it {
|
59
59
|
expect(subject.tap { |c| c.parse! }.data).to eql(
|
@@ -62,5 +62,40 @@ module Csv2hash
|
|
62
62
|
}
|
63
63
|
end
|
64
64
|
|
65
|
+
context 'discover with fail!' do
|
66
|
+
before do
|
67
|
+
definition.cells << Cell.new({ position: [[1,/foo/],2], key: 'sex', allow_blank: false })
|
68
|
+
end
|
69
|
+
it {
|
70
|
+
expect { subject.tap { |c| c.parse! }}.to raise_error("Y doesn't found for [[1, /foo/], 2] on :sex")
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'discover with fail' do
|
75
|
+
context 'allow_blank: false' do
|
76
|
+
before do
|
77
|
+
definition.cells << Cell.new({ position: [[1,/foo/],2], key: 'sex', allow_blank: false })
|
78
|
+
subject.tap { |c| c.parse }
|
79
|
+
end
|
80
|
+
specify do
|
81
|
+
expect(subject.errors).to_not be_empty
|
82
|
+
expect(subject.errors).to eql(
|
83
|
+
[{y: [1, /foo/], x: 2, message: "Y doesn't found for [[1, /foo/], 2] on :sex", key: 'sex'}])
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context 'allow_blank: true' do
|
88
|
+
before do
|
89
|
+
definition.cells << Cell.new({ position: [[1,/foo/],2], key: 'sex', allow_blank: true })
|
90
|
+
end
|
91
|
+
specify do
|
92
|
+
expect(subject.definition.cells.size).to eql(3)
|
93
|
+
subject.tap { |c| c.parse }
|
94
|
+
expect(subject.errors).to be_empty
|
95
|
+
expect(subject.definition.cells.size).to eql(2)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
65
100
|
end
|
66
101
|
end
|
@@ -6,10 +6,7 @@ module Csv2hash
|
|
6
6
|
Main.generate_definition :foo do
|
7
7
|
set_type { Definition::MAPPING }
|
8
8
|
mapping { cell position: [0,0], key: 'name' }
|
9
|
-
end.tap
|
10
|
-
definition.validate!
|
11
|
-
definition.default!
|
12
|
-
end
|
9
|
+
end.tap { |d| d.validate! ; d.default! }
|
13
10
|
end
|
14
11
|
|
15
12
|
subject do
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Csv2hash
|
4
|
+
describe TypeCoercer do
|
5
|
+
let(:data) do
|
6
|
+
[{ foo: ' y ', bar: 'no', zone: ' Null', mischievous: ' pronoun ' }]
|
7
|
+
end
|
8
|
+
let(:coercer) { TypeCoercer.new(data) }
|
9
|
+
|
10
|
+
before { coercer.deserialize! }
|
11
|
+
|
12
|
+
specify do
|
13
|
+
expect(coercer.data).to eql([{ foo: true, bar: false, zone: nil, mischievous: ' pronoun ' }])
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -7,10 +7,7 @@ module Csv2hash
|
|
7
7
|
Main.generate_definition :foo do
|
8
8
|
set_type { Definition::COLLECTION }
|
9
9
|
mapping { cell position: 0, key: 'name' }
|
10
|
-
end.tap
|
11
|
-
definition.validate!
|
12
|
-
definition.default!
|
13
|
-
end
|
10
|
+
end.tap { |d| d.validate! ; d.default! }
|
14
11
|
end
|
15
12
|
let(:ignore_blank_line) { false }
|
16
13
|
|
@@ -45,11 +42,11 @@ module Csv2hash
|
|
45
42
|
|
46
43
|
context 'with invalid data' do
|
47
44
|
let(:data_source) { [ [ ] ] }
|
48
|
-
it { expect { subject.validate_data! }.to raise_error('undefined name on [0, 0]') }
|
45
|
+
it { expect { subject.validate_data! }.to raise_error('undefined :name on [0, 0]') }
|
49
46
|
context 'with header' do
|
50
47
|
let(:options) { { header_size: 1 } }
|
51
48
|
let(:data_source) { [ [ 'Name' ], [ ] ]}
|
52
|
-
it { expect { subject.validate_data! }.to raise_error('undefined name on [1, 0]') }
|
49
|
+
it { expect { subject.validate_data! }.to raise_error('undefined :name on [1, 0]') }
|
53
50
|
end
|
54
51
|
end
|
55
52
|
|
@@ -60,11 +57,11 @@ module Csv2hash
|
|
60
57
|
allow(subject).to receive(:break_on_failure) { false }
|
61
58
|
end
|
62
59
|
|
63
|
-
it { expect(subject.parse.errors.to_csv).to eql ",\"undefined name on [0, 0]\"\n" }
|
60
|
+
it { expect(subject.parse.errors.to_csv).to eql ",\"undefined :name on [0, 0]\"\n" }
|
64
61
|
|
65
62
|
context 'errors should be filled' do
|
66
63
|
before { subject.parse }
|
67
|
-
its(:errors) { should eql [{x: 0, y: 0, message: 'undefined name on [0, 0]', key: 'name'}] }
|
64
|
+
its(:errors) { should eql [{x: 0, y: 0, message: 'undefined :name on [0, 0]', key: 'name'}] }
|
68
65
|
end
|
69
66
|
|
70
67
|
context 'original csv + errors should returned' do
|
@@ -72,13 +69,12 @@ module Csv2hash
|
|
72
69
|
Main.generate_definition :foo do
|
73
70
|
set_type { Definition::COLLECTION }
|
74
71
|
mapping { cell position: 0, key: 'agree', values: ['yes', 'no'] }
|
75
|
-
end.tap
|
76
|
-
definition.validate!
|
77
|
-
definition.default!
|
78
|
-
end
|
72
|
+
end.tap { |d| d.validate! ; d.default! }
|
79
73
|
end
|
80
74
|
let(:data_source) { [ [ 'what?' ], [ 'yes' ], [ 'no' ] ] }
|
81
|
-
it { expect(subject.parse.errors.to_csv).to eql
|
75
|
+
it { expect(subject.parse.errors.to_csv).to eql(
|
76
|
+
"what?,\"value <what?> not supported for :agree, please use one of <[\"\"yes\"\", \"\"no\"\"]>\"\n"
|
77
|
+
) }
|
82
78
|
end
|
83
79
|
end
|
84
80
|
|
@@ -25,7 +25,7 @@ module Csv2hash
|
|
25
25
|
|
26
26
|
context 'with invalid data' do
|
27
27
|
let(:data_source) { [ [ ] ]}
|
28
|
-
it { expect { subject.validate_data! }.to raise_error('undefined name on [0, 0]') }
|
28
|
+
it { expect { subject.validate_data! }.to raise_error('undefined :name on [0, 0]') }
|
29
29
|
end
|
30
30
|
|
31
31
|
context 'wihtout exception' do
|
@@ -35,11 +35,11 @@ module Csv2hash
|
|
35
35
|
allow(subject).to receive(:break_on_failure) { false }
|
36
36
|
end
|
37
37
|
|
38
|
-
it { expect(subject.parse.errors.to_csv).to eql ",\"undefined name on [0, 0]\"\n" }
|
38
|
+
it { expect(subject.parse.errors.to_csv).to eql ",\"undefined :name on [0, 0]\"\n" }
|
39
39
|
|
40
40
|
context 'errors should be filled' do
|
41
41
|
before { subject.parse }
|
42
|
-
its(:errors) { should eql [{x: 0, y: 0, message: 'undefined name on [0, 0]', key: 'name'}] }
|
42
|
+
its(:errors) { should eql [{x: 0, y: 0, message: 'undefined :name on [0, 0]', key: 'name'}] }
|
43
43
|
end
|
44
44
|
|
45
45
|
context 'original csv + errors should be returned' do
|
@@ -56,7 +56,7 @@ module Csv2hash
|
|
56
56
|
context 'string values' do
|
57
57
|
let(:data_source) { [ [ 'what?' ], [ 'yes', 'what?' ], [ 'yes', 'what?', 'no' ] ] }
|
58
58
|
it { expect(subject.parse.errors.to_csv).to eql(
|
59
|
-
"what?,\"
|
59
|
+
"what?,\"value <what?> not supported for :agree, please use one of <[\"\"yes\"\", \"\"no\"\"]>\"\n") }
|
60
60
|
end
|
61
61
|
context 'range values' do
|
62
62
|
let(:definition) do
|
@@ -70,7 +70,8 @@ module Csv2hash
|
|
70
70
|
end.tap { |d| d.validate! ; d.default! }
|
71
71
|
end
|
72
72
|
let(:data_source) { [ [ 12 ], [ 2, 12 ], [ 3, 12, 1 ] ] }
|
73
|
-
it { expect(subject.parse.errors.to_csv).to eql(
|
73
|
+
it { expect(subject.parse.errors.to_csv).to eql(
|
74
|
+
"12,\"value <12> not supported for :score, please use one of <1..10>\"\n") }
|
74
75
|
end
|
75
76
|
end
|
76
77
|
|
@@ -13,10 +13,10 @@ module Csv2hash
|
|
13
13
|
end
|
14
14
|
|
15
15
|
context 'string value' do
|
16
|
-
let(:rules) {{ foo: 'bar', message: ':foo are value of foo key' }}
|
16
|
+
let(:rules) {{ foo: 'bar', message: ':foo are value of foo key', key: 'bar' }}
|
17
17
|
|
18
18
|
it 'substitue value of key' do
|
19
|
-
expect(subject.send(:message, cell, nil, nil)).to eql 'bar are value of foo key'
|
19
|
+
expect(subject.send(:message, cell, nil, nil, 'foo')).to eql 'bar are value of foo key'
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
@@ -24,7 +24,7 @@ module Csv2hash
|
|
24
24
|
let(:rules) { { foo: ['bar', 'zone'], message: ':foo are values of foo key' } }
|
25
25
|
|
26
26
|
it 'substitue value of key' do
|
27
|
-
expect(subject.send(:message, cell, nil, nil)).to eql '["bar", "zone"] are values of foo key'
|
27
|
+
expect(subject.send(:message, cell, nil, nil, nil)).to eql '["bar", "zone"] are values of foo key'
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
@@ -32,7 +32,7 @@ module Csv2hash
|
|
32
32
|
let(:rules) { { message: 'value not found on :position' } }
|
33
33
|
|
34
34
|
it 'substitue value of key' do
|
35
|
-
expect(subject.send(:message, cell, 0, 2)).to eql 'value not found on [0, 2]'
|
35
|
+
expect(subject.send(:message, cell, 0, 2, nil)).to eql 'value not found on [0, 2]'
|
36
36
|
end
|
37
37
|
end
|
38
38
|
end
|
@@ -11,6 +11,7 @@ module Csv2hash
|
|
11
11
|
specify do
|
12
12
|
expect(subject.definition.name).to eql('example')
|
13
13
|
expect(subject.definition.header_size).to eql(2)
|
14
|
+
expect(subject.definition.cells.last.rules.fetch(:values)).to eql(18..90)
|
14
15
|
end
|
15
16
|
end
|
16
17
|
|
@@ -23,5 +24,16 @@ module Csv2hash
|
|
23
24
|
end
|
24
25
|
end
|
25
26
|
|
27
|
+
context 'extra validator' do
|
28
|
+
let(:config_file) { 'config/rules.extra_validator.erb.yml' }
|
29
|
+
|
30
|
+
specify do
|
31
|
+
expect(subject.definition.name).to eql('example')
|
32
|
+
expect(subject.definition.header_size).to eql(0)
|
33
|
+
expect(subject.definition.cells.first.rules.fetch(:extra_validator)).to be_a(DowncaseValidator)
|
34
|
+
expect(subject.definition.cells.last.rules.fetch(:position)[0][1]).to eql(/LastName/)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
26
38
|
end
|
27
39
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# require 'generators_helper'
|
2
|
+
#
|
3
|
+
# # Generators are not automatically loaded by Rails
|
4
|
+
# require 'generators/csv2hash/install_generator'
|
5
|
+
#
|
6
|
+
# describe Csv2hash::Generators::InstallGenerator do
|
7
|
+
# # Tell the generator where to put its output (what it thinks of as Rails.root)
|
8
|
+
# destination File.expand_path('../../../tmp', __FILE__)
|
9
|
+
# teardown :cleanup_destination_root
|
10
|
+
#
|
11
|
+
# before {
|
12
|
+
# prepare_destination
|
13
|
+
# }
|
14
|
+
#
|
15
|
+
# def cleanup_destination_root
|
16
|
+
# FileUtils.rm_rf destination_root
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# describe '...' do
|
20
|
+
# before { run_generator }
|
21
|
+
#
|
22
|
+
# describe 'config/initializers/csv2hash.rb' do
|
23
|
+
# subject { file('config/initializers/csv2hash.rb') }
|
24
|
+
# it { should exist }
|
25
|
+
# it { should contain "Csv2hash.configure do |config|"}
|
26
|
+
# # it { should contain "# conf.convert = false" }
|
27
|
+
# # it { should contain "# conf.true_values = ['yes','y','t']" }
|
28
|
+
# # it { should contain "# conf.false_values = ['no','n','f']" }
|
29
|
+
# # it { should contain "# conf.nil_values = ['nil','null']" }
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
# end
|
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.
|
4
|
+
version: 0.6.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joel AZEMAR
|
@@ -109,6 +109,7 @@ files:
|
|
109
109
|
- bin/load_rvm
|
110
110
|
- config/example.csv
|
111
111
|
- config/rules.erb.yml
|
112
|
+
- config/rules.extra_validator.erb.yml
|
112
113
|
- config/rules.yml
|
113
114
|
- coverage/.resultset.json.lock
|
114
115
|
- csv2hash.gemspec
|
@@ -118,6 +119,9 @@ files:
|
|
118
119
|
- lib/csv2hash/adapters/csv_adapter.rb
|
119
120
|
- lib/csv2hash/adapters/memory_adapter.rb
|
120
121
|
- lib/csv2hash/cell.rb
|
122
|
+
- lib/csv2hash/coercers/type_coercer.rb
|
123
|
+
- lib/csv2hash/coercers/yaml_coercer.rb
|
124
|
+
- lib/csv2hash/configuration.rb
|
121
125
|
- lib/csv2hash/csv_array.rb
|
122
126
|
- lib/csv2hash/data_wrapper.rb
|
123
127
|
- lib/csv2hash/definition.rb
|
@@ -128,6 +132,7 @@ files:
|
|
128
132
|
- lib/csv2hash/parser.rb
|
129
133
|
- lib/csv2hash/parser/collection.rb
|
130
134
|
- lib/csv2hash/parser/mapping.rb
|
135
|
+
- lib/csv2hash/railtie.rb
|
131
136
|
- lib/csv2hash/registry.rb
|
132
137
|
- lib/csv2hash/structure_validator.rb
|
133
138
|
- lib/csv2hash/structure_validator/deprecation.rb
|
@@ -138,16 +143,23 @@ files:
|
|
138
143
|
- lib/csv2hash/validator/mapping.rb
|
139
144
|
- lib/csv2hash/version.rb
|
140
145
|
- lib/csv2hash/yaml_loader.rb
|
146
|
+
- lib/generators/csv2hash/install/install_generator.rb
|
147
|
+
- lib/generators/csv2hash/install/templates/csh2hash.rb
|
148
|
+
- spec/csv2hash/coniguration_spec.rb
|
141
149
|
- spec/csv2hash/definition_spec.rb
|
142
150
|
- spec/csv2hash/parser/collection_spec.rb
|
143
151
|
- spec/csv2hash/parser/mapping_spec.rb
|
144
152
|
- spec/csv2hash/parser_spec.rb
|
145
153
|
- spec/csv2hash/structure_validator_spec.rb
|
154
|
+
- spec/csv2hash/type_coercer_spec.rb
|
146
155
|
- spec/csv2hash/validator/collection_spec.rb
|
147
156
|
- spec/csv2hash/validator/mapping_spec.rb
|
148
157
|
- spec/csv2hash/validator_spec.rb
|
149
158
|
- spec/csv2hash/yaml_loader_spec.rb
|
150
159
|
- spec/csv2hash_spec.rb
|
160
|
+
- spec/generators/csv2hash/install_generator_spec.rb
|
161
|
+
- spec/generators_helper.rb
|
162
|
+
- spec/lib/extra_validators/downcase_validator.rb
|
151
163
|
- spec/spec_helper.rb
|
152
164
|
homepage: https://github.com/joel/csv2hash
|
153
165
|
licenses:
|
@@ -174,15 +186,20 @@ signing_key:
|
|
174
186
|
specification_version: 4
|
175
187
|
summary: Mapping a CSV to a Ruby Hash
|
176
188
|
test_files:
|
189
|
+
- spec/csv2hash/coniguration_spec.rb
|
177
190
|
- spec/csv2hash/definition_spec.rb
|
178
191
|
- spec/csv2hash/parser/collection_spec.rb
|
179
192
|
- spec/csv2hash/parser/mapping_spec.rb
|
180
193
|
- spec/csv2hash/parser_spec.rb
|
181
194
|
- spec/csv2hash/structure_validator_spec.rb
|
195
|
+
- spec/csv2hash/type_coercer_spec.rb
|
182
196
|
- spec/csv2hash/validator/collection_spec.rb
|
183
197
|
- spec/csv2hash/validator/mapping_spec.rb
|
184
198
|
- spec/csv2hash/validator_spec.rb
|
185
199
|
- spec/csv2hash/yaml_loader_spec.rb
|
186
200
|
- spec/csv2hash_spec.rb
|
201
|
+
- spec/generators/csv2hash/install_generator_spec.rb
|
202
|
+
- spec/generators_helper.rb
|
203
|
+
- spec/lib/extra_validators/downcase_validator.rb
|
187
204
|
- spec/spec_helper.rb
|
188
205
|
has_rdoc:
|