diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index 0c16eda6..979ce524 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -27,7 +27,7 @@ jobs: strategy: matrix: ruby-version: ['2.3', '2.4', '2.5', '2.6', '2.7', '3.0'] - gemfile: [ rails_5.0, rails_5.1, rails_5.2, rails_6.0, rails_6.1 ] + gemfile: [ rails_5.0, rails_5.1, rails_5.2, rails_6.0, rails_6.1, rails_7.0, rails_edge ] experimental: [false] exclude: @@ -35,10 +35,26 @@ jobs: gemfile: rails_6.0 - ruby-version: '2.3' gemfile: rails_6.1 + - ruby-version: '2.3' + gemfile: rails_7.0 + - ruby-version: '2.3' + gemfile: rails_edge - ruby-version: '2.4' gemfile: rails_6.0 - ruby-version: '2.4' gemfile: rails_6.1 + - ruby-version: '2.4' + gemfile: rails_7.0 + - ruby-version: '2.4' + gemfile: rails_edge + - ruby-version: '2.5' + gemfile: rails_7.0 + - ruby-version: '2.5' + gemfile: rails_edge + - ruby-version: '2.6' + gemfile: rails_7.0 + - ruby-version: '2.6' + gemfile: rails_edge - ruby-version: '2.6' gemfile: rails_5.0 - ruby-version: '2.6' @@ -49,6 +65,8 @@ jobs: gemfile: rails_5.1 - ruby-version: '2.7' gemfile: rails_5.2 + - ruby-version: '2.7' + gemfile: rails_edge - ruby-version: '3.0' gemfile: rails_5.0 - ruby-version: '3.0' diff --git a/.gitignore b/.gitignore index a818e1f6..354f1680 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ spec/debug.log test/tmp test/version_tmp tmp +.envrc diff --git a/Rakefile b/Rakefile index 46c8aacd..50672dcd 100644 --- a/Rakefile +++ b/Rakefile @@ -16,7 +16,7 @@ namespace :testapp do FileUtils.mkdir_p('tmp/aruba') Dir.chdir('tmp') do FileUtils.rm_rf('railsapp') - sh 'rails new railsapp --skip-bundle --skip-webpack-install --skip-git' + sh 'rails new railsapp --skip-bundle --skip-javascript --skip-webpack-install --skip-git' end FileUtils.cp_r('spec/aruba/fixtures/railsapp/.', 'tmp/railsapp/') FileUtils.rm('tmp/railsapp/Gemfile') diff --git a/gemfiles/rails_7.0.gemfile b/gemfiles/rails_7.0.gemfile new file mode 100644 index 00000000..ffab2c18 --- /dev/null +++ b/gemfiles/rails_7.0.gemfile @@ -0,0 +1,6 @@ +source "https://rubygems.org" + +gem "activerecord", "~> 7.0.0" +gem "rails", "~> 7.0.0" + +gemspec :path => "../" diff --git a/gemfiles/rails_edge.gemfile b/gemfiles/rails_edge.gemfile new file mode 100644 index 00000000..4d75c650 --- /dev/null +++ b/gemfiles/rails_edge.gemfile @@ -0,0 +1,6 @@ +source "https://rubygems.org" + +gem 'rails', github: 'rails/rails', branch: 'main' +gem 'activerecord', github: 'rails/rails', branch: 'main' + +gemspec :path => "../" diff --git a/lib/active_record/tasks/chronomodel_database_tasks.rb b/lib/active_record/tasks/chronomodel_database_tasks.rb index b1796a6e..c484f66b 100644 --- a/lib/active_record/tasks/chronomodel_database_tasks.rb +++ b/lib/active_record/tasks/chronomodel_database_tasks.rb @@ -41,6 +41,14 @@ def data_load(source) private + def set_psql_env + if ActiveRecord::VERSION::STRING < '7.0' + super + else + psql_env + end + end + def configuration # In Rails 6.1.x the configuration instance variable is not available # and it's been replaced by @configuration_hash (which is frozen). diff --git a/lib/chrono_model/conversions.rb b/lib/chrono_model/conversions.rb index ea66e0ca..78187138 100644 --- a/lib/chrono_model/conversions.rb +++ b/lib/chrono_model/conversions.rb @@ -15,7 +15,7 @@ def string_to_utc_time(string) end def time_to_utc_string(time) - [time.to_s(:db), sprintf('%06d', time.usec)].join '.' + [time.to_formatted_s(:db), sprintf('%06d', time.usec)].join '.' end end diff --git a/lib/chrono_model/patches/preloader.rb b/lib/chrono_model/patches/preloader.rb index d76109d9..1627aeaf 100644 --- a/lib/chrono_model/patches/preloader.rb +++ b/lib/chrono_model/patches/preloader.rb @@ -13,6 +13,9 @@ module Preloader # def initialize(options = {}) @options = options.freeze + if ActiveRecord::VERSION::STRING >= '7.0' + super(associate_by_default: true, **options) + end end # Patches the AR Preloader (lib/active_record/associations/preloader.rb) diff --git a/lib/chrono_model/patches/relation.rb b/lib/chrono_model/patches/relation.rb index d850bd97..e94ec122 100644 --- a/lib/chrono_model/patches/relation.rb +++ b/lib/chrono_model/patches/relation.rb @@ -4,6 +4,26 @@ module Patches module Relation include ChronoModel::Patches::AsOfTimeHolder + def preload_associations(records) # :nodoc: + return super if ActiveRecord::VERSION::STRING < '7.0' + + preload = preload_values + preload += includes_values unless eager_loading? + scope = strict_loading_value ? StrictLoadingScope : nil + preload.each do |associations| + if as_of_time + ## the tree structure of preloading ensure that children are loaded from history + klass = if associations.is_a? Hash + associations.keys[0].to_s.classify.constantize + else + associations.to_s.classify.constantize + end + scope = klass.as_of(as_of_time) if klass.respond_to?(:as_of) + end + ActiveRecord::Associations::Preloader.new(records: records, associations: associations, scope: scope).call + end + end + def load return super unless @_as_of_time && !loaded? diff --git a/lib/chrono_model/time_machine/history_model.rb b/lib/chrono_model/time_machine/history_model.rb index 75424671..aad110c5 100644 --- a/lib/chrono_model/time_machine/history_model.rb +++ b/lib/chrono_model/time_machine/history_model.rb @@ -10,6 +10,15 @@ module HistoryModel scope :chronological, -> { order(Arel.sql('lower(validity) ASC')) } end + # ACTIVE RECORD 7 does not call class.find but a new internal method called _find_record + def _find_record(options) + if options && options[:lock] + self.class.preload(strict_loaded_associations).lock(options[:lock]).find_by_hid(hid) + else + self.class.preload(strict_loaded_associations).find_by_hid(hid) + end + end + # Methods that make up the history interface of the companion History # model, automatically built for each Model that includes TimeMachine # diff --git a/spec/aruba/fixtures/railsapp/config/environments/development.rb b/spec/aruba/fixtures/railsapp/config/environments/development.rb index 199e2bb8..6d4fb2c5 100644 --- a/spec/aruba/fixtures/railsapp/config/environments/development.rb +++ b/spec/aruba/fixtures/railsapp/config/environments/development.rb @@ -24,10 +24,10 @@ # Debug mode disables concatenation and preprocessing of assets. # This option may cause significant delays in view rendering with a large # number of complex assets. - config.assets.debug = true + config.assets.debug = true if Rails.version < '7.0' # Suppress logger output for asset requests. - config.assets.quiet = true + config.assets.quiet = true if Rails.version < '7.0' # Raises error for missing translations # config.action_view.raise_on_missing_translations = true diff --git a/spec/aruba/fixtures/railsapp/config/initializers/assets.rb b/spec/aruba/fixtures/railsapp/config/initializers/assets.rb new file mode 100644 index 00000000..ea9979e6 --- /dev/null +++ b/spec/aruba/fixtures/railsapp/config/initializers/assets.rb @@ -0,0 +1,3 @@ +## this file overrides the default initialization of assets.rb + +Rails.application.config.assets.version = "1.0" if Rails.version < '7.0' diff --git a/spec/aruba/rake_task_spec.rb b/spec/aruba/rake_task_spec.rb index ddef7b4e..06dcf13f 100644 --- a/spec/aruba/rake_task_spec.rb +++ b/spec/aruba/rake_task_spec.rb @@ -1,21 +1,23 @@ require 'spec_helper' - +require 'rake' +include ChronoTest::Aruba # add :announce_stdout, :announce_stderr, before the type: aruba tag in order # to see the commmands' stdout and stderr output. # + describe 'rake tasks', type: :aruba do describe 'bundle exec rake -T' do before { run_command_and_stop('bundle exec rake -T') } subject { last_command_started } - it { is_expected.to have_output(/db:structure:load/) } + it { is_expected.to have_output(load_schema_task(as_regexp: true)) } end - describe 'db:schema:dump' do + describe "#{dump_schema_task}" do let(:database_yml) { 'fixtures/database_without_username_and_password.yml' } before { write_file('config/database.yml', File.read(File.expand_path(database_yml, __dir__))) } - before { run_command_and_stop('bundle exec rake db:schema:dump SCHEMA=db/test.sql') } + before { run_command_and_stop("bundle exec rake #{dump_schema_task} SCHEMA=db/test.sql") } it { expect(last_command_started).to be_successfully_executed } it { expect('db/test.sql').to be_an_existing_file } @@ -34,8 +36,8 @@ it { expect(last_command_started).to have_output(/set_config/) } end - describe 'db:structure:load' do - let(:action) { run_command('bundle exec rake db:structure:load') } + describe "#{load_schema_task}" do + let(:action) { run_command("bundle exec rake #{load_schema_task}") } let(:last_command) { action && last_command_started } context 'given a file db/structure.sql' do diff --git a/spec/support/aruba.rb b/spec/support/aruba.rb index f19e454b..ada6498b 100644 --- a/spec/support/aruba.rb +++ b/spec/support/aruba.rb @@ -1,9 +1,32 @@ require 'aruba/rspec' require 'arel' +require 'rake' +require 'rails' module ChronoTest module Aruba + + def load_schema_task(as_regexp: false) + str = + if Rails.version < '7.0' + "db:structure:load" + else + "db:schema:load" + end + return as_regexp ? Regexp.new(str) : str + end + + def dump_schema_task(as_regexp: false) + str = + if Rails.version < '7.0' + "db:structure:dump" + else + "db:schema:dump" + end + return as_regexp ? Regexp.new(str) : str + end + def aruba_working_directory File.expand_path("../../#{::Aruba.config.working_directory}", __dir__) end