diff --git a/motion/adapters/array_model_adapter.rb b/motion/adapters/array_model_adapter.rb index 854d21a..fb926b7 100644 --- a/motion/adapters/array_model_adapter.rb +++ b/motion/adapters/array_model_adapter.rb @@ -144,7 +144,15 @@ def assign_id(options) #nodoc end def belongs_to_relation(col) # nodoc - col.classify.find_by_id(_get_attr(col.foreign_key)) + if col.polymorphic + klass, id = get_polymorphic_attr(col) + if klass + klass = Kernel.deep_const_get(klass) if klass.is_a?(String) + klass.find_by_id(id) + end + else + col.classify.find_by_id(_get_attr(col.foreign_key)) + end end def has_many_relation(col) # nodoc @@ -157,7 +165,11 @@ def has_one_relation(col) # nodoc def _has_many_has_one_relation(col) # nodoc related_klass = col.classify - related_klass.find(col.inverse_column.foreign_key).belongs_to(self, related_klass).eq(_get_attr(:id)) + if col.polymorphic + related_klass.find(col.inverse_column.foreign_key).eq(_get_attr(:id)).and(col.inverse_column.foreign_polymorphic_type).eq(self.className) + else + related_klass.find(col.inverse_column.foreign_key).belongs_to(self, related_klass).eq(_get_attr(:id)) + end end def do_insert(options = {}) diff --git a/motion/model/column.rb b/motion/model/column.rb index fed13ae..52217c3 100644 --- a/motion/model/column.rb +++ b/motion/model/column.rb @@ -43,8 +43,9 @@ def foreign_key end def classify - fail "Column#classify indeterminate for polymorphic associations" if type == :belongs_to && polymorphic - if @klass + if type == :belongs_to && polymorphic + nil + elsif @klass @klass else case @type diff --git a/motion/model/model.rb b/motion/model/model.rb index 9156970..e248181 100644 --- a/motion/model/model.rb +++ b/motion/model/model.rb @@ -722,7 +722,7 @@ def set_belongs_to_attr(col, owner, options = {}) # Determine if the :belongs_to relationship is synchronized. Checks the instance and the DB column attributes. def belongs_to_synced?(col, owner) # The :belongs_to that points to the instance has changed - return false if get_belongs_to_attr(col) != owner + return false if !col.polymorphic && get_belongs_to_attr(col) != owner # The polymorphic reference (_type, _id) columns do not match, maybe it was just saved return false if col.polymorphic && !polymorphic_attr_matches?(col, owner) @@ -765,7 +765,7 @@ def get_polymorphic_attr(col) unless id.nil? owner_class_name = _get_attr(_col.foreign_polymorphic_type) owner_class_name = String(owner_class_name) # RubyMotion issue, String#classify might fail otherwise - owner_class = Kernel::deep_const_get(owner_class_name.classify) + owner_class = Kernel::deep_const_get(owner_class_name) end [owner_class, id] end diff --git a/spec/date_spec.rb b/spec/date_spec.rb index cb7bf81..c0809cf 100644 --- a/spec/date_spec.rb +++ b/spec/date_spec.rb @@ -77,7 +77,7 @@ class ProtectedUpdateable class Model include MotionModel::Model include MotionModel::ArrayModelAdapter - columns :test_date => :date, + columns :test_date => :date end it 'parses ISO8601 format variant #1 (RoR default)' do diff --git a/spec/polymorphic_spec.rb b/spec/polymorphic_spec.rb new file mode 100644 index 0000000..2136ebc --- /dev/null +++ b/spec/polymorphic_spec.rb @@ -0,0 +1,98 @@ +class Tag + include MotionModel::Model + include MotionModel::ArrayModelAdapter + + columns( + name: :string + ) + + belongs_to :tagged, polymorphic: true +end + +class Gadget + include MotionModel::Model + include MotionModel::ArrayModelAdapter + + columns( + name: :string + ) + + has_many :tags, dependent: :destroy, class: Tag, inverse_of: :tagged, polymorphic: true +end + +class Widget + include MotionModel::Model + include MotionModel::ArrayModelAdapter + + columns( + name: :string + ) + + has_many :tags, dependent: :destroy, class: Tag, inverse_of: :tagged, polymorphic: true +end + +describe 'polymorphic relationships' do + describe 'polymorphic belongs to' do + before do + Tag.delete_all + Gadget.delete_all + Widdget.delete_all + end + + it 'can relate to different classes' do + gadget1 = Gadget.create(name: 'Gadget 1') + gadget2 = Gadget.create(name: 'Gadget 2') + gadget3 = Gadget.create(name: 'Gadget 3') + widget1 = Widget.create(name: 'Widget 1') + widget2 = Widget.create(name: 'Widget 2') + + tag1 = Tag.create(name: 'Tag 1', tagged: gadget1) + tag2 = Tag.create(name: 'Tag 2', tagged: gadget2) + tag3 = Tag.create(name: 'Tag 3', tagged: gadget2) + tag4 = Tag.create(name: 'Tag 4', tagged: gadget3) + tag5 = Tag.create(name: 'Tag 5', tagged: widget1) + tag6 = Tag.create(name: 'Tag 6', tagged: widget1) + tag7 = Tag.create(name: 'Tag 7', tagged: widget2) + + gadget1.tags.to_a.should == [tag1] + gadget2.tags.to_a.should == [tag2, tag3] + gadget3.tags.to_a.should == [tag4] + widget1.tags.to_a.should == [tag5, tag6] + widget2.tags.to_a.should == [tag7] + end + end + + describe 'polymorphic has many' do + before do + Tag.delete_all + Gadget.delete_all + Widdget.delete_all + end + + it 'can relate polymorphuc records' do + gadget1 = Gadget.create(name: 'Gadget 1') + gadget2 = Gadget.create(name: 'Gadget 2') + gadget3 = Gadget.create(name: 'Gadget 3') + widget1 = Widget.create(name: 'Widget 1') + widget2 = Widget.create(name: 'Widget 2') + + gadget1.tags << Tag.create(name: 'Tag 1') + gadget2.tags << Tag.create(name: 'Tag 2') + gadget2.tags << Tag.create(name: 'Tag 3') + gadget3.tags << Tag.create(name: 'Tag 4') + widget1.tags << Tag.create(name: 'Tag 5') + widget1.tags << Tag.create(name: 'Tag 6') + widget2.tags << Tag.create(name: 'Tag 7') + + Tag.all.to_a.map(:tagged).should == [ + gadget1, + gadget2, + gadget2, + gadget3, + widget1, + widget1, + widget2, + ] + end + end +end