csv2hash 0.6.4 → 0.6.5

Sign up to get free protection for your applications and to get access to all the features.
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: