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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/Gemfile +1 -0
  4. data/Gemfile.lock +1 -1
  5. data/README.md +60 -0
  6. data/UPGRADE.md +4 -0
  7. data/config/rules.erb.yml +2 -1
  8. data/config/rules.extra_validator.erb.yml +8 -0
  9. data/config/rules.yml +6 -2
  10. data/lib/csv2hash/cell.rb +10 -5
  11. data/lib/csv2hash/coercers/type_coercer.rb +25 -0
  12. data/lib/csv2hash/coercers/yaml_coercer.rb +43 -0
  13. data/lib/csv2hash/configuration.rb +17 -0
  14. data/lib/csv2hash/csv_array.rb +8 -6
  15. data/lib/csv2hash/definition.rb +1 -1
  16. data/lib/csv2hash/discover.rb +13 -7
  17. data/lib/csv2hash/railtie.rb +6 -0
  18. data/lib/csv2hash/registry.rb +11 -9
  19. data/lib/csv2hash/structure_validator.rb +1 -0
  20. data/lib/csv2hash/validator/collection.rb +1 -0
  21. data/lib/csv2hash/validator/mapping.rb +1 -0
  22. data/lib/csv2hash/validator.rb +39 -6
  23. data/lib/csv2hash/version.rb +1 -1
  24. data/lib/csv2hash.rb +25 -2
  25. data/lib/generators/csv2hash/install/install_generator.rb +15 -0
  26. data/lib/generators/csv2hash/install/templates/csh2hash.rb +6 -0
  27. data/spec/csv2hash/coniguration_spec.rb +18 -0
  28. data/spec/csv2hash/parser/mapping_spec.rb +36 -1
  29. data/spec/csv2hash/structure_validator_spec.rb +1 -4
  30. data/spec/csv2hash/type_coercer_spec.rb +16 -0
  31. data/spec/csv2hash/validator/collection_spec.rb +9 -13
  32. data/spec/csv2hash/validator/mapping_spec.rb +6 -5
  33. data/spec/csv2hash/validator_spec.rb +4 -4
  34. data/spec/csv2hash/yaml_loader_spec.rb +12 -0
  35. data/spec/generators/csv2hash/install_generator_spec.rb +32 -0
  36. data/spec/generators_helper.rb +10 -0
  37. data/spec/lib/extra_validators/downcase_validator.rb +9 -0
  38. metadata +18 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 67ed51815f96624700e2abe37d284061c776c386
4
- data.tar.gz: 2652b7a9c10c3470b257f9500f53aee4b0ebb4f4
3
+ metadata.gz: 1abbff1cb3d760b2d6f7c762a69cd6dbdcbbd2b3
4
+ data.tar.gz: aa458b6a73cc2de62b4245431f5adf987aaa7517
5
5
  SHA512:
6
- metadata.gz: bdf2f34865b2e4f88e57b314b2c6376225e5083589bb91774b534d71cace7e149fcf4097df9fad43a03f4c7ec2e427b91897df61c958912fffcd21129b3b7810
7
- data.tar.gz: 12cabfe3603c445771917d4832e53ca4c7cd09792be38d024f616c19edaab78a94622dcdc7146a4e36e877aebd4ca2e34a63ed3af0ca4bc3bb5e6666a74c8983
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
@@ -5,6 +5,7 @@ gemspec
5
5
  group :test do
6
6
  gem 'coveralls', require: false
7
7
  gem 'pry'
8
+ # gem 'rails'
8
9
  end
9
10
 
10
11
  group :doc do
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- csv2hash (0.6.4)
4
+ csv2hash (0.6.5)
5
5
  activesupport (~> 4.1)
6
6
 
7
7
  GEM
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
@@ -1,5 +1,9 @@
1
1
  # Upgrading
2
2
 
3
+ # Upgrading from 0.6.4 to 0.6.5
4
+
5
+ nothing
6
+
3
7
  # Upgrading from 0.6.3 to 0.6.4
4
8
 
5
9
  nothing
data/config/rules.erb.yml CHANGED
@@ -4,4 +4,5 @@ header_size: <%= 3 - 1 %>
4
4
  structure_rules: { max_columns: 20 }
5
5
  rules:
6
6
  - { position: [1,1], key: 'first_name' }
7
- - { position: [2,1], key: 'first_name' }
7
+ - { position: [2,1], key: 'last_name' }
8
+ - { position: [7,3], key: 'age', values: (18..90) }
@@ -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
- - { position: [1,1], key: 'first_name' }
7
- - { position: [2,1], key: 'first_name' }
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
- class Cell
3
+ require_relative 'coercers/yaml_coercer'
4
4
 
5
- attr_accessor :rules
5
+ module Csv2hash
6
+ class Cell
6
7
 
7
- def initialize *args
8
- self.rules = args.extract_options!
9
- end
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
@@ -1,12 +1,14 @@
1
1
  require 'csv'
2
- class CsvArray < Array
3
2
 
4
- def to_csv options = {}
5
- CSV.generate(options) do |csv|
6
- self.each do |element|
7
- csv << [element[:value], element[:message]]
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
@@ -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: ':key not supported, please use one of :values'
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
@@ -1,14 +1,20 @@
1
1
  module Csv2hash
2
2
  module Discover
3
3
 
4
- def find_positions!
5
- definition.cells.each do |cell|
6
- y, x = cell.rules.fetch :position
7
- if y.is_a?(Array)
8
- column, matcher = y
9
- y = data_source.index { |entries| entries[column] =~ matcher }
10
- cell.rules[:position] = [y, x]
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
 
@@ -0,0 +1,6 @@
1
+ require 'rails'
2
+
3
+ module Csv2hash
4
+ class Railtie < Rails::Railtie
5
+ end
6
+ end
@@ -1,13 +1,15 @@
1
- class Registry
2
- def initialize
3
- @definitions = Hash.new
4
- end
1
+ module Csv2hash
2
+ class Registry
3
+ def initialize
4
+ @definitions = Hash.new
5
+ end
5
6
 
6
- def [] name
7
- @definitions[name]
8
- end
7
+ def [] name
8
+ @definitions[name]
9
+ end
9
10
 
10
- def []= name, definition
11
- @definitions[name] = definition
11
+ def []= name, definition
12
+ @definitions[name] = definition
13
+ end
12
14
  end
13
15
  end
@@ -21,6 +21,7 @@ module Csv2hash
21
21
  raise if break_on_failure
22
22
  end
23
23
  end
24
+ nil
24
25
  end
25
26
 
26
27
  def rule_instance rule, options
@@ -11,6 +11,7 @@ module Csv2hash
11
11
  next if unexpected_line?(line, y)
12
12
  validate_rules y
13
13
  end
14
+ nil
14
15
  end
15
16
 
16
17
  protected
@@ -5,6 +5,7 @@ module Csv2hash
5
5
 
6
6
  def validate_data!
7
7
  validate_rules data_source
8
+ nil
8
9
  end
9
10
 
10
11
  protected
@@ -5,7 +5,7 @@ module Csv2hash
5
5
  include Discover
6
6
 
7
7
  def validate_rules y=nil
8
- find_positions!
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).tap do |msg|
47
- cell.rules.each { |key, value| msg.gsub!(":#{key.to_s}", value.to_s) unless key == :position }
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
- msg.gsub ':position', "[#{y}, #{x}]"
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
@@ -1,3 +1,3 @@
1
1
  module Csv2hash
2
- VERSION = '0.6.4'
2
+ VERSION = '0.6.5'
3
3
  end
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 Csv2hash::StructureValidator
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
- Csv2hash::DataWrapper.new.tap do |response|
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 do |definition|
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 do |definition|
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 do |definition|
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 "what?,\"agree not supported, please use one of [\"\"yes\"\", \"\"no\"\"]\"\n" }
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?,\"agree not supported, please use one of [\"\"yes\"\", \"\"no\"\"]\"\n") }
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("12,\"score not supported, please use one of 1..10\"\n") }
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
@@ -0,0 +1,10 @@
1
+ require 'csv2hash'
2
+ require 'bundler/setup'
3
+
4
+ # require 'rails'
5
+
6
+ module TestApp
7
+ class Application < ::Rails::Application
8
+ config.root = File.dirname(__FILE__)
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ require_relative '../../../lib/csv2hash/extra_validator'
2
+
3
+ module Csv2Hash
4
+ class DowncaseValidator < ExtraValidator
5
+ def valid? rule, value
6
+ !!(value.match /^[a-z]+$/)
7
+ end
8
+ end
9
+ 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
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: