1# frozen_string_literal: true 2 3require 'spec_helper' 4 5RSpec.describe 'DisableJoins' do 6 let(:primary_model) do 7 Class.new(ApplicationRecord) do 8 self.table_name = '_test_primary_records' 9 10 def self.name 11 'TestPrimary' 12 end 13 end 14 end 15 16 let(:bridge_model) do 17 Class.new(ApplicationRecord) do 18 self.table_name = '_test_bridge_records' 19 20 def self.name 21 'TestBridge' 22 end 23 end 24 end 25 26 let(:secondary_model) do 27 Class.new(ApplicationRecord) do 28 self.table_name = '_test_secondary_records' 29 30 def self.name 31 'TestSecondary' 32 end 33 end 34 end 35 36 context 'passing disable_joins as an association option' do 37 context 'when the association is a bare has_one' do 38 it 'disallows the disable_joins option' do 39 expect do 40 primary_model.has_one :test_bridge, disable_joins: true 41 end.to raise_error(ArgumentError, /Unknown key: :disable_joins/) 42 end 43 end 44 45 context 'when the association is a belongs_to' do 46 it 'disallows the disable_joins option' do 47 expect do 48 bridge_model.belongs_to :test_secondary, disable_joins: true 49 end.to raise_error(ArgumentError, /Unknown key: :disable_joins/) 50 end 51 end 52 53 context 'when the association is has_one :through' do 54 it 'allows the disable_joins option' do 55 primary_model.has_one :test_bridge 56 bridge_model.belongs_to :test_secondary 57 58 expect do 59 primary_model.has_one :test_secondary, through: :test_bridge, disable_joins: true 60 end.not_to raise_error 61 end 62 end 63 64 context 'when the association is a bare has_many' do 65 it 'disallows the disable_joins option' do 66 expect do 67 primary_model.has_many :test_bridges, disable_joins: true 68 end.to raise_error(ArgumentError, /Unknown key: :disable_joins/) 69 end 70 end 71 72 context 'when the association is a has_many :through' do 73 it 'allows the disable_joins option' do 74 primary_model.has_many :test_bridges 75 bridge_model.belongs_to :test_secondary 76 77 expect do 78 primary_model.has_many :test_secondaries, through: :test_bridges, disable_joins: true 79 end.not_to raise_error 80 end 81 end 82 end 83 84 context 'querying has_one :through when disable_joins is set' do 85 before do 86 create_tables(<<~SQL) 87 CREATE TABLE _test_primary_records ( 88 id serial NOT NULL PRIMARY KEY); 89 90 CREATE TABLE _test_bridge_records ( 91 id serial NOT NULL PRIMARY KEY, 92 primary_record_id int NOT NULL, 93 secondary_record_id int NOT NULL); 94 95 CREATE TABLE _test_secondary_records ( 96 id serial NOT NULL PRIMARY KEY); 97 SQL 98 99 primary_model.has_one :test_bridge, anonymous_class: bridge_model, foreign_key: :primary_record_id 100 bridge_model.belongs_to :test_secondary, anonymous_class: secondary_model, foreign_key: :secondary_record_id 101 primary_model.has_one :test_secondary, through: :test_bridge, anonymous_class: secondary_model, 102 disable_joins: -> { joins_disabled_flag } 103 104 primary_record = primary_model.create! 105 secondary_record = secondary_model.create! 106 bridge_model.create!(primary_record_id: primary_record.id, secondary_record_id: secondary_record.id) 107 end 108 109 context 'when disable_joins evaluates to true' do 110 let(:joins_disabled_flag) { true } 111 112 it 'executes separate queries' do 113 primary_record = primary_model.first 114 115 query_count = ActiveRecord::QueryRecorder.new { primary_record.test_secondary }.count 116 117 expect(query_count).to eq(2) 118 end 119 end 120 121 context 'when disable_joins evalutes to false' do 122 let(:joins_disabled_flag) { false } 123 124 it 'executes a single query' do 125 primary_record = primary_model.first 126 127 query_count = ActiveRecord::QueryRecorder.new { primary_record.test_secondary }.count 128 129 expect(query_count).to eq(1) 130 end 131 end 132 end 133 134 context 'querying has_many :through when disable_joins is set' do 135 before do 136 create_tables(<<~SQL) 137 CREATE TABLE _test_primary_records ( 138 id serial NOT NULL PRIMARY KEY); 139 140 CREATE TABLE _test_bridge_records ( 141 id serial NOT NULL PRIMARY KEY, 142 primary_record_id int NOT NULL); 143 144 CREATE TABLE _test_secondary_records ( 145 id serial NOT NULL PRIMARY KEY, 146 bridge_record_id int NOT NULL); 147 SQL 148 149 primary_model.has_many :test_bridges, anonymous_class: bridge_model, foreign_key: :primary_record_id 150 bridge_model.has_many :test_secondaries, anonymous_class: secondary_model, foreign_key: :bridge_record_id 151 primary_model.has_many :test_secondaries, through: :test_bridges, anonymous_class: secondary_model, 152 disable_joins: -> { disabled_join_flag } 153 154 primary_record = primary_model.create! 155 bridge_record = bridge_model.create!(primary_record_id: primary_record.id) 156 secondary_model.create!(bridge_record_id: bridge_record.id) 157 end 158 159 context 'when disable_joins evaluates to true' do 160 let(:disabled_join_flag) { true } 161 162 it 'executes separate queries' do 163 primary_record = primary_model.first 164 165 query_count = ActiveRecord::QueryRecorder.new { primary_record.test_secondaries.first }.count 166 167 expect(query_count).to eq(2) 168 end 169 end 170 171 context 'when disable_joins evalutes to false' do 172 let(:disabled_join_flag) { false } 173 174 it 'executes a single query' do 175 primary_record = primary_model.first 176 177 query_count = ActiveRecord::QueryRecorder.new { primary_record.test_secondaries.first }.count 178 179 expect(query_count).to eq(1) 180 end 181 end 182 end 183 184 context 'querying STI relationships' do 185 let(:child_bridge_model) do 186 Class.new(bridge_model) do 187 def self.name 188 'ChildBridge' 189 end 190 end 191 end 192 193 let(:child_secondary_model) do 194 Class.new(secondary_model) do 195 def self.name 196 'ChildSecondary' 197 end 198 end 199 end 200 201 before do 202 create_tables(<<~SQL) 203 CREATE TABLE _test_primary_records ( 204 id serial NOT NULL PRIMARY KEY); 205 206 CREATE TABLE _test_bridge_records ( 207 id serial NOT NULL PRIMARY KEY, 208 primary_record_id int NOT NULL, 209 type text); 210 211 CREATE TABLE _test_secondary_records ( 212 id serial NOT NULL PRIMARY KEY, 213 bridge_record_id int NOT NULL, 214 type text); 215 SQL 216 217 primary_model.has_many :child_bridges, anonymous_class: child_bridge_model, foreign_key: :primary_record_id 218 child_bridge_model.has_one :child_secondary, anonymous_class: child_secondary_model, foreign_key: :bridge_record_id 219 primary_model.has_many :child_secondaries, through: :child_bridges, anonymous_class: child_secondary_model, disable_joins: true 220 221 primary_record = primary_model.create! 222 parent_bridge_record = bridge_model.create!(primary_record_id: primary_record.id) 223 child_bridge_record = child_bridge_model.create!(primary_record_id: primary_record.id) 224 225 secondary_model.create!(bridge_record_id: child_bridge_record.id) 226 child_secondary_model.create!(bridge_record_id: parent_bridge_record.id) 227 child_secondary_model.create!(bridge_record_id: child_bridge_record.id) 228 end 229 230 it 'filters correctly by the STI type across multiple queries' do 231 primary_record = primary_model.first 232 233 query_recorder = ActiveRecord::QueryRecorder.new do 234 expect(primary_record.child_secondaries.count).to eq(1) 235 end 236 237 expect(query_recorder.count).to eq(2) 238 end 239 end 240 241 context 'querying polymorphic relationships' do 242 before do 243 create_tables(<<~SQL) 244 CREATE TABLE _test_primary_records ( 245 id serial NOT NULL PRIMARY KEY); 246 247 CREATE TABLE _test_bridge_records ( 248 id serial NOT NULL PRIMARY KEY, 249 primaryable_id int NOT NULL, 250 primaryable_type text NOT NULL); 251 252 CREATE TABLE _test_secondary_records ( 253 id serial NOT NULL PRIMARY KEY, 254 bridgeable_id int NOT NULL, 255 bridgeable_type text NOT NULL); 256 SQL 257 258 primary_model.has_many :test_bridges, anonymous_class: bridge_model, foreign_key: :primaryable_id, as: :primaryable 259 bridge_model.has_one :test_secondaries, anonymous_class: secondary_model, foreign_key: :bridgeable_id, as: :bridgeable 260 primary_model.has_many :test_secondaries, through: :test_bridges, anonymous_class: secondary_model, disable_joins: true 261 262 primary_record = primary_model.create! 263 primary_bridge_record = bridge_model.create!(primaryable_id: primary_record.id, primaryable_type: 'TestPrimary') 264 nonprimary_bridge_record = bridge_model.create!(primaryable_id: primary_record.id, primaryable_type: 'NonPrimary') 265 266 secondary_model.create!(bridgeable_id: primary_bridge_record.id, bridgeable_type: 'TestBridge') 267 secondary_model.create!(bridgeable_id: nonprimary_bridge_record.id, bridgeable_type: 'TestBridge') 268 secondary_model.create!(bridgeable_id: primary_bridge_record.id, bridgeable_type: 'NonBridge') 269 end 270 271 it 'filters correctly by the polymorphic type across multiple queries' do 272 primary_record = primary_model.first 273 274 query_recorder = ActiveRecord::QueryRecorder.new do 275 expect(primary_record.test_secondaries.count).to eq(1) 276 end 277 278 expect(query_recorder.count).to eq(2) 279 end 280 end 281 282 def create_tables(table_sql) 283 ApplicationRecord.connection.execute(table_sql) 284 285 bridge_model.reset_column_information 286 secondary_model.reset_column_information 287 end 288end 289