How make caching work with pagination and search form? - ruby-on-rails

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']} %>

Related

Rails 5 - Iterating over part of an array for the view

I currently use EasyAutoComplete for a search form. If you hit 'View All' it redirects to the same page but with params[:name] to show all cards.
I render this with:
<% #cards.in_groups_of(6, false).each do |group| %>
<div class='row'>
<% group.each do |card| %>
<div class='col-sm-2 col-md-2'>
<div class="wrapperImg">
<%= link_to image_tag(card.image_url, class: "img-responsive"), {:controller => "cards", :action => "show", :id => card.id }%>
</div>
</div>
<% end %>
</div>
</div>
<% end %>
However, if you look up a specific set of cards it's going to return a couple hundred (or more) of essentially the same card. I can identify these cards by a parameter(rarity)
I was originally going to try to modify it in the controller, but that is an issue because the 'def index' makes the EasyAutoComplete work
def index
wild_search = "%#{params[:name]}%"
#cards = Card.order(multiverse_id: :desc).limit(30)
# debugger
##cards = #cards.where("name like :name", name: wild_search).page(params[:page]) if params[:name]
#cards = #cards.where("name like :name OR setName like :name", name: wild_search).page(params[:page]) if params[:name]
end
Is there a way for me to do something like
cards = #cards.where('rarity IS NOT ?', 'Land') or something similar in the view, then modify my output from #cards.in_group_of to cards.in_group_of? Or is there a way to use the Controller to do this and use def search instead of def index?
Welcome any input.
Like this?
<% #cards.where.not(rarity: "Land").in_groups_of(6, false).each do |group| %>
http://api.rubyonrails.org/classes/ActiveRecord/QueryMethods/WhereChain.html

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/

Running into undefined method `total_pages' with will_paginate

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(...)

Pulling records from one table (products) depending on a value in another (department)

I have a Products table and a Departments table.
In products model:
has_one :department
In departments model:
belongs_to :products
I also have a view in my products controller that lists all departments as so:
<% #departments.each do |department| %>
<div class="column_entry">
<%= link_to image_tag(department.image_attachment), products_of_a_given_main_cat_url %>
</div>
<% end %>
Those load up fine. When I click on one of the images I want products_of_a_given_main_category view to dynamically populate with products from the department whose image I clicked.
In my products controller:
def products_of_a_given_main_cat
#products = Product.all
#department = Department.where(:name == :department)
respond_to do |format|
format.html # index.html.erb
format.json { render json: #products }
end
end
and in the view:
<div class="the_grid_prod">
<% #products.each do |product| %>
<% if product.department == #department.name %>
<div class="column_entry">
<%= link_to image_tag(product.product_image.url(:normal_page_size)), products_content_url(product.id) %>
</div>
<% end %>
<% end %>
</div>
When I click on the image for a department that I'm certain has products the page loads but there are no products. I'm certain I'm doing something very wrong but I'm not really sure what it is.
in routes file:
get 'products/products_of_a_given_main_cat' => :products_of_a_given_main_cat, :as => :products_of_a_given_main_cat
The problem is this:
#department = Department.where(:name == :department)
The :department is a symbol, not a string, and not a variable. Basically it is a number deep down, definietly not a name. Also, this will evaluate to Departments which have a name false. Don't do evaluation in the where part, only a hash.
You need the params[:department] if you get it from the request.
#department = Department.where(:name => params[:department])
Update:
Please modify your request link:
<%= link_to image_tag(department.image_attachment), products_of_a_given_main_cat_url(:department => department.name) %>
This will introduce a new parameter to the request, and it will work for you with the condition written above.
So to solve this problem I changed my associations as #TomL suggested in his comment. I also changed the following:
In products controller I deleted
#department = Department.where(:name => params[:department])
and changed
#products = Product.all
to
#products = Product.where(:department => params[:department])
The products_of_a_give_main_category_view now looks like this:
<div class="the_grid_prod">
<% #products.each do |product| %>
<div class="column_entry">
<%= link_to image_tag(product.product_image.url(:normal_page_size)), products_content_url(product.id) %>
</div>
<% end %>
</div>
Thanks to both #Matzi and #TomL for their help. It's greatly appreciated.

different action to index page. how to?

I have this code:
<% #cars.each do |car| %>
<div class="scroll-content-item ui-widget-header">
<%= link_to image_tag( car.profile_avatar.asset.url(:thumb) ), car %>
</div>
<% end %>
..which will display the number of cars that are defined in the index action:
def index
#search = Car.search(params[:search])
#cars = #search.all.paginate :page => params[:page], :per_page => 5
..so I'll have 5 images in the slider. I can't change it because I use it for the main <div>.
The question is, how do I make another statement in the controller and how do I make a reference to it in the view so that I can have more car pictures in the slider?
Thanks.
I got it.. I just add a new instance in controller like
#cars_footer = #search.all.paginate :page => params[:page], :per_page => 3
then I call it in the view with
<% #cars_footer.each do |car| %>
<div class="scroll-content-item ui-widget-header">
<%= link_to image_tag( car.profile_avatar.asset.url(:thumb) ), car %>
</div>
<% end %>
SO easy... well.. I am new in rails.

Resources