From 25b6571f1d9263667f9a0b68792fa3a324d2e49d Mon Sep 17 00:00:00 2001 From: Andy Date: Fri, 31 Oct 2025 13:51:02 -0400 Subject: [PATCH] Fix Ruby 3.4 frozen string literal warnings with StringIO PR #123 fixed frozen string warnings on line 127 (buffer creation) but missed the warnings triggered by calling binmode/set_encoding on StringIO objects. These methods modify StringIO's internal string encoding, which triggers Ruby 3.4 deprecation warnings when running with verbose warnings (-W2), which Rails test suites use by default. Solution: Make StringIO readonly before encoding modifications by calling close_write. This prevents the internal string modification that triggers the warning, while still allowing binmode/set_encoding to execute normally. Only affects StringIO objects (which have closed_write? method) - File objects are unaffected. This workaround should only be needed until Ruby 3.5, which will include an upstream fix: https://redmine.ruby-lang.org/issues/21280 --- lib/marcel/magic.rb | 6 +++++- test/magic_test.rb | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/lib/marcel/magic.rb b/lib/marcel/magic.rb index 60e3387..85cb96b 100644 --- a/lib/marcel/magic.rb +++ b/lib/marcel/magic.rb @@ -115,7 +115,11 @@ def self.child?(child, parent) end def self.magic_match(io, method) - return magic_match(StringIO.new(io.to_s), method) unless io.respond_to?(:read) + return magic_match(StringIO.new(+io.to_s), method) unless io.respond_to?(:read) + + # Make StringIO readonly before encoding changes to prevent Ruby 3.4 frozen string warnings. + # Should be fixed in Ruby 3.5+: https://redmine.ruby-lang.org/issues/21280 + io.close_write if io.respond_to?(:closed_write?) && !io.closed_write? io.binmode if io.respond_to?(:binmode) io.set_encoding(Encoding::BINARY) if io.respond_to?(:set_encoding) diff --git a/test/magic_test.rb b/test/magic_test.rb index 14d11e2..3bf8ad1 100644 --- a/test/magic_test.rb +++ b/test/magic_test.rb @@ -25,4 +25,25 @@ class Marcel::MimeType::MagicTest < Marcel::TestCase assert Marcel::Magic.child?('text/csv', 'text/plain') refute Marcel::Magic.child?('text/plain', 'text/csv') end + + test "no Ruby 3.4 frozen string warnings with StringIO" do + # Ruby 3.4 warns about code that will break when frozen string literals become default + # This test ensures marcel handles StringIO with frozen strings correctly + content = "Test content for mime detection" + io = StringIO.new(content) + + # Capture warnings + warnings = [] + original_stderr = $stderr + $stderr = StringIO.new + + begin + Marcel::MimeType.for(io) + warnings = $stderr.string.lines.grep(/marcel.*magic\.rb.*frozen/) + ensure + $stderr = original_stderr + end + + assert_empty warnings, "Expected no frozen string warnings, but got:\n#{warnings.join}" + end end