smarter_csv 1.17.3 → 1.17.4

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: ec50e8539c6872f9c86c25eabc2982e39846ad07dc5a21021fc687c7661f8084
4
- data.tar.gz: 977ce04d8dd225b6042ea03ad0c174305f3ea122340fad052e2c2ada440d6400
3
+ metadata.gz: 140f9359c26f8903b9075faeb59e9c1fc4b5c4b9dd5fcef664e12bb53fe13073
4
+ data.tar.gz: 0e7be3195610bcddb77870744a24f0eee9431643c4c28fcbea12d4b8663bb2db
5
5
  SHA512:
6
- metadata.gz: 0452dc7f15ab31b0cdfad83ca718e17e6456cf6c9826d177e606c5924f3ec72a155c86ee6f9f938540fe3b2ed8f694a981c95cf775b5a38d7f7e44318bc453a3
7
- data.tar.gz: c1c9732d6d4393fb2ffa995f0c7bb73cd60f566132d487a86007f8a5a623257365c2324ed894a5b38d681df7cfab67069d9fa2e61fd525ba51675954ddadad7a
6
+ metadata.gz: 176daa024372ade1d6431e5e4fd5355175cd1ebf03e7a216d70b8ff4554eb259a023969783b53118eba6c3fbf747a08e286ed7daad455ebc5cf8d7b61475d3d1
7
+ data.tar.gz: 8b1ca7263e5a54fc642c8f76bfd56577f6056784910acdcdcd0f18c6222c8793280016e749f2e08a61485bf8255cf341250ac1250a8d3ff424f8b07b1edbd51b
data/CHANGELOG.md CHANGED
@@ -2,7 +2,15 @@
2
2
  # SmarterCSV 1.x Change Log
3
3
 
4
4
  > [!TIP]
5
- > **Upgrading?** The [SmarterCSV Upgrade Wizard](https://tilo.github.io/smarter_csv/upgrade_wizard.html) walks you through what (if anything) you need to change for your specific version. Most hops do not require any changes.
5
+ > **Upgrading?** The [SmarterCSV Upgrade Wizard](https://tilo.github.io/smarter_csv/upgrade_wizard.html) walks you through what (if anything) you need to change for your specific version. Most steps do not require any changes.
6
+
7
+ ## 1.17.4 (2026-06-03)
8
+
9
+ ### Bug Fix
10
+
11
+ - fixed [Issue #337](https://github.com/tilo/smarter_csv/issues/337): `Pathname` input no longer worked (regression since 1.17.0); passing a `Pathname` raised `NoMethodError: private method 'gets' called`. `SmarterCSV` now opens any path-like input (`String` or `Pathname`) and reads directly from any already-open IO. Thanks to [Alex Shenia](https://github.com/alexshenia)
12
+
13
+
6
14
 
7
15
  ## 1.17.3 (2026-05-26)
8
16
 
data/CONTRIBUTORS.md CHANGED
@@ -1,4 +1,4 @@
1
- # A Big Thank You to all 64 Contributors!!
1
+ # A Big Thank You to all 65 Contributors!!
2
2
 
3
3
 
4
4
  A Big Thank you to everyone who filed issues, sent comments, and who contributed with pull requests:
@@ -67,3 +67,4 @@ A Big Thank you to everyone who filed issues, sent comments, and who contributed
67
67
  * [Paho Lurie-Gregg](https://github.com/paholg)
68
68
  * [Jonas Staškevičius](https://github.com/pirminis)
69
69
  * [conorg](https://github.com/conorg)
70
+ * [Alex Shenia](https://github.com/alexshenia)
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  ![Gem Version](https://img.shields.io/gem/v/smarter_csv) [![codecov](https://codecov.io/gh/tilo/smarter_csv/branch/main/graph/badge.svg?token=1L7OD80182)](https://codecov.io/gh/tilo/smarter_csv) [![Downloads](https://img.shields.io/gem/dt/smarter_csv)](https://rubygems.org/gems/smarter_csv) [![RubyGems](https://img.shields.io/badge/RubyGems-smarter__csv-brightgreen?logo=rubygems&logoColor=white)](https://rubygems.org/gems/smarter_csv) [![Ruby Toolbox](https://img.shields.io/badge/Ruby%20Toolbox-smarter__csv-brightgreen)](https://www.ruby-toolbox.com/projects/smarter_csv) [![Upgrade Wizard](https://img.shields.io/badge/Upgrade%20Wizard-Try%20it-2c7a2c?style=flat)](https://tilo.github.io/smarter_csv/upgrade_wizard.html)
5
5
 
6
6
  > [!TIP]
7
- > **Upgrading from an older version?** Use the [SmarterCSV Upgrade Wizard](https://tilo.github.io/smarter_csv/upgrade_wizard.html) to walk through what (if anything) you need to change for your specific version. Most hops do not require any changes.
7
+ > **Upgrading from an older version?** Use the [SmarterCSV Upgrade Wizard](https://tilo.github.io/smarter_csv/upgrade_wizard.html) to walk through what (if anything) you need to change for your specific version. Most steps do not require any changes.
8
8
 
9
9
  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.
10
10
 
@@ -336,7 +336,7 @@ For reporting issues, please:
336
336
  * open a pull-request adding a test that demonstrates the issue
337
337
  * mention your version of SmarterCSV, Ruby, Rails
338
338
 
339
- # [A Special Thanks to all 64 Contributors!](CONTRIBUTORS.md) 🎉🎉🎉
339
+ # [A Special Thanks to all 65 Contributors!](CONTRIBUTORS.md) 🎉🎉🎉
340
340
 
341
341
 
342
342
  ## Contributing
@@ -153,7 +153,7 @@ button.primary:disabled:hover { background: #e6e6e6; }
153
153
  <body>
154
154
 
155
155
  <h1>SmarterCSV Upgrade Wizard</h1>
156
- <p class="muted">This wizard walks you from your current version to the latest, one hop at a time.<br><br>Only the questions where you answer "Yes" will show migration steps.<br>Question answered with "No" represent risk-free upgrades.</p><br><br>
156
+ <p class="muted">This wizard walks you from your current version to the latest, one step at a time.<br><br>Only the questions where you answer "Yes" will show migration steps.<br>Question answered with "No" represent risk-free upgrades.</p><br><br>
157
157
 
158
158
  <div id="app"></div>
159
159
 
@@ -228,7 +228,7 @@ function renderHop(series, originalVersion) {
228
228
  let body;
229
229
  if (hop.actions.length === 0) {
230
230
  body = `<div class="hop dropin">
231
- Drop-in hop: no code changes needed for ${series}.x &rarr; ${targetRelease}. Just continue.
231
+ You can upgrade directly to version ${targetRelease}. No changes needed.
232
232
  </div>`;
233
233
  } else {
234
234
  body = `<div class="hop">
@@ -246,7 +246,7 @@ function renderHop(series, originalVersion) {
246
246
  }
247
247
 
248
248
  const nextLabel = hop.to === LATEST ? `Finish at ${targetRelease} &rarr;` : `Continue to ${targetRelease} &rarr;`;
249
- const reminder = hop.actions.length === 0 ? `<p class="reminder">You can upgrade directly to the version showing on the "Continue" button. No changes needed.</p>` :
249
+ const reminder = hop.actions.length === 0 ? `<p class="reminder">You can upgrade directly to version ${targetRelease}. No changes needed.</p>` :
250
250
  `<p class="reminder">If there are actions listed above, please ensure they are fixed before clicking "Continue".</p>`;
251
251
  app.innerHTML = `
252
252
  <p class="progress">Upgrading from ${series}.x &rarr; ${targetRelease}</p>
@@ -288,7 +288,7 @@ function renderHop(series, originalVersion) {
288
288
  } else if (yesAnswers.length === 0) {
289
289
  // All answered, all "No" — nothing applies, upgrade is direct.
290
290
  reminderEl.style.display = "";
291
- reminderEl.textContent = 'You can upgrade directly to the version showing on the "Continue" button. No changes needed.';
291
+ reminderEl.textContent = `You can upgrade directly to version ${targetRelease}. No changes needed.`;
292
292
  } else {
293
293
  // All answered, at least one "Yes" — actions must be applied first.
294
294
  reminderEl.style.display = "";
@@ -342,17 +342,21 @@ function renderDone(originalVersion) {
342
342
  const fullVersion = LATEST_RELEASE || LATEST;
343
343
  const seriesOnly = LATEST;
344
344
  const summaryHTML = renderSummary();
345
+ const introHTML = decisions.length === 0
346
+ ? `<p><strong>You can upgrade directly to version ${fullVersion}. No changes needed.</strong></p>
347
+ <p>You're already in the ${seriesOnly}.x series, so the upgrade is just a Gemfile bump.</p>`
348
+ : `<p>You've completed all the upgrade steps from ${escapeHTML(originalVersion || "your current version")} to ${fullVersion} (the latest version in the ${seriesOnly}.x series).</p>`;
345
349
 
346
350
  app.innerHTML = `
347
351
  <div class="done">
348
352
  <h2>You're done</h2>
349
- <p>You've walked all hops from ${escapeHTML(originalVersion || "your current version")} to ${fullVersion} (the latest patch in the ${seriesOnly}.x series).</p>
353
+ ${introHTML}
350
354
  ${summaryHTML}
351
355
  <p>Update your <code>Gemfile</code> to:</p>
352
356
  <pre><code>gem 'smarter_csv', '~&gt; ${seriesOnly}.0'</code></pre>
353
357
  <p>Then run:</p>
354
358
  <pre><code>bundle update smarter_csv</code></pre>
355
- <p>After that, run your test suite. If anything behaves unexpectedly, click "Start over" and walk back through the hops to find the migration step you might have missed.</p>
359
+ <p>After that, run your test suite. If anything behaves unexpectedly, click "Start over" and walk back through the steps to find the migration item you might have missed.</p>
356
360
  <p class="muted">Questions? Open an issue at <a href="https://github.com/tilo/smarter_csv/issues">github.com/tilo/smarter_csv/issues</a>.</p>
357
361
  <p><button id="restart">Start over</button></p>
358
362
  </div>
@@ -366,17 +370,17 @@ function renderSummary() {
366
370
  const matchedCount = decisions.reduce((n, d) => n + d.matched.length, 0);
367
371
  const dropInCount = decisions.filter(d => d.dropIn).length;
368
372
  const intro = matchedCount === 0
369
- ? `<p>Good news: <strong>none of the per-hop conditions applied to your code</strong> across the ${decisions.length} hops you walked${dropInCount ? ` (${dropInCount} were drop-in hops)` : ""}. The upgrade should be a clean Gemfile bump.</p>`
370
- : `<p>Across ${decisions.length} hops you walked, <strong>${matchedCount} migration step${matchedCount === 1 ? "" : "s"} apply</strong> to your code. Review and apply them before running <code>bundle update</code>:</p>`;
373
+ ? `<p>Good news &mdash; <strong>none of the conditions applied to your code</strong>. The upgrade is just a Gemfile bump.</p>`
374
+ : `<p>Apply the following <strong>${matchedCount} change${matchedCount === 1 ? "" : "s"}</strong> to your code, then run <code>bundle update</code>:</p>`;
371
375
 
372
376
  const list = decisions.map(d => {
373
377
  const targetRelease = latestReleaseFor(d.to);
374
378
  const heading = `<p class="summary-hop-heading"><strong>${d.from}.x &rarr; ${targetRelease}</strong></p>`;
375
379
  if (d.dropIn) {
376
- return `<div class="summary-hop">${heading}<p class="muted">Drop-in hop &mdash; no code changes needed.</p></div>`;
380
+ return `<div class="summary-hop">${heading}<p class="muted">No code changes needed for this step.</p></div>`;
377
381
  }
378
382
  if (d.matched.length === 0) {
379
- return `<div class="summary-hop">${heading}<p class="muted">None of the conditions on this hop applied to your code.</p></div>`;
383
+ return `<div class="summary-hop">${heading}<p class="muted">None of the conditions in this step applied to your code.</p></div>`;
380
384
  }
381
385
  const items = d.matched.map(a => `<li><strong>If</strong> ${a["if"]}<br>&rarr; ${a.then}</li>`).join("");
382
386
  return `<div class="summary-hop">${heading}<ul>${items}</ul></div>`;
@@ -32,13 +32,14 @@ module SmarterCSV
32
32
  # rubocop:disable Naming/MethodName
33
33
  def headerA
34
34
  record_warning(type: :deprecation, code: :header_a_method) do
35
- "Deprecarion Warning: 'headerA' will be removed in future versions. Use 'headders'"
35
+ "Deprecation Warning: 'headerA' will be removed in future versions. Use 'headers'"
36
36
  end
37
37
  @headerA
38
38
  end
39
39
  # rubocop:enable Naming/MethodName
40
40
 
41
- # first parameter: filename or input object which responds to readline method
41
+ # first parameter: a path (String or Pathname) to open, or an already-open readable IO
42
+ # (anything responding to #gets — File, StringIO, Tempfile, Zlib::GzipReader, pipes, ...)
42
43
  def initialize(input, given_options = {})
43
44
  @input = input
44
45
  @has_rails = !!defined?(Rails)
@@ -123,7 +124,14 @@ module SmarterCSV
123
124
  @verbose = options[:verbose]
124
125
 
125
126
  begin
126
- fh = input.is_a?(String) ? File.open(input, "r:#{options[:file_encoding]}") : input
127
+ # Decide whether `input` is an already-open, readable stream or a path we must open.
128
+ # The reader reads lines via #gets (see file_io.rb and PeekableIO), so a public #gets
129
+ # is exactly what we need: real IOs (File, StringIO, Tempfile, Zlib::GzipReader, pipes,
130
+ # custom non-seekable streams) expose it, while path-like inputs (String, Pathname) do
131
+ # not — their only #gets is the private Kernel#gets. 1.17.0 narrowed this to
132
+ # input.is_a?(String), which sent Pathname down the IO branch and then called its
133
+ # private Kernel#gets, raising "private method 'gets' called" (issue #337).
134
+ fh = input.respond_to?(:gets) ? input : File.open(input, "r:#{options[:file_encoding]}")
127
135
 
128
136
  # Rewindable inputs (File, Tempfile, StringIO, Zlib::GzipReader, ...) use
129
137
  # native rewind for auto-detection — no wrapper overhead in the hot loop.
@@ -272,10 +280,14 @@ module SmarterCSV
272
280
  start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) if on_start || on_complete
273
281
 
274
282
  if on_start
275
- input_meta = if @input.is_a?(String)
276
- { input: @input, file_size: (File.size(@input) rescue nil) }
277
- else
283
+ # Same path-vs-IO distinction as the File.open above: an already-open IO responds
284
+ # to #gets and we can't know its on-disk size, so we report its class name. A
285
+ # path-like input (String, or a Pathname via #to_path) gets its path and file size.
286
+ input_meta = if @input.respond_to?(:gets)
278
287
  { input: @input.class.name, file_size: nil }
288
+ else
289
+ path = @input.respond_to?(:to_path) ? @input.to_path : @input
290
+ { input: path, file_size: (File.size(path) rescue nil) }
279
291
  end
280
292
  on_start.call(input_meta.merge(col_sep: options[:col_sep], row_sep: options[:row_sep]))
281
293
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SmarterCSV
4
- VERSION = "1.17.3"
4
+ VERSION = "1.17.4"
5
5
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smarter_csv
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.17.3
4
+ version: 1.17.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tilo Sloboda
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-05-27 00:00:00.000000000 Z
10
+ date: 2026-06-03 00:00:00.000000000 Z
11
11
  dependencies: []
12
12
  description: |
13
13
  SmarterCSV is a high-performance CSV reader and writer for Ruby focused on