From c279792a36a660c46b3cb92660eed4555504af60 Mon Sep 17 00:00:00 2001 From: Brian Shand Date: Tue, 21 Oct 2025 14:42:57 +0100 Subject: [PATCH 1/2] Support Ruby 3.4. Drop support for Rails 7.0, Ruby 3.0, 3.1 --- .github/workflows/test.yml | 11 ++--------- CHANGELOG.md | 1 + gemfiles/Gemfile.rails70 | 9 --------- lib/ndr_support/string/clean_methodable.rb | 8 ++++---- ndr_support.gemspec | 6 +++--- test/utf8_encoding/control_characters_test.rb | 14 +++++++------- test/utf8_encoding_test.rb | 10 ++++------ test/yaml/serialization_test.rb | 14 +++++++------- 8 files changed, 28 insertions(+), 45 deletions(-) delete mode 100644 gemfiles/Gemfile.rails70 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1dfda55..40a9d3b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,26 +5,19 @@ on: [push] jobs: test: strategy: + fail-fast: false matrix: ruby-version: - - '3.0' - - '3.1' - '3.2' - '3.3' + - '3.4' gemfile: - - gemfiles/Gemfile.rails70 - gemfiles/Gemfile.rails71 - gemfiles/Gemfile.rails72 - gemfiles/Gemfile.rails80 exclude: - # rails 7.2 requires ruby >= 3.1 - # https://www.fastruby.io/blog/ruby/rails/versions/compatibility-table.html - - ruby-version: '3.0' - gemfile: 'gemfiles/Gemfile.rails72' # rails 8.0 requires ruby >= 3.2 # https://www.fastruby.io/blog/ruby/rails/versions/compatibility-table.html - - ruby-version: '3.0' - gemfile: 'gemfiles/Gemfile.rails80' - ruby-version: '3.1' gemfile: 'gemfiles/Gemfile.rails80' diff --git a/CHANGELOG.md b/CHANGELOG.md index 7048895..264892b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ * Exclude unneeded files from gem package * Add 2027 bank holidays * Replace Public Health England naming with NHS England +* Support Ruby 3.4. Drop support for Rails 7.0, Ruby 3.0, 3.1 ## 5.10.4 / 2024-11-13 ### Added diff --git a/gemfiles/Gemfile.rails70 b/gemfiles/Gemfile.rails70 deleted file mode 100644 index 4446ecb..0000000 --- a/gemfiles/Gemfile.rails70 +++ /dev/null @@ -1,9 +0,0 @@ -source 'https://rubygems.org' - -gemspec path: '..' - -gem 'activerecord', '~> 7.0.0' -gem 'activesupport', '~> 7.0.0' - -# Latest concurrent-ruby breaks Rails < 7.1. See https://github.com/rails/rails/issues/54260 -gem 'concurrent-ruby', '1.3.4' diff --git a/lib/ndr_support/string/clean_methodable.rb b/lib/ndr_support/string/clean_methodable.rb index 70e0332..bf16f51 100644 --- a/lib/ndr_support/string/clean_methodable.rb +++ b/lib/ndr_support/string/clean_methodable.rb @@ -125,11 +125,11 @@ def clean_roman5 end def clean_tnmcategory - sub!(/\A[tnm]/i, '') - if self =~ /\Ax\z/i - upcase + without_prefix = sub(/\A[tnm]/i, '') + if without_prefix =~ /\Ax\z/i + without_prefix.upcase else - downcase + without_prefix.downcase end end diff --git a/ndr_support.gemspec b/ndr_support.gemspec index 894ec9b..483f8ab 100644 --- a/ndr_support.gemspec +++ b/ndr_support.gemspec @@ -20,13 +20,13 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ['lib'] - spec.add_dependency 'activerecord', '>= 7.0', '< 8.1' - spec.add_dependency 'activesupport', '>= 7.0', '< 8.1' + spec.add_dependency 'activerecord', '>= 7.1', '< 8.1' + spec.add_dependency 'activesupport', '>= 7.1', '< 8.1' spec.add_development_dependency 'bundler' spec.add_development_dependency 'rake', '>= 12.3.3' - spec.required_ruby_version = '>= 3.0' + spec.required_ruby_version = '>= 3.2' # Avoid std-lib minitest (which has different namespace) spec.add_development_dependency 'minitest', '>= 5.0.0' diff --git a/test/utf8_encoding/control_characters_test.rb b/test/utf8_encoding/control_characters_test.rb index a5126b7..f580559 100644 --- a/test/utf8_encoding/control_characters_test.rb +++ b/test/utf8_encoding/control_characters_test.rb @@ -23,7 +23,7 @@ class ControlCharactersTest < Minitest::Test end test 'escape_control_chars! with harmless string' do - string = 'null \x00 characters suck' + string = +'null \x00 characters suck' expected = 'null \x00 characters suck' actual = escape_control_chars!(string) @@ -41,7 +41,7 @@ class ControlCharactersTest < Minitest::Test end test 'escape_control_chars! with unprintable control characters' do - string = "null \x00 characters suck" + string = +"null \x00 characters suck" expected = 'null 0x00 characters suck' actual = escape_control_chars!(string) @@ -50,15 +50,15 @@ class ControlCharactersTest < Minitest::Test end test 'escape_control_chars! with printable control characters' do - string = "null \x00 characters \r\n really \t suck \x07\x07\x07" + string = +"null \x00 characters \r\n really \t suck \x07\x07\x07" expected = "null 0x00 characters \r\n really \t suck 0x070x070x07" # ring ring ring assert_equal expected, escape_control_chars!(string) end test 'escape_control_chars_in_object! with array' do - array = %W( hello\tcruel \x00 world!\n \x07 ) - expected = %W( hello\tcruel 0x00 world!\n 0x07 ) + array = %W[hello\tcruel \x00 world!\n \x07].collect(&:dup) + expected = %W[hello\tcruel 0x00 world!\n 0x07] actual = escape_control_chars_in_object!(array) assert_equal expected, actual @@ -66,8 +66,8 @@ class ControlCharactersTest < Minitest::Test end test 'escape_control_chars_in_object! with hash' do - hash = { :a => "hello\tcruel", :b => "\x00", :c => "world!\n", :d => "\x07" } - expected = { :a => "hello\tcruel", :b => '0x00', :c => "world!\n", :d => '0x07' } + hash = { a: +"hello\tcruel", b: +"\x00", c: +"world!\n", d: +"\x07" } + expected = { a: "hello\tcruel", b: '0x00', c: "world!\n", d: '0x07' } actual = escape_control_chars_in_object!(hash) assert_equal expected, actual diff --git a/test/utf8_encoding_test.rb b/test/utf8_encoding_test.rb index ec7cc83..d32a2cc 100644 --- a/test/utf8_encoding_test.rb +++ b/test/utf8_encoding_test.rb @@ -1,5 +1,3 @@ -# encoding: UTF-8 - require 'test_helper' class Utf8EncodingTest < Minitest::Test @@ -14,7 +12,7 @@ class Utf8EncodingTest < Minitest::Test end test 'ensure_utf8! should return the same string' do - string1 = 'hello' + string1 = +'hello' string2 = ensure_utf8!(string1) assert string1.object_id == string2.object_id @@ -68,14 +66,14 @@ class Utf8EncodingTest < Minitest::Test end test 'coerce_utf8! should return the same string' do - string1 = 'hello' + string1 = +'hello' string2 = coerce_utf8!(string1) assert string1.object_id == string2.object_id end test 'ensure_utf8 should convert low bytes to UTF-8 if possible' do - string1 = 'hello'.force_encoding('Windows-1252') + string1 = (+'hello').force_encoding('Windows-1252') string2 = ensure_utf8!(string1) assert_equal string1, string2 @@ -83,7 +81,7 @@ class Utf8EncodingTest < Minitest::Test end test 'ensure_utf8 should convert high bytes to UTF-8 if possible' do - string1 = "dash \x96 dash".force_encoding('Windows-1252') + string1 = (+"dash \x96 dash").force_encoding('Windows-1252') assert_equal 11, string1.bytes.to_a.length assert_equal 11, string1.chars.to_a.length diff --git a/test/yaml/serialization_test.rb b/test/yaml/serialization_test.rb index 744142d..2d35d53 100644 --- a/test/yaml/serialization_test.rb +++ b/test/yaml/serialization_test.rb @@ -11,7 +11,7 @@ class SerializationTest < Minitest::Test test 'should support aliases correctly' do x = { 'c' => 5 } hash = { 'a' => x, 'b' => x } - hash_yaml = "---\na: &1\n c: 5\nb: *1\n" + hash_yaml = +"---\na: &1\n c: 5\nb: *1\n" assert_equal hash, load_yaml(hash_yaml), 'Deserialising known YAML with an alias' assert_equal hash, load_yaml(dump_yaml(hash)), 'Deserialising a structure with repeated objects' end @@ -22,24 +22,24 @@ class SerializationTest < Minitest::Test test 'should handle binary yaml with control chars' do # irb> "\xC2\xA1null \x00 characters \r\n suck!".to_yaml - yaml = "--- !binary |-\n wqFudWxsIAAgY2hhcmFjdGVycyANCiBzdWNrIQ==\n" + yaml = +"--- !binary |-\n wqFudWxsIAAgY2hhcmFjdGVycyANCiBzdWNrIQ==\n" assert_equal "¡null 0x00 characters \r\n suck!", load_yaml(yaml) # irb> {fulltext: "\xC2\xA1null \x00 characters \r\n suck!"}.to_yaml - yamled_hash = "---\n:fulltext: !binary |-\n wqFudWxsIAAgY2hhcmFjdGVycyANCiBzdWNrIQ==\n" + yamled_hash = +"---\n:fulltext: !binary |-\n wqFudWxsIAAgY2hhcmFjdGVycyANCiBzdWNrIQ==\n" assert_equal({ :fulltext => "¡null 0x00 characters \r\n suck!" }, load_yaml(yamled_hash)) end # Psych doesn't always base64-encode control characters: test 'should handle non-binary yaml with control chars' do #irb> Psych.dump("control \x01 char \n whoops!") - chr_1_yaml = "--- ! \"control \\x01 char \\n whoops!\"\n" + chr_1_yaml = +"--- ! \"control \\x01 char \\n whoops!\"\n" assert_equal "control 0x01 char \n whoops!", load_yaml(chr_1_yaml) end test 'should handle non-binary yaml with escaped things that look like control chars' do # irb> Psych.dump(['\\x01 \\xAF \\\\xAF', "\x01 \\\x01 \x01\\\x01"]) - escaped_yaml = "---\n- \"\\\\x01 \\\\xAF \\\\\\\\xAF\"\n- \"\\x01 \\\\\\x01 \\x01\\\\\\x01\"\n" + escaped_yaml = +"---\n- \"\\\\x01 \\\\xAF \\\\\\\\xAF\"\n- \"\\x01 \\\\\\x01 \\x01\\\\\\x01\"\n" assert_equal ['\\x01 \\xAF \\\\xAF', '0x01 \\0x01 0x01\\0x01'], load_yaml(escaped_yaml) end @@ -166,7 +166,7 @@ def assert_datetimes_with_zones end def assert_syck_1_8_yaml_loads_correctly - yaml = "--- \nname: Dr. Doctor\000\000\000 \ndiagnosis: \"CIN 1 \\xE2\\x80\\x93 CIN 2\"\n" + yaml = +"--- \nname: Dr. Doctor\000\000\000 \ndiagnosis: \"CIN 1 \\xE2\\x80\\x93 CIN 2\"\n" hash = load_yaml(yaml) # The null chars should be escaped: @@ -187,7 +187,7 @@ def assert_syck_1_8_handles_encoding(hash) def assert_yaml_coercion_behaviour # UTF-8, with an unmappable byte too: - yaml = "---\nfulltextreport: \"Here is \\xE2\\x80\\x93 a weird \\x9D char\"\n" + yaml = +"---\nfulltextreport: \"Here is \\xE2\\x80\\x93 a weird \\x9D char\"\n" # By default, we'd expect the (serialised) \x9D assert_raises(UTF8Encoding::UTF8CoercionError) { load_yaml(yaml) } From 4d27cd237d19c4d94c7430b47ff7b349404333cb Mon Sep 17 00:00:00 2001 From: Brian Shand Date: Wed, 22 Oct 2025 09:38:20 +0100 Subject: [PATCH 2/2] Support Ruby 3.4 --- .github/workflows/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 81c7841..4380a00 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -15,7 +15,7 @@ jobs: - name: Set up Ruby uses: ruby/setup-ruby@v1 with: - ruby-version: 3.0 + ruby-version: 3.4 - name: Install dependencies run: bundle install - name: Run RuboCop against BASE..HEAD changes