From 82f6d7078c263e936ed814324d66cb6e4896ac87 Mon Sep 17 00:00:00 2001 From: Josh Branham Date: Tue, 18 Oct 2022 23:45:22 -0600 Subject: [PATCH] Modern Ruby + ActiveRecord, Update Travis Config (#416) * Update Travis configuration * Drop os, use default * Use focal for OS * Restore patch versions of ruby, rvm not pulling latest patch * Update local test/run.sh script for easier local testing * Add some old AR back, remove 3+ rubies, drop deprecated codeclimate gem * Lock sqlite3 for Ruby 2.5 * ActiveRecord 6.0 and 6.1 tests failing, add later * Drop 2.5 * Update gemspec, pry for dev support * 5.1.1 AR works, update test/run.sh * Fix support for ActiveRecord 5.2 --- .travis.yml | 57 ++---------- attr_encrypted.gemspec | 11 +-- lib/attr_encrypted/adapters/active_record.rb | 25 +++-- test/active_record_test.rb | 98 ++++++++++---------- test/run.sh | 22 +++-- test/test_helper.rb | 7 +- 6 files changed, 88 insertions(+), 132 deletions(-) diff --git a/.travis.yml b/.travis.yml index f75b2ede..f395ade5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,59 +1,14 @@ -sudo: false language: ruby +dist: focal +os: linux cache: bundler rvm: - - 2.0 - - 2.1 - - 2.2.2 - - 2.3.0 - - 2.4.0 - - 2.5.0 - - rbx + - 2.6.10 + - 2.7.6 env: - - ACTIVERECORD=3.0.0 - - ACTIVERECORD=3.1.0 - - ACTIVERECORD=3.2.0 - - ACTIVERECORD=4.0.0 - - ACTIVERECORD=4.1.0 - - ACTIVERECORD=4.2.0 - - ACTIVERECORD=5.0.0 - ACTIVERECORD=5.1.1 -matrix: - exclude: - - rvm: 2.0 - env: ACTIVERECORD=5.0.0 - - rvm: 2.0 - env: ACTIVERECORD=5.1.1 - - rvm: 2.1 - env: ACTIVERECORD=5.0.0 - - rvm: 2.1 - env: ACTIVERECORD=5.1.1 - - rvm: 2.4.0 - env: ACTIVERECORD=3.0.0 - - rvm: 2.4.0 - env: ACTIVERECORD=3.1.0 - - rvm: 2.4.0 - env: ACTIVERECORD=3.2.0 - - rvm: 2.4.0 - env: ACTIVERECORD=4.0.0 - - rvm: 2.4.0 - env: ACTIVERECORD=4.1.0 - - rvm: 2.5.0 - env: ACTIVERECORD=3.0.0 - - rvm: 2.5.0 - env: ACTIVERECORD=3.1.0 - - rvm: 2.5.0 - env: ACTIVERECORD=3.2.0 - - rvm: 2.5.0 - env: ACTIVERECORD=4.0.0 - - rvm: 2.5.0 - env: ACTIVERECORD=4.1.0 - - rvm: rbx - env: ACTIVERECORD=5.0.0 - - rvm: rbx - env: ACTIVERECORD=5.1.1 - allow_failures: - - rvm: rbx + - ACTIVERECORD=5.2.8 +jobs: fast_finish: true addons: code_climate: diff --git a/attr_encrypted.gemspec b/attr_encrypted.gemspec index 41f96ed9..798f4704 100644 --- a/attr_encrypted.gemspec +++ b/attr_encrypted.gemspec @@ -19,15 +19,12 @@ Gem::Specification.new do |s| s.homepage = 'http://github.com/attr-encrypted/attr_encrypted' s.license = 'MIT' - s.has_rdoc = false - s.rdoc_options = ['--line-numbers', '--inline-source', '--main', 'README.rdoc'] - s.require_paths = ['lib'] s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- test/*`.split("\n") - s.required_ruby_version = '>= 2.0.0' + s.required_ruby_version = '>= 2.6.0' s.add_dependency('encryptor', ['~> 3.0.0']) # support for testing with specific active record version @@ -42,10 +39,6 @@ Gem::Specification.new do |s| s.add_development_dependency('rake') s.add_development_dependency('minitest') s.add_development_dependency('sequel') - if RUBY_VERSION < '2.1.0' - s.add_development_dependency('nokogiri', '< 1.7.0') - s.add_development_dependency('public_suffix', '< 3.0.0') - end if defined?(RUBY_ENGINE) && RUBY_ENGINE.to_sym == :jruby s.add_development_dependency('activerecord-jdbcsqlite3-adapter') s.add_development_dependency('jdbc-sqlite3', '< 3.8.7') # 3.8.7 is nice and broke @@ -53,9 +46,9 @@ Gem::Specification.new do |s| s.add_development_dependency('sqlite3') end s.add_development_dependency('dm-sqlite-adapter') + s.add_development_dependency('pry') s.add_development_dependency('simplecov') s.add_development_dependency('simplecov-rcov') - s.add_development_dependency("codeclimate-test-reporter", '<= 0.6.0') s.cert_chain = ['certs/saghaulor.pem'] s.signing_key = File.expand_path("~/.ssh/gem-private_key.pem") if $0 =~ /gem\z/ diff --git a/lib/attr_encrypted/adapters/active_record.rb b/lib/attr_encrypted/adapters/active_record.rb index fca9343e..fb53cc1d 100644 --- a/lib/attr_encrypted/adapters/active_record.rb +++ b/lib/attr_encrypted/adapters/active_record.rb @@ -53,21 +53,15 @@ def attr_encrypted(*attrs) super options = attrs.extract_options! attr = attrs.pop - attribute attr if ::ActiveRecord::VERSION::STRING >= "5.1.0" + attribute attr options.merge! encrypted_attributes[attr] define_method("#{attr}_was") do attribute_was(attr) end - if ::ActiveRecord::VERSION::STRING >= "4.1" - define_method("#{attr}_changed?") do |options = {}| - attribute_changed?(attr, options) - end - else - define_method("#{attr}_changed?") do - attribute_changed?(attr) - end + define_method("#{attr}_changed?") do |options = {}| + attribute_changed?(attr, options) end define_method("#{attr}_change") do @@ -75,6 +69,19 @@ def attr_encrypted(*attrs) end define_method("#{attr}_with_dirtiness=") do |value| + ## Source: https://github.com/priyankatapar/attr_encrypted/commit/7e8702bd5418c927a39d8dd72c0adbea522d5663 + # In ActiveRecord 5.2+, due to changes to the way virtual + # attributes are handled, @attributes[attr].value is nil which + # breaks attribute_was. Setting it here returns us to the expected + # behavior. + if ::ActiveRecord::VERSION::STRING >= "5.2" + # This is needed support attribute_was before a record has + # been saved + set_attribute_was(attr, __send__(attr)) if value != __send__(attr) + # This is needed to support attribute_was after a record has + # been saved + @attributes.write_from_user(attr.to_s, value) if value != __send__(attr) + end attribute_will_change!(attr) if value != __send__(attr) __send__("#{attr}_without_dirtiness=", value) end diff --git a/test/active_record_test.rb b/test/active_record_test.rb index 8ec31aea..9eaedeaf 100644 --- a/test/active_record_test.rb +++ b/test/active_record_test.rb @@ -45,16 +45,14 @@ def create_tables ActiveRecord::MissingAttributeError = ActiveModel::MissingAttributeError unless defined?(ActiveRecord::MissingAttributeError) -if ::ActiveRecord::VERSION::STRING > "4.0" - module Rack - module Test - class UploadedFile; end - end +module Rack + module Test + class UploadedFile; end end - - require 'action_controller/metal/strong_parameters' end +require 'action_controller/metal/strong_parameters' + class Person < ActiveRecord::Base self.attr_encrypted_options[:mode] = :per_attribute_iv_and_salt attr_encrypted :email, key: SECRET_KEY @@ -106,7 +104,6 @@ class PersonWithSerialization < ActiveRecord::Base class UserWithProtectedAttribute < ActiveRecord::Base self.table_name = 'users' attr_encrypted :password, key: SECRET_KEY - attr_protected :is_admin if ::ActiveRecord::VERSION::STRING < "4.0" end class PersonUsingAlias < ActiveRecord::Base @@ -221,52 +218,53 @@ def test_attribute_was_works_when_options_for_old_encrypted_value_are_different_ assert_equal pw.reverse, account.password end - if ::ActiveRecord::VERSION::STRING > "4.0" - def test_should_assign_attributes - @user = UserWithProtectedAttribute.new(login: 'login', is_admin: false) - @user.attributes = ActionController::Parameters.new(login: 'modified', is_admin: true).permit(:login) - assert_equal 'modified', @user.login + if ::ActiveRecord::VERSION::STRING >= "5.2" + def test_should_create_will_save_change_to_predicate + person = Person.create!(email: 'test@example.com') + refute person.will_save_change_to_email? + person.email = 'test@example.com' + refute person.will_save_change_to_email? + person.email = 'test2@example.com' + assert person.will_save_change_to_email? end - def test_should_not_assign_protected_attributes - @user = UserWithProtectedAttribute.new(login: 'login', is_admin: false) - @user.attributes = ActionController::Parameters.new(login: 'modified', is_admin: true).permit(:login) - assert !@user.is_admin? + def test_should_create_saved_change_to_predicate + person = Person.create!(email: 'test@example.com') + assert person.saved_change_to_email? + person.reload + person.email = 'test@example.com' + refute person.saved_change_to_email? + person.email = nil + refute person.saved_change_to_email? + person.email = 'test2@example.com' + refute person.saved_change_to_email? + person.save + assert person.saved_change_to_email? end + end - def test_should_raise_exception_if_not_permitted - @user = UserWithProtectedAttribute.new(login: 'login', is_admin: false) - assert_raises ActiveModel::ForbiddenAttributesError do - @user.attributes = ActionController::Parameters.new(login: 'modified', is_admin: true) - end - end + def test_should_assign_attributes + @user = UserWithProtectedAttribute.new(login: 'login', is_admin: false) + @user.attributes = ActionController::Parameters.new(login: 'modified', is_admin: true).permit(:login) + assert_equal 'modified', @user.login + end - def test_should_raise_exception_on_init_if_not_permitted - assert_raises ActiveModel::ForbiddenAttributesError do - @user = UserWithProtectedAttribute.new ActionController::Parameters.new(login: 'modified', is_admin: true) - end - end - else - def test_should_assign_attributes - @user = UserWithProtectedAttribute.new(login: 'login', is_admin: false) - @user.attributes = { login: 'modified', is_admin: true } - assert_equal 'modified', @user.login - end + def test_should_not_assign_protected_attributes + @user = UserWithProtectedAttribute.new(login: 'login', is_admin: false) + @user.attributes = ActionController::Parameters.new(login: 'modified', is_admin: true).permit(:login) + assert !@user.is_admin? + end - def test_should_not_assign_protected_attributes - @user = UserWithProtectedAttribute.new(login: 'login', is_admin: false) - @user.attributes = { login: 'modified', is_admin: true } - assert !@user.is_admin? + def test_should_raise_exception_if_not_permitted + @user = UserWithProtectedAttribute.new(login: 'login', is_admin: false) + assert_raises ActiveModel::ForbiddenAttributesError do + @user.attributes = ActionController::Parameters.new(login: 'modified', is_admin: true) end + end - def test_should_assign_protected_attributes - @user = UserWithProtectedAttribute.new(login: 'login', is_admin: false) - if ::ActiveRecord::VERSION::STRING > "3.1" - @user.send(:assign_attributes, { login: 'modified', is_admin: true }, without_protection: true) - else - @user.send(:attributes=, { login: 'modified', is_admin: true }, false) - end - assert @user.is_admin? + def test_should_raise_exception_on_init_if_not_permitted + assert_raises ActiveModel::ForbiddenAttributesError do + @user = UserWithProtectedAttribute.new ActionController::Parameters.new(login: 'modified', is_admin: true) end end @@ -291,11 +289,9 @@ def test_should_allow_proc_based_mode assert_nil @person.encrypted_credentials_iv end - if ::ActiveRecord::VERSION::STRING > "3.1" - def test_should_allow_assign_attributes_with_nil - @person = Person.new - assert_nil(@person.assign_attributes nil) - end + def test_should_allow_assign_attributes_with_nil + @person = Person.new + assert_nil(@person.assign_attributes nil) end def test_that_alias_encrypts_column diff --git a/test/run.sh b/test/run.sh index 7e9b777e..cfdef5ed 100755 --- a/test/run.sh +++ b/test/run.sh @@ -1,12 +1,20 @@ -#!/usr/bin/env sh -e +#!/usr/bin/env bash -for RUBY in 1.9.3 2.0.0 2.1 2.2 +set -e + +for RUBY in 2.6.10 2.7.6 do - for RAILS in 2.3.8 3.0.0 3.1.0 3.2.0 4.0.0 4.1.0 4.2.0 + for ACTIVERECORD in 5.1.1 5.2.8 do - if [[ $RUBY -gt 1.9.3 && $RAILS -lt 4.0.0 ]]; then - continue - fi - RBENV_VERSION=$RUBY ACTIVERECORD=$RAILS bundle && bundle exec rake + echo ">>> Testing with Ruby ${RUBY} and ActiveRecord ${ACTIVERECORD}." + export RBENV_VERSION=$RUBY + export ACTIVERECORD=$ACTIVERECORD + + rbenv install $RUBY --skip-existing + bundle install + bundle check + bundle exec rake test + rm Gemfile.lock + echo ">>> Finished testing with Ruby ${RUBY} and ActiveRecord ${ACTIVERECORD}." done done diff --git a/test/test_helper.rb b/test/test_helper.rb index c352a138..f8283fff 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,14 +1,13 @@ # frozen_string_literal: true +require 'pry' require 'simplecov' require 'simplecov-rcov' -require "codeclimate-test-reporter" SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new( [ SimpleCov::Formatter::HTMLFormatter, - SimpleCov::Formatter::RcovFormatter, - CodeClimate::TestReporter::Formatter + SimpleCov::Formatter::RcovFormatter ] ) @@ -16,8 +15,6 @@ add_filter 'test' end -CodeClimate::TestReporter.start - require 'minitest/autorun' # Rails 4.0.x pins to an old minitest