Skip to content

Commit ee3ee7e

Browse files
committed
wip
1 parent b7a2b4d commit ee3ee7e

File tree

3 files changed

+47
-17
lines changed

3 files changed

+47
-17
lines changed

ruby/hyper-model/lib/reactive_record/active_record/associations.rb

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,19 @@ class AssociationReflection
3838
attr_reader :macro
3939
attr_reader :owner_class
4040
attr_reader :source
41+
attr_reader :options
4142

4243
def initialize(owner_class, macro, name, options = {})
4344
owner_class.reflect_on_all_associations << self
4445
@owner_class = owner_class
4546
@macro = macro
4647
@options = options
4748
unless options[:polymorphic]
48-
@klass_name = options[:class_name] || (collection? && name.camelize.singularize) || name.camelize
49+
@klass_name = options[:class_name] || (collection? && name.camelize.singularize) || name.camelize
4950
end
50-
@association_foreign_key = options[:foreign_key] || (macro == :belongs_to && "#{name}_id") || "#{@owner_class.name.underscore}_id"
51+
@association_foreign_key =
52+
options[:foreign_key] || (macro == :belongs_to && "#{name}_id") ||
53+
(options[:as] && "#{options[:as]}_id") || "#{@owner_class.name.underscore}_id"
5154
@source = options[:source] || @klass_name.underscore if options[:through]
5255
@attribute = name
5356
end
@@ -94,32 +97,41 @@ def inverse(model)
9497
found
9598
end
9699

97-
def inverse_of(model)
100+
def inverse_of(model = nil)
98101
inverse(model)&.attribute
99102
end
100103

101-
def find_inverse(model)
102-
the_klass = klass || model.class
104+
def find_inverse(model) # private
105+
the_klass = klass || model
103106
return nil unless the_klass
104-
klass.reflect_on_all_associations.each do |association|
107+
the_klass.reflect_on_all_associations.each do |association|
105108
next if association.association_foreign_key != @association_foreign_key
106-
next if association.klass != @owner_class
109+
next unless target_klass_matches(association)
107110
next if association.attribute == attribute
108-
return association if klass == association.owner_class
111+
return association if the_klass == association.owner_class
109112
end
113+
return if options[:polymorphic] # can't dynamically create the polymorphic has_many
110114
# instead of raising an error go ahead and create the inverse relationship if it does not exist.
111115
# https://github.com/hyperstack-org/hyperstack/issues/89
112116
if macro == :belongs_to
113-
Hyperstack::Component::IsomorphicHelpers.log "**** warning dynamically adding relationship: #{klass}.has_many :#{@owner_class.name.underscore.pluralize}, foreign_key: #{@association_foreign_key}", :warning
114-
klass.has_many @owner_class.name.underscore.pluralize, foreign_key: @association_foreign_key
117+
Hyperstack::Component::IsomorphicHelpers.log "**** warning dynamically adding relationship: #{the_klass}.has_many :#{@owner_class.name.underscore.pluralize}, foreign_key: #{@association_foreign_key}", :warning
118+
the_klass.has_many @owner_class.name.underscore.pluralize, foreign_key: @association_foreign_key
119+
elsif options[:as]
120+
Hyperstack::Component::IsomorphicHelpers.log "**** warning dynamically adding relationship: #{the_klass}.belongs_to :#{options[:as]}, polymorphic: true", :warning
121+
the_klass.belongs_to options[:as], polymorphic: true
115122
else
116-
Hyperstack::Component::IsomorphicHelpers.log "**** warning dynamically adding relationship: #{klass}.belongs_to :#{@owner_class.name.underscore}, foreign_key: #{@association_foreign_key}", :warning
117-
klass.belongs_to @owner_class.name.underscore, foreign_key: @association_foreign_key
123+
Hyperstack::Component::IsomorphicHelpers.log "**** warning dynamically adding relationship: #{the_klass}.belongs_to :#{@owner_class.name.underscore}, foreign_key: #{@association_foreign_key}", :warning
124+
the_klass.belongs_to @owner_class.name.underscore, foreign_key: @association_foreign_key
118125
end
119126
end
120127

121128
def klass
122-
@klass ||= Object.const_get(@klass_name)
129+
@klass ||= Object.const_get(@klass_name) if @klass_name
130+
end
131+
132+
def target_klass_matches?(inverse_association)
133+
return inverse_association.options[:as] if options[:polymorphic]
134+
@owner_class == inverse_association.klass
123135
end
124136

125137
def collection?

ruby/hyper-model/lib/reactive_record/scope_description.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,9 @@ def map_joins_path(paths)
9393
vector = []
9494
path.split('.').inject(@model) do |model, attribute|
9595
association = model.reflect_on_association(attribute)
96-
inverse_of = association.inverse_of(nil) if association
96+
inverse_of = association.inverse_of if association
9797
raise build_error(path, model, attribute) unless inverse_of
98-
vector = [association.inverse_of, *vector]
98+
vector = [inverse_of, *vector]
9999
@joins[association.klass] << vector
100100
association.klass
101101
end

ruby/hyper-model/polymorph-notes.md

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ class Product < ApplicationRecord
1212
end
1313
```
1414

15-
product/employee.pictures -> works almost as normal has_many as far as Hyperstack client is concerned
16-
imageable is the "alias" of product/employee. Its as if there is a class Imageable that is the superclass
15+
product|employee.pictures -> works almost as normal has_many as far as Hyperstack client is concerned
16+
imageable is the "alias" of product|employee. Its as if there is a class Imageable that is the superclass
1717
of Product and Employee.
1818

1919
so has_many :pictures means the usual thing (i.e. there is a belongs_to relationship on Picture) its just that
@@ -89,3 +89,21 @@ end
8989
its all about the collection inverse. The inverse class of the has_many is the class containing the polymorphic belongs to. But the inverse of a polymorphic belongs to depends on the value. If the value is nil or a DummyPolyClass object then there is no inverse.
9090

9191
I think if inverse takes this into account then `<<` and `=` should just "work" (well almost) and probably everything else will to.
92+
93+
### NOTES on the DummyPolyClass...
94+
95+
it needs to respond to reflect_on_all_associations, but just return an empty array. This way when we search for matching inverse attribute we won't find it.
96+
97+
### Status
98+
99+
added model to inverse, inverse_of, find_inverse
100+
101+
if the relationship is a collection then we will always know the inverse.
102+
103+
The only time we might no know the inverse is if its NOT a collection (i.e. belongs_to)
104+
105+
So only places that are applying inverse to an association that is NOT a collection do we have to pass the model in.
106+
107+
All inverse_of method calls have been checked and updated
108+
109+
that leaves inverse which is only used in SETTERS hurray!

0 commit comments

Comments
 (0)