On my one of the current projects, there are two primary models each with a flag called approved. 99% of the front end part deals with only approved items. Unapproved items are usually only in the admin panel side of the story. So I started with using a named_scope called approved:
1 2 3 4 5 6 |
class Item < ActiveRecord::Base has_many :tags default_scope :order => 'items.name ASC' named_scope :approved, :conditions => { :published => true } end |
And now I’d have to use Item.approved.
1 2 3 4 5 6 7 8 9 10 11 12 |
class Item < ActiveRecord::Base has_many :tags default_scope :order => 'items.name ASC' end class PublishedItem < Item set_table_name 'items' set_inheritance_column nil # hax? default_scope :conditions => { :published => true }, :order => 'items.name ASC' end |
Checking this on console :
>> p = PublishedItem.first
SELECT * FROM `items` WHERE (`items`.`published` = 1) ORDER BY items.name ASC LIMIT 1
>> i = Item.first
SELECT * FROM `items` ORDER BY items.name ASC LIMIT 1Seems to work just fine.
You could do it the other way around too:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class RawItem < ActiveRecord::Base set_table_name 'items' has_many :tags default_scope :order => 'items.name ASC' end class Item < RawItem set_table_name 'items' set_inheritance_column nil # hax? default_scope :conditions => { :published => true }, :order => 'items.name ASC' end |
Whichever one works for you.
Please note that the above code is NOT using STI. It’s using set_inheritance_column nil workaround to bypass the Active Record STI stuff and rely just on the ruby inheritance.







great idea, ill try adding it to my app :)
And i would use default scope on item and then use Item.unapproved in admin pages, seems easier to me…
I dig this. It allows you to dry up your named scopes a bit but still allowing very easy access to the objects without the scope applied (no with_exclusive_scope hacks).
Hey Patrik,
Any reason why you didn’t use ActiveRecord::Base.abstract_class attribute instead of re-seting the table name and setting inheritance column to nil?
This would still look at the items table, as set in the abstract class, but eliminates the inheritance column hack ;)
Great tip, anyway.
Cheers
Hey Rodrigo,
The inheritance column hack isn’t actually needed if the “type” column is missing. But it’s good to be explicit in cases like this. Using abstract_class would work, but you’re not really supposed to create objects of abstract classes, so you never know when that stops working.
Wouldn’t it be possible to set default_scope to only published items and then in the admin panel use Item.with_exclusive_scope { find(:all) } instead of creating an inheriting class?
Yeah @aleco’s idea could work. It would be nice if you could just chain scopes and .find right off of with_exclusive_scope rather than having to pass it a block as a convenient way to bypass default_scope.
I find myself in this kind of situation all the time.
@Ben — I think you’re right. It should be really simple to add named_exclusive_scope.
@Pratik — It took me some time to figure out what you’re trying to do. If you’re only looking for unpublished items, it doesn’t seem like a whole separate class makes sense. I think aleco’s idea (with Ben and my extensions) would be the best way to handle this situation. (Although I do think your solution is pretty cool, and might have some other uses.)
I really like this solution. I tried to ignore the default scope first with with_exclusive_scope, but it ignore all scope. So user.comments_with_deleted gave me all the comments, and not scoped on ths user.
I do have problems here with a has_many_and_belongs_to
I have a certificate and a rawcertificate, on the certificate I have a default scope so that I also have the ‘deleted’ certificates with the rawcertifcate. This model is in a has_many_and_belongs_to relation with a odel called trade.
But I don’t seem to get the Rawcertificate working with the certificate_trades table where the many to many relation is defined.
Any solutions on that?
The problem with this is that it models state using types. The state can change, but the type doesn’t: you could get a PublishedItem and set published to false (which could easily happen if the user had the ability to unpublish an item).
Item and PublishedItem are not really types. They express not qualities of the objects, but qualities of the way those objects are found and created.
Using inheritance to save keystrokes rather than model data will be surprising to many people.
While I can see the different directions here I’d like to point out that what led me in pursuit of something like this is that I have a superclass Topic which I am happy to handle in its plural form.
However I have different subclass topics which have different model internals as they are processed in different ways but don’t deserve separate tables.
Now I may have a topic called Walking which is of type Walking and I do not wish for it to exist in plural, Walkings form. But where I can cancel pluralize in the environment I don’t wish a blanket cancel on all models, it hence is not an appropriate option.
I tried setting table name to Topics but this yields an “undefined method Walkings” error.
I like the inheritance hack so far but I’d rather not have a hack which may break in the future.
Can you guys suggest something?
“I tried setting table name to Topics but this yields an “undefined method Walkings_path” error.”