@@ -38,16 +38,39 @@ class AssociationReflection
3838 attr_reader :macro
3939 attr_reader :owner_class
4040 attr_reader :source
41+ attr_reader :source_type
42+ attr_reader :options
43+ attr_reader :polymorphic_type_attribute
4144
4245 def initialize ( owner_class , macro , name , options = { } )
4346 owner_class . reflect_on_all_associations << self
4447 @owner_class = owner_class
4548 @macro = macro
4649 @options = options
47- @klass_name = options [ :class_name ] || ( collection? && name . camelize . singularize ) || name . camelize
48- @association_foreign_key = options [ :foreign_key ] || ( macro == :belongs_to && "#{ name } _id" ) || "#{ @owner_class . name . underscore } _id"
49- @source = options [ :source ] || @klass_name . underscore if options [ :through ]
50+ unless options [ :polymorphic ]
51+ @klass_name = options [ :class_name ] || ( collection? && name . camelize . singularize ) || name . camelize
52+ end
53+ @association_foreign_key =
54+ options [ :foreign_key ] ||
55+ ( macro == :belongs_to && "#{ name } _id" ) ||
56+ ( options [ :as ] && "#{ options [ :as ] } _id" ) ||
57+ ( options [ :polymorphic ] && "#{ name } _id" ) ||
58+ "#{ @owner_class . name . underscore } _id"
59+ if options [ :through ]
60+ @source = options [ :source ] || @klass_name . underscore
61+ @source_type = options [ :source_type ] || @klass_name
62+ end
63+ @polymorphic_type_attribute = "#{ name } _type" if options [ :polymorphic ]
5064 @attribute = name
65+ @through_associations = Hash . new { |_h , k | [ ] unless k }
66+ end
67+
68+ def collection?
69+ @macro == :has_many
70+ end
71+
72+ def singular?
73+ @macro != :has_many
5174 end
5275
5376 def through_association
@@ -62,63 +85,116 @@ def through_association
6285
6386 alias through_association? through_association
6487
65- def through_associations
88+ # class Membership < ActiveRecord::Base
89+ # belongs_to :uzer
90+ # belongs_to :memerable, polymorphic: true
91+ # end
92+ #
93+ # class Project < ActiveRecord::Base
94+ # has_many :memberships, as: :memerable, dependent: :destroy
95+ # has_many :uzers, through: :memberships
96+ # end
97+ #
98+ # class Group < ActiveRecord::Base
99+ # has_many :memberships, as: :memerable, dependent: :destroy
100+ # has_many :uzers, through: :memberships
101+ # end
102+ #
103+ # class Uzer < ActiveRecord::Base
104+ # has_many :memberships
105+ # has_many :groups, through: :memberships, source: :memerable, source_type: 'Group'
106+ # has_many :projects, through: :memberships, source: :memerable, source_type: 'Project'
107+ # end
108+
109+ # so find the belongs_to relationship whose attribute == ta.source
110+ # now find the inverse of that relationship using source_value as the model
111+ # now find any has many through relationships that use that relationship as there source.
112+ # each of those attributes in the source_value have to be updated.
113+
114+ # self is the through association
115+
116+
117+ def through_associations ( model )
118+ # given self is a belongs_to association currently pointing to model
66119 # find all associations that use the inverse association as the through association
67120 # that is find all associations that are using this association in a through relationship
68- @through_associations ||= klass . reflect_on_all_associations . select do |assoc |
69- assoc . through_association && assoc . inverse == self
121+ the_klass = klass ( model )
122+ @through_associations [ the_klass ] ||= the_klass . reflect_on_all_associations . select do |assoc |
123+ assoc . through_association &.inverse == self
70124 end
71125 end
72126
73- def source_associations
74- # find all associations that use this association as the source
75- # that is final all associations that are using this association as the source in a
76- # through relationship
77- @source_associations ||= owner_class . reflect_on_all_associations . collect do |sibling |
78- sibling . klass . reflect_on_all_associations . select do |assoc |
79- assoc . source == attribute
127+ def source_belongs_to_association # private
128+ # given self is a has_many_through association return the corresponding belongs_to association
129+ # for the source
130+ @source_belongs_to_association ||=
131+ through_association . inverse . owner_class . reflect_on_all_associations . detect do |sibling |
132+ sibling . attribute == source
80133 end
81- end . flatten
82134 end
83135
84- def inverse
85- @inverse ||=
86- through_association ? through_association . inverse : find_inverse
136+ def source_associations ( model )
137+ # given self is a has_many_through association find the source_association for the given model
138+ source_belongs_to_association . through_associations ( model )
87139 end
88140
89- def inverse_of
90- @inverse_of ||= inverse . attribute
141+ alias :polymorphic? polymorphic_type_attribute
142+
143+ def inverse ( model = nil )
144+ return @inverse if @inverse
145+ ta = through_association
146+ found = ta ? ta . inverse : find_inverse ( model )
147+ @inverse = found unless polymorphic?
148+ found
91149 end
92150
93- def find_inverse
94- klass . reflect_on_all_associations . each do |association |
151+ def inverse_of ( model = nil )
152+ inverse ( model ) . attribute
153+ end
154+
155+ def find_inverse ( model ) # private
156+ the_klass = klass ( model )
157+ the_klass . reflect_on_all_associations . each do |association |
95158 next if association . association_foreign_key != @association_foreign_key
96- next if association . klass != @owner_class
97159 next if association . attribute == attribute
98- return association if klass == association . owner_class
160+ return association if the_klass == association . owner_class
99161 end
162+ raise "could not find inverse of polymorphic belongs_to: #{ model . inspect } #{ self . inspect } " if options [ :polymorphic ]
100163 # instead of raising an error go ahead and create the inverse relationship if it does not exist.
101164 # https://github.com/hyperstack-org/hyperstack/issues/89
102165 if macro == :belongs_to
103- Hyperstack ::Component ::IsomorphicHelpers . log "**** warning dynamically adding relationship: #{ klass } .has_many :#{ @owner_class . name . underscore . pluralize } , foreign_key: #{ @association_foreign_key } " , :warning
104- klass . has_many @owner_class . name . underscore . pluralize , foreign_key : @association_foreign_key
166+ Hyperstack ::Component ::IsomorphicHelpers . log "**** warning dynamically adding relationship: #{ the_klass } .has_many :#{ @owner_class . name . underscore . pluralize } , foreign_key: #{ @association_foreign_key } " , :warning
167+ the_klass . has_many @owner_class . name . underscore . pluralize , foreign_key : @association_foreign_key
168+ elsif options [ :as ]
169+ Hyperstack ::Component ::IsomorphicHelpers . log "**** warning dynamically adding relationship: #{ the_klass } .belongs_to :#{ options [ :as ] } , polymorphic: true" , :warning
170+ the_klass . belongs_to options [ :as ] , polymorphic : true
105171 else
106- Hyperstack ::Component ::IsomorphicHelpers . log "**** warning dynamically adding relationship: #{ klass } .belongs_to :#{ @owner_class . name . underscore } , foreign_key: #{ @association_foreign_key } " , :warning
107- klass . belongs_to @owner_class . name . underscore , foreign_key : @association_foreign_key
172+ Hyperstack ::Component ::IsomorphicHelpers . log "**** warning dynamically adding relationship: #{ the_klass } .belongs_to :#{ @owner_class . name . underscore } , foreign_key: #{ @association_foreign_key } " , :warning
173+ the_klass . belongs_to @owner_class . name . underscore , foreign_key : @association_foreign_key
108174 end
109175 end
110176
111- def klass
112- @klass ||= Object . const_get ( @klass_name )
177+ def klass ( model = nil )
178+ @klass ||= Object . const_get ( @klass_name ) if @klass_name
179+ raise "model is not correct class" if @klass && model && model . class != @klass
180+ raise "no model supplied for polymorphic relationship" unless @klass || model
181+ @klass || model . class
113182 end
114183
115184 def collection?
116185 [ :has_many ] . include? @macro
117186 end
118187
119- end
188+ def remove_member ( member , owner )
189+ collection = owner . attributes [ attribute ]
190+ return if collection . nil?
191+ collection . delete ( member )
192+ end
120193
194+ def add_member ( member , owner )
195+ owner . attributes [ attribute ] ||= ReactiveRecord ::Collection . new ( owner_class , owner , self )
196+ owner . attributes [ attribute ] << member
197+ end
198+ end
121199 end
122-
123-
124200end
0 commit comments