Полиморфный has_many: через NoMethodError: неопределенный метод `klass' для nil:NilClass

Итак, я начал настраивать полиморфную ассоциацию has_many :through в своем приложении. Модели выглядят следующим образом:

class Song < ActiveRecord::Base
  has_many :collections, through: :collectionitems
  has_many :collectionitems, through: :collectable
end

class Album < ActiveRecord::Base
  has_many :collections, through: :collectionitems
  has_many :collectionitems, through: :collectable
end

class Collection < ActiveRecord::Base
  has_many :albums, through: :collectionitems, source: :collectable, source_type: "Album"
  has_many :songs, through: :collectionitems, source: :collectable, source_type: "Song"
  has_many :collectionitems
end

class Collectionitem < ActiveRecord::Base
  belongs_to :collection
  belongs_to :collectable, polymorphic: true
end

Это позволяет мне делать следующие вызовы:

Collection.first.songs => возвращает массив песен для первой коллекции Collection.first.albums => возвращает массив альбомов для первой коллекции Collectionitem.first.collection => возвращает коллекцию, к которой принадлежит этот элемент коллекции Collectionitem.first.collectable => возвращает запись, к которой принадлежит этот элемент коллекции (песню или альбом)

Моя проблема возникает, когда я пытаюсь найти все коллекции, к которым принадлежит определенный альбом или песня.

оба Song.first.collections и Album.first.collections возвращают следующую ошибку при вызове в консоли rails.

Song.first.collections
Song Load (0.2ms)  SELECT "songs".* FROM "songs" ORDER BY "songs"."id" ASC LIMIT 1
NoMethodError: undefined method `klass' for nil:NilClass
    from /Users/jonathan/.rvm/gems/ruby-1.9.3-p392/gems/activerecord-4.0.0/lib/active_record/reflection.rb:420:in `block in source_reflection'
    from /Users/jonathan/.rvm/gems/ruby-1.9.3-p392/gems/activerecord-4.0.0/lib/active_record/reflection.rb:420:in `collect'
    from /Users/jonathan/.rvm/gems/ruby-1.9.3-p392/gems/activerecord-4.0.0/lib/active_record/reflection.rb:420:in `source_reflection'
    from /Users/jonathan/.rvm/gems/ruby-1.9.3-p392/gems/activerecord-4.0.0/lib/active_record/reflection.rb:579:in `derive_class_name'
    from /Users/jonathan/.rvm/gems/ruby-1.9.3-p392/gems/activerecord-4.0.0/lib/active_record/reflection.rb:133:in `class_name'
    from /Users/jonathan/.rvm/gems/ruby-1.9.3-p392/gems/activerecord-4.0.0/lib/active_record/reflection.rb:178:in `klass'
    from /Users/jonathan/.rvm/gems/ruby-1.9.3-p392/gems/activerecord-4.0.0/lib/active_record/reflection.rb:420:in `block in source_reflection'
    from /Users/jonathan/.rvm/gems/ruby-1.9.3-p392/gems/activerecord-4.0.0/lib/active_record/reflection.rb:420:in `collect'
    from /Users/jonathan/.rvm/gems/ruby-1.9.3-p392/gems/activerecord-4.0.0/lib/active_record/reflection.rb:420:in `source_reflection'
    from /Users/jonathan/.rvm/gems/ruby-1.9.3-p392/gems/activerecord-4.0.0/lib/active_record/reflection.rb:557:in `check_validity!'
    from /Users/jonathan/.rvm/gems/ruby-1.9.3-p392/gems/activerecord-4.0.0/lib/active_record/associations/association.rb:25:in `initialize'
    from /Users/jonathan/.rvm/gems/ruby-1.9.3-p392/gems/activerecord-4.0.0/lib/active_record/associations/has_many_through_association.rb:9:in `initialize'
    from /Users/jonathan/.rvm/gems/ruby-1.9.3-p392/gems/activerecord-4.0.0/lib/active_record/associations.rb:157:in `new'
    from /Users/jonathan/.rvm/gems/ruby-1.9.3-p392/gems/activerecord-4.0.0/lib/active_record/associations.rb:157:in `association'
    from /Users/jonathan/.rvm/gems/ruby-1.9.3-p392/gems/activerecord-4.0.0/lib/active_record/associations/builder/association.rb:70:in `collections'
    from (irb):3
    from /Users/jonathan/.rvm/gems/ruby-1.9.3-p392/gems/railties-4.0.0/lib/rails/commands/console.rb:90:in `start'
    from /Users/jonathan/.rvm/gems/ruby-1.9.3-p392/gems/railties-4.0.0/lib/rails/commands/console.rb:9:in `start'
    from /Users/jonathan/.rvm/gems/ruby-1.9.3-p392/gems/railties-4.0.0/lib/rails/commands.rb:64:in `<top (required)>'
    from bin/rails:4:in `require'

Может кто-нибудь сказать мне, что я получил неправильно здесь. Как будто он не видит отношения песен к сборнику? Я просто не знаю, что я сделал не так.


person JonathanSimmons    schedule 28.08.2013    source источник
comment
Я думаю, что through: :collectable должно быть as: :collectable.   -  person Damien Roche    schedule 29.08.2013
comment
Это сработало, @Damien! Большое спасибо. Могу я спросить, как вы это узнали? Это где-то задокументировано, изменение рельсов 4?   -  person JonathanSimmons    schedule 29.08.2013
comment
Здорово! Да, это задокументировано в руководствах по Rails: guides.rubyonrails.org/. Несмотря на то, что у вас может быть охвачено большинство вещей, определенно стоит просмотреть руководства пару раз.   -  person Damien Roche    schedule 29.08.2013


Ответы (1)


Как отметил @damien в комментарии, мне нужно было изменить:

has_many :collectionitems, through: :collectable

to:

has_many :collectionitems, as: :collectable

Как только это было на месте, все заработало, как и ожидалось.

Еще раз спасибо @damien!

person JonathanSimmons    schedule 28.08.2013
comment
У меня очень похожая проблема, но я не вижу, где :collectable упоминается как класс в вашем вопросе. - person csakon; 22.01.2014
comment
:collectable - это не класс, это полиморфное присвоение. - person JonathanSimmons; 23.01.2014