You could try the mixed model approach, but it's a fair amount of work to set up. It's bit of of a kludge that trades performance for more efficient database storage.
The idea is that you use STI to handle all the common Post fields, and delegate the unique fields for each subclass to an another table and eager load that association.
The base Post class could look like this. Note the class_eval could be abstracted into a module that gets included and exended into a subclass.
#columns: id:integer, timestamps, user_id:integer,
# topic_id:integer, type:string
class Post < ActiveRecord::Base
# common methods/validations/associations
belongs_to :user
belongs_to :topic
def self.relate_to_detail
class_eval <<-"EOF"
has_one :detail, :class_name => "#{self.name}Detail"
accepts_nested_attributes_for :detail
default_scope :include => :detail
def method_missing(method, *args)
build_detail if detail.nil?
if detail && detail.respond_to?(method, true)
detail.send(method, *args)
else
super(method, *args)
end
end
def respond_to?( method, include_private = false)
build_detail if detail.nil?
super(method, include_private) ||
detail.respond_to?(method, include_private)
end
EOF
end
end
Then you'll need to define the sub and detail class for each type.
#uses posts table
class ImagePost < Post
relate_to_detail
end
#columns: id, image_post_id, url:string, height:integer, :width:integer
class ImagePostDetail < ActiveRecord::Base
belongs_to :image_post
end
#uses posts table
class MessagePost < Post
relate_to_detail
end
#columns: id, message_post_id, message:string
class MessagePostDetail < ActiveRecord::Base
belongs_to :image_post
end
Now we can do things like this:
@user.message_posts.create(:message => "This is a message")
@image_post.url
Which will create a new MessagePost, where the user_id, timestamps, post_id, post_type are all stored in the posts table, while the message is stored in the MessagePostDetails table and return the url of an ImagePost respectively.
The new method_missing and respond_to? definitions work the magic to hide the division.
@Post.all
will now list Posts of all types. The only downside is that the detail fields will not be displayed when you fetch a post.