@@ -2,90 +2,93 @@ module MetaSearch
22
33 module JoinDependency
44
5+ class JoinAssociation < ::ActiveRecord ::Associations ::JoinDependency ::JoinAssociation
6+
7+ def initialize ( reflection , join_dependency , parent = nil , polymorphic_class = nil )
8+ if polymorphic_class && ::ActiveRecord ::Base > polymorphic_class
9+ swapping_reflection_klass ( reflection , polymorphic_class ) do |reflection |
10+ super ( reflection , join_dependency , parent )
11+ end
12+ else
13+ super ( reflection , join_dependency , parent )
14+ end
15+ end
16+
17+ def swapping_reflection_klass ( reflection , klass )
18+ reflection = reflection . clone
19+ original_polymorphic = reflection . options . delete ( :polymorphic )
20+ reflection . instance_variable_set ( :@klass , klass )
21+ yield reflection
22+ ensure
23+ reflection . options [ :polymorphic ] = original_polymorphic
24+ end
25+
26+ def ==( other )
27+ super && active_record == other . active_record
28+ end
29+
30+ def build_constraint ( reflection , table , key , foreign_table , foreign_key )
31+ if reflection . options [ :polymorphic ]
32+ super . and (
33+ foreign_table [ reflection . foreign_type ] . eq ( reflection . klass . name )
34+ )
35+ else
36+ super
37+ end
38+ end
39+
40+ end
41+
42+ # Yes, I'm using alias_method_chain here. No, I don't feel too
43+ # bad about it. JoinDependency, or, to call it by its full proper
44+ # name, ::ActiveRecord::Associations::JoinDependency, is one of the
45+ # most "for internal use only" chunks of ActiveRecord.
546 def self . included ( base )
647 base . class_eval do
7- alias_method_chain :graft , :metasearch
48+ alias_method_chain :graft , :meta_search
849 end
950 end
1051
11- def graft_with_metasearch ( *associations )
52+ def graft_with_meta_search ( *associations )
1253 associations . each do |association |
1354 join_associations . detect { |a | association == a } ||
14- (
15- association . class == MetaSearch ::PolymorphicJoinAssociation ?
16- build_with_metasearch ( association . reflection . name , association . find_parent_in ( self ) || join_base , association . join_type , association . reflection . klass ) :
17- build ( association . reflection . name , association . find_parent_in ( self ) || join_base , association . join_type )
18- )
55+ build_polymorphic ( association . reflection . name , association . find_parent_in ( self ) || join_base , association . join_type , association . reflection . klass )
1956 end
2057 self
2158 end
2259
23- protected
60+ # Should only be called by MetaSearch, and only with a single association name
61+ def build_polymorphic ( association , parent = nil , join_type = Arel ::OuterJoin , klass = nil )
62+ parent ||= join_parts . last
63+ reflection = parent . reflections [ association ] or
64+ raise ::ActiveRecord ::ConfigurationError , "Association named '#{ association } ' was not found; perhaps you misspelled it?"
65+ unless join_association = find_join_association_respecting_polymorphism ( reflection , parent , klass )
66+ @reflections << reflection
67+ join_association = build_join_association_respecting_polymorphism ( reflection , parent , klass )
68+ join_association . join_type = join_type
69+ @join_parts << join_association
70+ cache_joined_association ( join_association )
71+ end
2472
25- def build_with_metasearch ( associations , parent = nil , join_type = Arel ::Nodes ::InnerJoin , polymorphic_class = nil )
26- parent ||= @joins . last
27- case associations
28- when Symbol , String
29- reflection = parent . reflections [ associations . to_s . intern ] or
30- raise ConfigurationError , "Association named '#{ associations } ' was not found; perhaps you misspelled it?"
31- unless ( association = find_join_association ( reflection , parent ) ) && ( !polymorphic_class || association . active_record == polymorphic_class )
32- @reflections << reflection
33- if reflection . options [ :polymorphic ]
34- raise ArgumentError , "You can't create a polymorphic belongs_to join without specifying the polymorphic class!" unless polymorphic_class
35- association = PolymorphicJoinAssociation . new ( reflection , self , polymorphic_class , parent )
36- else
37- association = build_join_association ( reflection , parent )
38- end
39- association . join_type = join_type
40- @joins << association
41- end
73+ join_association
74+ end
75+
76+ def find_join_association_respecting_polymorphism ( reflection , parent , klass )
77+ if association = find_join_association ( reflection , parent )
78+ unless reflection . options [ :polymorphic ]
4279 association
4380 else
44- build ( associations , parent , join_type )
81+ association if association . active_record == klass
4582 end
4683 end
47- end
48-
49- class PolymorphicJoinAssociation < ActiveRecord ::Associations ::ClassMethods ::JoinDependency ::JoinAssociation
50-
51- def initialize ( reflection , join_dependency , polymorphic_class , parent = nil )
52- reflection . check_validity!
53- @active_record = polymorphic_class
54- @cached_record = { }
55- @join_dependency = join_dependency
56- @parent = parent || join_dependency . join_base
57- @reflection = reflection . clone
58- @reflection . instance_variable_set ( :"@klass" , polymorphic_class )
59- @aliased_prefix = "t#{ join_dependency . joins . size } "
60- @parent_table_name = @parent . active_record . table_name
61- @aliased_table_name = aliased_table_name_for ( table_name )
62- @join = nil
63- @join_type = Arel ::Nodes ::InnerJoin
64- end
65-
66- def ==( other )
67- other . class == self . class &&
68- other . reflection == reflection &&
69- other . active_record == active_record &&
70- other . parent == parent
7184 end
7285
73- def association_join
74- return @join if @join
75-
76- aliased_table = Arel ::Table . new ( table_name , :as => @aliased_table_name , :engine => arel_engine )
77- parent_table = Arel ::Table . new ( parent . table_name , :as => parent . aliased_table_name , :engine => arel_engine )
78-
79- @join = [
80- aliased_table [ options [ :primary_key ] || reflection . klass . primary_key ] . eq ( parent_table [ options [ :foreign_key ] || reflection . primary_key_name ] ) ,
81- parent_table [ options [ :foreign_type ] ] . eq ( active_record . name )
82- ]
83-
84- if options [ :conditions ]
85- @join << interpolate_sql ( sanitize_sql ( options [ :conditions ] , aliased_table_name ) )
86+ def build_join_association_respecting_polymorphism ( reflection , parent , klass = nil )
87+ if reflection . options [ :polymorphic ] && klass
88+ JoinAssociation . new ( reflection , self , parent , klass )
89+ else
90+ JoinAssociation . new ( reflection , self , parent )
8691 end
87-
88- @join
8992 end
9093 end
9194end
0 commit comments