I am trying to fuse to objects into a single stream.
I created a new model and controller called Newsfeed. I have it so that newsfeed has_many :messages and has_many :images and images and messages belong_to :newsfeed. I am setting up the controller right now and I have:
def index
@messages = Messages.all
@images = Images.all
@feed = ?
end
How do I fuse them so it can pull from both and put them in chronological order when each requires separate formating?
Any help would be greatly appreciated, thanks.
You could do something as :
controller:
def index
messages = Messages.all
images = Images.all
@feed = (messages + images).order_by(&:created_at)
end
view index.html.erb :
<%= render @feed %>
view _feed.html.erb :
<% if feed.is_a? Message %>
<%= render "message", :locals => {:message => feed}%>
<% else %>
<%= render "image", :locals => {:image => feed}%>
<% end %>
And add 2 partials _message.html.erb and _image.html.erb
I'm sure this can be streamlined from it's ghetto-polymorphism, but it works for me, and I had an identical struggle with the "how do I combine all these model results?!"
I use a separate model, like you, and an observer:
class NewsfeedObserver < ActiveRecord::Observer
observe Message, Image
def after_save(object)
@item = Newsfeed.create(
:item_id => object.id,
:item_type => object.class.to_s
)
end
end
Then in the Newsfeed model you can pull and collate as you like:
def self.get_items
item_list = []
items = find(:all, :order => "created_at DESC", :limit => 30)
items.collect{ |i| i.item_type.constantize.find(i.item_id) }
end
Then just pull them and display in your view however you like.
#Controller
def index
@feed = Newsfeed.get_items
end
Moving forward I will probably start to pack more into the stream-tracking model in order to save hits on the other tables, but hopefully you get the idea.
This feels like a bit of a loaded question, and though I don't have the time to dig into it in full, here are some initial thoughts for you:
you can combine the two types of items you want to display into a single array and then sort this array based on a field which ought to be common between the two (created_at comes to mind) to get them into chronological order.
In the view, you can use this single collection to iterate over and then use helpers and unique partials to render the items in the correct format. This could be done by checking the objects type as you are trying to display it in the view.
Something along the lines of:
if item.is_a? Message
render :partial => 'message', :locals => {:message => item }
elsif item.is_a? Image
render :partial => 'image', :locals => {:image => item }
end
hopefully this helps some!
EDIT: to combine the arrays, you can simply append them together using the '+' operator, since arrays in Ruby do not need to contain a single object type.
your Messages.all and Images.all calls give you arrays of objects, so just try
@messages + @images
and you should have your combined array.
EDIT #2: If I were writing this myself, I would probably handle the views somewhat like this:
at the top level, simply pass your feeds collection to a partial which is designed to render a single item:
render :partial => "item", :collection => @feed
this will render the '_item.html.erb' partial for each item in @feed. Also, it will give the partial a local variable named 'item' each time, which is the item in @feed for that particular iteration.
now, I would probably have the '_item' partial be very simple, like this:
<%= display_item(item) %>
where display_item is a helper method outside the view and is implemented with the "is_a?" type logic from above to choose the correct display format for the item.
the reason I would break it up like this is it takes advantage of some nice rails features (pass a collection to a partial instead of looping over the collection yourself) and then pulling the logic out into helpers (or even the models if it makes sense to) is useful because it simplifies your views. keeping logic out of the view whenever possible is generally a good practice as such logic can be hard to test and your views will become very hard to read.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With