simple_csv 0.1.0 → 0.2.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 +4 -4
- data/.gitignore +2 -2
- data/.rubocop.yml +2 -0
- data/.travis.yml +4 -0
- data/README.md +120 -21
- data/bin/console +4 -22
- data/lib/simple_csv/base.rb +21 -11
- data/lib/simple_csv/reader.rb +36 -19
- data/lib/simple_csv/settings.rb +59 -0
- data/lib/simple_csv/version.rb +1 -1
- data/lib/simple_csv/writer.rb +19 -13
- data/lib/simple_csv.rb +21 -0
- data/simple_csv.gemspec +2 -0
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: accda7d430cbe5c71e9a775bac95f9f8f7c01038
|
4
|
+
data.tar.gz: c06a504dbdeaed0f91ee652655ce871a8ef5b18b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a979d33a0aab1573a75f7e8f8c98f4fea21b7bd4752b3ef80945f7ccaccb06f289efc947e537929e22d05c3db081ae9b267fb4ba89a562b8067133d07875a2a4
|
7
|
+
data.tar.gz: d217b4ccea541d1136d5b2a6381720eb9defdf4d7232445b5d24c5870c3530a2cf3b84fab8266955a1c18cdba59786a6ab3d513a45f4b9fb9e7c61400061772f
|
data/.gitignore
CHANGED
data/.rubocop.yml
ADDED
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -29,17 +29,84 @@ Or install it yourself as:
|
|
29
29
|
|
30
30
|
## Usage
|
31
31
|
|
32
|
-
###
|
32
|
+
### General
|
33
|
+
|
34
|
+
By default, the settings used will be those of `CSV::DEFAULT_OPTIONS` generally.
|
35
|
+
`SimpleCsv` sets the `:headers` property to true by default, this is due to the nature of `SimpleCsv`.
|
36
|
+
|
37
|
+
Headers have to be defined either before reading or generating a CSV.
|
38
|
+
Since `:headers` is now `true` by default, `SimpleCsv` will allow `CSV` to parse the first line as headers.
|
39
|
+
These headers are then converted in method calls to use within an `SimpleCsv::Reader#each_row` loop.
|
40
|
+
|
41
|
+
If however, your file lacks headers, you have the ability to set `:has_headers` to false and supply headers manually before calling `SimpleCsv::Reader#each_row`.
|
42
|
+
The headers will be picked up and used instead of the first line.
|
43
|
+
|
44
|
+
#### SimpleCsv default settings
|
45
|
+
|
46
|
+
These are the settings that will be merged with settings passed throogh either `SimpleCsv#generate` or `SimpleCsv#read`
|
47
|
+
|
48
|
+
| setting | value |
|
49
|
+
|----------------------|---------------------------------------|
|
50
|
+
|`:col_sep` | `","` |
|
51
|
+
|`:row_sep` | `:auto` |
|
52
|
+
|`:quote_char` | `"\"` |
|
53
|
+
|`:field_size_limit` | `nil` |
|
54
|
+
|`:converters` | `[:all, :blank_to_nil, :null_to_nil]` |
|
55
|
+
|`:unconverted_fields` | `nil` |
|
56
|
+
|`:headers` | `true` |
|
57
|
+
|`:return_headers` | `false` |
|
58
|
+
|`:header_converters` | `nil` |
|
59
|
+
|`:skip_blanks` | `false` |
|
60
|
+
|`:force_quotes` | `true` |
|
61
|
+
|`:skip_lines` | `nil` |
|
62
|
+
|
63
|
+
The following settings differ from the `CSV::DEFAULT_OPTIONS`
|
64
|
+
|
65
|
+
* `:converters` is set to `[:all, :blank_to_nil, :null_to_nil]`
|
66
|
+
* `:headers` is `true` by default
|
67
|
+
* `:force_quotes` is `true` by default
|
68
|
+
|
69
|
+
This essentially means that when reading a CSV file, headers are required otherwise a `SimpleCsv::HeadersNotSet` exception will be thrown.
|
70
|
+
Also, when reading a CSV, all values will be parsed to their respective types, so `"1"` would become `1` as end value.
|
71
|
+
|
72
|
+
#### SimpleCsv::Writer additional default settings
|
73
|
+
|
74
|
+
Additionally, `SimpleCsv::Writer` has one additional default setting that ensures an entire row is written before being able to write another one.
|
75
|
+
This setting enforces you to call each method once before calling one of them again, if this condition is not met a `SimpleCsv::RowNotComplete` exception will be thrown
|
33
76
|
|
34
|
-
|
35
|
-
|
77
|
+
| setting | value |
|
78
|
+
|------------------------|---------------------------------------|
|
79
|
+
|`:force_row_completion` | `true` |
|
36
80
|
|
37
|
-
|
81
|
+
#### Setting aliasses
|
82
|
+
|
83
|
+
An _alias_ can be used instead of it's respective _setting_.
|
84
|
+
|
85
|
+
| setting | alias |
|
86
|
+
|---------------|---------------|
|
87
|
+
| `:col_sep` | `:seperator` |
|
88
|
+
| `headers` | `has_headers` |
|
89
|
+
|
90
|
+
#### Converters
|
91
|
+
|
92
|
+
The above `:converters` option is set to include `:all` converters and additionally, `:blank_to_nil` and `:null_to_nil`
|
93
|
+
The code for these two can be summed up in two lines:
|
38
94
|
|
39
95
|
```ruby
|
40
|
-
|
96
|
+
CSV::Converters[:blank_to_nil] = ->(f) { f && f.empty? ? nil : f }
|
97
|
+
CSV::Converters[:null_to_nil] = ->(f) { f && f == 'NULL' ? nil : f }
|
41
98
|
```
|
42
99
|
|
100
|
+
What they do replace empty values or the string `'NULL'` by nil within a column when it's being parsed.
|
101
|
+
For now, these are the default two and they are always used unless the `:converters` option is set to `nil` within `SimpleCsv#generate` or `SimpleCsv#read`
|
102
|
+
|
103
|
+
### Generating a CSV file
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
SimpleCsv.generate path, options = { ... }, &block
|
107
|
+
```
|
108
|
+
|
109
|
+
The `SimpleCsv#generate` method takes a (required) path, an (optional) hash of options and a (required) block to start building a CSV file.
|
43
110
|
To generate a CSV file we use `SimpleCsv#generate` (using the [faker](https://github.com/stympy/faker) gem to provide fake data)
|
44
111
|
|
45
112
|
```ruby
|
@@ -63,23 +130,34 @@ end
|
|
63
130
|
|
64
131
|
### Reading a CSV file
|
65
132
|
|
66
|
-
The `SimpleCsv#read` method takes a path and options to use for reading the file.
|
67
|
-
By default, the expected seperator is a comma (`,`), `:headers` is set to `true` so that `CSV` parses the file expecting headers.
|
68
|
-
The `:converters` option ensures values are converted to a proper type if possible, by default (if this is not set) all values are returned as strings
|
69
|
-
|
70
|
-
When supplying options they will be merged into the existing defaults rather than overwriting them completely.
|
71
|
-
|
72
133
|
```ruby
|
73
|
-
SimpleCsv.read
|
74
|
-
converters: [:all, :blank_to_nil, :null_to_nil] })
|
134
|
+
SimpleCsv.read path, options = { ... }, &block
|
75
135
|
```
|
76
136
|
|
137
|
+
The `SimpleCsv#generate` method takes a (required) path, an (optional) hash of options and a (required) block to start building a CSV file.
|
138
|
+
|
77
139
|
To read a CSV file we use `SimpleCsv#read`, we will pass it a file path and a block as arguments.
|
78
140
|
Within the block we define the headers present in the file, these will be transformed into methods you can call within `SimpleCsv::Reader#each_row` to get that property's current value
|
79
141
|
|
80
142
|
```ruby
|
81
143
|
SimpleCsv.read('input.csv') do
|
82
|
-
#
|
144
|
+
# assumes headers are set, they will be read and callable within each_row
|
145
|
+
|
146
|
+
each_row do
|
147
|
+
puts [first_name, last_name, birth_date, employed_at].compact.join ', '
|
148
|
+
end
|
149
|
+
end
|
150
|
+
```
|
151
|
+
|
152
|
+
### Reading a CSV file without headers
|
153
|
+
|
154
|
+
Last but not least, if we have a CSV file that does not contain headers we can use the following setup.
|
155
|
+
Setting `:has_headers` to `false` means we do not expect the first line to be headers.
|
156
|
+
Therefore we have to explicitly define the headers before looping the CSV.
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
SimpleCsv.read('headerless.csv', has_headers: false) do
|
160
|
+
# first define the headers in the file manually if the file does not have them
|
83
161
|
headers :first_name, :last_name, :birth_date, :employed_at
|
84
162
|
|
85
163
|
each_row do
|
@@ -89,18 +167,18 @@ SimpleCsv.read('input.csv') do
|
|
89
167
|
end
|
90
168
|
```
|
91
169
|
|
92
|
-
|
170
|
+
### Batch operations
|
171
|
+
|
172
|
+
If we have a large CSV we might want to batch operations (say, if we are inserting this data into a database or through an API).
|
93
173
|
For this we can use `SimpleCsv::Reader#in_groups_of` and pass the size of the group.
|
94
174
|
Within that we call `SimpleCsv::Reader#each_row` as usual
|
95
175
|
|
96
176
|
```ruby
|
97
177
|
SimpleCsv.read('input.csv') do
|
98
|
-
#
|
99
|
-
headers :first_name, :last_name, :birth_date, :employed_at
|
178
|
+
# assumes headers are set, they will be read and callable within each_row
|
100
179
|
|
101
180
|
in_groups_of(100) do
|
102
181
|
each_row do
|
103
|
-
# print each field defined in headers (that is not nil)
|
104
182
|
puts [first_name, last_name, birth_date, employed_at].compact.join ', '
|
105
183
|
end
|
106
184
|
# execute after every 100 rows
|
@@ -109,15 +187,36 @@ SimpleCsv.read('input.csv') do
|
|
109
187
|
end
|
110
188
|
```
|
111
189
|
|
190
|
+
### Aliassing existing headers
|
191
|
+
|
192
|
+
Should you want to map existing headers to different names, this is possible by passing a hash at the end with key value pairs.
|
193
|
+
When generating a CSV file, aliasses are ignored and therefore should not be passed.
|
194
|
+
|
195
|
+
When defining columns manually using `headers` for a file without headers, ALL columns must be named before defining aliasses.
|
196
|
+
This means that if your CSV exists of 3 columns, 3 headers must be defined before aliassing any of those to something shorter or more concise.
|
197
|
+
|
198
|
+
To create an alias `date_of_birth` of `birth_date` *(In a CSV file without headers)* we would write *(notice `:birth_date` is present twice, once as column entry, and once more as key for an alias)*:
|
199
|
+
|
200
|
+
```ruby
|
201
|
+
headers :first_name, :last_name, :employed_at, :birth_date, birth_date: :date_of_birth
|
202
|
+
```
|
203
|
+
|
112
204
|
## Development
|
113
205
|
|
114
|
-
After checking out the repo, run `
|
206
|
+
After checking out the repo, run `bundle` to install dependencies. Then, run `rspec` to run the tests. You can also use the `bin/console` file to play around and debug.
|
207
|
+
|
208
|
+
To install this gem onto your local machine, run `rake install`.
|
209
|
+
|
210
|
+
To release a new version:
|
115
211
|
|
116
|
-
|
212
|
+
* Run CI in dev branch, if tests pass, merge into master
|
213
|
+
* Update version number in _lib/simple_csv/version.rb_ according to [symver](http://semver.org/)
|
214
|
+
* Update _README.md_ to reflect your changes
|
215
|
+
* run `rake release` to push commits, create a tag for the current commit and push the `.gem` file to RubyGems
|
117
216
|
|
118
217
|
## Contributing
|
119
218
|
|
120
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
219
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/sidofc/simple_csv. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
121
220
|
|
122
221
|
|
123
222
|
## License
|
data/bin/console
CHANGED
@@ -5,27 +5,9 @@ require 'pry'
|
|
5
5
|
require 'faker'
|
6
6
|
require 'simple_csv'
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
100.times do
|
13
|
-
first_name Faker::Name.first_name
|
14
|
-
last_name Faker::Name.last_name
|
15
|
-
birth_date Faker::Date.between(Date.today << 1000, Date.today << 200)
|
16
|
-
employed_at [Faker::Company.name, nil].sample
|
17
|
-
end
|
8
|
+
SimpleCsv.read('spec/files/headerless.csv', has_headers: false) do
|
9
|
+
headers :first_name, :last_name, :birth_date, :employed_at, employed_at: :job
|
10
|
+
each_row { p job }
|
18
11
|
end
|
19
12
|
|
20
|
-
#
|
21
|
-
SimpleCsv.read('spec/files/output.csv') do
|
22
|
-
headers :first_name, :last_name, :birth_date, :employed_at
|
23
|
-
|
24
|
-
in_groups_of(10) do
|
25
|
-
each_row :with_index do
|
26
|
-
puts [index, first_name, last_name,
|
27
|
-
birth_date, employed_at].compact.join ', '
|
28
|
-
end
|
29
|
-
sleep 1
|
30
|
-
end
|
31
|
-
end
|
13
|
+
# Pry.start
|
data/lib/simple_csv/base.rb
CHANGED
@@ -2,28 +2,38 @@ module SimpleCsv
|
|
2
2
|
class Base
|
3
3
|
attr_reader :index
|
4
4
|
|
5
|
-
COMMON_DELIMITERS = %w(, ; |)
|
6
|
-
|
7
|
-
|
5
|
+
COMMON_DELIMITERS = %w(, ; |).freeze
|
6
|
+
|
7
|
+
def debug_headers
|
8
|
+
p "@headers is now: #{@headers.join ','}"
|
9
|
+
p "@col_map is now #{@col_map}"
|
10
|
+
end
|
8
11
|
|
9
12
|
private
|
10
13
|
|
11
14
|
def settings(**opts)
|
12
|
-
@settings
|
13
|
-
@settings
|
15
|
+
return @settings.merge opts if opts.any? && @settings
|
16
|
+
@settings ||= Settings.new opts
|
17
|
+
end
|
18
|
+
|
19
|
+
def headers(*cols, **col_map)
|
20
|
+
@headers ||= []
|
21
|
+
@headers.concat cols.map(&:to_s) if cols.any?
|
22
|
+
|
23
|
+
@col_map ||= {}
|
24
|
+
@col_map.merge! stringify_col_map(col_map) if col_map.any?
|
14
25
|
|
15
|
-
@
|
26
|
+
@headers_set ||= @headers.any?
|
16
27
|
|
17
|
-
@
|
28
|
+
@headers
|
18
29
|
end
|
19
30
|
|
20
|
-
def
|
21
|
-
|
22
|
-
@headers = column_names.map(&:to_s)
|
31
|
+
def stringify_col_map(col_map)
|
32
|
+
col_map.to_a.map { |m| m.reverse.map(&:to_s) }.to_h
|
23
33
|
end
|
24
34
|
|
25
35
|
def respond_to_missing?(mtd, include_private = false)
|
26
|
-
@headers.include?(mtd) || super
|
36
|
+
@headers.include?(mtd) || @col_map.key?(mtd.to_s) || super
|
27
37
|
end
|
28
38
|
end
|
29
39
|
end
|
data/lib/simple_csv/reader.rb
CHANGED
@@ -3,15 +3,13 @@ module SimpleCsv
|
|
3
3
|
attr_reader :index
|
4
4
|
|
5
5
|
def initialize(path, **opts, &block)
|
6
|
-
|
6
|
+
@csv_path = File.expand_path path
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
end
|
8
|
+
opts[:seperator] ||= detect_delimiter
|
9
|
+
settings.apply opts
|
11
10
|
|
12
|
-
|
11
|
+
load_csv_with_auto_headers if settings.for_csv[:headers]
|
13
12
|
|
14
|
-
@csv = @original = CSV.open File.expand_path(path), settings
|
15
13
|
instance_eval(&block)
|
16
14
|
end
|
17
15
|
|
@@ -27,6 +25,8 @@ module SimpleCsv
|
|
27
25
|
def each_row(*arr_opts, &block)
|
28
26
|
@index ||= 0 if arr_opts.include?(:with_index)
|
29
27
|
|
28
|
+
load_csv_with_manual_headers unless @csv
|
29
|
+
|
30
30
|
@csv.each do |record|
|
31
31
|
@record = record
|
32
32
|
instance_eval(&block)
|
@@ -34,30 +34,47 @@ module SimpleCsv
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
-
|
38
|
-
super
|
39
|
-
end
|
37
|
+
private
|
40
38
|
|
41
|
-
def
|
42
|
-
|
39
|
+
def load_csv_with_auto_headers
|
40
|
+
headers(*find_headers)
|
41
|
+
|
42
|
+
@csv = @original = CSV.open @csv_path, settings.for_csv
|
43
43
|
end
|
44
44
|
|
45
|
-
|
45
|
+
def load_csv_with_manual_headers
|
46
|
+
SimpleCsv.csv_manually_set_headers! unless @headers_set
|
47
|
+
csv_arr = CSV.open(@csv_path).to_a
|
46
48
|
|
47
|
-
|
48
|
-
|
49
|
-
|
49
|
+
if csv_arr.first.size == headers.size
|
50
|
+
csv_str = csv_arr.unshift(headers).map(&:to_csv).join
|
51
|
+
settings.apply(headers: true)
|
52
|
+
@csv = @original = CSV.new csv_str, settings.for_csv
|
53
|
+
else
|
54
|
+
SimpleCsv.csv_not_enough_headers!
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def find_headers
|
59
|
+
first_line.split(detect_delimiter).map { |h| h.gsub(/^"*|"*$/, '') }
|
50
60
|
end
|
51
61
|
|
52
|
-
def detect_delimiter
|
53
|
-
line = first_line
|
62
|
+
def detect_delimiter
|
63
|
+
line = first_line
|
54
64
|
@delimiters = COMMON_DELIMITERS.map { |sep| [sep, line.scan(sep).length] }
|
55
65
|
.sort { |a, b| b[1] <=> a[1] }
|
56
66
|
@delimiter ||= @delimiters[0][0]
|
57
67
|
end
|
58
68
|
|
59
|
-
def first_line
|
60
|
-
@first_line ||= File.open
|
69
|
+
def first_line
|
70
|
+
@first_line ||= File.open @csv_path, &:readline
|
71
|
+
end
|
72
|
+
|
73
|
+
def method_missing(mtd, *args, &block)
|
74
|
+
m = mtd.to_s
|
75
|
+
return @record[m] if headers.include?(m)
|
76
|
+
return @record[@col_map[m]] if @col_map[m]
|
77
|
+
super
|
61
78
|
end
|
62
79
|
end
|
63
80
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module SimpleCsv
|
2
|
+
class Settings
|
3
|
+
DEFAULTS = { headers: true, col_sep: ',', seperator: ',',
|
4
|
+
force_quotes: true,
|
5
|
+
converters: [:all, :blank_to_nil, :null_to_nil] }.freeze
|
6
|
+
ALIASSED = { seperator: :col_sep, has_headers: :headers }.freeze
|
7
|
+
INVERTED_ALIASSES = ALIASSED.to_a.map(&:reverse).to_h
|
8
|
+
|
9
|
+
def initialize(**opts)
|
10
|
+
@settings = DEFAULTS.dup.merge opts
|
11
|
+
end
|
12
|
+
|
13
|
+
def [](setting)
|
14
|
+
send setting
|
15
|
+
end
|
16
|
+
|
17
|
+
def []=(m, val)
|
18
|
+
@settings[m] = val
|
19
|
+
return @settings[ALIASSED[m]] = val if ALIASSED.key? m
|
20
|
+
return @settings[INVERTED_ALIASSES[m]] = val if INVERTED_ALIASSES.key? m
|
21
|
+
val
|
22
|
+
end
|
23
|
+
|
24
|
+
def for_csv
|
25
|
+
settings = @settings.dup
|
26
|
+
|
27
|
+
ALIASSED.each do |opt_alias, opt|
|
28
|
+
settings[opt] = settings.delete(opt_alias) if settings.key? opt_alias
|
29
|
+
end
|
30
|
+
|
31
|
+
CSV::DEFAULT_OPTIONS.each_with_object({}) do |(prop, default), csv_hash|
|
32
|
+
csv_hash[prop] = settings.key?(prop) ? settings[prop] : default
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def apply(*hashes)
|
37
|
+
hashes.each { |opts| opts.each { |k, v| self[k] = v } } && @settings
|
38
|
+
end
|
39
|
+
|
40
|
+
def any?
|
41
|
+
@settings && @settings.any?
|
42
|
+
end
|
43
|
+
|
44
|
+
def respond_to_missing?(mtd, include_private = false)
|
45
|
+
accepted_method? mtd || super
|
46
|
+
end
|
47
|
+
|
48
|
+
def method_missing(mtd, *args, &block)
|
49
|
+
return super unless accepted_method? mtd
|
50
|
+
mtd = ALIASSED[mtd] if ALIASSED.key? mtd
|
51
|
+
mtd = INVERTED_ALIASSES[mtd] if INVERTED_ALIASSES.key? mtd
|
52
|
+
@settings[mtd]
|
53
|
+
end
|
54
|
+
|
55
|
+
def accepted_method?(mtd)
|
56
|
+
@settings.key?(mtd) || ALIASSED.key?(mtd) || INVERTED_ALIASSES.key?(mtd)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/simple_csv/version.rb
CHANGED
data/lib/simple_csv/writer.rb
CHANGED
@@ -1,12 +1,10 @@
|
|
1
1
|
module SimpleCsv
|
2
2
|
class Writer < Base
|
3
|
-
DEFAULTS = { force_quotes: true }.freeze
|
4
|
-
|
5
3
|
def initialize(path, **opts, &block)
|
6
|
-
settings
|
7
|
-
CSV.open(File.expand_path(path), 'w',
|
4
|
+
settings.apply({force_row_completion: true}, opts)
|
5
|
+
CSV.open(File.expand_path(path), 'w', settings.for_csv) do |csv|
|
8
6
|
@csv = csv
|
9
|
-
@current_row =
|
7
|
+
@current_row = {}
|
10
8
|
instance_eval(&block)
|
11
9
|
end
|
12
10
|
end
|
@@ -18,17 +16,25 @@ module SimpleCsv
|
|
18
16
|
end
|
19
17
|
|
20
18
|
def method_missing(mtd, *args, &block)
|
21
|
-
|
19
|
+
SimpleCsv.csv_manually_set_headers! unless @headers_written
|
20
|
+
super unless headers.include? mtd.to_s
|
21
|
+
|
22
|
+
if settings.force_row_completion && @current_row.key?(mtd)
|
23
|
+
SimpleCsv.row_not_complete!(mtd, args.first)
|
24
|
+
end
|
22
25
|
|
23
|
-
@current_row
|
24
|
-
|
25
|
-
|
26
|
+
@current_row[mtd] = args.first || ''
|
27
|
+
|
28
|
+
return unless @current_row.size == headers.size
|
29
|
+
|
30
|
+
@csv << @current_row.values
|
31
|
+
@current_row = {}
|
26
32
|
end
|
27
33
|
|
28
|
-
def headers(*
|
29
|
-
|
30
|
-
|
31
|
-
@headers
|
34
|
+
def headers(*col_names)
|
35
|
+
return @headers if col_names.empty?
|
36
|
+
super(*col_names)
|
37
|
+
(@csv << @headers) && @headers_written = true if @csv
|
32
38
|
end
|
33
39
|
end
|
34
40
|
end
|
data/lib/simple_csv.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'csv'
|
2
2
|
|
3
3
|
require 'simple_csv/version'
|
4
|
+
require 'simple_csv/settings'
|
4
5
|
require 'simple_csv/base'
|
5
6
|
require 'simple_csv/reader'
|
6
7
|
require 'simple_csv/writer'
|
@@ -8,6 +9,26 @@ require 'simple_csv/writer'
|
|
8
9
|
module SimpleCsv
|
9
10
|
@converters_initialized = false
|
10
11
|
|
12
|
+
class RowNotComplete < StandardError; end
|
13
|
+
class UnparseableCsv < StandardError; end
|
14
|
+
class HeadersNotSet < UnparseableCsv; end
|
15
|
+
class NotEnoughHeaders < UnparseableCsv; end
|
16
|
+
|
17
|
+
def self.row_not_complete!(mtd, value)
|
18
|
+
raise RowNotComplete,
|
19
|
+
"Row not complete! #{mtd} called twice with value #{value}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.csv_not_enough_headers!
|
23
|
+
raise NotEnoughHeaders, 'Not enough headers defined!'
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.csv_manually_set_headers!
|
27
|
+
raise HeadersNotSet,
|
28
|
+
['CSV does not contain headers',
|
29
|
+
'please add headers in them manually or in the file'].join(', ')
|
30
|
+
end
|
31
|
+
|
11
32
|
def self.read(path, **options, &block)
|
12
33
|
initialize_converters unless converters_initialized
|
13
34
|
Reader.new path, options, &block
|
data/simple_csv.gemspec
CHANGED
@@ -22,6 +22,8 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
23
|
spec.require_paths = ['lib']
|
24
24
|
|
25
|
+
spec.required_ruby_version = '>= 2.1'
|
26
|
+
|
25
27
|
spec.add_development_dependency 'bundler', '~> 1.13'
|
26
28
|
spec.add_development_dependency 'rake', '~> 10.0'
|
27
29
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simple_csv
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sidney Liebrand
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-12-
|
11
|
+
date: 2016-12-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -117,6 +117,7 @@ extra_rdoc_files: []
|
|
117
117
|
files:
|
118
118
|
- ".gitignore"
|
119
119
|
- ".rspec"
|
120
|
+
- ".rubocop.yml"
|
120
121
|
- ".travis.yml"
|
121
122
|
- CODE_OF_CONDUCT.md
|
122
123
|
- Gemfile
|
@@ -128,6 +129,7 @@ files:
|
|
128
129
|
- lib/simple_csv.rb
|
129
130
|
- lib/simple_csv/base.rb
|
130
131
|
- lib/simple_csv/reader.rb
|
132
|
+
- lib/simple_csv/settings.rb
|
131
133
|
- lib/simple_csv/version.rb
|
132
134
|
- lib/simple_csv/writer.rb
|
133
135
|
- simple_csv.gemspec
|
@@ -143,7 +145,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
143
145
|
requirements:
|
144
146
|
- - ">="
|
145
147
|
- !ruby/object:Gem::Version
|
146
|
-
version: '
|
148
|
+
version: '2.1'
|
147
149
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
148
150
|
requirements:
|
149
151
|
- - ">="
|