From 8837ef72df69dd0c87e8cc13c114f1aad3db59c2 Mon Sep 17 00:00:00 2001 From: Pedro Paiva Date: Thu, 15 Sep 2022 18:05:15 -0300 Subject: [PATCH] Add Rails 7 (#1506) --- .github/workflows/ci.yml | 4 + Appraisals | 27 ++ gemfiles/rails_7_0.gemfile | 40 +++ gemfiles/rails_7_0.gemfile.lock | 328 ++++++++++++++++++ .../validate_presence_of_matcher.rb | 5 +- lib/shoulda/matchers/rails_shim.rb | 14 + spec/support/tests/database_configuration.rb | 2 + spec/support/unit/helpers/class_builder.rb | 4 +- spec/support/unit/rails_application.rb | 10 +- .../validate_inclusion_of_matcher_spec.rb | 5 +- .../validate_presence_of_matcher_spec.rb | 36 +- .../define_enum_for_matcher_spec.rb | 12 +- 12 files changed, 463 insertions(+), 24 deletions(-) create mode 100644 gemfiles/rails_7_0.gemfile create mode 100644 gemfiles/rails_7_0.gemfile.lock diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2fb40845..7abf15a8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,6 +32,7 @@ jobs: - 2.7.5 - 2.6.9 appraisal: + - rails_7_0 - rails_6_1 - rails_6_0 - rails_5_2 @@ -41,6 +42,9 @@ jobs: exclude: - { ruby: 3.1.2, appraisal: rails_5_2 } - { ruby: 3.0.3, appraisal: rails_5_2 } + - { ruby: 3.0.3, appraisal: rails_7_0 } + - { ruby: 2.7.5, appraisal: rails_7_0 } + - { ruby: 2.6.9, appraisal: rails_7_0 } env: DATABASE_ADAPTER: ${{ matrix.adapter }} BUNDLE_GEMFILE: gemfiles/${{ matrix.appraisal }}.gemfile diff --git a/Appraisals b/Appraisals index cb67b914..fcd97e2a 100644 --- a/Appraisals +++ b/Appraisals @@ -93,3 +93,30 @@ appraise 'rails_6_1' do gem 'pg', '>= 0.18', '< 2.0' gem 'sqlite3', '~> 1.4' end + +appraise 'rails_7_0' do + instance_eval(&shared_spring_dependencies) + instance_eval(&shared_test_dependencies) + + gem 'rails', '~> 7.0.1' + gem 'sprockets-rails' + gem 'puma', '~> 5.0' + gem 'importmap-rails' + gem 'turbo-rails' + gem 'stimulus-rails' + gem 'jbuilder' + gem 'redis', '~> 4.0' + gem 'bootsnap', require: false + gem 'capybara' + gem 'selenium-webdriver' + gem 'webdrivers' + + # other dependencies + gem 'bcrypt', '~> 3.1.7' + gem 'rails-controller-testing' + gem 'debug', platforms: %i[mri mingw x64_mingw] + + # Database adapters + gem 'sqlite3', '~> 1.4' + gem 'pg', '~> 1.1' +end diff --git a/gemfiles/rails_7_0.gemfile b/gemfiles/rails_7_0.gemfile new file mode 100644 index 00000000..cd23b156 --- /dev/null +++ b/gemfiles/rails_7_0.gemfile @@ -0,0 +1,40 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "appraisal", "2.2.0" +gem "bundler", "~> 2.0" +gem "pry" +gem "pry-byebug" +gem "rake", "13.0.1" +gem "rspec", "~> 3.9" +gem "rubocop", require: false +gem "rubocop-packaging", require: false +gem "rubocop-rails", require: false +gem "warnings_logger" +gem "zeus", require: false +gem "fssm" +gem "redcarpet" +gem "rouge" +gem "yard" +gem "spring" +gem "spring-commands-rspec" +gem "rspec-rails", "~> 4.0" +gem "shoulda-context", "~> 1.2.0" +gem "rails", "~> 7.0.1" +gem "sprockets-rails" +gem "puma", "~> 5.0" +gem "importmap-rails" +gem "turbo-rails" +gem "stimulus-rails" +gem "jbuilder" +gem "redis", "~> 4.0" +gem "bootsnap", require: false +gem "capybara" +gem "selenium-webdriver" +gem "webdrivers" +gem "bcrypt", "~> 3.1.7" +gem "rails-controller-testing" +gem "debug", platforms: [:mri, :mingw, :x64_mingw] +gem "sqlite3", "~> 1.4" +gem "pg", "~> 1.1" diff --git a/gemfiles/rails_7_0.gemfile.lock b/gemfiles/rails_7_0.gemfile.lock new file mode 100644 index 00000000..59c2c4e6 --- /dev/null +++ b/gemfiles/rails_7_0.gemfile.lock @@ -0,0 +1,328 @@ +GEM + remote: https://rubygems.org/ + specs: + actioncable (7.0.1) + actionpack (= 7.0.1) + activesupport (= 7.0.1) + nio4r (~> 2.0) + websocket-driver (>= 0.6.1) + actionmailbox (7.0.1) + actionpack (= 7.0.1) + activejob (= 7.0.1) + activerecord (= 7.0.1) + activestorage (= 7.0.1) + activesupport (= 7.0.1) + mail (>= 2.7.1) + net-imap + net-pop + net-smtp + actionmailer (7.0.1) + actionpack (= 7.0.1) + actionview (= 7.0.1) + activejob (= 7.0.1) + activesupport (= 7.0.1) + mail (~> 2.5, >= 2.5.4) + net-imap + net-pop + net-smtp + rails-dom-testing (~> 2.0) + actionpack (7.0.1) + actionview (= 7.0.1) + activesupport (= 7.0.1) + rack (~> 2.0, >= 2.2.0) + rack-test (>= 0.6.3) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.0, >= 1.2.0) + actiontext (7.0.1) + actionpack (= 7.0.1) + activerecord (= 7.0.1) + activestorage (= 7.0.1) + activesupport (= 7.0.1) + globalid (>= 0.6.0) + nokogiri (>= 1.8.5) + actionview (7.0.1) + activesupport (= 7.0.1) + builder (~> 3.1) + erubi (~> 1.4) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.1, >= 1.2.0) + activejob (7.0.1) + activesupport (= 7.0.1) + globalid (>= 0.3.6) + activemodel (7.0.1) + activesupport (= 7.0.1) + activerecord (7.0.1) + activemodel (= 7.0.1) + activesupport (= 7.0.1) + activestorage (7.0.1) + actionpack (= 7.0.1) + activejob (= 7.0.1) + activerecord (= 7.0.1) + activesupport (= 7.0.1) + marcel (~> 1.0) + mini_mime (>= 1.1.0) + activesupport (7.0.1) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 1.6, < 2) + minitest (>= 5.1) + tzinfo (~> 2.0) + addressable (2.8.0) + public_suffix (>= 2.0.2, < 5.0) + appraisal (2.2.0) + bundler + rake + thor (>= 0.14.0) + ast (2.4.2) + bcrypt (3.1.16) + bootsnap (1.10.2) + msgpack (~> 1.2) + builder (3.2.4) + byebug (11.1.3) + capybara (3.36.0) + addressable + matrix + mini_mime (>= 0.1.3) + nokogiri (~> 1.8) + rack (>= 1.6.0) + rack-test (>= 0.6.3) + regexp_parser (>= 1.5, < 3.0) + xpath (~> 3.2) + childprocess (4.1.0) + coderay (1.1.3) + concurrent-ruby (1.1.9) + crass (1.0.6) + debug (1.4.0) + irb (>= 1.3.6) + reline (>= 0.2.7) + diff-lcs (1.5.0) + digest (3.1.0) + erubi (1.10.0) + fssm (0.2.10) + globalid (1.0.0) + activesupport (>= 5.0) + i18n (1.8.11) + concurrent-ruby (~> 1.0) + importmap-rails (1.0.2) + actionpack (>= 6.0.0) + railties (>= 6.0.0) + io-console (0.5.11) + io-wait (0.2.1) + irb (1.4.1) + reline (>= 0.3.0) + jbuilder (2.11.5) + actionview (>= 5.0.0) + activesupport (>= 5.0.0) + loofah (2.13.0) + crass (~> 1.0.2) + nokogiri (>= 1.5.9) + mail (2.7.1) + mini_mime (>= 0.1.1) + marcel (1.0.2) + matrix (0.4.2) + method_source (1.0.0) + mini_mime (1.1.2) + mini_portile2 (2.7.1) + minitest (5.15.0) + msgpack (1.4.4) + net-imap (0.2.3) + digest + net-protocol + strscan + net-pop (0.1.1) + digest + net-protocol + timeout + net-protocol (0.1.2) + io-wait + timeout + net-smtp (0.3.1) + digest + net-protocol + timeout + nio4r (2.5.8) + nokogiri (1.13.1) + mini_portile2 (~> 2.7.0) + racc (~> 1.4) + parallel (1.21.0) + parser (3.1.0.0) + ast (~> 2.4.1) + pg (1.3.0) + pry (0.13.1) + coderay (~> 1.1) + method_source (~> 1.0) + pry-byebug (3.9.0) + byebug (~> 11.0) + pry (~> 0.13.0) + public_suffix (4.0.6) + puma (5.5.2) + nio4r (~> 2.0) + racc (1.6.0) + rack (2.2.3) + rack-test (1.1.0) + rack (>= 1.0, < 3) + rails (7.0.1) + actioncable (= 7.0.1) + actionmailbox (= 7.0.1) + actionmailer (= 7.0.1) + actionpack (= 7.0.1) + actiontext (= 7.0.1) + actionview (= 7.0.1) + activejob (= 7.0.1) + activemodel (= 7.0.1) + activerecord (= 7.0.1) + activestorage (= 7.0.1) + activesupport (= 7.0.1) + bundler (>= 1.15.0) + railties (= 7.0.1) + rails-controller-testing (1.0.5) + actionpack (>= 5.0.1.rc1) + actionview (>= 5.0.1.rc1) + activesupport (>= 5.0.1.rc1) + rails-dom-testing (2.0.3) + activesupport (>= 4.2.0) + nokogiri (>= 1.6) + rails-html-sanitizer (1.4.2) + loofah (~> 2.3) + railties (7.0.1) + actionpack (= 7.0.1) + activesupport (= 7.0.1) + method_source + rake (>= 12.2) + thor (~> 1.0) + zeitwerk (~> 2.5) + rainbow (3.1.1) + rake (13.0.1) + redcarpet (3.5.1) + redis (4.5.1) + regexp_parser (2.2.0) + reline (0.3.1) + io-console (~> 0.5) + rexml (3.2.5) + rouge (3.26.0) + rspec (3.10.0) + rspec-core (~> 3.10.0) + rspec-expectations (~> 3.10.0) + rspec-mocks (~> 3.10.0) + rspec-core (3.10.1) + rspec-support (~> 3.10.0) + rspec-expectations (3.10.2) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.10.0) + rspec-mocks (3.10.2) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.10.0) + rspec-rails (4.1.2) + actionpack (>= 4.2) + activesupport (>= 4.2) + railties (>= 4.2) + rspec-core (~> 3.10) + rspec-expectations (~> 3.10) + rspec-mocks (~> 3.10) + rspec-support (~> 3.10) + rspec-support (3.10.3) + rubocop (1.25.0) + parallel (~> 1.10) + parser (>= 3.1.0.0) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 1.8, < 3.0) + rexml + rubocop-ast (>= 1.15.1, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 1.4.0, < 3.0) + rubocop-ast (1.15.1) + parser (>= 3.0.1.1) + rubocop-packaging (0.5.1) + rubocop (>= 0.89, < 2.0) + rubocop-rails (2.13.1) + activesupport (>= 4.2.0) + rack (>= 1.1) + rubocop (>= 1.7.0, < 2.0) + ruby-progressbar (1.11.0) + rubyzip (2.3.2) + selenium-webdriver (4.1.0) + childprocess (>= 0.5, < 5.0) + rexml (~> 3.2, >= 3.2.5) + rubyzip (>= 1.2.2) + shoulda-context (1.2.2) + spring (4.0.0) + spring-commands-rspec (1.0.4) + spring (>= 0.9.1) + sprockets (4.0.2) + concurrent-ruby (~> 1.0) + rack (> 1, < 3) + sprockets-rails (3.4.2) + actionpack (>= 5.2) + activesupport (>= 5.2) + sprockets (>= 3.0.0) + sqlite3 (1.4.2) + stimulus-rails (1.0.2) + railties (>= 6.0.0) + strscan (3.0.1) + thor (1.2.1) + timeout (0.2.0) + turbo-rails (1.0.1) + actionpack (>= 6.0.0) + railties (>= 6.0.0) + tzinfo (2.0.4) + concurrent-ruby (~> 1.0) + unicode-display_width (2.1.0) + warnings_logger (0.1.1) + webdrivers (5.0.0) + nokogiri (~> 1.6) + rubyzip (>= 1.3.0) + selenium-webdriver (~> 4.0) + webrick (1.7.0) + websocket-driver (0.7.5) + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.5) + xpath (3.2.0) + nokogiri (~> 1.8) + yard (0.9.27) + webrick (~> 1.7.0) + zeitwerk (2.5.3) + zeus (0.15.14) + method_source (>= 0.6.7) + +PLATFORMS + ruby + +DEPENDENCIES + appraisal (= 2.2.0) + bcrypt (~> 3.1.7) + bootsnap + bundler (~> 2.0) + capybara + debug + fssm + importmap-rails + jbuilder + pg (~> 1.1) + pry + pry-byebug + puma (~> 5.0) + rails (~> 7.0.1) + rails-controller-testing + rake (= 13.0.1) + redcarpet + redis (~> 4.0) + rouge + rspec (~> 3.9) + rspec-rails (~> 4.0) + rubocop + rubocop-packaging + rubocop-rails + selenium-webdriver + shoulda-context (~> 1.2.0) + spring + spring-commands-rspec + sprockets-rails + sqlite3 (~> 1.4) + stimulus-rails + turbo-rails + warnings_logger + webdrivers + yard + zeus + +BUNDLED WITH + 2.3.6 diff --git a/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb b/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb index 65fbce37..9b047e7a 100644 --- a/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +++ b/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb @@ -166,7 +166,8 @@ module Shoulda possibly_ignore_interference_by_writer - if secure_password_being_validated? + if secure_password_being_validated? && + Shoulda::Matchers::RailsShim.active_model_lt_7? ignore_interference_by_writer.default_to(when: :blank?) disallowed_values.all? do |value| @@ -232,7 +233,7 @@ validation for you? Instead of using `validate_presence_of`, try end def possibly_ignore_interference_by_writer - if secure_password_being_validated? + if secure_password_being_validated? && RailsShim.active_model_lt_7? ignore_interference_by_writer.default_to(when: :blank?) end end diff --git a/lib/shoulda/matchers/rails_shim.rb b/lib/shoulda/matchers/rails_shim.rb index 2474d736..f1f4e31a 100644 --- a/lib/shoulda/matchers/rails_shim.rb +++ b/lib/shoulda/matchers/rails_shim.rb @@ -19,6 +19,20 @@ module Shoulda Gem::Version.new('0') end + def active_model_version + Gem::Version.new(::ActiveModel::VERSION::STRING) + rescue NameError + Gem::Version.new('0') + end + + def active_model_gte_7? + Gem::Requirement.new('>= 7').satisfied_by?(active_model_version) + end + + def active_model_lt_7? + Gem::Requirement.new('< 7').satisfied_by?(active_model_version) + end + def generate_validation_message( record, attribute, diff --git a/spec/support/tests/database_configuration.rb b/spec/support/tests/database_configuration.rb index 71d4ed4f..626af33a 100644 --- a/spec/support/tests/database_configuration.rb +++ b/spec/support/tests/database_configuration.rb @@ -17,6 +17,8 @@ module Tests end def load_file + YAML::load_file(File.join(__dir__, "database_adapters/config/#{adapter}.yml"), aliases: true) + rescue ArgumentError YAML::load_file(File.join(__dir__, "database_adapters/config/#{adapter}.yml")) end end diff --git a/spec/support/unit/helpers/class_builder.rb b/spec/support/unit/helpers/class_builder.rb index c7e294b8..569aca74 100644 --- a/spec/support/unit/helpers/class_builder.rb +++ b/spec/support/unit/helpers/class_builder.rb @@ -34,7 +34,7 @@ module UnitTests RUBY namespace.const_get(name_without_namespace).tap do |constant| - constant.unloadable + constant.unloadable if constant.respond_to?(:unloadable) # if Rails is in classic mode, mark it unloadable @_defined_modules = defined_modules | [constant] if block @@ -55,7 +55,7 @@ module UnitTests RUBY namespace.const_get(name_without_namespace).tap do |constant| - constant.unloadable + constant.unloadable if constant.respond_to?(:unloadable) # if Rails is in classic mode, mark it unloadable @_defined_modules = defined_modules | [constant] if block diff --git a/spec/support/unit/rails_application.rb b/spec/support/unit/rails_application.rb index 7af743cb..1e8f4192 100644 --- a/spec/support/unit/rails_application.rb +++ b/spec/support/unit/rails_application.rb @@ -109,7 +109,9 @@ end end def write_database_configuration - YAML.dump(database.config.load_file, fs.open('config/database.yml', 'w')) + fs.open('config/database.yml', 'w') do |file| + YAML.dump(database.config.load_file, file) + end end def write_activerecord_model_with_different_connection @@ -163,7 +165,11 @@ end def run_migrations fs.within_project do - run_command! 'bundle exec rake db:drop:all db:create:all db:migrate' + run_command! 'bundle exec rake db:drop:all db:create:all' + end + + fs.within_project do + run_command! 'bundle exec rake db:migrate' end end diff --git a/spec/unit/shoulda/matchers/active_model/validate_inclusion_of_matcher_spec.rb b/spec/unit/shoulda/matchers/active_model/validate_inclusion_of_matcher_spec.rb index 80599530..90247e70 100644 --- a/spec/unit/shoulda/matchers/active_model/validate_inclusion_of_matcher_spec.rb +++ b/spec/unit/shoulda/matchers/active_model/validate_inclusion_of_matcher_spec.rb @@ -145,7 +145,7 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode end context 'against a datetime attribute' do - now = DateTime.now + now = DateTime.new(2022, 1, 1) define_method(:now) { now } @@ -785,8 +785,7 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode include_context 'for a generic attribute' context 'against a timestamp column' do - now = DateTime.now - + now = DateTime.new(2022, 1, 1) define_method(:now) { now } it_behaves_like 'it supports in_array', diff --git a/spec/unit/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb b/spec/unit/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb index 7132e4e5..89441bcf 100644 --- a/spec/unit/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb +++ b/spec/unit/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb @@ -860,22 +860,36 @@ validation exception on failure, but this could not be proved. end context 'against a pre-set password in a model that has_secure_password' do - it 'raises a CouldNotSetPasswordError' do - user_class = define_model :user, password_digest: :string do - has_secure_password validations: false - validates_presence_of :password - end + if Shoulda::Matchers::RailsShim.active_model_gte_7? + it 'does not raises a CouldNotSetPasswordError' do + user_class = define_model :user, password_digest: :string do + has_secure_password :password, validations: false + validates_presence_of :password + end - user = user_class.new - user.password = 'something' + user = user_class.new + user.password = 'something' - assertion = lambda do expect(user).to validate_presence_of(:password) end + else + it 'raises a CouldNotSetPasswordError' do + user_class = define_model :user, password_digest: :string do + has_secure_password validations: false + validates_presence_of :password + end - expect(&assertion).to raise_error( - Shoulda::Matchers::ActiveModel::CouldNotSetPasswordError, - ) + user = user_class.new + user.password = 'something' + + assertion = lambda do + expect(user).to validate_presence_of(:password) + end + + expect(&assertion).to raise_error( + Shoulda::Matchers::ActiveModel::CouldNotSetPasswordError, + ) + end end end diff --git a/spec/unit/shoulda/matchers/active_record/define_enum_for_matcher_spec.rb b/spec/unit/shoulda/matchers/active_record/define_enum_for_matcher_spec.rb index a58c03eb..8e4190ae 100644 --- a/spec/unit/shoulda/matchers/active_record/define_enum_for_matcher_spec.rb +++ b/spec/unit/shoulda/matchers/active_record/define_enum_for_matcher_spec.rb @@ -945,11 +945,15 @@ describe Shoulda::Matchers::ActiveRecord::DefineEnumForMatcher, type: :model do _suffix: suffix, } - if rails_version =~ '~> 6.0' - params.merge!(_scopes: scopes) - end + if rails_version >= 7.0 + model.enum(enum_name, values, prefix: prefix, suffix: suffix) + else + if rails_version =~ '~> 6.0' + params.merge!(_scopes: scopes) + end - model.enum(params) + model.enum(params) + end model.new end