Single table inheritance scopes
Asked Answered
F

1

9

Is it possible to set-up a scope on a single table inheritance that returns the subclass?

For example:

class Post < ActiveRecord::Base
  scope :sticky, -> { where(type: 'StickyPost') }
end

class StickyPost < Post
end

Now, when I call sticky on a collection of posts I get a collection of StickyPost instances.

But when I call posts.sticky.build, the type is set to StickyPost, but the class still is Post.

posts.sticky.build
=> #<Post id: nil, message: nil, type: "StickyPost", created_at: nil, updated_at: nil>

Update

Apparently this works.

posts.sticky.build type: 'StickyPost'
=> #<StickyPost id: nil, message: nil, type: "StickyPost", created_at: nil, updated_at: nil>

Which is strange, since the scope already sets the type, it seems a bit redundant. Any way to set this behaviour in the scope?

Forrestforrester answered 18/9, 2013 at 9:58 Comment(6)
#11388336Mattress
Why you want it to build with Posts? and not with StickyPost.new?Houseboat
api.rubyonrails.org/classes/ActiveRecord/Scoping/Named/…Autograph
@Houseboat Because I am working with a collection, and until a certain point I don't know whether to use StickyPost or another subclass.Forrestforrester
@lol007 Yes Post.new type: 'StickyPost works. Strange that it doesn't work through the scope, since it also sets the type.Forrestforrester
Updated my answer to reflect @lol007 his comment.Forrestforrester
P
-1

You can make the sticky scope return the correct class by using becomes method in the scope:

class Post < ActiveRecord::Base
  scope :sticky, -> { where(type: 'StickyPost').becomes(StickyPost) }
end

The becomes method maps an instance of one class to another class in the single-table inheritance hierarchy. In this case, it maps each instance of Post returned by the sticky scope to StickyPost. With this change, calling posts.sticky.build will now return an instance of StickyPost:

posts.sticky.build
=> #<StickyPost id: nil, message: nil, type: "StickyPost", created_at: nil, updated_at: nil>
Prove answered 10/2, 2023 at 11:6 Comment(2)
This seems to fail with undefined method "becomes" for #<ActiveRecord::Relation ..., since becomes is an instance method, not a class method.Covert
This doesn't workGezira

© 2022 - 2024 — McMap. All rights reserved.