csv_converter 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.rspec +3 -0
- data/.rubocop.yml +17 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.md +0 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +70 -0
- data/LICENSE +21 -0
- data/README.md +132 -0
- data/Rakefile +8 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/csv_converter.gemspec +39 -0
- data/lib/csv_converter.rb +84 -0
- data/lib/csv_converter/attribute_processor.rb +74 -0
- data/lib/csv_converter/converters/array_converter.rb +47 -0
- data/lib/csv_converter/converters/base_converter.rb +70 -0
- data/lib/csv_converter/converters/big_decimal_converter.rb +34 -0
- data/lib/csv_converter/converters/boolean_converter.rb +49 -0
- data/lib/csv_converter/converters/date_converter.rb +47 -0
- data/lib/csv_converter/converters/float_converter.rb +32 -0
- data/lib/csv_converter/converters/hash_converter.rb +63 -0
- data/lib/csv_converter/converters/integer_converter.rb +30 -0
- data/lib/csv_converter/converters/lowercase_converter.rb +32 -0
- data/lib/csv_converter/converters/string_converter.rb +32 -0
- data/lib/csv_converter/converters/uppercase_converter.rb +32 -0
- data/lib/csv_converter/entity_processor.rb +67 -0
- data/lib/csv_converter/file_processor.rb +58 -0
- data/lib/csv_converter/version.rb +5 -0
- metadata +174 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 20286f21df3a885fb2b08dffcf7eb1ab51c79adbd3962042b8ee7cfcec5ffdf7
|
4
|
+
data.tar.gz: b3d6d7ffa1b284f14d3122d5cbf6cb407f829befda0496996f6a950c13414a2c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b929f7e217e5aa0d8d4d31828a31b35102a520824b172dd27a80e0b98275d2eb4765d8e94e04bb617de172b7ee8840c9c474e171191ac42ad8aecbaafe1b5512
|
7
|
+
data.tar.gz: c1c5decb31c6fdae2086400ff514709b8201ccdf526ff06502d48c86b963e25d382f1cd18ed9e05b256accc61f86b48d1a9bc850e1e99f5fdb8eb0488c765a28
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
File without changes
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
csv_converter (0.1.0)
|
5
|
+
activesupport (~> 6.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
activesupport (6.0.0)
|
11
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
12
|
+
i18n (>= 0.7, < 2)
|
13
|
+
minitest (~> 5.1)
|
14
|
+
tzinfo (~> 1.1)
|
15
|
+
zeitwerk (~> 2.1, >= 2.1.8)
|
16
|
+
ast (2.4.0)
|
17
|
+
byebug (11.0.1)
|
18
|
+
concurrent-ruby (1.1.5)
|
19
|
+
diff-lcs (1.3)
|
20
|
+
i18n (1.7.0)
|
21
|
+
concurrent-ruby (~> 1.0)
|
22
|
+
jaro_winkler (1.5.3)
|
23
|
+
minitest (5.12.2)
|
24
|
+
parallel (1.18.0)
|
25
|
+
parser (2.6.5.0)
|
26
|
+
ast (~> 2.4.0)
|
27
|
+
rainbow (3.0.0)
|
28
|
+
rake (10.5.0)
|
29
|
+
rb-readline (0.5.5)
|
30
|
+
rspec (3.9.0)
|
31
|
+
rspec-core (~> 3.9.0)
|
32
|
+
rspec-expectations (~> 3.9.0)
|
33
|
+
rspec-mocks (~> 3.9.0)
|
34
|
+
rspec-core (3.9.0)
|
35
|
+
rspec-support (~> 3.9.0)
|
36
|
+
rspec-expectations (3.9.0)
|
37
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
38
|
+
rspec-support (~> 3.9.0)
|
39
|
+
rspec-mocks (3.9.0)
|
40
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
41
|
+
rspec-support (~> 3.9.0)
|
42
|
+
rspec-support (3.9.0)
|
43
|
+
rubocop (0.75.1)
|
44
|
+
jaro_winkler (~> 1.5.1)
|
45
|
+
parallel (~> 1.10)
|
46
|
+
parser (>= 2.6)
|
47
|
+
rainbow (>= 2.2.2, < 4.0)
|
48
|
+
ruby-progressbar (~> 1.7)
|
49
|
+
unicode-display_width (>= 1.4.0, < 1.7)
|
50
|
+
ruby-progressbar (1.10.1)
|
51
|
+
thread_safe (0.3.6)
|
52
|
+
tzinfo (1.2.5)
|
53
|
+
thread_safe (~> 0.1)
|
54
|
+
unicode-display_width (1.6.0)
|
55
|
+
zeitwerk (2.2.0)
|
56
|
+
|
57
|
+
PLATFORMS
|
58
|
+
ruby
|
59
|
+
|
60
|
+
DEPENDENCIES
|
61
|
+
bundler (~> 2.0)
|
62
|
+
byebug (~> 11.0)
|
63
|
+
csv_converter!
|
64
|
+
rake (~> 10.0)
|
65
|
+
rb-readline (~> 0.5)
|
66
|
+
rspec (~> 3.0)
|
67
|
+
rubocop (~> 0.75)
|
68
|
+
|
69
|
+
BUNDLED WITH
|
70
|
+
2.0.2
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) [2019] [Jose Francisco Rojas Soto]
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
# csv_converter [](https://travis-ci.org/francisco-rojas/basic_ruby)
|
2
|
+
|
3
|
+
*csv_converter* is a library for facilitating the grouping and transformation of tabulated data contained in files such as csv or spreadsheets files. This is **not** a library for parsing files. There are already plenty of libraries out there for reading and parsing files in different formats.
|
4
|
+
|
5
|
+
Instead, this library focuses on the conversion of the data provided in the files. Often times, it is required to cast the text data into a ruby object, perform validations on that data, and map it to the corresponding db tables/models and columns/attributes. This library aims to simplify that process.
|
6
|
+
|
7
|
+
For example, given the following csv content:
|
8
|
+
|
9
|
+
```
|
10
|
+
First Name,Last Name,Make,Model,Year,Color,Purchase Date
|
11
|
+
John,Smith,Ford,Mustang,2000,Black,25/01/99
|
12
|
+
Julian,Moore,Toyota,Yaris,2005,Red,13/04/05
|
13
|
+
Joe,Black,Volvo,V40,2015,Gold,03/02/16
|
14
|
+
```
|
15
|
+
|
16
|
+
| First Name | Last Name | Make | Model | Year | Color | Purchase Date |
|
17
|
+
| ----------- | --------- | ------- | ------- | ---- | ----- | ------------- |
|
18
|
+
| John | Smith | Ford | Mustang | 2000 | Black | 25/01/99 |
|
19
|
+
| Julian | Moore | Toyota | Yaris | 2005 | Red | 13/04/05 |
|
20
|
+
| Joe | Black | Volvo | V40 | 2015 | Gold | 03/02/16 |
|
21
|
+
|
22
|
+
|
23
|
+
you might want something like this:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
[
|
27
|
+
{
|
28
|
+
"owner" => {
|
29
|
+
"first_name" => "John",
|
30
|
+
"last_name" => "Smith"
|
31
|
+
},
|
32
|
+
"vehicle" => {
|
33
|
+
"make" => "Ford",
|
34
|
+
"model" => "Mustang",
|
35
|
+
"year" => 2000,
|
36
|
+
"color" => "Black",
|
37
|
+
"purchase_date" => #<Date: 1999-01-25 ((2451204j,0s,0n),+0s,2299161j)>
|
38
|
+
}
|
39
|
+
},
|
40
|
+
{
|
41
|
+
"owner" => {
|
42
|
+
"first_name" => "Julian",
|
43
|
+
"last_name" => "Moore"
|
44
|
+
},
|
45
|
+
"vehicle" => {
|
46
|
+
"make" => "Toyota",
|
47
|
+
"model" => "Yaris",
|
48
|
+
"year" => 2005,
|
49
|
+
"color" => "Red",
|
50
|
+
"purchase_date" => #<Date: 2005-04-15 ((2453476j,0s,0n),+0s,2299161j)>
|
51
|
+
}
|
52
|
+
},
|
53
|
+
{
|
54
|
+
"owner" => {
|
55
|
+
"first_name" => "Joe",
|
56
|
+
"last_name" => "Black"
|
57
|
+
},
|
58
|
+
"vehicle" => {
|
59
|
+
"make" => "Volvo",
|
60
|
+
"model" => "V40",
|
61
|
+
"year" => 2015,
|
62
|
+
"color" => "Gold",
|
63
|
+
"purchase_date" => #<Date: 2016-03-02 ((2457450j,0s,0n),+0s,2299161j)>
|
64
|
+
}
|
65
|
+
}
|
66
|
+
]
|
67
|
+
```
|
68
|
+
|
69
|
+
In this example, each column from the csv has been grouped according to the configuration provided. Also, the data for each column has been converted to the expected data type.
|
70
|
+
|
71
|
+
This is performed by *csv_converter* based on the configuration provided in a .yml file (or a ruby hash) that lookes like this:
|
72
|
+
|
73
|
+
```
|
74
|
+
owner:
|
75
|
+
first_name:
|
76
|
+
header: First Name
|
77
|
+
last_name:
|
78
|
+
header: Last Name
|
79
|
+
vehicle:
|
80
|
+
make:
|
81
|
+
header: Make
|
82
|
+
model:
|
83
|
+
header: Model
|
84
|
+
year:
|
85
|
+
header: Year
|
86
|
+
converters:
|
87
|
+
integer:
|
88
|
+
color:
|
89
|
+
header: Color
|
90
|
+
purchase_date:
|
91
|
+
header: Purchase Date
|
92
|
+
converters:
|
93
|
+
date:
|
94
|
+
date_format: "%m/%d/%y"
|
95
|
+
```
|
96
|
+
|
97
|
+
## Usage
|
98
|
+
|
99
|
+
*csv_converter* supports more advanced data conversions, processing data based on column position instead of headers and nested records within a column.
|
100
|
+
|
101
|
+
Please refer to the [*wiki page*](https://github.com/francisco-rojas/csv_converter/wiki) for further instructions and more advanced examples.
|
102
|
+
|
103
|
+
## Installation
|
104
|
+
|
105
|
+
Add this line to your application's Gemfile:
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
gem 'csv_converter'
|
109
|
+
```
|
110
|
+
|
111
|
+
And then execute:
|
112
|
+
|
113
|
+
$ bundle
|
114
|
+
|
115
|
+
Or install it yourself as:
|
116
|
+
|
117
|
+
$ gem install csv_converter
|
118
|
+
|
119
|
+
## Development
|
120
|
+
|
121
|
+
Currently, the library is stable. I believe it supports the most common use cases so most likely the code won't be updated very frequently. However, if you have any feature requests or find a bug feel free to open a github issue and I will reply as soon as I can.
|
122
|
+
|
123
|
+
- for **feature requests** please use the **enhancement** label.
|
124
|
+
- for **bugs** please use the **bugs** label.
|
125
|
+
|
126
|
+
## Contributing
|
127
|
+
|
128
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/francisco-rojas/csv_converter
|
129
|
+
|
130
|
+
## License
|
131
|
+
|
132
|
+
MIT License. Copyright 2019 Francisco Rojas. https://github.com/francisco-rojas
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'csv_converter'
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
+
# require "pry"
|
12
|
+
# Pry.start
|
13
|
+
|
14
|
+
require 'irb'
|
15
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'csv_converter/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'csv_converter'
|
9
|
+
spec.version = CSVConverter::VERSION
|
10
|
+
spec.authors = ['Francisco Rojas']
|
11
|
+
spec.email = ['josefcorojas@gmail.com']
|
12
|
+
|
13
|
+
spec.summary = 'Groups and converts tabulated data'
|
14
|
+
spec.description = 'Groups and converts tabulated data based on the mappings provided'
|
15
|
+
spec.homepage = 'https://github.com/francisco-rojas/csv_converter'
|
16
|
+
spec.license = 'MIT'
|
17
|
+
|
18
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
19
|
+
spec.metadata['wiki_uri'] = 'https://github.com/francisco-rojas/csv_converter/wiki'
|
20
|
+
spec.metadata['source_code_uri'] = 'https://github.com/francisco-rojas/csv_converter'
|
21
|
+
spec.metadata['changelog_uri'] = 'https://github.com/francisco-rojas/csv_converter/CHANGELOG.md'
|
22
|
+
|
23
|
+
# Specify which files should be added to the gem when it is released.
|
24
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
25
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
26
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
27
|
+
end
|
28
|
+
spec.bindir = 'exe'
|
29
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
30
|
+
spec.require_paths = ['lib']
|
31
|
+
|
32
|
+
spec.add_dependency 'activesupport', '~> 6.0'
|
33
|
+
spec.add_development_dependency 'bundler', '~> 2.0'
|
34
|
+
spec.add_development_dependency 'byebug', '~> 11.0'
|
35
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
36
|
+
spec.add_development_dependency 'rb-readline', '~> 0.5'
|
37
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
38
|
+
spec.add_development_dependency 'rubocop', '~> 0.75'
|
39
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
require 'active_support'
|
6
|
+
require 'active_support/core_ext/object/blank'
|
7
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
8
|
+
require 'active_support/inflector'
|
9
|
+
|
10
|
+
require 'csv_converter/version'
|
11
|
+
require 'csv_converter/converters/base_converter'
|
12
|
+
require 'csv_converter/converters/array_converter'
|
13
|
+
require 'csv_converter/converters/hash_converter'
|
14
|
+
require 'csv_converter/converters/big_decimal_converter'
|
15
|
+
require 'csv_converter/converters/boolean_converter'
|
16
|
+
require 'csv_converter/converters/date_converter'
|
17
|
+
require 'csv_converter/converters/float_converter'
|
18
|
+
require 'csv_converter/converters/integer_converter'
|
19
|
+
require 'csv_converter/converters/string_converter'
|
20
|
+
require 'csv_converter/converters/uppercase_converter'
|
21
|
+
require 'csv_converter/converters/lowercase_converter'
|
22
|
+
require 'csv_converter/file_processor'
|
23
|
+
require 'csv_converter/entity_processor'
|
24
|
+
require 'csv_converter/attribute_processor'
|
25
|
+
|
26
|
+
# CSVConverter groups and transforms tabulated data contained in files such as csv or spreadsheets.
|
27
|
+
module CSVConverter
|
28
|
+
# Error holds error messages as well as details of the data being processed.
|
29
|
+
class Error < StandardError
|
30
|
+
# Details of the data being processed when the error happened. By default this includes:
|
31
|
+
# filename: the name of the file being processed
|
32
|
+
# row_num: number of the row being processed
|
33
|
+
# entity: the name of the entity being processed as provided in the mappings
|
34
|
+
# row: the raw data of the row being processed
|
35
|
+
# attr: the name of the attribute being processed as provided in the mappings
|
36
|
+
# Additionally it contains all the options provided to the converter in the mappings.
|
37
|
+
# @return [Hash]
|
38
|
+
attr_reader :details
|
39
|
+
|
40
|
+
# A new instance of Error.
|
41
|
+
# @param message [String] the error description
|
42
|
+
# @param details [Hash] info about the data being proccesed at the time of the error
|
43
|
+
def initialize(message, details = {})
|
44
|
+
@details = details
|
45
|
+
super("#{message} on: #{details}")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
ALIASES = {
|
50
|
+
array: 'CSVConverter::Converters::ArrayConverter',
|
51
|
+
boolean: 'CSVConverter::Converters::BooleanConverter',
|
52
|
+
date: 'CSVConverter::Converters::DateConverter',
|
53
|
+
decimal: 'CSVConverter::Converters::BigDecimalConverter',
|
54
|
+
float: 'CSVConverter::Converters::FloatConverter',
|
55
|
+
hash: 'CSVConverter::Converters::HashConverter',
|
56
|
+
integer: 'CSVConverter::Converters::IntegerConverter',
|
57
|
+
lowercase: 'CSVConverter::Converters::LowercaseConverter',
|
58
|
+
string: 'CSVConverter::Converters::StringConverter',
|
59
|
+
uppercase: 'CSVConverter::Converters::UppercaseConverter'
|
60
|
+
}.with_indifferent_access
|
61
|
+
|
62
|
+
# When no custom aliases are included it returns CSVConverter::ALIASES.
|
63
|
+
# When custom converter alises are included it returns the whole list of aliases.
|
64
|
+
# @return [Hash] list of aliases for each converter class.
|
65
|
+
def self.aliases
|
66
|
+
@aliases || ALIASES
|
67
|
+
end
|
68
|
+
|
69
|
+
# Adds an alias to the list of aliases
|
70
|
+
# @param new_alias [Symbol, String] the name of the alias
|
71
|
+
# @param klass [Symbol, String] class name of the converter
|
72
|
+
# @return (@see #aliases)
|
73
|
+
def self.add_alias(new_alias, klass)
|
74
|
+
@aliases = aliases.merge(new_alias.to_sym => klass.to_s)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Adds one or more alieases to the list of aliases
|
78
|
+
# @param new_aliases [Hash] list of aliases to append to the list,
|
79
|
+
# where the key is the name of the alias and the value is the class name of the converter
|
80
|
+
# @return (@see #aliases)
|
81
|
+
def self.add_aliases(new_aliases)
|
82
|
+
@aliases = aliases.merge(new_aliases)
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CSVConverter
|
4
|
+
# Extracts the value for a column from the csv row and applies conversions to it.
|
5
|
+
class AttributeProcessor
|
6
|
+
# The row being processed.
|
7
|
+
# @return [Array, Hash]
|
8
|
+
attr_reader :row
|
9
|
+
|
10
|
+
# The column being processed.
|
11
|
+
# @return [String]
|
12
|
+
attr_reader :data
|
13
|
+
|
14
|
+
# Attribute mappings.
|
15
|
+
# @return [Hash] configuration used to process the data.
|
16
|
+
attr_reader :attr_mappings
|
17
|
+
|
18
|
+
# Details of the data being processed. By default this includes:
|
19
|
+
# filename: the name of the file being processed.
|
20
|
+
# row_num: number of the row being processed.
|
21
|
+
# entity: the name of the entity being processed as provided in the mappings.
|
22
|
+
# row: the raw data of the row being processed.
|
23
|
+
# attr: the name of the attribute being processed as provided in the mappings.
|
24
|
+
# Additionally it will contain all the options provided to the converter in the mappings.
|
25
|
+
# @return [Hash]
|
26
|
+
attr_reader :options
|
27
|
+
|
28
|
+
# A new instance of AttributeProcessor.
|
29
|
+
# @param row (@see #row)
|
30
|
+
# @param attr_mappings (@see #attr_mappings)
|
31
|
+
# @param options (@see #options)
|
32
|
+
def initialize(row, attr_mappings, options = {})
|
33
|
+
@row = row
|
34
|
+
@attr_mappings = attr_mappings
|
35
|
+
@options = options
|
36
|
+
@data = row[attr_mappings[:header]]
|
37
|
+
end
|
38
|
+
|
39
|
+
# Converts the data of the attribute into the type provided in the mappings by invoking the converters.
|
40
|
+
# @return [Object] an object of the expected type.
|
41
|
+
# If an error occurs during conversion the nullable value for the attributes is returned.
|
42
|
+
def call
|
43
|
+
convert_attr(&:call)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Converts the data of the attribute into the type provided in the mappings by invoking the converters.
|
47
|
+
# @return [Object] an object of the expected type.
|
48
|
+
# If an error occurs during conversion an error is raised.
|
49
|
+
def call!
|
50
|
+
convert_attr(&:call!)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def convert_attr(&block)
|
56
|
+
return data if attr_mappings.dig(:converters).blank?
|
57
|
+
|
58
|
+
attr_mappings.dig(:converters).inject(data) do |d, (converter, opts)|
|
59
|
+
opts = (opts || {}).merge(options)
|
60
|
+
invoke_converter(converter, d, opts, &block)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def invoke_converter(converter, data, opts)
|
65
|
+
return converter.call(data, opts) if converter.is_a?(Proc)
|
66
|
+
|
67
|
+
converter = converter.to_s
|
68
|
+
converter = CSVConverter::ALIASES[converter] if CSVConverter::ALIASES.keys.include?(converter)
|
69
|
+
converter = converter.constantize.new(data, opts)
|
70
|
+
|
71
|
+
yield(converter)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CSVConverter
|
4
|
+
module Converters
|
5
|
+
# Converts a string separated by a given char into an array of strings.
|
6
|
+
class ArrayConverter < BaseConverter
|
7
|
+
# A new instance of ArrayConverter.
|
8
|
+
# @param raw_data [String] the raw data of the attribute being processed.
|
9
|
+
# @param options [Hash] the options for the converter provided in the mappings.
|
10
|
+
# Additionally, contains the details of the data being processed. See BaseConverter#option.
|
11
|
+
# The *separator* key is required. If *separator* is nil then an error is raised.
|
12
|
+
def initialize(raw_data, options = { separator: ',' })
|
13
|
+
super(raw_data, options)
|
14
|
+
|
15
|
+
validate_options
|
16
|
+
end
|
17
|
+
|
18
|
+
# Converts *data* into an array by splitting the string on the *separator* provided in the mappings.
|
19
|
+
# @return [Array] if an error occurs during conversion an empty array is returned.
|
20
|
+
def call
|
21
|
+
call!
|
22
|
+
rescue CSVConverter::Error
|
23
|
+
nullable_object
|
24
|
+
end
|
25
|
+
|
26
|
+
# Converts *data* into an array by splitting the string on the *separator* provided in the mappings.
|
27
|
+
# @return [Array] if an error occurs during conversion an error is raised.
|
28
|
+
def call!
|
29
|
+
data.split(options[:separator]).map(&:strip)
|
30
|
+
rescue StandardError => e
|
31
|
+
raise CSVConverter::Error.new(e.message, options)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def validate_options
|
37
|
+
return if options && !options[:separator].nil?
|
38
|
+
|
39
|
+
raise CSVConverter::Error.new('no `key_value_separator` provided', options)
|
40
|
+
end
|
41
|
+
|
42
|
+
def nullable_object
|
43
|
+
[]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CSVConverter
|
4
|
+
module Converters
|
5
|
+
# Defines the interface that all converters must implement
|
6
|
+
class BaseConverter
|
7
|
+
# @return [String] the raw data of the attribute being processed.
|
8
|
+
attr_reader :raw_data
|
9
|
+
# Details of the data being processed. By default this includes:
|
10
|
+
# filename: the name of the file being processed
|
11
|
+
# row_num: number of the row being processed
|
12
|
+
# entity: the name of the entity being processed as provided in the mappings
|
13
|
+
# row: the raw data of the row being processed
|
14
|
+
# attr: the name of the attribute being processed as provided in the mappings
|
15
|
+
# Additionally it contains all the options provided to the converter in the mappings.
|
16
|
+
# @return [Hash]
|
17
|
+
attr_reader :options
|
18
|
+
|
19
|
+
# A new instance of BaseConverter.
|
20
|
+
# @param raw_data [String] the raw data of the attribute being processed.
|
21
|
+
# @param options [Hash] the options for the converter provided in the mappings.
|
22
|
+
# Additionally, contains the details of the data being processed.
|
23
|
+
def initialize(raw_data, options = {})
|
24
|
+
@raw_data = raw_data.to_s.strip
|
25
|
+
@options = options || {}
|
26
|
+
end
|
27
|
+
|
28
|
+
# Converts raw_data into the type specified in the mappings.
|
29
|
+
# Must be implemented by children
|
30
|
+
def call
|
31
|
+
raise NotImplementedError
|
32
|
+
end
|
33
|
+
|
34
|
+
# Converts raw_data into the type specified in the mappings.
|
35
|
+
# Must be implemented by children
|
36
|
+
def call!
|
37
|
+
raise NotImplementedError
|
38
|
+
end
|
39
|
+
|
40
|
+
# Evaluates raw_data and returns the proper value for it.
|
41
|
+
# @return [String]
|
42
|
+
# If raw_data is not empty returns raw_data.
|
43
|
+
# If raw_data is empty and no default value is provided in the mappings returns the nullable object.
|
44
|
+
# If raw_data is empty and a default value is provided in the mappings returns the default value.
|
45
|
+
def data
|
46
|
+
@data ||= begin
|
47
|
+
return raw_data if raw_data.present? && !empty_value?
|
48
|
+
|
49
|
+
return nullable_object if options.dig(:default).blank?
|
50
|
+
|
51
|
+
options.dig(:default)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Checks if raw_data is contained in the list of *empty_values* provided in the mapping.
|
56
|
+
# @return [Boolean]
|
57
|
+
def empty_value?
|
58
|
+
return false unless options.dig(:empty_values)
|
59
|
+
|
60
|
+
options.dig(:empty_values).include?(raw_data)
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def nullable_object
|
66
|
+
raise NotImplementedError
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bigdecimal'
|
4
|
+
|
5
|
+
module CSVConverter
|
6
|
+
module Converters
|
7
|
+
# Converts a string into a decimal number
|
8
|
+
class BigDecimalConverter < BaseConverter
|
9
|
+
# Converts *data* into a BigDecimal.
|
10
|
+
# If the decimal separator is a comma it is replaced by a period before parsing.
|
11
|
+
# @return [BigDecimal] if an error occurs during conversion nil is returned.
|
12
|
+
def call
|
13
|
+
call!
|
14
|
+
rescue CSVConverter::Error
|
15
|
+
nullable_object
|
16
|
+
end
|
17
|
+
|
18
|
+
# Converts *data* into a BigDecimal.
|
19
|
+
# If the decimal separator is a comma it is replaced by a period before parsing.
|
20
|
+
# @return [BigDecimal] if an error occurs during conversion an error is raised.
|
21
|
+
def call!
|
22
|
+
BigDecimal(data.sub(',', '.'))
|
23
|
+
rescue StandardError => e
|
24
|
+
raise CSVConverter::Error.new(e.message, options)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def nullable_object
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CSVConverter
|
4
|
+
module Converters
|
5
|
+
# Converts a string into a boolean
|
6
|
+
class BooleanConverter < BaseConverter
|
7
|
+
# A new instance of BooleanConverter.
|
8
|
+
# @param raw_data [String] the raw data of the attribute being processed.
|
9
|
+
# @param options [Hash] the options for the converter provided in the mappings.
|
10
|
+
# Additionally, contains the details of the data being processed. See BaseConverter#option.
|
11
|
+
# The *truthy_values* key is required. If *truthy_values* is blank then an error is raised.
|
12
|
+
def initialize(raw_data, options = { truthy_values: %w[true false] })
|
13
|
+
super(raw_data, options)
|
14
|
+
|
15
|
+
validate_options
|
16
|
+
end
|
17
|
+
|
18
|
+
# Converts *data* into a Boolean by checking if *data*
|
19
|
+
# is contained in the list of *truthy_values* provided in the mappings.
|
20
|
+
# @return [Boolean] if an error occurs during conversion `false` is returned.
|
21
|
+
def call
|
22
|
+
call!
|
23
|
+
rescue CSVConverter::Error
|
24
|
+
nullable_object
|
25
|
+
end
|
26
|
+
|
27
|
+
# Converts *data* into a Boolean by checking if *data*
|
28
|
+
# is contained in the list of *truthy_values* provided in the mappings.
|
29
|
+
# @return [Boolean] if an error occurs during conversion an error is raised.
|
30
|
+
def call!
|
31
|
+
options[:truthy_values].include?(data)
|
32
|
+
rescue StandardError => e
|
33
|
+
raise CSVConverter::Error.new(e.message, options)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def validate_options
|
39
|
+
return if options && options[:truthy_values].present?
|
40
|
+
|
41
|
+
raise CSVConverter::Error.new('no `truthy_values` provided', options)
|
42
|
+
end
|
43
|
+
|
44
|
+
def nullable_object
|
45
|
+
false
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CSVConverter
|
4
|
+
module Converters
|
5
|
+
# Converts a string into a date
|
6
|
+
class DateConverter < BaseConverter
|
7
|
+
# A new instance of DateConverter.
|
8
|
+
# @param raw_data [String] the raw data of the attribute being processed.
|
9
|
+
# @param options [Hash] the options for the converter provided in the mappings.
|
10
|
+
# Additionally, contains the details of the data being processed. See BaseConverter#option.
|
11
|
+
# The *date_format* key is required. If *date_format* is blank then an error is raised.
|
12
|
+
def initialize(raw_data, options = { date_format: '%m/%d/%y' })
|
13
|
+
super(raw_data, options)
|
14
|
+
|
15
|
+
validate_options
|
16
|
+
end
|
17
|
+
|
18
|
+
# Converts *data* into a Date using the format provided in the mappings.
|
19
|
+
# @return [Date] if an error occurs during conversion `nil` is returned.
|
20
|
+
def call
|
21
|
+
call!
|
22
|
+
rescue CSVConverter::Error
|
23
|
+
nullable_object
|
24
|
+
end
|
25
|
+
|
26
|
+
# Converts *data* into a Date using the format provided in the mappings.
|
27
|
+
# @return [Date] if an error occurs during conversion an error is raised.
|
28
|
+
def call!
|
29
|
+
Date.strptime(data, options[:date_format])
|
30
|
+
rescue StandardError => e
|
31
|
+
raise CSVConverter::Error.new(e.message, options)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def validate_options
|
37
|
+
return if options && options[:date_format].present?
|
38
|
+
|
39
|
+
raise CSVConverter::Error.new('no `date_format` provided', options)
|
40
|
+
end
|
41
|
+
|
42
|
+
def nullable_object
|
43
|
+
nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CSVConverter
|
4
|
+
module Converters
|
5
|
+
# Converts a string into a float
|
6
|
+
class FloatConverter < BaseConverter
|
7
|
+
# Converts *data* into a Float.
|
8
|
+
# If the decimal separator is a comma it is replaced by a period before parsing.
|
9
|
+
# @return [Float] if an error occurs during conversion nil is returned.
|
10
|
+
def call
|
11
|
+
call!
|
12
|
+
rescue CSVConverter::Error
|
13
|
+
nullable_object
|
14
|
+
end
|
15
|
+
|
16
|
+
# Converts *data* into a Float.
|
17
|
+
# If the decimal separator is a comma it is replaced by a period before parsing.
|
18
|
+
# @return [Float] if an error occurs during conversion an error is raised.
|
19
|
+
def call!
|
20
|
+
Float(data.sub(',', '.'))
|
21
|
+
rescue StandardError => e
|
22
|
+
raise CSVConverter::Error.new(e.message, options)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def nullable_object
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CSVConverter
|
4
|
+
module Converters
|
5
|
+
# Converts a string with key pair values into ruby hashes
|
6
|
+
class HashConverter < BaseConverter
|
7
|
+
# A new instance of HashConverter.
|
8
|
+
# @param raw_data [String] the raw data of the attribute being processed.
|
9
|
+
# @param options [Hash] the options for the converter provided in the mappings.
|
10
|
+
# Additionally, contains the details of the data being processed. See BaseConverter#option.
|
11
|
+
# The *item_separator* key is required. If *item_separator* is nil then an error is raised.
|
12
|
+
# The *key_value_separator* key is required. If *key_value_separator* is nil then an error is raised.
|
13
|
+
def initialize(raw_data, options = {})
|
14
|
+
super(raw_data, options)
|
15
|
+
|
16
|
+
validate_options
|
17
|
+
end
|
18
|
+
|
19
|
+
# Converts *data* into a hash by splitting the string on the *item_separator* to get the items
|
20
|
+
# and then by spliting the items on *key_value_separator* to get the key/value.
|
21
|
+
# @return [Hash] if an error occurs during conversion an empty hash is returned.
|
22
|
+
def call
|
23
|
+
call!
|
24
|
+
rescue CSVConverter::Error
|
25
|
+
nullable_object
|
26
|
+
end
|
27
|
+
|
28
|
+
# Converts *data* into a hash by splitting the string on the *item_separator* to get the items
|
29
|
+
# and then by spliting the items on *key_value_separator* to get the key/value.
|
30
|
+
# @return [Hash] if an error occurs during conversion an error is raised.
|
31
|
+
def call!
|
32
|
+
data.split(options[:item_separator]).map do |items|
|
33
|
+
items.split(options[:key_value_separator]).map(&:strip)
|
34
|
+
end.to_h
|
35
|
+
rescue StandardError => e
|
36
|
+
raise CSVConverter::Error.new(e.message, options)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def validate_options
|
42
|
+
validate_separator
|
43
|
+
validate_key_value_separator
|
44
|
+
end
|
45
|
+
|
46
|
+
def validate_separator
|
47
|
+
return unless options[:item_separator].nil?
|
48
|
+
|
49
|
+
raise CSVConverter::Error.new('no `item_separator` provided', options)
|
50
|
+
end
|
51
|
+
|
52
|
+
def validate_key_value_separator
|
53
|
+
return unless options[:key_value_separator].nil?
|
54
|
+
|
55
|
+
raise CSVConverter::Error.new('no `key_value_separator` provided', options)
|
56
|
+
end
|
57
|
+
|
58
|
+
def nullable_object
|
59
|
+
{}
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CSVConverter
|
4
|
+
module Converters
|
5
|
+
# Converts a string into an integer
|
6
|
+
class IntegerConverter < BaseConverter
|
7
|
+
# Converts *data* into an Integer.
|
8
|
+
# @return [Integer] if an error occurs during conversion nil is returned.
|
9
|
+
def call
|
10
|
+
call!
|
11
|
+
rescue CSVConverter::Error
|
12
|
+
nullable_object
|
13
|
+
end
|
14
|
+
|
15
|
+
# Converts *data* into an Integer.
|
16
|
+
# @return [Integer] if an error occurs during conversion an error is raised.
|
17
|
+
def call!
|
18
|
+
Integer(data)
|
19
|
+
rescue StandardError => e
|
20
|
+
raise CSVConverter::Error.new(e.message, options)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def nullable_object
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CSVConverter
|
4
|
+
module Converters
|
5
|
+
# Converts a string to lowercase
|
6
|
+
class LowercaseConverter < BaseConverter
|
7
|
+
# Converts a string to lowercase
|
8
|
+
# @return [String] if *data* is empty returns an empty string.
|
9
|
+
def call
|
10
|
+
call!
|
11
|
+
rescue CSVConverter::Error
|
12
|
+
nullable_object
|
13
|
+
end
|
14
|
+
|
15
|
+
# Converts a string to lowercase
|
16
|
+
# @return [String] if *data* is empty an error is raised.
|
17
|
+
def call!
|
18
|
+
raise ArgumentError, 'no data provided' if data.blank?
|
19
|
+
|
20
|
+
data.downcase
|
21
|
+
rescue StandardError => e
|
22
|
+
raise CSVConverter::Error.new(e.message, options)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def nullable_object
|
28
|
+
''
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CSVConverter
|
4
|
+
module Converters
|
5
|
+
# Cleans up a string
|
6
|
+
class StringConverter < BaseConverter
|
7
|
+
# Cleans up the *data* string.
|
8
|
+
# @return [String] if *data* is empty returns an empty string.
|
9
|
+
def call
|
10
|
+
call!
|
11
|
+
rescue CSVConverter::Error
|
12
|
+
nullable_object
|
13
|
+
end
|
14
|
+
|
15
|
+
# Cleans up the *data* string.
|
16
|
+
# @return [String] if *data* is empty an error is raised.
|
17
|
+
def call!
|
18
|
+
raise ArgumentError, 'no data provided' if data.blank?
|
19
|
+
|
20
|
+
data
|
21
|
+
rescue StandardError => e
|
22
|
+
raise CSVConverter::Error.new(e.message, options)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def nullable_object
|
28
|
+
''
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CSVConverter
|
4
|
+
module Converters
|
5
|
+
# Converts a string to uppercase
|
6
|
+
class UppercaseConverter < BaseConverter
|
7
|
+
# Converts a string to uppercase
|
8
|
+
# @return [String] if *data* is empty returns an empty string.
|
9
|
+
def call
|
10
|
+
call!
|
11
|
+
rescue CSVConverter::Error
|
12
|
+
nullable_object
|
13
|
+
end
|
14
|
+
|
15
|
+
# Converts a string to uppercase
|
16
|
+
# @return [String] if *data* is empty an error is raised.
|
17
|
+
def call!
|
18
|
+
raise ArgumentError, 'no data provided' if data.blank?
|
19
|
+
|
20
|
+
data.upcase
|
21
|
+
rescue StandardError => e
|
22
|
+
raise CSVConverter::Error.new(e.message, options)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def nullable_object
|
28
|
+
''
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CSVConverter
|
4
|
+
# Iterates over the columns of a row and processes the data accordingly.
|
5
|
+
class EntityProcessor
|
6
|
+
# The row being processed.
|
7
|
+
# @return [Array, Hash]
|
8
|
+
attr_reader :row
|
9
|
+
|
10
|
+
# Entity mappings.
|
11
|
+
# @return [Hash] configuration used to process the data.
|
12
|
+
attr_reader :entity_mappings
|
13
|
+
|
14
|
+
# Details of the data being processed. By default this includes:
|
15
|
+
# filename: the name of the file being processed.
|
16
|
+
# row_num: number of the row being processed.
|
17
|
+
# entity: the name of the entity being processed as provided in the mappings.
|
18
|
+
# row: the raw data of the row being processed.
|
19
|
+
# attr: the name of the attribute being processed as provided in the mappings.
|
20
|
+
# Additionally it will contain all the options provided to the converter in the mappings.
|
21
|
+
# @return [Hash]
|
22
|
+
attr_reader :options
|
23
|
+
|
24
|
+
# A new instance of EntityProcessor.
|
25
|
+
# @param row (@see #row)
|
26
|
+
# @param entity_mappings (@see #entity_mappings)
|
27
|
+
# @param options (@see #options)
|
28
|
+
def initialize(row, entity_mappings, options = {})
|
29
|
+
@row = row
|
30
|
+
@entity_mappings = entity_mappings
|
31
|
+
@options = options
|
32
|
+
end
|
33
|
+
|
34
|
+
# Iterates over the attributes of each entity converting the data into the format expected by the mappings.
|
35
|
+
# @return [Hash] the attributes for each entity.
|
36
|
+
# If an error occurs while processing the error is rescued and nullable values returned for the
|
37
|
+
# attributes that caused the error.
|
38
|
+
def call
|
39
|
+
entity_attrs(&:call)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Iterates over the attributes of each entity converting the data into the format expected by the mappings.
|
43
|
+
# @return [Hash] the attributes for each entity.
|
44
|
+
# If an error occurs while processing the an error is raised.
|
45
|
+
def call!
|
46
|
+
entity_attrs(&:call!)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def entity_attrs(&block)
|
52
|
+
return nested_entity(&block) if entity_mappings[:nested]
|
53
|
+
|
54
|
+
entity_mappings.each_with_object({}) do |(attr, attr_mappings), hash|
|
55
|
+
processor = CSVConverter::AttributeProcessor.new(row, attr_mappings, options.merge(attr: attr))
|
56
|
+
hash[attr] = block.call(processor)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def nested_entity
|
61
|
+
separator = entity_mappings[:separator] || ','
|
62
|
+
nested_row = row[entity_mappings[:header]].split(separator)
|
63
|
+
processor = self.class.new(nested_row, entity_mappings[:mappings], options)
|
64
|
+
yield(processor)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CSVConverter
|
4
|
+
# Iterates over a collection and processes the data accordingly.
|
5
|
+
class FileProcessor
|
6
|
+
# Name of the file being processed.
|
7
|
+
# @return [String] the name of the file.
|
8
|
+
attr_reader :filename
|
9
|
+
|
10
|
+
# Collection being processed.
|
11
|
+
# @return [Array] collection of rows being processed.
|
12
|
+
attr_reader :rows
|
13
|
+
|
14
|
+
# File mappings.
|
15
|
+
# @return [Hash] configuration used to process the data.
|
16
|
+
attr_reader :file_mappings
|
17
|
+
|
18
|
+
# A new instance of FileProcessor.
|
19
|
+
# @param filename (@see #filename)
|
20
|
+
# @param rows (@see #rows)
|
21
|
+
# @param file_mappings (@see #file_mappings)
|
22
|
+
def initialize(filename, rows, file_mappings)
|
23
|
+
@filename = filename
|
24
|
+
@rows = rows
|
25
|
+
@file_mappings = file_mappings.with_indifferent_access
|
26
|
+
end
|
27
|
+
|
28
|
+
# Iterates over the rows grouping and converting the data as expected based on the mappings.
|
29
|
+
# @return [Array] Collection of hashes containing the entities obtained after processing the rows.
|
30
|
+
# If an error occurs while processing the error is rescued and nullable values returned for the
|
31
|
+
# attributes that caused the error.
|
32
|
+
def call
|
33
|
+
rows.map.with_index do |row, row_num|
|
34
|
+
process_entities(row, row_num, &:call)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Iterates over the rows grouping and converting the data as expected based on the mappings.
|
39
|
+
# @return [Array] Collection of hashes containing the entities obtained after processing the rows.
|
40
|
+
# If an error occurs while processing the rows an error is raised.
|
41
|
+
def call!
|
42
|
+
rows.map.with_index do |row, row_num|
|
43
|
+
process_entities(row, row_num, &:call!)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def process_entities(row, row_num)
|
50
|
+
file_mappings.each_with_object({}) do |(entity, entity_mappings), hash|
|
51
|
+
hash[entity] = {} unless hash.key?(entity)
|
52
|
+
options = { filename: filename, row_num: row_num, entity: entity, row: row }
|
53
|
+
processor = CSVConverter::EntityProcessor.new(row, entity_mappings, options)
|
54
|
+
hash[entity] = yield(processor)
|
55
|
+
end.with_indifferent_access
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
metadata
ADDED
@@ -0,0 +1,174 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: csv_converter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Francisco Rojas
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-11-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '6.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '6.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: byebug
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '11.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '11.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '10.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '10.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rb-readline
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.5'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.5'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '3.0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '3.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubocop
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0.75'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0.75'
|
111
|
+
description: Groups and converts tabulated data based on the mappings provided
|
112
|
+
email:
|
113
|
+
- josefcorojas@gmail.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- ".gitignore"
|
119
|
+
- ".rspec"
|
120
|
+
- ".rubocop.yml"
|
121
|
+
- ".travis.yml"
|
122
|
+
- CHANGELOG.md
|
123
|
+
- Gemfile
|
124
|
+
- Gemfile.lock
|
125
|
+
- LICENSE
|
126
|
+
- README.md
|
127
|
+
- Rakefile
|
128
|
+
- bin/console
|
129
|
+
- bin/setup
|
130
|
+
- csv_converter.gemspec
|
131
|
+
- lib/csv_converter.rb
|
132
|
+
- lib/csv_converter/attribute_processor.rb
|
133
|
+
- lib/csv_converter/converters/array_converter.rb
|
134
|
+
- lib/csv_converter/converters/base_converter.rb
|
135
|
+
- lib/csv_converter/converters/big_decimal_converter.rb
|
136
|
+
- lib/csv_converter/converters/boolean_converter.rb
|
137
|
+
- lib/csv_converter/converters/date_converter.rb
|
138
|
+
- lib/csv_converter/converters/float_converter.rb
|
139
|
+
- lib/csv_converter/converters/hash_converter.rb
|
140
|
+
- lib/csv_converter/converters/integer_converter.rb
|
141
|
+
- lib/csv_converter/converters/lowercase_converter.rb
|
142
|
+
- lib/csv_converter/converters/string_converter.rb
|
143
|
+
- lib/csv_converter/converters/uppercase_converter.rb
|
144
|
+
- lib/csv_converter/entity_processor.rb
|
145
|
+
- lib/csv_converter/file_processor.rb
|
146
|
+
- lib/csv_converter/version.rb
|
147
|
+
homepage: https://github.com/francisco-rojas/csv_converter
|
148
|
+
licenses:
|
149
|
+
- MIT
|
150
|
+
metadata:
|
151
|
+
homepage_uri: https://github.com/francisco-rojas/csv_converter
|
152
|
+
wiki_uri: https://github.com/francisco-rojas/csv_converter/wiki
|
153
|
+
source_code_uri: https://github.com/francisco-rojas/csv_converter
|
154
|
+
changelog_uri: https://github.com/francisco-rojas/csv_converter/CHANGELOG.md
|
155
|
+
post_install_message:
|
156
|
+
rdoc_options: []
|
157
|
+
require_paths:
|
158
|
+
- lib
|
159
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
160
|
+
requirements:
|
161
|
+
- - ">="
|
162
|
+
- !ruby/object:Gem::Version
|
163
|
+
version: '0'
|
164
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
165
|
+
requirements:
|
166
|
+
- - ">="
|
167
|
+
- !ruby/object:Gem::Version
|
168
|
+
version: '0'
|
169
|
+
requirements: []
|
170
|
+
rubygems_version: 3.0.3
|
171
|
+
signing_key:
|
172
|
+
specification_version: 4
|
173
|
+
summary: Groups and converts tabulated data
|
174
|
+
test_files: []
|