8

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?

Arjan
  • 6,264
  • 2
  • 26
  • 42
  • 1
    http://stackoverflow.com/questions/11387836/activerecord-builds-instance-of-wrong-class-through-a-scope-targeting-an-sti-cla – Oleg Haidul Sep 18 '13 at 10:09
  • 1
    Why you want it to build with Posts? and not with StickyPost.new? – techvineet Sep 18 '13 at 10:09
  • http://api.rubyonrails.org/classes/ActiveRecord/Scoping/Named/ClassMethods.html#method-i-scope – j03w Sep 18 '13 at 10:15
  • @techvineet Because I am working with a collection, and until a certain point I don't know whether to use `StickyPost` or another subclass. – Arjan Sep 18 '13 at 10:17
  • @lol007 Yes `Post.new type: 'StickyPost` works. Strange that it doesn't work through the scope, since it also sets the type. – Arjan Sep 18 '13 at 10:18
  • Updated my answer to reflect @lol007 his comment. – Arjan Sep 18 '13 at 10:23

1 Answers1

0

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>