chanzuckerberg/sorbet-rails

Running tests raises a NameError for sigs receiving or returning an ActiveRecord_AssociationRelation

franciscoj opened this issue · 12 comments

Describe the bug:

When you declare a method which receives or returns an ActiveRecord_AssociationRelation of some sort, tests fail with:

     NameError:
       private constant #<Class:0x00007fa75c4e8d28>::ActiveRecord_AssociationRelation referenced

Steps to reproduce:

create a method which returns an association relation on a model

class Post < ApplicationRecord
  belongs_to :user

  scope :published ->() { where(published: true) }
end

class User < ApplicationRecord
  extend T::Sig

  has_many :posts

  sig { returns(Post::ActiveRecord_AssociationRelation) }
  def published_posts
    posts.published
  end
end

RSpec.describe User do
  describe '#published_posts' do
    it 'returns only the published posts' do
      user = User.create!
      published = user.posts.create(published: true)
      user.posts.create(published: false)

      expect(user.published_posts).to eq([published])
    end
  end
end

This would raise a NameError claming that Post::ActiveRecord_AssociationRelation is a private class.

Note as well that in my case the models are on a rails engine.

Expected behavior:

The test passes

Versions:

  • Ruby: 2.4
  • Rails: 5.2.3
  • Sorbet: Sorbet typechecker 0.4.4891 git 4c9a4534439483b46e869c7212007ad103923203 built on 2019-10-17 22:56:24 GMT debug_symbols=true
  • Sorbet-Rails: 0.5.8

You're totally right.

I think I've managed to fix it by moving gem 'sorbet-rails' on the Gemfile from the group :development block to the general group.

Thanks a lot!

I have sorbet-rails in the general group, but I receive the same issue with private constant being referenced.

EDIT: I notice that in development mode, the inherited hook does not fire, while in production mode it does. Guessing it's because of eager_load?

I can confirm that moving it to the general group fixed the issue for me. Besides the code is now running on CI and on production with some MyModel::ActiveRecord_Association typed methods.

@International what I did to see what was happening on my case was to:

bundle open sorbet-rails

Go to the railties file and add a byebug on the inherited hook.

Then starting the rails console on development (where it worked) and on test, where it didn't. That way I realised what the issue was.

Not sure it can help you debug your case... but you never know!

Guessing it's because of eager_load

I doubt that could be the case, take into account that these patches sorbet-rails do are hooked to certaing loading events which are fired when things like ActiveRecord::Base load. It would mean that your app isn't loading those, which would be really weird.

If you wanted to confirm you could add an initializer in the same fashion as sorbet-rails does and check if it gets called or not.

Hi @franciscoj , thanks for the tip, I did that :)
On one side, what I noticed is that the hook only gets called for about 10 out of 200 models, in development mode, on the other it looks like the railtie should probably be required kind of early so that models coming from gems would also get the hook?
I noticed that also for models defined in gems, the constant is private.

I noticed that also for models defined in gems, the constant is private.

AFAIK for models coming from gems you need to use plugins, like the ones described here https://github.com/chanzuckerberg/sorbet-rails#enabling-built-in-plugins

But I'm afraid I don't know how to add new ones or create your own plugins.

I will try to play around with load order, see if I can force it to get it loaded. Let you know what I find. Thanks

FYI there is a PR to improve the documentation around this #214

The documentation change is merged