How do I render all Comments in a Rails view? - ruby-on-rails

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

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 -%>

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

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>

Related

Rails nested resource is showing up twice.. once at the start and once at the end

I have a resource called a ClinicalCase which has_many questions (nested resource). The premise is to show a case all the time, and be able to see one question at a time. My issue is that, if a case has 5 questions, the app is only showing 4. It will show the same question for the first array index and last array index, and everything in between will be the same.
For example, if case 1 has questions "foo", "bar" and "baz", "foo" and "bar" will only show up, with "foo" at the start and the end. I have no idea why this is happening.
I'm using will_paginate to assist with the pagination, as each user needs to see one case at a time, and one question at a time that belongs to the case.
Here's my code (working fine, except that the first and last question of a case show up as the exact same)
controller
def tagged
#cases = ClinicalCase.with_tag(params[:tag]).paginate(:page => params[:case], :per_page => 1)
end
model
class ClinicalCase < ApplicationRecord
belongs_to :user
has_many :questions, dependent: :destroy, inverse_of: :clinical_case
has_many :answers
accepts_nested_attributes_for :questions, allow_destroy: true, reject_if: :all_blank
acts_as_taggable
scope :with_tag, -> (tag) { tagged_with(tag) if tag.present? }
def paginated_questions(page, per_page = 1)
questions.paginate(page: page, per_page: per_page)
end
view part 1: tagged.html.erb
<% #cases.each do |clin_case| %>
<%= render "clinical_cases/question", clin_case: clin_case, questions: clin_case.questions %>
<%= will_paginate #cases, :param_name => 'case', :previous_label => 'Previous Case', :next_label => 'Next Case', :page_links => false %><br>
<% end %>
view part 2: _question.html.erb
<% questions = clin_case.paginated_questions(params[:page]) %>
<% questions.each do |question| %>
<%= question.title %>
<% question.answers.shuffle.each do |x| %>
<% if x.correct? %>
<%= x.choice %>
<% else %>
<%= x.choice %>
<% end %>
<% end %>
<%= will_paginate questions, :previous_label => 'Prev. Ques.', :next_label => 'Next Question', :page_links => false %>
<% end %>
I figured it out, and I'm not sure why it started working but it did.
<% #questions = clin_case.questions.order("created_at ASC").paginate(:page => params[:question], :per_page => 1) %>
<%= render #questions %>
I used the local variable to get the association and added an order method in ASC, and that caused it to work.

undefined method `total_pages' for #<Array:0x8e052a0>, how to paginate correctly?

I would be able to paginate easily but I'm using two models which complicates it a bit.
First here's the controller
class BrowseController < ApplicationController
def index
#hashtags = Hashtag.find(:all, :order => 'created_at DESC')
#posts = post.all
end
end
Then here's the view (browse\index.html.erb)
<ul>
<% #posts.each do |post| %>
<% if post.hashtags.present? %>
<li> <%= link_to post.hashtags.map{|h| "##{h.hashtags}"}.join(', '), post.user %> </li>
<% else %>
<% end %>
<% end %>
</ul>
I'm trying to paginate that view. It's not just post, but the hashtag of the posts. What would be the correct <%= will_paginate %> code?
I'm trying to paginate the hashtags of the posts, not just the posts themselves.
If you are using the awesome will_paginate gem: https://github.com/mislav/will_paginate, simple change your controller code to:
# per_page 30 posts, change :per_page to the number you´d like
#posts = Post.paginate(:page => params[:page], :per_page => 30)
and change your view to:
<%= will_paginate #posts %>

How can I implement fragment cache with dalli when using pagination (kaminari)?

I know dalli (caching) is pretty powerful plugin to enhance performance for static pages.
But what about dynamic pages with pagination, which are updated quite often?
What is the correct way to set up dalli?
One problem I've encountered for example: dalli recognizes different params[:page] as the same page when using pagination:(
How would you guys design the system when using dalli for both
the page that gets updated so often
the page that won't be updated so often
For example. I have 4 models such as User, Community, Topic, and Comment(defined Polymorphic to both Community, and Topic. They can be created or shown in #show pages)
It's like
Community > (has_many) > Topics > (has_many) > Comments
Community > (has_many) > Comments
All of them belongs to user(creator)
My current codes are just like this(This is quite bit long. Sorry about that)
I'm facing the pagination problem when using caching...
-------------------------------------Settings----------------------------------------
config/environments/development.rb
config.consider_all_requests_local = true
config.action_controller.perform_caching = true
config.cache_store = :dalli_store
routes.rb
resources :communities do
resources :topics do
resources :comments do
end
end
-------------------------------------Community----------------------------------------
controllers/communities_controller.rb#index&show
caches_page :show
caches_action :edit, :new
def index
....
if params[:sort] == 'new'
#communities = Community.scoped.page(params[:page]).order("created_at DESC")
elsif params[:sort] = 'popular'
#communities = Community.scoped.page(params[:page]).order("follow_count DESC")
elsif params[:sort] = 'reputation'
#communities = Community.scoped.page(params[:page]).order("reputation_count DESC")
else
#communities = Community.scoped.page(params[:page]).order("created_at DESC")
end
....
end
def show
#community = Community.find(params[:community_id])
#comments #community.comments.page(params[:page]
#topics = #community.topics.limit(10)
end
views/communities/index.html.erb
note: However, the content will be the same even if I move to next params[:page]:( It seems caching recognize different page as the same contents...
#here, the url will could be something like this example.com/communities?utf8=✓&location=14&genre=3&search=strawberry
But this is going to create gigantic petterns of the caches:(
So I want to make it work only when params[:sort] was not empty. Because, no other parameters come with when params[:sort] is not empty.
It could be like, example.com/communities?sort=new, example.com/communities?sort=popular, example.com/communities?sort=reputation
...
<% if params[:sort] %>
<% #key = "community_index_" + params[:sort] + params[:page] %>
<% cache(:controller => "communities", :action => "index", :action_suffix => #key) do %>
<%= page_entries_info(#communities, :entry_name => 'community').html_safe %>
<%= paginate #communities, :window => 4 %>
<% #communities.each do |community| %>
<%= render 'communities/community', :community => community %>
<% end %>
<% end %>
<% end %>
...
views/communities/show.html.erb
...
<% #key = params[:community_name] + "_community_show_information" %>
<% cache(:controller => "communities", :action => "show", :action_suffix => #key) do %>
<h2>Information</h2>
<div class="CommunityInformation">
<%= render 'communities/information'%>
</div>
<% end %>
<% #key = params[:community_name] + "_community_show_wall_" + params[:page] + %>
<% cache(:controller => "communities", :action => "show", :action_suffix => #key) do %>
<%= paginate #comments, :window => 4 %>
<h2>Topic</h2>
<div class="WallInformation">
<% #comments.eager.recent.each do |comment| %>
<%= render 'communities/comment', :comment => comment %>
<% end %>
</div>
<% end %>
<% #key = params[:community_name] + "_community_show_topics" %>
<% cache(:controller => "communities", :action => "show", :action_suffix => #key) do %>
<h2>Topic</h2>
<div class="TopicInformation">
<% #topics.eager.recent.each do |topic| %>
<%= render 'communities/topic', :topic => topic %>
<% end %>
</div>
<% end %>
...
models/community_sweeper.rb
class CommunitySweeper < ActionController::Caching::Sweeper
observe Community
def after_save(record)
expire_fragment(url_for(:action => 'index', :only_path => true) + '?????(all the caches related to the community#index)')
expire_fragment(url_for(:action => 'show', :only_path => true) + '?????(the cache related to the particular community#show)')
end
end
end
-------------------------------------Topic----------------------------------------
controllers/topics_controller.rb#index&show
caches_page :show
caches_action :edit, :new
def index
....
#community = Community.find(params[:community_id])
#topics = #community.topics.page(params[:page]
....
end
def show
#topic = Topic.find(params[:id])
#comments = #topic.comments.page(params[:page]
end
views/topics/index.html.erb
...
<% #key = params[:community_id] + "_topic_index_" + params[:page] %>
<% cache(:controller => "topics", :action => "index", :action_suffix => #key) do %>
<%= page_entries_info(#communities, :entry_name => 'community').html_safe %>
<%= paginate #communities, :window => 4 %>
<% #communities.each do |community| %>
<%= render 'communities/community', :community => community %>
<% end %>
<% end %>
<% end %>
...
views/topics/show.html.erb
...
<% #key = params[:community_name] + "_topic_show_" + params[:id] + "_comments" + params[:page] %>
<% cache(:controller => "topics", :action => "show", :action_suffix => #key) do %>
<%= paginate #comments, :window => 4 %>
<%= page_entries_info(#comments, :entry_name => 'comment').html_safe %>
<h2>Comment</h2>
<div class="CommentInformation">
<% #comments.each do |comment| %>
<%= render 'topics/comment', :comment => comment %>
<% end %>
</div>
<% end %>
...
models/topic_sweeper.rb
class TopicSweeper < ActionController::Caching::Sweeper
observe Topic
def after_save(record)
expire_fragment(url_for(:action => 'index', :only_path => true) + '?????(all the caches related to the topic#index)')
expire_fragment(url_for(:action => 'show', :only_path => true) + '?????(the cache related to the particular topic#show)')
end
end
-------------------------------------Comment----------------------------------------
models/comment_sweeper.rb
class CommentSweeper < ActionController::Caching::Sweeper
observe Comment
def after_save(record)
expire_fragment(url_for(:action => 'index', :only_path => true) + '?????(all the caches related to the topic#index)')
expire_fragment(url_for(:action => 'show', :only_path => true) + '?????(the cache related to the particular topic#show)')
end
end
I am following on on from my answer to
How should I set up dalli for a dynamic page with lots of content updates?
Please note that I am not an expert but I did try your approach and abandoned it as it just kept getting more and more complex as my application grew and I went for the cache key fragment based approach mentioned in the last question. There are a few things to bear in mind.
The cache sweeper approach requires you to make sure that the sweeper is up to date as you make changes to your application which means extra maintenance and testing work.
In a dynamic application you probably wont be able to easily cache whole pages especially if the views show information from many models.
You wont be able to deal with the paging issue unless the page parameter becomes part of your cache keys.
When your cache gets full, memcached will simply remove the least used / oldest cache items when putting in a new cache fragment.
So there is no issue with you just pushing in a new cache fragments when your models change and letting memcached clear out the old out of date cache items.
Therefore fragment caching in your views will probably be the easiest solution as it will deal with paging and you wont have to deal with manual cache invalidation
So looking at one of your views, what I might do is
<% if params[:sort] %>
<%= page_entries_info(#communities, :entry_name => 'community').html_safe %>
<%= paginate #communities, :window => 4 %>
<% #communities.each do |community| %>
<% cache(community, :suffix => "community_index") do
<%= render 'communities/community', :community => community %>
<% end %>
<% end %>
<% end %>
What I have done is cache the rendering of each community record and paging becomes irrelavent

Rails: Ajax/jQuery Issue with Pagination

I have a comment model that posts under a micropost both are paginated and both show on the same page.
I have done everything from http://railscasts.com/episodes/174-pagination-with-ajax?view=asciicast and everything should work but the problem is that both micropost and commet are paginated and both are on the same page.
The links for both pagination turns into this href="/users/2?page=2" rather than href="/users/2/micropost?page=2" or href="/users/2/comment?page=2". I am unsure how to go about solving this problem. Here are some of my code. All suggestions are much appreciated!
Micropost Render HTML
<table class="microposts">
<% if microposts.any? %>
<%= render microposts %>
<%= will_paginate microposts, :page_links => false %>
<% else %>
<div class="EmptyContainer"><span class='Empty'>Add a thread!</span></div>
<% end %>
</table>
Comment Section HTML
<div id='CommentContainer-<%= micropost.id%>' class='CommentContainer Condensed2'>
<div class='Comment'>
<%= render :partial => "comments/form", :locals => { :micropost => micropost } %>
</div>
<div id='comments'>
<% comments = micropost.comments.paginate(:per_page => 5, :page => params[:page]) %>
<%= render comments %>
<%= will_paginate comments, :class =>"pagination" %>
</div>
</div>
User Controller for the Show Page
def show
#user = User.find(params[:id])
#comment = Comment.find(params[:id])
#micropost = Micropost.new
#comment = Comment.new
#comment = #micropost.comments.build(params[:comment])
#comments = #micropost.comments.paginate(:page => params[:page], :per_page => 5)
#microposts = #user.microposts.order('created_at DESC').paginate(:per_page => 10, :page => params[:page])
respond_to do |format|
format.html
format.js
end
end
to separate the two, you should go here
http://blog.devinterface.com/2011/08/tips-multiple-pagination-with-will_paginate/
you can pass in separate params, so that only one will paginate even if both are on the same page

How I can add an one object on the others object's layout in ruby on rails?

Hello I'm having 2 objects the one events and the other one categories. I want to put the categories in the layout of the events. i tried but i;m getting an error message
Showing layouts/events.html.erb where line #40 raised:
You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.each
<% for category in #categories %>
<li><%=h category.name %>
<ul>
<% for subcategory in #subcategories %>
<% if subcategory.category_id == category.id %>
<li><%=h subcategory.name %></li>
<% end %>
<% end %>
</ul>
</li>
<% end %>
events_controller
def index
#subcategories = Subcategory.find(:all, :order=>"category_id , name")
#categories = Category.find(:all)
#events = Event.find(:all)
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #events }
end
end
class Subcategory < ActiveRecord::Base
belongs_to:category
has_many:events
end
class Event < ActiveRecord::Base
belongs_to:subcategory
end
class Category < ActiveRecord::Base
has_many:subcategories
end
my routes
map.root :controller => 'events'
# Index
map.connect '/index/', :controller=>'events', :action=>'index'
map.connect '/index/events/', :controller=>'events', :action=>'index'
map.connect '/index/category/:id', :controller=>'events', :action=>'showallcategoryposts'
# Admin
map.connect '/admin/', :controller=>'events', :action=>'adminindex'
map.connect '/admin/events/new', :controller=>'events', :action=>'new'
map.connect '/admin/category/', :controller=>'subcategories', :action=>'index'
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'
I'm using Instant Rails 2.0
how i can correct my code in order to work?
Thank you
Apart from whatever problem you have... IMHO, the code looks quite compromising.
Following are the things, which shouldn't be done the way they are currently:
Building the URLs manually like href="/index/category/<%=h subcategory.id %>" But I can't say much on it to correct it without knowing which Rails version you are on and without seeing your routes.rb
Controller Code is incorrect
Model code also needs improvements
Models should look like this:
class Subcategory < ActiveRecord::Base
belongs_to :category
has_many :events, :order => "created_at desc"
end
class Event < ActiveRecord::Base
belongs_to :subcategory
end
class Category < ActiveRecord::Base
has_many :subcategories, :order => "name"
end
EventsController should be like this:
def index
#categories = Category.all(:order => "name", :include => {:subcategories => [:events]})
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #categories }
end
end
And your view should look like:
<% unless #categories.empty? %>
<% #categories.each do |category| %>
<li>
<%=h category.name %>
<ul>
<% category.subcategories.each do |subcategory| %>
<li>
<%=h subcategory.name %>
</li>
<% end %>
</ul>
</li>
<% end %>
<% else %>
Sorry, no events found!
<% end %>
I hope this code will fix your bugs as well.
Please try it.
NOTE: More improvements required in views, depending on your Rails version and your routes.rb ... you should use the path/url methods like category_path(category) etc.
You didn't post your model code. I wonder if you're taking advantage of ActiveRecord associations. If you were, you wouldn't need anything more than #events. You could do stuff like:
<% #events.each do |event| %><br/>
<% event.categories.each do |category| %>
stuff
<% end %>
<% end %>
Check out this guide.
I am pretty sure EventsController#index is rendering views/events/index.html.erb, and layouts/events.html.erb knows nothing of #categories which is set for use in said rendering. Where do you render layouts/events.html.erb? Try placing this below your class definition:
layout 'events'
Also, it is worth checking out what #categories is within the controller with a Rails.logger.debug. Is it nil there as well?

Resources