Running into undefined method `total_pages' with will_paginate - ruby-on-rails

My goal is to have a tag cloud within my posts page which will allow the user to filter posts by clicking on a tag. However, I am running into an undefined method 'total_pages" error after making the following changes to my Post_Controller method:
class PostsController < ApplicationController
def index
if params[:tag]
#posts = Post.tagged_with(params[:tag])
else
#posts = Post.visible_to(current_user).where("posts.created_at > ?", 7.days.ago).paginate(page: params[:page], per_page: 10)
end
end
end
I am trying to use the acts-as-taggable-on gem, and this logic will show me the posts with the appropriate tags.
The issue happens in the posts/index.html.erb view:
<div class="row">
<div class="col-md-8">
<h1> Trending </h1>
<p class="lead"> Active posts this week </p>
<div id="tag_cloud">
Tag Cloud:
<% tag_cloud Post.tag_counts, %w[s m l] do |tag, css_class| %>
<%= link_to tag.name, tag_path(tag.name), class: css_class %>
<% end %>
</div>
<%= render partial: 'posts/post', collection: #posts %>
<%= will_paginate #posts %>
</div>
<div class="col-md-4">
</div>
</div>
The will_paginate line will not render all the posts on that page. The work around is getting rid of <%= will_paginate #posts %> and replacing
#posts = Post.visible_to(current_user).where("posts.created_at > ?", 7.days.ago).paginate(page: params[:page], per_page: 10)
with #posts = Post.all. However, this gives me an entire page of posts, which is ugly. Does anyone know why I am running into an undefined method 'total_pages' error?

It looks like when you're sending a tag (params[:tag]) it is fetching posts with
#posts = Post.tagged_with(params[:tag])
which is lacking the will paginate call. I believe you could get it working by adding the will paginate scope, like this:
#posts = Post.tagged_with(params[:tag]).paginate(...)

Related

Will_paginate for includes comments

I'm discovering the gem will_paginate which is great ! But I'm facing a problem of using. I'm building a group>post>comments app, so in my group show page i'm displaying posts and their comments. To limit the numbers of queries, i'm using includes method like this :
Group_controller :
def show
#posts = #group.posts.order(upd_at: :desc).includes(:user).includes(comments: :user).paginate(page: params[:page], per_page: 10)
end
So I would like to also paginate my comments. Do you know a way to do that ?
My code :
Group_show =
<h1>Groupe <%= #group.name %></h1>
<div class="post_list<%=#group.id%>">
<%= render #posts %>
</div>
<%= will_paginate #posts, renderer: BootstrapPagination::Rails %>
And my posts/_post =
<% #comments = post.comments %>
<ul id="comment_list<%=post.id%>">
<%- if #comments.any? %>
<%= render #comments, post: post %>
<%= will_paginate #comments, renderer: BootstrapPagination::Rails %>
<% end %>
</ul>
By the way if you have a method to define #comments directly in the Groups_controller(show), it can be really useful ;)
Not 100% tested, but I think this should work. Do you know how all these components work? If not, let me know and I can explain.
posts/_post
<% #comments = post.comments.order(created_at: :desc).limit(3) %>
<ul id="comment_list<%=post.id%>">
<%- if #comments.any? %>
<%= render #comments, post: post %>
<%- if post.comments.offset(3).exists? # this is more efficient than count > 3 bc it quits counting after 3 %>
<!-- the below link_to creates: href="/posts/:id/comments" ... -->
<!-- ... and `remote: true` makes that an ajax request -->
<li><%= link_to "more", comments_post_path(post), class: "more-comments-btn", remote: true %></li>
<% end %>
<% end %>
</ul>
config/routes.rb
resources :posts do
# `member do` is explained here: http://guides.rubyonrails.org/routing.html#adding-more-restful-actions
member do
get :comments
end
end
posts_controller.rb
# GET /posts/:id/comments
def comments
#post = Post.find(params[:id])
#comments = #post.comments.order(created_at: :desc)
# since you requested this url via ajax with `remote: true` rails will automatically render `posts/comments.js.erb` ...
# ... rather than a typical html request where rails would automatically render `posts/comments.html.erb`
end
views/posts/comments.js.erb
// some people like to use render #comments as shorthand like you did above. I'm a fan of being more explicit like the below
$("#comment_list<%= #post.id %>").html("<%= escape_javascript(render partial: 'comments/comments', locals: {comments: #comments, post: #post}) %>");
// now remove the more comments button
$("#comment_list<%= #post.id %>").find(".more-comments-btn").remove();
The documentation here explains the use of remote: true for ajax requests. Scroll down to section "3.1.2 link_to" and then section 5.1 for the controller and js.erb view.

How make caching work with pagination and search form?

I working with ruby on rails and my products views is cached and I can find products with search form. I want to use pagination as well, but when added, it didn't worked and also search form. Found code line ,but not sure how to use it, code below.
<% cache ["v1-#{params[:page]-#{params[:q]", cache_key_for_products]
> do %>
My code
Index.html.erb
<div class="products">
<div class="container">
<center><h1>All products</h1></center>
<div class="row">
<% cache(cache_key_for_products) do %>
<%= render #products %>
<% end %>
</div>
<%= will_paginate #comments, :container => false %>
</div>
</div>
_product.html.erb
<% cache product do %>
<div class="col-md-3 col-lg-3 col-sm-6 col-xs-12">
<div class="product-block">
<h4 id="product-name"><%= product.name %></h4>
<%= link_to product_path(product), class: 'product_link' do %>
<%= image_tag(product.image_url, class: "img-responsive") %>
<% end %>
<div class="price">
<h4>£ <%= product.price %></h4>
</div>
</div>
</div>
<% end %>
products_helper.rb
module ProductsHelper
def cache_key_for_products
count = Product.count
max_updated_at = Product.maximum(:updated_at).try(:utc).try(:to_s, :number)
"products/#{params[:q] || "all"}-#{count}-#{max_updated_at}#{signed_in? && current_user.admin? ? "-admin" : "normal"}"
end
end
products_controller.rb
def index
if params[:q].present?
search_term = params[:q]
if Rails.env.development?
#products = Product.where("name LIKE ?", "%#{search_term}%")
else
#products = Product.where("name ilike ?", "%#{search_term}%")
end
else
#products = Product.all.paginate(:page => params[:page], :per_page => 30)
end
end
In your controller, looks like you are paginating correctly when not performing a search, but need to add the pagination to your search query also:
def index
if params[:q].present?
search_term = params[:q]
if Rails.env.development?
#products = Product.where("name LIKE ?", "%#{search_term}%").paginate(:page => params[:page], :per_page => 30)
else
#products = Product.where("name ilike ?", "%#{search_term}%").paginate(:page => params[:page], :per_page => 30)
end
else
#products = Product.all.paginate(:page => params[:page], :per_page => 30)
end
end
Also you are caching each search result set, this means that the same products could be potentially cached multiple times in many different searches. This will quickly bloat your cache. It would be better to cache each product once and fetch these products from cache regardless of the search.
I see you are caching each product (in _product.html.erb partial). In index.html.erb change the code to this:
<div class="products">
<div class="container">
<center><h1>All products</h1></center>
<div class="row">
<%= render #products, cache: true %>
</div>
<%= will_paginate #comments, :container => false %>
</div>
</div>
This will take advantage of multi fetch fragment caching which Rails 5 has built in:
1.3.1 Collection caching
The render helper can also cache individual templates rendered for a
collection. It can even one up the previous example with each by
reading all cache templates at once instead of one by one. This is
done by passing cached: true when rendering the collection:
<%= render partial: 'products/product', collection: #products, cached:
true %> All cached templates from previous renders will be fetched at
once with much greater speed. Additionally, the templates that haven't
yet been cached will be written to cache and multi fetched on the next
render.
Otherwise if you are < Rails 5, use the Multi Fetch Fragments gem to enable this functionality.
In index.html.erb you could modify the collection cache rendering to something this to use a custom cache key:
<%= render #products, cache: Proc.new{|item| [item, 'show']} %>

Solr - undefined method `results' for Ransack

I'm working on a project on Ruby on rails and wanted to added a search feature. I installed the solr gem following the steps from this site:
http://es.asciicasts.com/episodes/278-busquedas-con-sunspot
right now, this is how my controller looks like:
class DealsController < ApplicationController
def index
# #deals = Deal.paginate(page: params[:page])
#search = Deal.search do
fulltext params[:search]
end
#deals = #search.results
end
private
def deal_params
params.require(:deal).permit(:title)
end
end
this is my model:
class Deal < ActiveRecord::Base
searchable do
text :title, :info1, :page
end
end
and this is my view:
<div class='container'>
<div class='row upper_container'>
<div class='search_container'>
<%= form_tag deals_path, :method => :get, :class => 'navbar-form navbar-left' do %>
<div class='form-group'>
<%= text_field_tag :search, params[:search], class: 'form-control' %>
</div>
<%= submit_tag 'Search', :name => nil %>
<% end %>
</div>
</div>
<% #deals.each_with_index do |d, i| %>
<% if i % 3 == 0 %>
<div class='row middle_container'>
<% end %>
<div class='col-md-4'>
<div class='deal_container'>
<%= d.title %>
<img src='<%= d.photo %>'>
</div>
</div>
<% if (i % 3 == 2) || (i == (#deals.length - 1)) %>
</div>
<% end %>
<% end %>
<div class='text-center'>
<%= will_paginate #deals %>
</div>
</div>
But i get the following error:
undefined method `results' for Ransack::Search>:Ransack::Search
on this line: #deals = #search.results
Any help? thanks!
Jude, change your
#deals = #search.results
to
#deals = #search.result
Are you by chance using the sunspot gem?
If you are using Active Admin and the Sunspot gem for Rails, beware:
they conflict on the search method leading to some very confusing
results.
Active Admin has a dependency on meta_search which provides a
.search() method on Active Record classes. Sunspot attempts to provide
the same method, aliased from solr_search, but only if the method does
not already exist.
In short, searching can be done using solr_search() rather than
search():
#search = Profile.solr_search do
keywords params[:q]
paginate page: params[:page], per_page: page_size
end
#results = #search.results
http://mrdanadams.com/2012/beware-using-active_admin-and-sunspot-rails-gems-together/

will_paginate gem rendering each entry 4 times rails

I am using will_paginate with rails 4 and have the following code in my users controller:
def index
#users = User.paginate(page: params[:page], :per_page => 10)
end
and in my view:
<% provide(:title, 'All users') %>
<h1>All Users</h1>
<%= will_paginate %>
<ul class="users">
<% #users.each do |user| %>
<%= render #users %>
<% end %>
</ul>
<%= will_paginate %>
This renders all the users however there are 40 per page, not 10. Each page has the correct users but 4 times. For example it renders users 1 .. 10 then 1 .. 10 again and so on. There are currently 101 users and if I set the per page limit to 1 it renders 101 pages with one user on each page as it should however any limit > 1 and it breaks.
Any insight on how to fix it so only 10 users appear on each page would be much appreciated.
I found your mistake. You need to render #users once. Hope it works on your side. :)
<ul class="users">
<%= render #users %>
</ul>
also you need to render pagination with following code
<%= will_paginate #users %>
You need to reference the block variable user within the render call, not #users
<ul class="users">
<% #users.each do |user| %>
<%= render user %>
<% end %>
</ul>

Rails: Rendering with Pagination Issues of a Comment Model

The current page that I have been working on is a page where a user can create a comment under a specific micropost. All the microposts are shown on the same page and those microposts are paginated. Under each specific micropost, there are comments and those comments should be paginated and the issue I am currently having is that the pagination only happens when the HTML code is this:
HTML
<div id='comments'>
<%=render #comments %>
<%= will_paginate #comments, :class =>"pagination" %>
</div>
But the thing is when it is like that only the comments for the first micropost will show up on every single micropost rather than the specific micropost comments. So I figured why not put the code like this:
HTML
<div id='comments'>
<%=render micropost.comments %>
<%= will_paginate #comments, :class =>"pagination" %>
</div>
This basically set all the specific comments in the right micropost BUT it didn't paginate but the pagination links showed, so I figured putting this would work but it didn't, got an error:
HTML
<div id='comments'>
<%=render micropost.comments %>
<%= will_paginate micropost.comments, :class =>"pagination" %>
</div>
I am very confused and unsure what to do and yes the micropost is suppose to have no #
I would appreicate any suggestions that would help! Thank you!
My User#Show
User Controller
class UsersController < ApplicationController
def show
#user = User.find_by_id(params[:id])
#school = School.find(params[:id])
#micropost = Micropost.new
#comment = Comment.new
#comment = #micropost.comments.build(params[:comment])
#comments = #micropost.comments.paginate(:page => params[:page], :per_page => 10)
#microposts = #user.microposts.paginate(:per_page => 10, :page => params[:page])
end
end
This will work, but the fact that you need to do an assignment inside your view tells you this is probably not the way to do it:
<div id='comments'>
<% comments = micropost.comments.paginate(:per_page => 10, :page => params[:page]) %>
<%= render comments %>
<%= will_paginate comments, :class =>"pagination" %>
</div>
Do realize that the micropost & comments both use the same page parameter. So that if you want to view the second page of comments of any of the given microposts you will also switch to the second page of microposts. I would reconsider showing all this information on the same page.

Resources