I’d like to create a ‘timeline’ feature for an existing Card model. The Card already has_many Notes and has_many Attachments. I’d like to be able to:
- access notes, attachments (and other models eventually) in a unified collection with a nice method like: card.timeline
- still be able to access a card’s notes and attachments like: card.notes
- still be able to access a note’s parent card like: note.card
- be able to add items to the card’s timeline, with an API like: card.timeline << note
I think I have my DB set up correctly, it’s the association declaration I can’t seem to get right. Here’s my schema:
create_table "cards", :force => true do |t|
t.string "name"
end
create_table "timeline_items", :force => true do |t|
t.integer "card_id", :null => false # FK from cards table
t.integer "item_id", :null => false # FK from notes or attachments table
t.string "item_type", :null => false # either 'Note' or 'Attachment'
end
create_table "notes", :force => true do |t|
t.text "content"
end
create_table "attachments", :force => true do |t|
t.string "file_file_name"
end
Anyone know how I can achieve this using ActiveRecord? It’s driving me fudging mental!
A starting point is:
class Card < ActiveRecord::Base
has_many :timeline_items
has_many :notes, :through => :timeline_items, :source => :item, :source_type => 'Note', :order => 'updated_at DESC'
has_many :attachments, :through => :timeline_items, :source => :item, :source_type => 'Attachment', :order => 'updated_at DESC'
end
class TimelineItem < ActiveRecord::Base
belongs_to :card
belongs_to :item, :polymorphic => true
end
class Note < ActiveRecord::Base
has_one :card, :through => :timeline_items
has_one :timeline_item, :as => :item
end
Thanks in advance
~Stu
Okay – after struggling on and off with this, I crack it within 10mins of posting to stackoverflow! Typical.
To save others from banging their heads against walls, here’s what I had wrong:
Note should have been:
And that was it! I was trying to use the creation methods used in this article, but actually that’s not required.
Here’s the console output, showing that the sql statements are all using the timeline_items table:
EDIT:
I just noticed that my requirement of having card.timeline wasn’t met yet. Because the join is coming from multiple tables, I wasn’t able to get AR to handle the join for me (ideally *card.timeline_items.join(:notes, :attachments)* would have done the trick).
To solve this, I added the following method to my card class: