chronomodel
chronomodel copied to clipboard
`includes` does not work as expected in historical queries
Reproducible test case
# frozen_string_literal: true
require 'bundler/inline'
gemfile(true) do
source 'https://rubygems.org'
gem 'chrono_model'
# Test against latest Chronomodel:
# gem 'chrono_model', github: 'ifad/chronomodel'
gem 'pg'
gem 'debug'
gem 'rails'
end
require 'chrono_model'
require 'minitest/autorun'
require 'logger'
require 'debug'
# Needs a database called `chronomodel_test`
ActiveRecord::Base.establish_connection(adapter: 'chronomodel', database: 'chronomodel_test')
ActiveRecord::Base.logger = Logger.new($stdout)
ActiveRecord::Schema.define do
enable_extension :btree_gist
create_table :activities, temporal: true, force: true do |t|
t.string :name
t.timestamps
end
create_table :activity_roadmaps, temporal: true, force: true do |t|
t.belongs_to :activity
t.string :name
t.timestamps
end
create_table :activity_steps, temporal: true, force: true do |t|
t.belongs_to :activity_roadmap
t.string :name
t.integer :position, default: 1
t.boolean :active, default: false
t.timestamps
end
end
class Activity < ActiveRecord::Base
include ChronoModel::TimeMachine
has_one :activity_roadmap, dependent: :destroy
has_many :activity_steps, through: :activity_roadmap
has_one :current_activity_step, through: :activity_roadmap
scope :with_includes, lambda {
includes(
:current_activity_step,
activity_roadmap: %i[activity_steps]
)
}
end
class ActivityRoadmap < ActiveRecord::Base
include ChronoModel::TimeMachine
belongs_to :activity
has_many :activity_steps, -> { sorted }, dependent: :destroy, inverse_of: :activity_roadmap
has_one :current_activity_step, -> { active.sorted }, class_name: 'ActivityStep', dependent: nil, inverse_of: false
end
class ActivityStep < ActiveRecord::Base
include ChronoModel::TimeMachine
belongs_to :activity_roadmap
scope :sorted, -> { order(:position) }
scope :active, -> { where(active: true) }
end
activity = Activity.create!(name: 'Test')
activity_roadmap = activity.create_activity_roadmap!(name: 'Test')
activity_roadmap.activity_steps.create!(position: 1, active: false)
activity_roadmap.activity_steps.create!(position: 2, active: true)
class BugTest < Minitest::Test
def test_temporal_includes
assert Activity.with_includes.first
end
def test_historical_includes
assert Activity.as_of(Time.now).with_includes.first
end
end
Workarounds
includes(
- :current_activity_step,
- activity_roadmap: :activity_steps
+ activity_roadmap: :activity_steps,
+ current_activity_step: []
)
or
+ # Use `activity.current_activity_step` through the roadmap to take advantage of eager loading
includes(
- :current_activity_step,
- activity_roadmap: :activity_steps
+ activity_roadmap: %i[activity_steps current_activity_step]
)