Hi!
I have an Subject model which should have many Photos through SubjectPhotoAssignments. When I test this, I get undefined method 'klass' for nil:NilClass. Here is my setup:
class Subject < ActiveRecord::Base
belongs_to :project, touch: true
has_many :photos, through: :subject_photo_assignments
end
class SubjectPhotoAssignment < ActiveRecord::Base
belongs_to :photo
belongs_to :subject, touch: true
end
class Photo < ActiveRecord::Base
belongs_to :project, touch: true
has_many :album_photo_assignments
has_many :subject_photo_assignments
end
Here is the failing test:
RSpec.describe Subject, type: :model do
it { should have_many(:photos).through(:subject_photo_assignments) }
end
2) Subject should have many photos through subject_photo_assignments
Failure/Error: it { should have_many(:photos).through(:subject_photo_assignments) }
NoMethodError:
undefined method `klass' for nil:NilClass
What am I doing wrong? Any help is greatly appreciated. Let me know if more context would be helpful.
I'm using v2.7.0
Yeah, can you re-run the tests with the --backtrace option?
Failure/Error: it { should have_many(:photos).through(:subject_photo_assignments) }
NoMethodError:
undefined method `klass' for nil:NilClass
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/activerecord-4.1.7/lib/active_record/reflection.rb:537:in `source_reflection'
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/activerecord-4.1.7/lib/active_record/reflection.rb:725:in `derive_class_name'
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/activerecord-4.1.7/lib/active_record/reflection.rb:176:in `class_name'
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/shoulda-matchers-2.7.0/lib/shoulda/matchers/active_record/association_matcher.rb:968:in `rescue in class_exists?'
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/shoulda-matchers-2.7.0/lib/shoulda/matchers/active_record/association_matcher.rb:965:in `class_exists?'
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/shoulda-matchers-2.7.0/lib/shoulda/matchers/active_record/association_matcher.rb:847:in `matches?'
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/rspec-expectations-3.1.2/lib/rspec/expectations/handler.rb:48:in `handle_matcher'
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.7/lib/rspec/core/memoized_helpers.rb:81:in `should'
# ./spec/models/subject_spec.rb:5:in `block (2 levels) in <top (required)>'
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.7/lib/rspec/core/example.rb:152:in `instance_exec'
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.7/lib/rspec/core/example.rb:152:in `block in run'
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.7/lib/rspec/core/example.rb:222:in `call'
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.7/lib/rspec/core/example.rb:222:in `call'
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/rspec-rails-3.1.0/lib/rspec/rails/adapters.rb:72:in `block (2 levels) in <module:MinitestLifecycleAdapter>'
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.7/lib/rspec/core/example.rb:322:in `instance_exec'
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.7/lib/rspec/core/example.rb:322:in `instance_exec'
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.7/lib/rspec/core/hooks.rb:380:in `execute_with'
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.7/lib/rspec/core/hooks.rb:446:in `block (2 levels) in run'
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.7/lib/rspec/core/example.rb:222:in `call'
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.7/lib/rspec/core/example.rb:222:in `call'
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.7/lib/rspec/core/hooks.rb:447:in `run'
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.7/lib/rspec/core/hooks.rb:500:in `run'
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.7/lib/rspec/core/example.rb:331:in `with_around_example_hooks'
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.7/lib/rspec/core/example.rb:149:in `run'
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.7/lib/rspec/core/example_group.rb:490:in `block in run_examples'
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.7/lib/rspec/core/example_group.rb:486:in `map'
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.7/lib/rspec/core/example_group.rb:486:in `run_examples'
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.7/lib/rspec/core/example_group.rb:453:in `run'
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.7/lib/rspec/core/runner.rb:111:in `block (2 levels) in run_specs'
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.7/lib/rspec/core/runner.rb:111:in `map'
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.7/lib/rspec/core/runner.rb:111:in `block in run_specs'
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.7/lib/rspec/core/reporter.rb:53:in `report'
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.7/lib/rspec/core/runner.rb:107:in `run_specs'
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.7/lib/rspec/core/runner.rb:85:in `run'
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.7/lib/rspec/core/runner.rb:69:in `run'
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.7/lib/rspec/core/runner.rb:37:in `invoke'
# /home/xtagon/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.7/exe/rspec:4:in `<top (required)>'
# /home/xtagon/.rbenv/versions/2.1.4/bin/rspec:23:in `load'
# /home/xtagon/.rbenv/versions/2.1.4/bin/rspec:23:in `<main>'
You're saying that Subject.has_many :photos, through: :subject_photo_assignments, but you don't have a :subject_photo_assignments association.
Ideally, we should probably have a check to ensure that your through association exists. But that's why you're getting an error.
I added the :subject_photo_assignments association and the tests pass. For some reason I was thinking that argument was specifying the join table, not another association. Thank you very much for clearing up my blunder!
Feel free to close the issue, unless you want to keep it open for adding a different error message.
Sounds good, glad I could help.
I have this error with code like this one:
class Subject < ActiveRecord::Base
has_one :subject_photo_assignment
has_many :photos, through: :subject_photo_assignments
end
@1v You have a subject_photo_assignment association, but you don't have a subject_photo_assignments association (note the "s" at the end).
@mcmire it's working with "s" too, but tests failing.
@1v Okay. Have you tried:
class Subject < ActiveRecord::Base
has_one :subject_photo_assignment
has_many :photos, through: :subject_photo_assignment # note that this is singular, not plural
end
@mcmire yeah that's how I do right now. But Rails accepts plural too. It took me time to figure out because error message not informative.
@1v Okay, fair enough. I'll re-open this issue.
I also have this error with this "setup":
ruby
module Hanuman
class Observation < ActiveRecord::Base
belongs_to :survey, touch: true
end
end
ruby
Hanuman::Observation.class_eval do
has_one :user, through: :survey
end
ruby
module Hanuman
RSpec.describe ObservationAnswer, type: :model do
it { is_expected.to have_one(:user).through(:survey) }
end
end
Failure/Error: it { is_expected.to have_one(:user).through(:survey) }
NoMethodError:
undefined method `klass' for nil:NilClass
Related issue: #646. #723 may solve both of these at once, I'll have to check.
I can verify that the reported issue has been fixed in the version 4.4.1, so we can close this issue for now.
Before:
Failure/Error: it { should have_many(:words).through(:pag) }
NoMethodError:
undefined method `class_name' for nil:NilClass
Did you mean? class_eval
After:
Failure/Error: it { should have_many(:words).through(:pag) }
Expected Book to have a has_many association called words through pages (Could not find the source association(s) "word" or :words in model Page. Try 'has_many :words, :through => :pages, :source => <name>'. Is it one of book?)
_Error text differs as i'm using rails 6.1.0.alpha_
Thanks! Closing.
Most helpful comment
You're saying that
Subject.has_many :photos, through: :subject_photo_assignments, but you don't have a:subject_photo_assignmentsassociation.Ideally, we should probably have a check to ensure that your
throughassociation exists. But that's why you're getting an error.