views:

179

answers:

3

I am new to rails so go easy. I have created a blog. I have successfully implemented comments and attached them to each post. Now...I would like to display, in the sidebar, a list of the most recent comments from across all posts. I think there are two things involved here, an update to the comment_controller.rb, and then the call from the actual page. Here is the comments controller code.

class CommentsController < ApplicationController

  def create
    @post = Post.find(params[:post_id])
    @comment = @post.comments.create!(params[:comment])

    respond_to do |format|
      format.html { redirect_to @post}
      format.js
    end
  end
end
+3  A: 

If you want to display all the comments from any post, in most recent order, you could do:

@comments = Comment.find(:all, :order => 'created_at DESC', :limit => 10)

And in the view you can do:

<% @comments.each do |comment| -%>
    <p>
        <%= comment.text %> on the post <%= comment.post.title %>
    </p>
<% end -%>
Garrett
This looks like what I am looking for. Just tried it an it didn't work. I suspect it is because I am referencing it incorrectly. The second part (the view) is being put into the layout folder for posts...does that change the @comments to comments, and vice versa?
bgadoci
If you're putting it in the layout then you may just want to inline the find statement into the layout like so: <% Comment.find(:all, :order => 'created_at DESC', :limit => 10).each do |comment| -%>I'm not sure how that will format in a comment, but hopefully that makes sense. In general putting logic in views is frowned upon, but the only other way I can think you'd have access to the @comments variable on every page is if you put a hook to run before every method in ApplicationController.
mmrobins
That worked man. Thanks.
bgadoci
+4  A: 

I'm posting a separate answer since code apparently doesn't format well at all in comments.

I'm guessing the problem you're having with the previous answer is that you're putting

@comments = Comment.find(:all, :order => 'created_at DESC', :limit => 10)

in one of your controller methods. However, you want @comments to be available to a layout file, so you'd have to put that on every controller method for every controller in order for that to work. Although putting logic in views is frowned upon, I think it would be acceptable to do the following in your layout file:

<% Comment.find(:all, :order => 'created_at DESC', :limit => 10).each do |comment| -%>
    <p>
        <%= comment.text %> on the post <%= comment.post.title %>
    </p>
<% end -%>

To get some of the logic out of the view though we can move it into the Comment model

class Comment < ActiveRecord::Base
  named_scope :recent, :order => ["created_at DESC"], :limit => 10

Now you can do this in your view:

<% Comment.recent.each do |comment| -%>
    <p>
        <%= comment.text %> on the post <%= comment.post.title %>
    </p>
<% end -%>

This makes for a nice fat model and skinny controller

mmrobins
I like the named scope, and even though the view code is very clean this bypasses the controller completely. I guess my first shot would be to call `Comment.recent` in a before_filter in the App Controller, but I could be wrong on that.
Andy Gaskell
+1 for named scope, but Andy's right. This belongs in the application controller as a before filter. It still works, but views should not be processing models directly.
EmFi
I see the point about the before filter, but I think I actually prefer the logic in the view this time. In my mind it's a lot cleaner and more intention revealing than a before filter in the Application controller where you might end up scratching your head later reading the code wondering where the @comments variable came from. At least if you're going to put the filter in the the application controller you should choose a very unique variable name so as not to pollute the namespace. So @recent_comments_for_layout instead of @comments.
mmrobins
I disagree with this method, keep the logic out of the views. There should be a `before_filter` in the ApplicationController, or make a method a helper, which can be accessed in all views.
Garrett
Another reason I don't like this in the Application controller as a before filter is that you'll be hitting the database to get comments for every controller method even if you're not displaying comments for that method (update for example, or if you're getting xml and not html). A helper method seems unecessary unless you're going to use this in more than one place, and it soudns to me like it will really only be used in one layout view.
mmrobins
+1 for the named scope approach, -1 for the suggestion to add it to a filter. Putting it in the view is the better approach here
Patrick Reagan
A: 

I tend to use a helper for this:

# in app/helpers/application_helper.rb:
def sidebar_comments(force_refresh = false)
  @sidebar_comments = nil if force_refresh
  @sidebar_comments ||= Comment.find(:all, :order => 'created_at DESC', :limit => 10)
  # or ||= Comment.recent.limited(10) if you are using nifty named scopes
end

# in app/views/layouts/application.html.erb:
<div id='sidebar'>
  <ul id='recent_comments'>
    <% sidebar_comments.each do |c| %>
      <li class='comment'>
        <blockquote cite="<%= comment_path(c) -%>"><%= c.text -%></blockquote>
      </li>
    <% end %>
  </ul>
</div>
James A. Rosen