Skip to content

Instantly share code, notes, and snippets.

@kirikak2
Created February 1, 2019 11:01
Show Gist options
  • Save kirikak2/50cb7f2252397c64221812945b3c3078 to your computer and use it in GitHub Desktop.
Save kirikak2/50cb7f2252397c64221812945b3c3078 to your computer and use it in GitHub Desktop.
diff --git a/config/initializers/type_caster_patch.rb b/config/initializers/type_caster_patch.rb
new file mode 100644
index 0000000..58778a3
--- /dev/null
+++ b/config/initializers/type_caster_patch.rb
@@ -0,0 +1,3 @@
+require Rails.root.join('lib', 'activerecord', 'associations', 'alias_tracker.rb')
+require Rails.root.join('lib', 'activerecord', 'associations', 'join_dependency.rb')
+require Rails.root.join('lib', 'activerecord', 'associations', 'association_scope.rb')
\ No newline at end of file
diff --git a/lib/activerecord/associations/alias_tracker.rb b/lib/activerecord/associations/alias_tracker.rb
new file mode 100644
index 0000000..2857ab6
--- /dev/null
+++ b/lib/activerecord/associations/alias_tracker.rb
@@ -0,0 +1,55 @@
+require 'active_support/core_ext/string/conversions'
+
+module ActiveRecord
+ module Associations
+ # Keeps track of table aliases for ActiveRecord::Associations::JoinDependency
+ class AliasTracker # :nodoc:
+ attr_reader :aliases
+
+ def self.create(connection, initial_table)
+ aliases = Hash.new(0)
+ aliases[initial_table] = 1
+ new(connection, aliases)
+ end
+
+ def self.create_with_joins(connection, initial_table, joins)
+ if joins.empty?
+ create(connection, initial_table)
+ else
+ aliases = Hash.new { |h, k|
+ h[k] = initial_count_for(connection, k, joins)
+ }
+ aliases[initial_table] = 1
+ new(connection, aliases)
+ end
+ end
+
+ # table_joins is an array of arel joins which might conflict with the aliases we assign here
+ def initialize(connection, aliases)
+ @aliases = aliases
+ @connection = connection
+ end
+
+ def aliased_table_for(table_name, aliased_name, type_caster)
+ if aliases[table_name].zero?
+ # If it's zero, we can have our table_name
+ aliases[table_name] = 1
+ Arel::Table.new(table_name, type_caster: type_caster)
+ else
+ # Otherwise, we need to use an alias
+ aliased_name = @connection.table_alias_for(aliased_name)
+
+ # Update the count
+ aliases[aliased_name] += 1
+
+ table_alias = if aliases[aliased_name] > 1
+ "#{truncate(aliased_name)}_#{aliases[aliased_name]}"
+ else
+ aliased_name
+ end
+ Arel::Table.new(table_name, type_caster: type_caster).alias(table_alias)
+ end
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/lib/activerecord/associations/association_scope.rb b/lib/activerecord/associations/association_scope.rb
new file mode 100644
index 0000000..8a5ba0b
--- /dev/null
+++ b/lib/activerecord/associations/association_scope.rb
@@ -0,0 +1,34 @@
+module ActiveRecord
+ module Associations
+ class AssociationScope #:nodoc:
+ def scope(association, connection)
+ klass = association.klass
+ reflection = association.reflection
+ scope = klass.unscoped
+ owner = association.owner
+ alias_tracker = AliasTracker.create connection, association.klass.table_name
+ chain_head, chain_tail = get_chain(reflection, association, alias_tracker)
+
+ scope.extending! reflection.extensions
+ add_constraints(scope, owner, klass, reflection, chain_head, chain_tail)
+ end
+
+ private
+ def get_chain(reflection, association, tracker)
+ name = reflection.name
+ runtime_reflection = Reflection::RuntimeReflection.new(reflection, association)
+ previous_reflection = runtime_reflection
+ reflection.chain.drop(1).each do |refl|
+ alias_name = tracker.aliased_table_for(
+ refl.table_name,
+ refl.alias_candidate(name),
+ refl.klass.type_caster)
+ proxy = ReflectionProxy.new(refl, alias_name)
+ previous_reflection.next = proxy
+ previous_reflection = proxy
+ end
+ [runtime_reflection, previous_reflection]
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/lib/activerecord/associations/join_dependency.rb b/lib/activerecord/associations/join_dependency.rb
new file mode 100644
index 0000000..e4b5b91
--- /dev/null
+++ b/lib/activerecord/associations/join_dependency.rb
@@ -0,0 +1,22 @@
+module ActiveRecord
+ module Associations
+ class JoinDependency # :nodoc:
+ def initialize(base, associations, joins)
+ @alias_tracker = AliasTracker.create_with_joins(base.connection, base.table_name, joins)
+ tree = self.class.make_tree associations
+ @join_root = JoinBase.new base, build(tree, base)
+ @join_root.children.each { |child| construct_tables! @join_root, child }
+ end
+
+ def table_aliases_for(parent, node)
+ node.reflection.chain.map { |reflection|
+ alias_tracker.aliased_table_for(
+ reflection.table_name,
+ table_alias_for(reflection, parent, reflection != node.reflection),
+ reflection.klass.type_caster
+ )
+ }
+ end
+ end
+ end
+end
\ No newline at end of file
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment