Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Has many through association: undefined method `klass' for nil:NilClass #611

Closed
xtagon opened this issue Nov 6, 2014 · 15 comments
Closed
Labels

Comments

@xtagon
Copy link

xtagon commented Nov 6, 2014

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

@mcmire
Copy link
Collaborator

mcmire commented Nov 6, 2014

Yeah, can you re-run the tests with the --backtrace option?

@xtagon
Copy link
Author

xtagon commented Nov 6, 2014

  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>'

@mcmire
Copy link
Collaborator

mcmire commented Nov 6, 2014

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.

@xtagon
Copy link
Author

xtagon commented Nov 6, 2014

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.

@mcmire
Copy link
Collaborator

mcmire commented Nov 6, 2014

Sounds good, glad I could help.

@mcmire mcmire closed this as completed Nov 6, 2014
@1v
Copy link

1v commented Mar 18, 2017

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

@mcmire
Copy link
Collaborator

mcmire commented Mar 21, 2017

@1v You have a subject_photo_assignment association, but you don't have a subject_photo_assignments association (note the "s" at the end).

@1v
Copy link

1v commented Mar 21, 2017

@mcmire it's working with "s" too, but tests failing.

@mcmire
Copy link
Collaborator

mcmire commented Mar 22, 2017

@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

@1v
Copy link

1v commented Mar 22, 2017

@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.

@mcmire
Copy link
Collaborator

mcmire commented Mar 23, 2017

@1v Okay, fair enough. I'll re-open this issue.

@mcmire mcmire added the UX label Mar 23, 2017
@mcmire mcmire reopened this Mar 23, 2017
@esbanarango
Copy link

I also have this error with this "setup":

module Hanuman
  class Observation < ActiveRecord::Base
    belongs_to :survey, touch: true
  end
end
Hanuman::Observation.class_eval do
  has_one  :user, through: :survey
end
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

@mcmire
Copy link
Collaborator

mcmire commented Jun 9, 2019

Related issue: #646. #723 may solve both of these at once, I'll have to check.

@KapilSachdev
Copy link
Collaborator

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

@mcmire
Copy link
Collaborator

mcmire commented Sep 7, 2020

Thanks! Closing.

@mcmire mcmire closed this as completed Sep 7, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants