smarter_csv 1.16.0 → 1.16.1
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/.rspec +2 -0
- data/CHANGELOG.md +44 -0
- data/CONTRIBUTORS.md +1 -0
- data/README.md +10 -4
- data/docs/_introduction.md +6 -1
- data/docs/bad_row_quarantine.md +90 -33
- data/docs/basic_read_api.md +1 -0
- data/docs/basic_write_api.md +1 -0
- data/docs/batch_processing.md +1 -0
- data/docs/column_selection.md +1 -0
- data/docs/data_transformations.md +1 -0
- data/docs/examples.md +1 -0
- data/docs/header_transformations.md +1 -0
- data/docs/header_validations.md +1 -0
- data/docs/history.md +2 -0
- data/docs/instrumentation.md +1 -0
- data/docs/migrating_from_csv.md +364 -89
- data/docs/options.md +1 -0
- data/docs/parsing_strategy.md +2 -1
- data/docs/real_world_csv.md +1 -0
- data/docs/releases/1.16.0/changes.md +1 -0
- data/docs/row_col_sep.md +1 -0
- data/docs/ruby_csv_pitfalls.md +514 -0
- data/docs/value_converters.md +1 -0
- data/ext/smarter_csv/smarter_csv.c +10 -11
- data/lib/smarter_csv/parser.rb +5 -8
- data/lib/smarter_csv/version.rb +1 -1
- data/lib/smarter_csv.rb +41 -3
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 043745aedb1c63fd4a044b9ae46bb8e5d98324c14e609214ee3d895acfd5f501
|
|
4
|
+
data.tar.gz: c39a10521b767daf51887278c9020c9ff6d8d93c32c5ec3f95a17ec575ebdab5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5f1d125138443f02e0276e964dac9e584b996de6acafe8b3856316852a38220094e69a2c14302f922bcc93b6d23cc594bbf926940ccd70a2bd65ab08c5a18b49
|
|
7
|
+
data.tar.gz: '0929051996781c8643c0239556d123c840e7041d9c12f7d867e3800dfb2c2eb92e6f6fb77b5fc08660a36f34f470cb826f255943fa445d3e29d231e648da51b4'
|
data/.rspec
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,54 @@
|
|
|
1
1
|
|
|
2
2
|
# SmarterCSV 1.x Change Log
|
|
3
3
|
|
|
4
|
+
## 1.16.1 (2026-03-16) — Bug Fixes & New Features
|
|
5
|
+
|
|
6
|
+
RSpec tests: **1,247 → 1,410** (+163 tests)
|
|
7
|
+
|
|
8
|
+
### New Features
|
|
9
|
+
|
|
10
|
+
* **`SmarterCSV.errors`** — class-level error access after any `process`, `parse`, `each`, or `each_chunk` call.
|
|
11
|
+
Exposes the same `reader.errors` hash without requiring access to the `Reader` instance.
|
|
12
|
+
Errors are cleared at the start of each call and stored per-thread (safe in Puma/Sidekiq).
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
# Previously — required Reader instance to access errors
|
|
16
|
+
reader = SmarterCSV::Reader.new('data.csv', on_bad_row: :skip)
|
|
17
|
+
reader.process
|
|
18
|
+
puts reader.errors[:bad_row_count]
|
|
19
|
+
|
|
20
|
+
# Now — works with the class-level API too
|
|
21
|
+
SmarterCSV.process('data.csv', on_bad_row: :skip)
|
|
22
|
+
puts SmarterCSV.errors[:bad_row_count]
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
> **Note:** `SmarterCSV.errors` only surfaces errors from the **most recent run on the
|
|
26
|
+
> current thread**. In a multi-threaded environment (Puma, Sidekiq), each thread maintains
|
|
27
|
+
> its own error state independently. If you call `SmarterCSV.process` twice in the same
|
|
28
|
+
> thread, the second call's errors replace the first's. For long-running or complex
|
|
29
|
+
> pipelines where you need to aggregate errors across multiple files, use the Reader API.
|
|
30
|
+
>
|
|
31
|
+
> ⚠️ **Fibers:** `SmarterCSV.errors` uses `Thread.current` for storage, which is **shared
|
|
32
|
+
> across all fibers running in the same thread**. If you process CSV files concurrently
|
|
33
|
+
> in fibers (e.g. with `Async`, `Falcon`, or manual `Fiber` scheduling), `SmarterCSV.errors`
|
|
34
|
+
> may return stale or wrong results. **Use `SmarterCSV::Reader` directly** — errors are
|
|
35
|
+
> scoped to the reader instance and are always correct regardless of fiber context.
|
|
36
|
+
|
|
37
|
+
### Bug Fixes
|
|
38
|
+
|
|
39
|
+
* fixed [#325](https://github.com/tilo/smarter_csv/issues/325): `col_sep` in quoted headers was handled incorrectly; Thanks to Paho Lurie-Gregg.
|
|
40
|
+
* fixed issue with quoted numeric fields that were not converted to numeric
|
|
41
|
+
|
|
42
|
+
### Tests
|
|
43
|
+
|
|
44
|
+
* Added 163 tests covering new features and corner cases
|
|
45
|
+
|
|
4
46
|
## 1.16.0 (2026-03-12) — Minor Breaking Change
|
|
5
47
|
|
|
6
48
|
[Full details](docs/releases/1.16.0/changes.md) · [Benchmarks](docs/releases/1.16.0/benchmarks.md) · [Performance notes](docs/releases/1.16.0/performance_notes.md)
|
|
7
49
|
|
|
50
|
+
RSpec tests: **714 → 1,247** (+533 tests)
|
|
51
|
+
|
|
8
52
|
### Minor Breaking Change
|
|
9
53
|
|
|
10
54
|
New option **`quote_boundary:`**
|
data/CONTRIBUTORS.md
CHANGED
|
@@ -64,3 +64,4 @@ A Big Thank you to everyone who filed issues, sent comments, and who contributed
|
|
|
64
64
|
* [Mark Bumiller](https://github.com/makrsmark)
|
|
65
65
|
* [Tophe](https://github.com/tophe)
|
|
66
66
|
* [Dom Lebron](https://github.com/biglebronski)
|
|
67
|
+
* [Paho Lurie-Gregg](https://github.com/paholg)
|
data/README.md
CHANGED
|
@@ -3,9 +3,14 @@
|
|
|
3
3
|
|
|
4
4
|
 [](https://codecov.io/gh/tilo/smarter_csv) [View on RubyGems](https://rubygems.org/gems/smarter_csv) [View on RubyToolbox](https://www.ruby-toolbox.com/search?q=smarter_csv)
|
|
5
5
|
|
|
6
|
-
SmarterCSV is a high-performance CSV ingestion and generation for Ruby, focused on
|
|
6
|
+
SmarterCSV is a high-performance CSV ingestion and generation for Ruby, focused on fast end-to-end CSV ingestion of real-world data — no silent failures, no surprises, not just tokenization.
|
|
7
7
|
|
|
8
|
-
⭐ If SmarterCSV saved you hours of import time, please star the repo.
|
|
8
|
+
⭐ If SmarterCSV saved you hours of import time, please star the repo, and consider sponsoring this project.
|
|
9
|
+
|
|
10
|
+
Ruby's built-in CSV library has 10 documented failure modes that can silently corrupt or lose data — duplicate headers, blank header cells, extra columns, BOMs, whitespace, encoding issues, and more — all without raising an exception.
|
|
11
|
+
SmarterCSV handles 8 our of 10 by default, and the remaining 2 with a single option each.
|
|
12
|
+
|
|
13
|
+
> See [**Ruby CSV Pitfalls**](docs/ruby_csv_pitfalls.md) for 10 ways `CSV.read` silently corrupts or loses data, and how SmarterCSV handles them.
|
|
9
14
|
|
|
10
15
|
Beyond raw speed, SmarterCSV is designed to provide a significantly more convenient and developer-friendly interface than traditional CSV libraries. Instead of returning raw arrays that require substantial post-processing, SmarterCSV produces Rails-ready hashes for each row, making the data immediately usable with ActiveRecord, Sidekiq pipelines, parallel processing, and JSON-based workflows such as S3.
|
|
11
16
|
|
|
@@ -66,7 +71,7 @@ rows = SmarterCSV.process('data.csv')
|
|
|
66
71
|
data = SmarterCSV.parse(csv_string)
|
|
67
72
|
```
|
|
68
73
|
|
|
69
|
-
See [**Migrating from Ruby CSV**](docs/migrating_from_csv.md) for a full comparison of options, behavior differences, and a quick-reference table.
|
|
74
|
+
* See [**Migrating from Ruby CSV**](docs/migrating_from_csv.md) for a full comparison of options, behavior differences, and a quick-reference table.
|
|
70
75
|
|
|
71
76
|
## Examples
|
|
72
77
|
|
|
@@ -205,6 +210,7 @@ Or install it yourself as:
|
|
|
205
210
|
|
|
206
211
|
* [Introduction](docs/_introduction.md)
|
|
207
212
|
* [**Migrating from Ruby CSV**](docs/migrating_from_csv.md)
|
|
213
|
+
* [Ruby CSV Pitfalls](docs/ruby_csv_pitfalls.md)
|
|
208
214
|
* [Parsing Strategy](docs/parsing_strategy.md)
|
|
209
215
|
* [The Basic Read API](docs/basic_read_api.md)
|
|
210
216
|
* [The Basic Write API](docs/basic_write_api.md)
|
|
@@ -243,7 +249,7 @@ For reporting issues, please:
|
|
|
243
249
|
* open a pull-request adding a test that demonstrates the issue
|
|
244
250
|
* mention your version of SmarterCSV, Ruby, Rails
|
|
245
251
|
|
|
246
|
-
# [A Special Thanks to all
|
|
252
|
+
# [A Special Thanks to all 62 Contributors!](CONTRIBUTORS.md) 🎉🎉🎉
|
|
247
253
|
|
|
248
254
|
|
|
249
255
|
## Contributing
|
data/docs/_introduction.md
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
* [**Introduction**](./_introduction.md)
|
|
5
5
|
* [Migrating from Ruby CSV](./migrating_from_csv.md)
|
|
6
|
+
* [Ruby CSV Pitfalls](./ruby_csv_pitfalls.md)
|
|
6
7
|
* [Parsing Strategy](./parsing_strategy.md)
|
|
7
8
|
* [The Basic Read API](./basic_read_api.md)
|
|
8
9
|
* [The Basic Write API](./basic_write_api.md)
|
|
@@ -29,7 +30,11 @@
|
|
|
29
30
|
|
|
30
31
|
## Why another CSV library?
|
|
31
32
|
|
|
32
|
-
Ruby's built-in `csv` library
|
|
33
|
+
**Inconvenient.** Ruby's built-in `csv` library returns arrays of arrays, which means your application code must handle column indexing, header normalization, type conversion, and whitespace stripping manually. It also has no built-in support for chunked or parallel processing of large files.
|
|
34
|
+
|
|
35
|
+
**Hidden failure modes.** `CSV.read` has 10 ways to silently corrupt or lose data — no exception, no warning, no log line. Duplicate headers, blank header cells, extra columns, BOMs, whitespace, inconsistent empty-field representation, runaway quoted fields, and encoding issues all fail silently. See [Ruby CSV Pitfalls](./ruby_csv_pitfalls.md) for reproducible examples and the SmarterCSV fix for each.
|
|
36
|
+
|
|
37
|
+
**Slow.** On top of everything else, it is up to 129× slower than SmarterCSV for equivalent end-to-end work.
|
|
33
38
|
|
|
34
39
|

|
|
35
40
|
|
data/docs/bad_row_quarantine.md
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
* [Introduction](./_introduction.md)
|
|
5
5
|
* [Migrating from Ruby CSV](./migrating_from_csv.md)
|
|
6
|
+
* [Ruby CSV Pitfalls](./ruby_csv_pitfalls.md)
|
|
6
7
|
* [Parsing Strategy](./parsing_strategy.md)
|
|
7
8
|
* [The Basic Read API](./basic_read_api.md)
|
|
8
9
|
* [The Basic Write API](./basic_write_api.md)
|
|
@@ -55,27 +56,33 @@ SmarterCSV.process('data.csv')
|
|
|
55
56
|
# => raises SmarterCSV::MalformedCSV on the first bad row
|
|
56
57
|
```
|
|
57
58
|
|
|
58
|
-
|
|
59
|
+
The `on_bad_row` option controls what happens when a bad row is encountered:
|
|
59
60
|
|
|
60
|
-
|
|
61
|
-
`
|
|
61
|
+
* `on_bad_row: :raise` (default) fails fast.
|
|
62
|
+
* `on_bad_row: :collect` quarantines them — error records available via `SmarterCSV.errors` or `reader.errors`.
|
|
63
|
+
* `on_bad_row: ->(rec) { ... }` calls your lambda per bad row — works with both `SmarterCSV.process` and `SmarterCSV::Reader`.
|
|
64
|
+
* `on_bad_row: :skip` discards bad rows silently — count available via `SmarterCSV.errors` or `reader.errors`.
|
|
62
65
|
|
|
63
|
-
|
|
64
|
-
reader = SmarterCSV::Reader.new('data.csv', on_bad_row: :skip)
|
|
65
|
-
result = reader.process
|
|
66
|
+
### `:collect`
|
|
66
67
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
Continue processing and store a structured error record for each bad row.
|
|
69
|
+
Error records are available via `SmarterCSV.errors[:bad_rows]` (class-level API)
|
|
70
|
+
or `reader.errors[:bad_rows]` (Reader API).
|
|
70
71
|
|
|
71
|
-
|
|
72
|
+
```ruby
|
|
73
|
+
# Class-level API — use SmarterCSV.errors after the call
|
|
74
|
+
good_rows = SmarterCSV.process('data.csv', on_bad_row: :collect)
|
|
72
75
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
good_rows.each { |row| MyModel.create!(row) }
|
|
77
|
+
|
|
78
|
+
SmarterCSV.errors[:bad_rows].each do |rec|
|
|
79
|
+
Rails.logger.warn "Bad row at line #{rec[:csv_line_number]}: #{rec[:error_message]}"
|
|
80
|
+
Rails.logger.warn "Raw content: #{rec[:raw_logical_line]}"
|
|
81
|
+
end
|
|
82
|
+
```
|
|
77
83
|
|
|
78
84
|
```ruby
|
|
85
|
+
# Reader API — use when you also need access to headers or other reader state
|
|
79
86
|
reader = SmarterCSV::Reader.new('data.csv', on_bad_row: :collect)
|
|
80
87
|
result = reader.process
|
|
81
88
|
|
|
@@ -90,35 +97,50 @@ end
|
|
|
90
97
|
### Callable (lambda / proc)
|
|
91
98
|
|
|
92
99
|
Pass any object that responds to `#call`. It is invoked once per bad row with the
|
|
93
|
-
error record hash, then processing continues.
|
|
94
|
-
|
|
100
|
+
error record hash, then processing continues. Because the lambda receives errors
|
|
101
|
+
inline, **this works with both `SmarterCSV.process` and `SmarterCSV::Reader`** —
|
|
102
|
+
you do not need a `Reader` instance to handle bad rows.
|
|
103
|
+
|
|
104
|
+
```ruby
|
|
105
|
+
# Works with SmarterCSV.process — no Reader instance needed
|
|
106
|
+
bad_rows = []
|
|
107
|
+
good_rows = SmarterCSV.process('data.csv',
|
|
108
|
+
on_bad_row: ->(rec) { bad_rows << rec })
|
|
109
|
+
```
|
|
95
110
|
|
|
96
111
|
```ruby
|
|
97
112
|
# Log to a dead-letter file
|
|
98
113
|
quarantine = File.open('quarantine.csv', 'w')
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
on_bad_row: ->(rec) { quarantine.puts(rec[:raw_logical_line]) }
|
|
102
|
-
)
|
|
103
|
-
reader.process
|
|
114
|
+
SmarterCSV.process('data.csv',
|
|
115
|
+
on_bad_row: ->(rec) { quarantine.puts(rec[:raw_logical_line]) })
|
|
104
116
|
quarantine.close
|
|
105
117
|
```
|
|
106
118
|
|
|
107
119
|
```ruby
|
|
108
120
|
# Send to a monitoring system
|
|
109
|
-
|
|
110
|
-
on_bad_row: ->(rec) { Metrics.increment('csv.bad_rows', tags: { error: rec[:error_class].name }) }
|
|
111
|
-
)
|
|
112
|
-
reader.process
|
|
121
|
+
SmarterCSV.process('data.csv',
|
|
122
|
+
on_bad_row: ->(rec) { Metrics.increment('csv.bad_rows', tags: { error: rec[:error_class].name }) })
|
|
113
123
|
```
|
|
114
124
|
|
|
125
|
+
### `:skip`
|
|
126
|
+
|
|
127
|
+
Silently skip bad rows and continue. The count of skipped rows is available via
|
|
128
|
+
`SmarterCSV.errors[:bad_row_count]` (class-level API) or `reader.errors[:bad_row_count]`
|
|
129
|
+
(Reader API). No error records are stored.
|
|
130
|
+
|
|
115
131
|
```ruby
|
|
116
|
-
#
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
132
|
+
# Class-level API — use SmarterCSV.errors after the call
|
|
133
|
+
SmarterCSV.process('data.csv', on_bad_row: :skip)
|
|
134
|
+
puts "Skipped: #{SmarterCSV.errors[:bad_row_count] || 0} bad rows"
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
```ruby
|
|
138
|
+
# Reader API — access reader.errors directly
|
|
139
|
+
reader = SmarterCSV::Reader.new('data.csv', on_bad_row: :skip)
|
|
121
140
|
result = reader.process
|
|
141
|
+
|
|
142
|
+
puts "Processed: #{result.size} good rows"
|
|
143
|
+
puts "Skipped: #{reader.errors[:bad_row_count] || 0} bad rows"
|
|
122
144
|
```
|
|
123
145
|
|
|
124
146
|
## Error record structure
|
|
@@ -173,15 +195,50 @@ end
|
|
|
173
195
|
|
|
174
196
|
## Accessing errors
|
|
175
197
|
|
|
176
|
-
|
|
198
|
+
There are two ways to access bad row data after processing:
|
|
199
|
+
|
|
200
|
+
### Via `SmarterCSV.errors` (class-level API)
|
|
201
|
+
|
|
202
|
+
`SmarterCSV.errors` returns the errors from the most recent call to `process`, `parse`,
|
|
203
|
+
`each`, or `each_chunk` on the current thread. It is cleared at the start of each new call.
|
|
204
|
+
|
|
205
|
+
```ruby
|
|
206
|
+
SmarterCSV.process('data.csv', on_bad_row: :skip)
|
|
207
|
+
puts SmarterCSV.errors[:bad_row_count] # => 3
|
|
208
|
+
|
|
209
|
+
SmarterCSV.process('data.csv', on_bad_row: :collect)
|
|
210
|
+
puts SmarterCSV.errors[:bad_row_count] # => 3
|
|
211
|
+
puts SmarterCSV.errors[:bad_rows].size # => 3
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
> **Note:** `SmarterCSV.errors` only surfaces errors from the **most recent run on the
|
|
215
|
+
> current thread**. In a multi-threaded environment (Puma, Sidekiq), each thread maintains
|
|
216
|
+
> its own error state independently. If you call `SmarterCSV.process` twice in the same
|
|
217
|
+
> thread, the second call's errors replace the first's. For long-running or complex
|
|
218
|
+
> pipelines where you need to aggregate errors across multiple files, use the Reader API.
|
|
219
|
+
>
|
|
220
|
+
> ⚠️ **Fibers:** `SmarterCSV.errors` uses `Thread.current` for storage, which is **shared
|
|
221
|
+
> across all fibers running in the same thread**. If you process CSV files concurrently
|
|
222
|
+
> in fibers (e.g. with `Async`, `Falcon`, or manual `Fiber` scheduling), `SmarterCSV.errors`
|
|
223
|
+
> may return stale or wrong results. **Use `SmarterCSV::Reader` directly** — errors are
|
|
224
|
+
> scoped to the reader instance and are always correct regardless of fiber context.
|
|
225
|
+
|
|
226
|
+
### Via `reader.errors` (Reader API)
|
|
227
|
+
|
|
228
|
+
For full control — including access to headers, raw headers, and errors from a specific
|
|
229
|
+
call — use `SmarterCSV::Reader` directly:
|
|
177
230
|
|
|
178
231
|
| Attribute | Description |
|
|
179
232
|
|-----------|-------------|
|
|
180
233
|
| `reader.errors[:bad_row_count]` | Total bad rows encountered (all modes) |
|
|
181
234
|
| `reader.errors[:bad_rows]` | Array of error records (`:collect` mode only) |
|
|
182
235
|
|
|
183
|
-
|
|
184
|
-
|
|
236
|
+
```ruby
|
|
237
|
+
reader = SmarterCSV::Reader.new('data.csv', on_bad_row: :collect)
|
|
238
|
+
reader.process
|
|
239
|
+
puts reader.errors[:bad_row_count]
|
|
240
|
+
puts reader.headers.inspect
|
|
241
|
+
```
|
|
185
242
|
|
|
186
243
|
## Chunked processing
|
|
187
244
|
|
data/docs/basic_read_api.md
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
* [Introduction](./_introduction.md)
|
|
5
5
|
* [Migrating from Ruby CSV](./migrating_from_csv.md)
|
|
6
|
+
* [Ruby CSV Pitfalls](./ruby_csv_pitfalls.md)
|
|
6
7
|
* [Parsing Strategy](./parsing_strategy.md)
|
|
7
8
|
* [**The Basic Read API**](./basic_read_api.md)
|
|
8
9
|
* [The Basic Write API](./basic_write_api.md)
|
data/docs/basic_write_api.md
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
* [Introduction](./_introduction.md)
|
|
5
5
|
* [Migrating from Ruby CSV](./migrating_from_csv.md)
|
|
6
|
+
* [Ruby CSV Pitfalls](./ruby_csv_pitfalls.md)
|
|
6
7
|
* [Parsing Strategy](./parsing_strategy.md)
|
|
7
8
|
* [The Basic Read API](./basic_read_api.md)
|
|
8
9
|
* [**The Basic Write API**](./basic_write_api.md)
|
data/docs/batch_processing.md
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
* [Introduction](./_introduction.md)
|
|
5
5
|
* [Migrating from Ruby CSV](./migrating_from_csv.md)
|
|
6
|
+
* [Ruby CSV Pitfalls](./ruby_csv_pitfalls.md)
|
|
6
7
|
* [Parsing Strategy](./parsing_strategy.md)
|
|
7
8
|
* [The Basic Read API](./basic_read_api.md)
|
|
8
9
|
* [The Basic Write API](./basic_write_api.md)
|
data/docs/column_selection.md
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
* [Introduction](./_introduction.md)
|
|
5
5
|
* [Migrating from Ruby CSV](./migrating_from_csv.md)
|
|
6
|
+
* [Ruby CSV Pitfalls](./ruby_csv_pitfalls.md)
|
|
6
7
|
* [Parsing Strategy](./parsing_strategy.md)
|
|
7
8
|
* [The Basic Read API](./basic_read_api.md)
|
|
8
9
|
* [The Basic Write API](./basic_write_api.md)
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
* [Introduction](./_introduction.md)
|
|
5
5
|
* [Migrating from Ruby CSV](./migrating_from_csv.md)
|
|
6
|
+
* [Ruby CSV Pitfalls](./ruby_csv_pitfalls.md)
|
|
6
7
|
* [Parsing Strategy](./parsing_strategy.md)
|
|
7
8
|
* [The Basic Read API](./basic_read_api.md)
|
|
8
9
|
* [The Basic Write API](./basic_write_api.md)
|
data/docs/examples.md
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
* [Introduction](./_introduction.md)
|
|
5
5
|
* [Migrating from Ruby CSV](./migrating_from_csv.md)
|
|
6
|
+
* [Ruby CSV Pitfalls](./ruby_csv_pitfalls.md)
|
|
6
7
|
* [Parsing Strategy](./parsing_strategy.md)
|
|
7
8
|
* [The Basic Read API](./basic_read_api.md)
|
|
8
9
|
* [The Basic Write API](./basic_write_api.md)
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
* [Introduction](./_introduction.md)
|
|
5
5
|
* [Migrating from Ruby CSV](./migrating_from_csv.md)
|
|
6
|
+
* [Ruby CSV Pitfalls](./ruby_csv_pitfalls.md)
|
|
6
7
|
* [Parsing Strategy](./parsing_strategy.md)
|
|
7
8
|
* [The Basic Read API](./basic_read_api.md)
|
|
8
9
|
* [The Basic Write API](./basic_write_api.md)
|
data/docs/header_validations.md
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
* [Introduction](./_introduction.md)
|
|
5
5
|
* [Migrating from Ruby CSV](./migrating_from_csv.md)
|
|
6
|
+
* [Ruby CSV Pitfalls](./ruby_csv_pitfalls.md)
|
|
6
7
|
* [Parsing Strategy](./parsing_strategy.md)
|
|
7
8
|
* [The Basic Read API](./basic_read_api.md)
|
|
8
9
|
* [The Basic Write API](./basic_write_api.md)
|
data/docs/history.md
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
* [Introduction](./_introduction.md)
|
|
5
5
|
* [Migrating from Ruby CSV](./migrating_from_csv.md)
|
|
6
|
+
* [Ruby CSV Pitfalls](./ruby_csv_pitfalls.md)
|
|
6
7
|
* [Parsing Strategy](./parsing_strategy.md)
|
|
7
8
|
* [The Basic Read API](./basic_read_api.md)
|
|
8
9
|
* [The Basic Write API](./basic_write_api.md)
|
|
@@ -59,6 +60,7 @@ The first gem release was **v1.0.1 on 2012-07-30**.
|
|
|
59
60
|
| 1.15.1 | 2026-02-17 | Fix for backslash in quoted fields (`quote_escaping:` option) |
|
|
60
61
|
| 1.15.2 | 2026-02-20 | Further C-path optimisations; 5.4×–37.4× faster than 1.14.4 |
|
|
61
62
|
| **1.16.0** | **2026-03-12** | **New `each`/`each_chunk` enumerator API; `SmarterCSV.parse`; bad row quarantine; column selection `headers: { only: }`; 1.8×–8.6× faster than Ruby CSV.read; new features for Reader and Writer; minor breaking: `quote_boundary: :standard`** |
|
|
63
|
+
| 1.16.1 | 2026-03-16 | `SmarterCSV.errors` class-level error access; fix `col_sep` in quoted headers (#325); fix quoted numeric conversion |
|
|
62
64
|
|
|
63
65
|
---
|
|
64
66
|
|
data/docs/instrumentation.md
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
* [Introduction](./_introduction.md)
|
|
5
5
|
* [Migrating from Ruby CSV](./migrating_from_csv.md)
|
|
6
|
+
* [Ruby CSV Pitfalls](./ruby_csv_pitfalls.md)
|
|
6
7
|
* [Parsing Strategy](./parsing_strategy.md)
|
|
7
8
|
* [The Basic Read API](./basic_read_api.md)
|
|
8
9
|
* [The Basic Write API](./basic_write_api.md)
|