diff --git a/.travis.yml b/.travis.yml index 21d8946..916231d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -90,5 +90,6 @@ gemfile: - gemfiles/rails_4.2.3.gemfile - gemfiles/rails_4.2.4.gemfile - gemfiles/rails_4.2.5.1.gemfile + - gemfiles/rails_4.2.6.gemfile sudo: false diff --git a/Appraisals b/Appraisals index 4eaef59..338f280 100644 --- a/Appraisals +++ b/Appraisals @@ -85,6 +85,7 @@ RAILS_VERSIONS = %w( 4.2.3 4.2.4 4.2.5.1 + 4.2.6 ) RAILS_VERSIONS.each do |version| diff --git a/gemfiles/rails_4.2.6.gemfile b/gemfiles/rails_4.2.6.gemfile new file mode 100644 index 0000000..b19415f --- /dev/null +++ b/gemfiles/rails_4.2.6.gemfile @@ -0,0 +1,7 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "activerecord", "4.2.6" + +gemspec :path => "../" diff --git a/lib/store_base_sti_class_for_4_2.rb b/lib/store_base_sti_class_for_4_2.rb index f3317af..7d8ae23 100644 --- a/lib/store_base_sti_class_for_4_2.rb +++ b/lib/store_base_sti_class_for_4_2.rb @@ -1,3 +1,5 @@ +require 'active_record/associations/join_dependency/join_part' + if ActiveRecord::VERSION::STRING =~ /^4\.2/ module ActiveRecord @@ -29,6 +31,72 @@ def creation_attributes end end + class JoinDependency # :nodoc: + class JoinAssociation < JoinPart # :nodoc: + def join_constraints(foreign_table, foreign_klass, node, join_type, tables, scope_chain, chain) + joins = [] + bind_values = [] + tables = tables.reverse + + scope_chain_index = 0 + scope_chain = scope_chain.reverse + + # 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 = tables.shift + klass = reflection.klass + + join_keys = reflection.join_keys(klass) + key = join_keys.key + foreign_key = join_keys.foreign_key + + constraint = build_constraint(klass, table, key, foreign_table, foreign_key) + + scope_chain_items = scope_chain[scope_chain_index].map do |item| + if item.is_a?(Relation) + item + else + ActiveRecord::Relation.create(klass, table).instance_exec(node, &item) + end + end + scope_chain_index += 1 + + scope_chain_items.concat [klass.send(:build_default_scope, ActiveRecord::Relation.create(klass, table))].compact + + rel = scope_chain_items.inject(scope_chain_items.shift) do |left, right| + left.merge right + end + + if rel && !rel.arel.constraints.empty? + bind_values.concat rel.bind_values + constraint = constraint.and rel.arel.constraints + end + + if reflection.type + # START PATCH + # original: + # value = foreign_klass.base_class.name + value = ActiveRecord::Base.store_base_sti_class ? foreign_klass.base_class.name : foreign_klass.name + # END PATCH + column = klass.columns_hash[reflection.type.to_s] + + substitute = klass.connection.substitute_at(column) + bind_values.push [column, value] + constraint = constraint.and table[reflection.type].eq substitute + end + + joins << table.create_join(table, table.create_on(constraint), join_type) + + # The current table in this iteration becomes the foreign table in the next + foreign_table, foreign_klass = table, klass + end + + JoinInformation.new joins, bind_values + end + end + end + class BelongsToPolymorphicAssociation private @@ -231,6 +299,27 @@ def construct_join_attributes(*records) end + class HasManyThroughAssociation + + def build_through_record(record) + @through_records[record.object_id] ||= begin + ensure_mutable + + through_record = through_association.build(*options_for_through_record) + through_record.send("#{source_reflection.name}=", record) + + # START PATCH + if ActiveRecord::Base.store_base_sti_class + if options[:source_type] + through_record.send("#{source_reflection.foreign_type}=", options[:source_type]) + end + end + # END PATCH + + through_record + end + end + end end module Reflection diff --git a/test/test_store_base_sti_class.rb b/test/test_store_base_sti_class.rb index 1e81361..821ebac 100644 --- a/test/test_store_base_sti_class.rb +++ b/test/test_store_base_sti_class.rb @@ -148,6 +148,13 @@ def test_polymorphic_has_many_through_with_double_sti_on_join_model assert_equal post, tagging.taggable end + def test_join_association + tag = SpecialTag.create!(:name => 'Special') + tag.polytaggings << Tagging.new + + assert SpecialTag.joins(:polytaggings).where(id: tag.id).first + end + if Gem::Version.new(ActiveRecord::VERSION::STRING) < Gem::Version.new('4.1.0') def test_finder_sql_is_supported author = Author.create!(:name => 'Bob')