Skip to content

Commit

Permalink
Merge pull request activerecord-hackery#927 from gregmolnar/polyamorous
Browse files Browse the repository at this point in the history
absorb polyamorous
  • Loading branch information
scarroll32 authored Jun 24, 2018
2 parents 0fe5eac + d0805ac commit 0031c90
Show file tree
Hide file tree
Showing 29 changed files with 1,056 additions and 8 deletions.
10 changes: 9 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,22 @@ env:
- RAILS=5-2-stable DB=mysql
- RAILS=5-2-stable DB=postgres

- RAILS=v5.2.0 DB=sqlite3
- RAILS=v5.2.0 DB=mysql
- RAILS=v5.2.0 DB=postgres

- RAILS=5-0-stable DB=sqlite3
- RAILS=5-0-stable DB=mysql
- RAILS=5-0-stable DB=postgres

- RAILS=4-2-stable DB=sqlite3
- RAILS=4-2-stable DB=mysql
- RAILS=4-2-stable DB=postgres

matrix:
allow_failures:
- env: RAILS=5-2-stable DB=sqlite3
- env: RAILS=5-2-stable DB=mysql
- env: RAILS=5-2-stable DB=postgres
before_script:
- mysql -e 'create database ransack collate utf8_general_ci;'
- mysql -e 'use ransack;show variables like "%character%";show variables like "%collation%";'
Expand Down
9 changes: 3 additions & 6 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,6 @@ gem 'rake'

rails = ENV['RAILS'] || '5-0-stable'

if rails == 'master'
gem 'polyamorous', github: 'activerecord-hackery/polyamorous'
else
gem 'polyamorous', '~> 1.3'
end

gem 'pry'

# Provide timezone information on Windows
Expand All @@ -28,6 +22,9 @@ when /^v/ # A tagged version
gem 'activerecord', require: false
gem 'actionpack'
end
if rails == 'v5.2.0'
gem 'mysql2', '~> 0.4.4'
end
else
git 'git://github.com/rails/rails.git', :branch => rails do
gem 'activesupport'
Expand Down
55 changes: 55 additions & 0 deletions lib/polyamorous.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
if defined?(::ActiveRecord)
module Polyamorous
if defined?(Arel::InnerJoin)
InnerJoin = Arel::InnerJoin
OuterJoin = Arel::OuterJoin
else
InnerJoin = Arel::Nodes::InnerJoin
OuterJoin = Arel::Nodes::OuterJoin
end

if defined?(::ActiveRecord::Associations::JoinDependency)
JoinDependency = ::ActiveRecord::Associations::JoinDependency
JoinAssociation = ::ActiveRecord::Associations::JoinDependency::JoinAssociation
JoinBase = ::ActiveRecord::Associations::JoinDependency::JoinBase
else
JoinDependency = ::ActiveRecord::Associations::ClassMethods::JoinDependency
JoinAssociation = ::ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation
JoinBase = ::ActiveRecord::Associations::ClassMethods::JoinDependency::JoinBase
end
end

require 'polyamorous/tree_node'
require 'polyamorous/join'
require 'polyamorous/swapping_reflection_class'

ar_version = ::ActiveRecord::VERSION::STRING[0,3]
ar_version = '3_and_4.0' if ar_version < '4.1'
ar_version = ::ActiveRecord::VERSION::STRING[0,5] if ar_version == '5.2'

method, ruby_version =
if RUBY_VERSION >= '2.0' && ar_version >= '4.1'
# Ruby 2; we can use `prepend` to patch Active Record cleanly.
[:prepend, '2']
else
# Ruby 1.9; we must use `alias_method` to patch Active Record.
[:include, '1.9']
end

%w(join_association join_dependency).each do |file|
require "polyamorous/activerecord_#{ar_version}_ruby_#{ruby_version}/#{file}"
end

Polyamorous::JoinDependency.send(method, Polyamorous::JoinDependencyExtensions)
if method == :prepend
Polyamorous::JoinDependency.singleton_class
.send(:prepend, Polyamorous::JoinDependencyExtensions::ClassMethods)
end
Polyamorous::JoinAssociation.send(method, Polyamorous::JoinAssociationExtensions)

Polyamorous::JoinBase.class_eval do
if method_defined?(:active_record)
alias_method :base_klass, :active_record
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# active_record_3_and_4.0_ruby_1.9/join_association.rb
module Polyamorous
module JoinAssociationExtensions
include SwappingReflectionClass
def self.included(base)
base.class_eval do
alias_method_chain :initialize, :polymorphism
alias_method :equality_without_polymorphism, :==
alias_method :==, :equality_with_polymorphism
if base.method_defined?(:active_record)
alias_method :base_klass, :active_record
end

if ActiveRecord::VERSION::STRING =~ /^3\.0\./
alias_method_chain :association_join, :polymorphism
else
alias_method_chain :build_constraint, :polymorphism
end
end
end

def initialize_with_polymorphism(
reflection, join_dependency, parent = nil, polymorphic_class = nil
)
if polymorphic_class && ::ActiveRecord::Base > polymorphic_class
swapping_reflection_klass(reflection, polymorphic_class) do |reflection|
initialize_without_polymorphism(reflection, join_dependency, parent)
self.reflection.options[:polymorphic] = true
end
else
initialize_without_polymorphism(reflection, join_dependency, parent)
end
end

def equality_with_polymorphism(other)
equality_without_polymorphism(other) && base_klass == other.base_klass
end

def build_constraint_with_polymorphism(
reflection, table, key, foreign_table, foreign_key
)
if reflection.options[:polymorphic]
build_constraint_without_polymorphism(
reflection, table, key, foreign_table, foreign_key
)
.and(foreign_table[reflection.foreign_type].eq(reflection.klass.name))
else
build_constraint_without_polymorphism(
reflection, table, key, foreign_table, foreign_key
)
end
end

def association_join_with_polymorphism
return @join if @Join
@join = association_join_without_polymorphism
if reflection.macro == :belongs_to && reflection.options[:polymorphic]
aliased_table = Arel::Table.new(
table_name,
as: @aliased_table_name,
engine: arel_engine,
columns: klass.columns
)
parent_table = Arel::Table.new(
parent.table_name,
as: parent.aliased_table_name,
engine: arel_engine,
columns: parent.base_klass.columns
)
@join << parent_table[reflection.options[:foreign_type]]
.eq(reflection.klass.name)
end
@join
end
end
end
96 changes: 96 additions & 0 deletions lib/polyamorous/activerecord_3_and_4.0_ruby_1.9/join_dependency.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# active_record_3_and_4.0_ruby_1.9/join_dependency.rb
module Polyamorous
module JoinDependencyExtensions
def self.included(base)
base.class_eval do
alias_method_chain :build, :polymorphism
alias_method_chain :graft, :polymorphism
if base.method_defined?(:active_record)
alias_method :base_klass, :active_record
end
end
end

def graft_with_polymorphism(*associations)
associations.each do |association|
unless join_associations.detect { |a| association == a }
if association.reflection.options[:polymorphic]
build(
Join.new(
association.reflection.name,
association.join_type,
association.reflection.klass
),
association.find_parent_in(self) || join_base,
association.join_type
)
else
build(
association.reflection.name,
association.find_parent_in(self) || join_base,
association.join_type
)
end
end
end
self
end

if ActiveRecord::VERSION::STRING =~ /^3\.0\./
def _join_parts
@joins
end
else
def _join_parts
@join_parts
end
end

def build_with_polymorphism(
associations, parent = nil, join_type = InnerJoin
)
case associations
when Join
parent ||= _join_parts.last
reflection = parent.reflections[associations.name] or
raise ::ActiveRecord::ConfigurationError,
"Association named '#{associations.name
}' was not found; perhaps you misspelled it?"

unless join_association = find_join_association_respecting_polymorphism(
reflection, parent, associations.klass
)
@reflections << reflection
join_association = build_join_association_respecting_polymorphism(
reflection, parent, associations.klass
)
join_association.join_type = associations.type
_join_parts << join_association
cache_joined_association(join_association)
end

join_association
else
build_without_polymorphism(associations, parent, join_type)
end
end

def find_join_association_respecting_polymorphism(reflection, parent, klass)
if association = find_join_association(reflection, parent)
unless reflection.options[:polymorphic]
association
else
association if association.base_klass == klass
end
end
end

def build_join_association_respecting_polymorphism(reflection, parent, klass)
if reflection.options[:polymorphic] && klass
JoinAssociation.new(reflection, self, parent, klass)
else
JoinAssociation.new(reflection, self, parent)
end
end
end
end
2 changes: 2 additions & 0 deletions lib/polyamorous/activerecord_4.1_ruby_1.9/join_association.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# active_record_4.1_ruby_1.9/join_association.rb
require 'polyamorous/activerecord_4.2_ruby_1.9/join_association'
4 changes: 4 additions & 0 deletions lib/polyamorous/activerecord_4.1_ruby_1.9/join_dependency.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# active_record_4.1_ruby_1.9/join_dependency.rb
require 'polyamorous/activerecord_4.2_ruby_2/join_dependency'
require 'polyamorous/activerecord_4.2_ruby_1.9/join_dependency'
require 'polyamorous/activerecord_4.1_ruby_2/make_polyamorous_inner_joins'
2 changes: 2 additions & 0 deletions lib/polyamorous/activerecord_4.1_ruby_2/join_association.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# active_record_4.1_ruby_2/join_association.rb
require 'polyamorous/activerecord_5.0_ruby_2/join_association'
3 changes: 3 additions & 0 deletions lib/polyamorous/activerecord_4.1_ruby_2/join_dependency.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# active_record_4.1_ruby_2/join_dependency.rb
require 'polyamorous/activerecord_4.2_ruby_2/join_dependency'
require 'polyamorous/activerecord_4.1_ruby_2/make_polyamorous_inner_joins'
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module Polyamorous
module JoinDependencyExtensions
# Replaces ActiveRecord::Associations::JoinDependency#make_inner_joins
#
def make_polyamorous_inner_joins(parent, child)
make_constraints(
parent, child, child.tables, child.join_type || Arel::Nodes::InnerJoin
)
.concat child.children.flat_map { |c|
make_polyamorous_inner_joins(child, c)
}
end
end
end
46 changes: 46 additions & 0 deletions lib/polyamorous/activerecord_4.2_ruby_1.9/join_association.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# active_record_4.2_ruby_1.9/join_association.rb
module Polyamorous
module JoinAssociationExtensions
include SwappingReflectionClass
def self.included(base)
base.class_eval do
attr_reader :join_type
alias_method_chain :initialize, :polymorphism
alias_method_chain :build_constraint, :polymorphism
end
end

def initialize_with_polymorphism(reflection, children,
polymorphic_class = nil, join_type = Arel::Nodes::InnerJoin)
@join_type = join_type
if polymorphic_class && ::ActiveRecord::Base > polymorphic_class
swapping_reflection_klass(reflection, polymorphic_class) do |reflection|
initialize_without_polymorphism(reflection, children)
self.reflection.options[:polymorphic] = true
end
else
initialize_without_polymorphism(reflection, children)
end
end

# Reference https://github.com/rails/rails/commit/9b15db51b78028bfecdb85595624de4b838adbd1
def ==(other)
base_klass == other.base_klass
end

def build_constraint_with_polymorphism(
klass, table, key, foreign_table, foreign_key
)
if reflection.polymorphic?
build_constraint_without_polymorphism(
klass, table, key, foreign_table, foreign_key
)
.and(foreign_table[reflection.foreign_type].eq(reflection.klass.name))
else
build_constraint_without_polymorphism(
klass, table, key, foreign_table, foreign_key
)
end
end
end
end
Loading

0 comments on commit 0031c90

Please sign in to comment.