smarter_csv 1.14.0 → 1.14.2
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/.rubocop.yml +20 -0
- data/CHANGELOG.md +9 -0
- data/README.md +3 -1
- data/docs/basic_write_api.md +3 -3
- data/docs/options.md +5 -0
- data/lib/smarter_csv/parser.rb +1 -1
- data/lib/smarter_csv/version.rb +1 -1
- data/lib/smarter_csv/writer.rb +15 -8
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4b6c0ad6a61721dac33f4ef31cf34da3cdd221804aaa45ff8e49cf7b5894b539
|
4
|
+
data.tar.gz: 8c1307aa7a74fc4f434eec362519dcb4392774aa35f563fc2674d542f6b4ea62
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8b7cef2ec65c990d3f6c8b05acaefa328952e70571c5868293531ac51c44fd13306dfad5e976f786eb61d206214bc7eae91cf8c2e00023a26c75d689769ed684
|
7
|
+
data.tar.gz: e204946071d76d264b0c8206b1d9170eb7b5c89bb25f22975322fbdc0c4d52869f4b7cf06245d5143298c6d3a89b8c2bc6aa6529fc32f0895e57e1d2ddef97d1
|
data/.rubocop.yml
CHANGED
@@ -58,6 +58,9 @@ Style/ClassMethods:
|
|
58
58
|
Style/ConditionalAssignment:
|
59
59
|
Enabled: false
|
60
60
|
|
61
|
+
Style/CommentAnnotation:
|
62
|
+
Enabled: false
|
63
|
+
|
61
64
|
Style/CommentedKeyword:
|
62
65
|
Enabled: false
|
63
66
|
|
@@ -76,6 +79,9 @@ Style/Encoding:
|
|
76
79
|
Style/EvalWithLocation:
|
77
80
|
Enabled: false
|
78
81
|
|
82
|
+
Style/EvenOdd:
|
83
|
+
Enabled: false
|
84
|
+
|
79
85
|
Style/FormatString:
|
80
86
|
Enabled: false
|
81
87
|
|
@@ -94,9 +100,15 @@ Style/IfUnlessModifier:
|
|
94
100
|
Style/InverseMethods:
|
95
101
|
Enabled: false
|
96
102
|
|
103
|
+
Style/Lambda:
|
104
|
+
Enabled: false
|
105
|
+
|
97
106
|
Style/NestedTernaryOperator:
|
98
107
|
Enabled: false
|
99
108
|
|
109
|
+
Style/OptionalBooleanParameter:
|
110
|
+
Enabled: false
|
111
|
+
|
100
112
|
Style/PreferredHashMethods:
|
101
113
|
Enabled: false
|
102
114
|
|
@@ -115,6 +127,9 @@ Style/SafeNavigation:
|
|
115
127
|
Style/SlicingWithRange:
|
116
128
|
Enabled: false
|
117
129
|
|
130
|
+
Style/SoleNestedConditional:
|
131
|
+
Enabled: false
|
132
|
+
|
118
133
|
Style/SpecialGlobalVars: # DANGER: unsafe rule!!
|
119
134
|
Enabled: false
|
120
135
|
|
@@ -135,8 +150,13 @@ Style/SymbolArray:
|
|
135
150
|
Style/SymbolProc: # old Ruby versions can't do this
|
136
151
|
Enabled: false
|
137
152
|
|
153
|
+
Style/TrailingCommaInArrayLiteral:
|
154
|
+
Enabled: false
|
155
|
+
EnforcedStyleForMultiline: consistent_comma
|
156
|
+
|
138
157
|
Style/TrailingCommaInHashLiteral:
|
139
158
|
Enabled: false
|
159
|
+
EnforcedStyleForMultiline: consistent_comma
|
140
160
|
|
141
161
|
Style/TrailingUnderscoreVariable:
|
142
162
|
Enabled: false
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,15 @@
|
|
1
1
|
|
2
2
|
# SmarterCSV 1.x Change Log
|
3
3
|
|
4
|
+
## 1.14.2 (2025-04-10)
|
5
|
+
* bugfix: SmarterCSV::Writer fixing corner case with `quote_headers: true`
|
6
|
+
* new option: `header_converter` allows to programatically modify the headers
|
7
|
+
|
8
|
+
## 1.14.1 (2025-04-09)
|
9
|
+
* bugfix: SmarterCSV::Writer empty hash results in a blank line ([issue 299](https://github.com/tilo/smarter_csv/issues/299))
|
10
|
+
* bugfix: SmarterCSV::Writer need to automatically quote problematic headers ([issue #300](https://github.com/tilo/smarter_csv/issues/300))
|
11
|
+
* new option: `quote_headers` allows to explicitly quote all headers
|
12
|
+
|
4
13
|
## 1.14.0 (2025-04-07)
|
5
14
|
* adding advanced configuration options for writing CSV files. ([issue 297](https://github.com/tilo/smarter_csv/issues/297) thanks to Robert Reiz, [issue 296](https://github.com/tilo/smarter_csv/issues/296))
|
6
15
|
|
data/README.md
CHANGED
@@ -47,9 +47,11 @@ Or install it yourself as:
|
|
47
47
|
|
48
48
|
# Articles
|
49
49
|
* [Parsing CSV Files in Ruby with SmarterCSV](https://tilo-sloboda.medium.com/parsing-csv-files-in-ruby-with-smartercsv-6ce66fb6cf38)
|
50
|
+
* [CSV Writing with SmarterCSV](https://tilo-sloboda.medium.com/csv-writing-with-smartercsv-26136d47ad0c)
|
50
51
|
* [Processing 1.4 Million CSV Records in Ruby, fast ](https://lcx.wien/blog/processing-14-million-csv-records-in-ruby/)
|
51
52
|
* [Faster Parsing CSV with Parallel Processing](http://xjlin0.github.io/tech/2015/05/25/faster-parsing-csv-with-parallel-processing) by [Jack lin](https://github.com/xjlin0/)
|
52
|
-
*
|
53
|
+
* The original [Stackoverflow Question](https://stackoverflow.com/questions/7788618/update-mongodb-with-array-from-csv-join-table/7788746#7788746) that inspired SmarterCSV
|
54
|
+
* [The original post](http://www.unixgods.org/Ruby/process_csv_as_hashes.html) for SmarterCSV
|
53
55
|
|
54
56
|
# [ChangeLog](./CHANGELOG.md)
|
55
57
|
|
data/docs/basic_write_api.md
CHANGED
@@ -62,14 +62,14 @@ The simplified interface takes a block:
|
|
62
62
|
### Full Interface
|
63
63
|
|
64
64
|
```
|
65
|
-
|
65
|
+
csv_writer = SmarterCSV::Writer.new(file_path, options)
|
66
66
|
|
67
67
|
MyModel.find_in_batches(batch_size: 100) do |batch|
|
68
68
|
batch.pluck(:name, :description, :instructor).each do |record|
|
69
69
|
csv_writer << record
|
70
70
|
end
|
71
71
|
|
72
|
-
|
72
|
+
csv_writer.finalize
|
73
73
|
```
|
74
74
|
|
75
75
|
## Advanced Features: Customizing the Output Format
|
@@ -141,7 +141,7 @@ You can also use the special keyword `:_all` to define transformations that are
|
|
141
141
|
value_converters: {
|
142
142
|
disable_auto_quoting: true, # ⚠️ Important: turn off auto-quoting because we're messing with it below
|
143
143
|
active: ->(v) { !!v ? 'YES' : 'NO' },
|
144
|
-
_all: ->(
|
144
|
+
_all: ->(_k, v) { v.is_a?(String) ? "\"#{v}\"" : v } # only double-quote string fields
|
145
145
|
}
|
146
146
|
}
|
147
147
|
```
|
data/docs/options.md
CHANGED
@@ -28,10 +28,15 @@
|
|
28
28
|
| | | ⚠️ This disables automatic header detection! |
|
29
29
|
| :map_headers | {} | Similar to `headers`, but also maps each desired key to a user-specified value that is uesd as the header. |
|
30
30
|
| | | ⚠️ This disables automatic header detection! |
|
31
|
+
| :value_converters | nil | allows to define lambdas to programmatically modify values |
|
32
|
+
| | | * either for specific `key` names |
|
33
|
+
| | | * or using `_all` for all fields |
|
34
|
+
| :header_converter | nil | allows to define one lambda to programmatically modify the headers |
|
31
35
|
| :discover_headers | true | Automatically detects all keys in the input before writing the header |
|
32
36
|
| | | Do not manually set this to `false`. ⚠️ |
|
33
37
|
| | | But you can set this to `true` when using `map_headers` option. |
|
34
38
|
| :disable_auto_quoting | false | To manually disable auto-quoting of special characters. ⚠️ Be careful with this! |
|
39
|
+
| :quote_headers | false | To force quoting all headers (only needed in rare cases) |
|
35
40
|
|
36
41
|
|
37
42
|
## CSV Reading
|
data/lib/smarter_csv/parser.rb
CHANGED
data/lib/smarter_csv/version.rb
CHANGED
data/lib/smarter_csv/writer.rb
CHANGED
@@ -29,6 +29,7 @@ module SmarterCSV
|
|
29
29
|
# quote_char : defaults to "
|
30
30
|
# discover_headers : defaults to true
|
31
31
|
# headers : defaults to []
|
32
|
+
# quote_headers: defaults to false
|
32
33
|
# force_quotes: defaults to false
|
33
34
|
# map_headers: defaults to {}, can be a hash of key -> value mappings
|
34
35
|
# value_converters: optional hash of key -> lambda to control serialization
|
@@ -48,14 +49,16 @@ module SmarterCSV
|
|
48
49
|
@col_sep = options[:col_sep] || ','
|
49
50
|
@quote_char = options[:quote_char] || '"'
|
50
51
|
@force_quotes = options[:force_quotes] == true
|
52
|
+
@quote_headers = options[:quote_headers] == true
|
51
53
|
@disable_auto_quoting = options[:disable_auto_quoting] == true
|
52
54
|
@value_converters = options[:value_converters] || {}
|
53
55
|
@map_all_keys = @value_converters.has_key?(:_all)
|
54
56
|
@mapped_keys = @value_converters.keys - [:_all]
|
57
|
+
@header_converter = options[:header_converter]
|
55
58
|
|
56
59
|
@discover_headers = true
|
57
60
|
if options.has_key?(:discover_headers)
|
58
|
-
@discover_headers = options[:discover_headers] == true
|
61
|
+
@discover_headers = options[:discover_headers] == true
|
59
62
|
else
|
60
63
|
@discover_headers = !(options.has_key?(:map_headers) || options.has_key?(:headers))
|
61
64
|
end
|
@@ -87,10 +90,14 @@ module SmarterCSV
|
|
87
90
|
|
88
91
|
def finalize
|
89
92
|
mapped_headers = @headers.map { |header| @map_headers[header] || header }
|
90
|
-
|
93
|
+
|
94
|
+
mapped_headers = @headers.map { |header| @header_converter.call(header) } if @header_converter
|
95
|
+
|
96
|
+
force_quotes = @quote_headers || @force_quotes
|
97
|
+
mapped_headers = mapped_headers.map { |x| escape_csv_field(x, force_quotes) }
|
91
98
|
|
92
99
|
@temp_file.rewind
|
93
|
-
@output_file.write(mapped_headers.join(@col_sep) + @row_sep)
|
100
|
+
@output_file.write(mapped_headers.join(@col_sep) + @row_sep) unless mapped_headers.empty?
|
94
101
|
@output_file.write(@temp_file.read)
|
95
102
|
@output_file.flush
|
96
103
|
@output_file.close
|
@@ -117,10 +124,10 @@ module SmarterCSV
|
|
117
124
|
# then apply general mapping rules
|
118
125
|
value = map_all_values(header, value) if @map_all_keys
|
119
126
|
|
120
|
-
escape_csv_field(value) # for backwards compatibility
|
127
|
+
escape_csv_field(value, @force_quotes) # for backwards compatibility
|
121
128
|
end
|
122
129
|
|
123
|
-
@temp_file.write
|
130
|
+
@temp_file.write(ordered_row.join(@col_sep) + @row_sep) unless ordered_row.empty?
|
124
131
|
end
|
125
132
|
|
126
133
|
def map_value(key, value)
|
@@ -131,13 +138,13 @@ module SmarterCSV
|
|
131
138
|
@value_converters[:_all].call(key, value)
|
132
139
|
end
|
133
140
|
|
134
|
-
def escape_csv_field(field)
|
141
|
+
def escape_csv_field(field, force_quotes = false)
|
135
142
|
str = field.to_s
|
136
|
-
return str if @disable_auto_quoting
|
143
|
+
return str if @disable_auto_quoting && !force_quotes
|
137
144
|
|
138
145
|
# double-quote fields if we force that, or if the field contains the comma, new-line, or quote character
|
139
146
|
contains_special_char = str.to_s.match(@quote_regex)
|
140
|
-
if
|
147
|
+
if force_quotes || contains_special_char
|
141
148
|
str = str.gsub(@quote_char, @quote_char * 2) if contains_special_char # escape double-quote
|
142
149
|
|
143
150
|
"\"#{str}\""
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: smarter_csv
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.14.
|
4
|
+
version: 1.14.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tilo Sloboda
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-04-
|
11
|
+
date: 2025-04-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: awesome_print
|