Using scope, defined in parent model, inside it's child (STI pattern)
Asked Answered
T

3

6

I implement a class hierarchy using STI pattern

class A
  scope :aaa, where([someField]:[someValue])
end

class B < A
end

The problem is that when I try to call something like:

B.limit(5).aaa
=> SELECT "[table]".* FROM "[table]" WHERE "[table]"."type" IN ('A') AND ([someField] = [someValue]) LIMIT 5

So I am getting 5 objects of type A, which satisfies scope :aaa But I need to do the same with rows where type = "B"

Is there any way to use scopes from parent, without redifinning it in childs in STI pattern?

Thanks in advance

EDITED

I just discussed it with my frind and he showed me one important thing. A in not the root class of STI. IN fact whole hierarchy looks like

class O < ActiveRecord::Base
end

class A < O
  scope ..... .....
end

class B < A
end

maybe the reason is in hierarchy itself?...

Taishataisho answered 17/9, 2012 at 14:49 Comment(0)
R
2

You are correct that this is a result of the multi-level hierarchy.

The scope would work as expected if you had just one level of inheritance (i.e. if A inherited from ActiveRecord::Base instead of inheriting from O).

STI in Rails does not always work as expected once you have intermediate classes.

I'm not sure if this is a bug in Rails or just a result of it not being feasible to infer exactly what should be done. I'll have to research that more.

The scope issue you experienced is just one example of the unexpected behavior. Another example is that querying the intermediate class will only query for the class types it is aware of, which may not include all of its subclasses unless you specifically require them:

See the following thread for more information, especially the post at the bottom by MatthewRudy: https://groups.google.com/forum/#!topic/hkror/iCg3kxXkxnA

If you still want to use scopes instead of class methods as suggested by @Atastor, you can put the scope on O instead of A. This isn't ideal since the scope may not be applicable to every subclass of O, but it does seem to cause Rails to query the correct classes in the type column, and is a quick workaround.

Riverine answered 20/10, 2012 at 16:33 Comment(0)
B
1

I would switch to using a class method instead of a scope, e.g. in class A

def self.aaa
  A.where([someField]:[someValue])
end 

and afterwards in class B

def self.bbb
  self.aaa.where("type = ?","B")
end
Blown answered 17/9, 2012 at 15:36 Comment(1)
It works, but I still curious why my example works not as I expect itTaishataisho
P
0

Watch out for STI intermediate classes because it can mess the scope you're trying.

In my case I had:

class A < ActiveRecord
blabla
end
class B < A
scope :dates, ->(start_date, end_date) { where(:date => start_date..end_date) }
end
class C < B
<call scope_dates>
end
class D < B
<call scope_dates>
end

And the scope was actually including class C and class D combined.

So, if you have STI in Rails Please put the scopes in the Base class, not intermediate classes

Pacorro answered 18/2, 2015 at 15:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.