From 3b014b3fd5b7394919a630003f59b73e452155c9 Mon Sep 17 00:00:00 2001 From: Alfonso Uceda Date: Fri, 20 Mar 2026 10:48:50 +0100 Subject: [PATCH 1/4] Rails 5 compatibility code is removed RAILS_5 constant and ActiveRecord version checks are no longer needed now that Rails 5+ is the minimum supported version. --- .../lib/validators/db_presence_validator.rb | 8 +--- .../rspec/uniqueness_validator_matcher.rb | 4 +- spec/spec_helper.rb | 3 -- spec/validations/db_belongs_to_spec.rb | 36 +++++--------- .../validates_db_uniqueness_of_spec.rb | 48 ++++++++----------- 5 files changed, 36 insertions(+), 63 deletions(-) diff --git a/lib/database_validations/lib/validators/db_presence_validator.rb b/lib/database_validations/lib/validators/db_presence_validator.rb index cf939c0..dc5ebbe 100644 --- a/lib/database_validations/lib/validators/db_presence_validator.rb +++ b/lib/database_validations/lib/validators/db_presence_validator.rb @@ -1,6 +1,6 @@ module DatabaseValidations class DbPresenceValidator < ActiveRecord::Validations::PresenceValidator - REFLECTION_MESSAGE = ActiveRecord::VERSION::MAJOR < 5 ? :blank : :required + REFLECTION_MESSAGE = :required attr_reader :klass @@ -53,11 +53,7 @@ def validates_db_presence_of(*attr_names) end def db_belongs_to(name, scope = nil, **options) - if ActiveRecord::VERSION::MAJOR < 5 - options[:required] = false - else - options[:optional] = true - end + options[:optional] = true belongs_to(name, scope, **options) diff --git a/lib/database_validations/rspec/uniqueness_validator_matcher.rb b/lib/database_validations/rspec/uniqueness_validator_matcher.rb index fe380a8..f590da8 100644 --- a/lib/database_validations/rspec/uniqueness_validator_matcher.rb +++ b/lib/database_validations/rspec/uniqueness_validator_matcher.rb @@ -63,15 +63,13 @@ end end - case_sensitive_default = ActiveRecord::VERSION::MAJOR >= 6 ? nil : true - @validators.include?( field: field, scope: Array.wrap(@scope), where: @where, message: @message, index_name: @index_name, - case_sensitive: @case_sensitive.nil? ? case_sensitive_default : @case_sensitive + case_sensitive: @case_sensitive.nil? ? nil : @case_sensitive ) end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 43975ac..9952d24 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -4,9 +4,6 @@ require 'db-query-matchers' require_relative '../config/database_config' -# Use this constant to enable Rails 5+ compatible specs -RAILS_5 = ActiveRecord::VERSION::MAJOR >= 5 - DBQueryMatchers.configure do |config| config.schemaless = true end diff --git a/spec/validations/db_belongs_to_spec.rb b/spec/validations/db_belongs_to_spec.rb index 2f3672b..1b28ff5 100644 --- a/spec/validations/db_belongs_to_spec.rb +++ b/spec/validations/db_belongs_to_spec.rb @@ -15,11 +15,7 @@ def name 'BelongsToUserTemp' end - if RAILS_5 - belongs_to :company, optional: false - else - belongs_to :company, required: true - end + belongs_to :company, optional: false end end @@ -29,11 +25,7 @@ def name 'BelongsToUserWithFKTemp' end - if RAILS_5 - belongs_to :company, optional: false - else - belongs_to :company, required: true - end + belongs_to :company, optional: false end end @@ -108,26 +100,22 @@ def define_tables describe 'save' do include_examples 'pack', :save - if RAILS_5 - it 'respects validate: false' do - expect { belongs_to_user_with_fk_klass.new(company_id: -1).save(validate: false) } - .to raise_error(ActiveRecord::InvalidForeignKey) - expect { db_belongs_to_user_klass.new(company_id: -1).save(validate: false) } - .to raise_error(ActiveRecord::InvalidForeignKey) - end + it 'respects validate: false' do + expect { belongs_to_user_with_fk_klass.new(company_id: -1).save(validate: false) } + .to raise_error(ActiveRecord::InvalidForeignKey) + expect { db_belongs_to_user_klass.new(company_id: -1).save(validate: false) } + .to raise_error(ActiveRecord::InvalidForeignKey) end end describe 'save!' do include_examples 'pack', :save! - if RAILS_5 - it 'respects validate: false' do - expect { belongs_to_user_with_fk_klass.new(company_id: -1).save!(validate: false) } - .to raise_error(ActiveRecord::InvalidForeignKey) - expect { db_belongs_to_user_klass.new(company_id: -1).save!(validate: false) } - .to raise_error(ActiveRecord::InvalidForeignKey) - end + it 'respects validate: false' do + expect { belongs_to_user_with_fk_klass.new(company_id: -1).save!(validate: false) } + .to raise_error(ActiveRecord::InvalidForeignKey) + expect { db_belongs_to_user_klass.new(company_id: -1).save!(validate: false) } + .to raise_error(ActiveRecord::InvalidForeignKey) end end diff --git a/spec/validations/validates_db_uniqueness_of_spec.rb b/spec/validations/validates_db_uniqueness_of_spec.rb index f0316d2..430f768 100644 --- a/spec/validations/validates_db_uniqueness_of_spec.rb +++ b/spec/validations/validates_db_uniqueness_of_spec.rb @@ -26,7 +26,7 @@ old = app_uniqueness.new(persisted_attrs).tap(&:valid?) expect(old.errors.messages.sort).to eq(new.errors.messages.sort) - RAILS_5 && (expect(old.errors.details.sort).to eq(new.errors.details.sort)) + expect(old.errors.details.sort).to eq(new.errors.details.sort) end context 'when wrapped by transaction' do @@ -40,7 +40,7 @@ end expect(old.errors.messages.sort).to eq(new.errors.messages.sort) - RAILS_5 && (expect(old.errors.details.sort).to eq(new.errors.details.sort)) + expect(old.errors.details.sort).to eq(new.errors.details.sort) end end end @@ -55,15 +55,13 @@ .and not_change(parent_class, :count) end - if RAILS_5 - it 'respects validate: false' do - expect { db_uniqueness.new(persisted_attrs).save(validate: false) } - .to raise_error(ActiveRecord::RecordNotUnique) - .and not_change(parent_class, :count) - expect { app_uniqueness.new(persisted_attrs).save(validate: false) } - .to raise_error(ActiveRecord::RecordNotUnique) - .and not_change(parent_class, :count) - end + it 'respects validate: false' do + expect { db_uniqueness.new(persisted_attrs).save(validate: false) } + .to raise_error(ActiveRecord::RecordNotUnique) + .and not_change(parent_class, :count) + expect { app_uniqueness.new(persisted_attrs).save(validate: false) } + .to raise_error(ActiveRecord::RecordNotUnique) + .and not_change(parent_class, :count) end # Database raise only one unique constraint error per query @@ -73,10 +71,10 @@ old = app_uniqueness.create(persisted_attrs) expect(new.errors.messages).to be_present - RAILS_5 && (expect(new.errors.details).to be_present) + expect(new.errors.details).to be_present expect(old.errors.messages.to_h).to include(new.errors.messages.to_h) - RAILS_5 && (expect(old.errors.details.to_h).to include(new.errors.details.to_h)) + expect(old.errors.details.to_h).to include(new.errors.details.to_h) end context 'when wrapped by transaction' do @@ -90,10 +88,10 @@ end expect(new.errors.messages).to be_present - RAILS_5 && (expect(new.errors.details).to be_present) + expect(new.errors.details).to be_present expect(old.errors.messages.to_h).to include(new.errors.messages.to_h) - RAILS_5 && (expect(old.errors.details.to_h).to include(new.errors.details.to_h)) + expect(old.errors.details.to_h).to include(new.errors.details.to_h) end end end @@ -111,15 +109,13 @@ .and not_change(parent_class, :count) end - if RAILS_5 - it 'respects validate: false' do - expect { db_uniqueness.new(persisted_attrs).save!(validate: false) } - .to raise_error(ActiveRecord::RecordNotUnique) - .and not_change(parent_class, :count) - expect { app_uniqueness.new(persisted_attrs).save!(validate: false) } - .to raise_error(ActiveRecord::RecordNotUnique) - .and not_change(parent_class, :count) - end + it 'respects validate: false' do + expect { db_uniqueness.new(persisted_attrs).save!(validate: false) } + .to raise_error(ActiveRecord::RecordNotUnique) + .and not_change(parent_class, :count) + expect { app_uniqueness.new(persisted_attrs).save!(validate: false) } + .to raise_error(ActiveRecord::RecordNotUnique) + .and not_change(parent_class, :count) end def catch_error_message @@ -473,7 +469,7 @@ def catch_error_message it "doesn't rescue from the constraint violation" do expect_any_instance_of(ActiveRecord::Validations::UniquenessValidator) # rubocop:disable RSpec/AnyInstance - .to receive(:scope_relation).twice.and_return(RAILS_5 ? app_uniqueness.none : '1=0') + .to receive(:scope_relation).twice.and_return(app_uniqueness.none) expect { app_uniqueness.create(persisted_attrs) } .to raise_error(ActiveRecord::RecordNotUnique) @@ -831,8 +827,6 @@ def catch_error_message end shared_examples 'supports complex indexes' do - next unless RAILS_5 - context 'with index_name option' do let(:app_uniqueness) { define_class { validates_uniqueness_of :field, case_sensitive: false } } let(:db_uniqueness) { define_class { validates_db_uniqueness_of :field, index_name: :unique_index, case_sensitive: false } } From 925fa9e3ea4b1464099fe13587eddb5423ead005 Mon Sep 17 00:00:00 2001 From: Alfonso Uceda Date: Fri, 20 Mar 2026 10:50:44 +0100 Subject: [PATCH 2/4] Minimum versions are updated, Ruby 3.0 to 3.2 and Rails 7.0 to 7.2 --- .github/workflows/ci.yml | 4 ++-- .rubocop.yml | 2 +- database_validations.gemspec | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0e0e8df..ac6d71f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,8 +15,8 @@ jobs: matrix: include: - - ruby: '3.0' - gemfile: rails70 + - ruby: '3.2' + gemfile: rails72 - ruby: '3.4' gemfile: rails80 - ruby: '4.0' diff --git a/.rubocop.yml b/.rubocop.yml index 6e40760..91e9cb0 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -6,7 +6,7 @@ AllCops: NewCops: disable SuggestExtensions: false DisplayCopNames: true - TargetRubyVersion: 3.0 + TargetRubyVersion: 3.2 Include: - '**/*.rb' - 'Gemfile' diff --git a/database_validations.gemspec b/database_validations.gemspec index 634dcf9..1490515 100644 --- a/database_validations.gemspec +++ b/database_validations.gemspec @@ -19,9 +19,9 @@ and ActiveRecord validations with better performance and consistency." spec.license = 'MIT' spec.files = Dir['lib/**/*'] spec.require_paths = ['lib'] - spec.required_ruby_version = '>= 3.0.0' + spec.required_ruby_version = '>= 3.2.0' - spec.add_dependency 'activerecord', '>= 7.0.0' + spec.add_dependency 'activerecord', '>= 7.2.0' spec.add_development_dependency 'benchmark-ips' spec.add_development_dependency 'bundler' From 0277dde70c1e9d0ebde0ba155c891766a9727b84 Mon Sep 17 00:00:00 2001 From: Alfonso Uceda Date: Fri, 20 Mar 2026 10:50:48 +0100 Subject: [PATCH 3/4] rails70 gemfile is replaced with rails72 Rails 7.0 is no longer supported; the CI matrix now tests 7.2 as the lowest Rails version. --- gemfiles/rails70.gemfile | 6 ------ gemfiles/rails72.gemfile | 5 +++++ 2 files changed, 5 insertions(+), 6 deletions(-) delete mode 100644 gemfiles/rails70.gemfile create mode 100644 gemfiles/rails72.gemfile diff --git a/gemfiles/rails70.gemfile b/gemfiles/rails70.gemfile deleted file mode 100644 index cc15713..0000000 --- a/gemfiles/rails70.gemfile +++ /dev/null @@ -1,6 +0,0 @@ -source 'https://rubygems.org' - -gem 'activerecord', '~> 7.0.0' -gem 'sqlite3', '~> 1.4' - -gemspec path: '../' diff --git a/gemfiles/rails72.gemfile b/gemfiles/rails72.gemfile new file mode 100644 index 0000000..9d23fa1 --- /dev/null +++ b/gemfiles/rails72.gemfile @@ -0,0 +1,5 @@ +source 'https://rubygems.org' + +gem 'activerecord', '~> 7.2.0' + +gemspec path: '../' From bb5d0333d658211290d475317019b3298d9e84e3 Mon Sep 17 00:00:00 2001 From: Alfonso Uceda Date: Fri, 20 Mar 2026 10:55:24 +0100 Subject: [PATCH 4/4] CHANGELOG is updated with minimum version changes --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9051f58..a52aaf2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,8 @@ ### Improvements - Add Rails 8.1 support -- Set minimum Ruby version to 3.0 +- Set minimum Ruby version to 3.2 and minimum Rails version to 7.2 +- Remove Rails 5 compatibility code - Loosen gemspec development dependency version constraints - Modernize CI configuration and add dedicated RuboCop workflow - Consolidate database configuration into `config/database.yml`