AR CollectionProxy include? returns false when it shouldn't

Under what circumstances might this happen? Is it because the thing has a different class of parent?

puts object.things.inspect
> [#<ActiveRecord::Associations::CollectionProxy [#<Thing id: 1, name: "Bruno">]>]

puts other.thing.inspect
> #<Thing id: 1, name: "Bruno">

puts object.things.include?(other.thing)
> false
1 Like

I was unable to reproduce this problem using objects from one of my applications – Country which has_many :places. But I did notice that when I ran country1.places.inspect in the Rails console, the output was not surrounded by square brackets, whereas yours is.

By chance, have you explicitly defined a method named things?

1 Like

Yes, things is a model method, which returns a CollectionProxy containing other.things.

  def things
    self.other.joins(:things).map(&:things)
  end

I’m not actually using this code or anything like it; my needs were better served by a scope and a method on the things model. I’m just curious why .include? will not see the object in the CollectionProxy when it’s obviously there. Could be useful to know for the future under what circumstances .include? cannot be used.

1 Like

Thanks for that information. Mystery solved.

Your #things method returns an Array of CollectionProxy objects, as evidenced by the nested square brackets:

puts object.things.inspect
> [#<ActiveRecord::Associations::CollectionProxy [#<Thing id: 1, name: "Bruno">]>]

So in the statement object.things.include?(other.thing), the Array#include? method is comparing CollectionProxy objects to a Thing object, therefore the comparison always fails.

Assuming that the class definition for object and other includes the statement has_many :things, Rails will automatically provide a #things method. You’re overriding (monkey-patching) that method, which is not a good practice.

The method provided by Rails would return a single CollectionProxy object rather than an Array of them, and the CollectionProxy#include? method would return true.

Hope this helps.

3 Likes

That makes perfect sense, thank you! Should have noticed the surrounding square brackets myself :grin:

1 Like

If you are in the console you will also see funniness like this between reloads. But in this case yes your things accessor is a little funny it seems.

pod = Pod.first
rep = pod.representatives.first
pod.representatives.include?(rep) #=> true

reload!
pod = Pod.first
pod.representatives.include?(rep) #=> false