gayavat
gayavat

Reputation: 19398

behavior of activerecord relations

I met interesting behavior of activerecord relations.

class A < ActiveRecord::Base
  def self.one
    where("1=1")
  end

  def self.two
    puts A.where("2=2").to_sql
  end

  def self.test
     one.two
  end
end
A.test # prints  ... WHERE 1=1 AND 2=2

I expected that Ad.where("2=2") doesn't include "1=1" in self.two, but it is. Is it feature?

Upvotes: 0

Views: 79

Answers (1)

m_x
m_x

Reputation: 12564

Yes, it is a feature.

All values passed to where are internally stored by the relation (@values[:where]) and joined with AND to build the WHERE statement.

When calling two in the context of the relation returned by one, the "where values" stored by one are used as a basis to build the new scope.

You can obtain desired behavior using unscoped :

one.unscoped.two

EDIT: yes, it is clearly weird and counter-intuitive.

I just got bit by the same problem as you.

After a lot of digging, it appears that since rails 4, all models keep a thread-local registry that stores the current scope used on that class / relation. I don't know exactly why they did it, my guess is it has to do with performance.

The point is, when you already have chained a bunch of scopes and try to call another one that references the class (like two in your example), ActiveRecord just fetches the current scope for the class - that is, the relation on which you called the scope in the first place ! Pretty silly. In fact, your two method could be rewritten without the A class reference, it wouldn't change anything.

Weirder and weirder, another problem appeared to me when using STI : in my scope, I made a totally unrelated call to the parent model using pluck. Not only this "feature" affected the query in unexpected ways, but it seems it also has "reset" the default scope, so that the next query (which was the actual return of the scope) "forgot" about all conditions passed by previous scopes... WTF ?

Conclusion : I think we should open a ticket on rails github and at least ask the team to document this weird behaviour. (using MyClass.unscoped on my first query solved my problem, but is not intuitive)

Upvotes: 2

Related Questions