diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7daa432f..0025eb93 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,13 +13,13 @@ jobs: fail-fast: false matrix: rails: + - 7-2-stable - v7.1.0 - v7.0.3 - v6.1.6 ruby: - 3.2.2 - 3.1.4 - - 3.0.6 env: DB: sqlite3 RAILS: ${{ matrix.rails }} @@ -39,13 +39,13 @@ jobs: fail-fast: false matrix: rails: + - 7-2-stable - v7.1.0 - v7.0.3 - v6.1.6 ruby: - 3.2.2 - 3.1.4 - - 3.0.6 env: DB: mysql RAILS: ${{ matrix.rails }} @@ -74,13 +74,13 @@ jobs: fail-fast: false matrix: rails: + - 7-2-stable - v7.1.0 - v7.0.3 - v6.1.6 ruby: - 3.2.2 - 3.1.4 - - 3.0.6 env: DB: postgres RAILS: ${{ matrix.rails }} diff --git a/Gemfile b/Gemfile index 4b3b92e4..577330c2 100644 --- a/Gemfile +++ b/Gemfile @@ -15,7 +15,7 @@ rails_version = case rails end gem 'faker' -gem 'sqlite3' +gem 'sqlite3', '~> 1.4' gem 'pg' gem 'pry' gem 'byebug' diff --git a/lib/polyamorous/activerecord/join_association_7_2.rb b/lib/polyamorous/activerecord/join_association_7_2.rb new file mode 100644 index 00000000..7cfa82fd --- /dev/null +++ b/lib/polyamorous/activerecord/join_association_7_2.rb @@ -0,0 +1,55 @@ +module Polyamorous + module JoinAssociationExtensions + # Same as #join_constraints, but instead of constructing tables from the + # given block, uses the ones passed + def join_constraints_with_tables(foreign_table, foreign_klass, join_type, alias_tracker, tables) + joins = [] + chain = [] + + reflection.chain.each.with_index do |reflection, i| + table = tables[i] + + @table ||= table + chain << [reflection, table] + end + + base_klass.with_connection do |connection| + # The chain starts with the target table, but we want to end with it here (makes + # more sense in this context), so we reverse + chain.reverse_each do |reflection, table| + klass = reflection.klass + + join_scope = reflection.join_scope(table, foreign_table, foreign_klass) + + unless join_scope.references_values.empty? + join_dependency = join_scope.construct_join_dependency( + join_scope.eager_load_values | join_scope.includes_values, Arel::Nodes::OuterJoin + ) + join_scope.joins!(join_dependency) + end + + arel = join_scope.arel(alias_tracker.aliases) + nodes = arel.constraints.first + + if nodes.is_a?(Arel::Nodes::And) + others = nodes.children.extract! do |node| + !Arel.fetch_attribute(node) { |attr| attr.relation.name == table.name } + end + end + + joins << table.create_join(table, table.create_on(nodes), join_type) + + if others && !others.empty? + joins.concat arel.join_sources + append_constraints(connection, joins.last, others) + end + + # The current table in this iteration becomes the foreign table in the next + foreign_table, foreign_klass = table, klass + end + + joins + end + end + end +end diff --git a/lib/polyamorous/polyamorous.rb b/lib/polyamorous/polyamorous.rb index 032f7171..9e558709 100644 --- a/lib/polyamorous/polyamorous.rb +++ b/lib/polyamorous/polyamorous.rb @@ -15,6 +15,10 @@ module Polyamorous require 'polyamorous/activerecord/join_dependency' require 'polyamorous/activerecord/reflection' + if ::ActiveRecord.version > ::Gem::Version.new("7.1") + require "polyamorous/activerecord/join_association_7_2" + end + ActiveRecord::Reflection::AbstractReflection.send(:prepend, Polyamorous::ReflectionExtensions) Polyamorous::JoinDependency.send(:prepend, Polyamorous::JoinDependencyExtensions) diff --git a/lib/ransack/adapters/active_record/context.rb b/lib/ransack/adapters/active_record/context.rb index 48a6ea80..e28fd812 100644 --- a/lib/ransack/adapters/active_record/context.rb +++ b/lib/ransack/adapters/active_record/context.rb @@ -110,7 +110,7 @@ def klassify(obj) # def join_sources base, joins = begin - alias_tracker = ::ActiveRecord::Associations::AliasTracker.create(self.klass.connection, @object.table.name, []) + alias_tracker = @object.alias_tracker constraints = @join_dependency.join_constraints(@object.joins_values, alias_tracker, @object.references_values) [ @@ -278,7 +278,7 @@ def build_joins(relation) join_list = join_nodes + convert_join_strings_to_ast(relation.table, string_joins) - alias_tracker = ::ActiveRecord::Associations::AliasTracker.create(self.klass.connection, relation.table.name, join_list) + alias_tracker = relation.alias_tracker(join_list) join_dependency = Polyamorous::JoinDependency.new(relation.klass, relation.table, association_joins, Arel::Nodes::OuterJoin) join_dependency.instance_variable_set(:@alias_tracker, alias_tracker) join_nodes.each do |join|