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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7f7308ee39fc18fb93f23273b32559ae83139375bfc84c975a1b6cf048368a5e
4
- data.tar.gz: a50d9ea0b40bf28adc829613d8d0b2744018b708453b96cc339653688a1c1f40
3
+ metadata.gz: 4b6c0ad6a61721dac33f4ef31cf34da3cdd221804aaa45ff8e49cf7b5894b539
4
+ data.tar.gz: 8c1307aa7a74fc4f434eec362519dcb4392774aa35f563fc2674d542f6b4ea62
5
5
  SHA512:
6
- metadata.gz: baf9bad5261b6ab75d4d49fc7b0044536a9977955ef01d38b9d9431478649be8481a378a4b988b6091b7f2c5ba5f6c2a8899dfc2143f2bd11c834386453024d1
7
- data.tar.gz: 7283acb30cbdde33c463f369a619cc93235ea2931153babdcaedabe468cf50e198ab0264e9c70ee166a81cc072b44ab874d89e4de835379495155784b7a31035
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
- * [The original post](http://www.unixgods.org/Ruby/process_csv_as_hashes.html) that started SmarterCSV
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
 
@@ -62,14 +62,14 @@ The simplified interface takes a block:
62
62
  ### Full Interface
63
63
 
64
64
  ```
65
- writer = SmarterCSV::Writer.new(file_path, options)
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
- writer.finalize
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: ->(k, v) { v.is_a?(String) ? "\"#{v}\"" : v } # only double-quote string fields
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
@@ -110,7 +110,7 @@ module SmarterCSV
110
110
  end
111
111
 
112
112
  # Replace double quotes with a single quote
113
- field.gsub!("#{quote * 2}", quote)
113
+ field.gsub!((quote * 2).to_s, quote)
114
114
 
115
115
  field
116
116
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SmarterCSV
4
- VERSION = "1.14.0"
4
+ VERSION = "1.14.2"
5
5
  end
@@ -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 # ⚠️ this option should not be exposed
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
- mapped_headers = mapped_headers.map { |x| escape_csv_field(x) } if @force_quotes
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 ordered_row.join(@col_sep) + @row_sep
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 @force_quotes || contains_special_char
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.0
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-07 00:00:00.000000000 Z
11
+ date: 2025-04-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: awesome_print