will_paginate problem with action and fragment caching - ruby-on-rails

i have added action caching to my index part and listing 9 records in a page
will_paginate dosent work, and renders same set of recors again and again.
and same issue with fragment caching
please suggest some solution for this..
thanks

try this: Rails action caching with querystring parameters

Unless you have a very basic Rails app, I would recommend using fragment caching as it's a much easier to use and maintain compared to action/page caching. If you decide to use fragment caching, you could add something like the following to your index template:
<% #models.each do |model| %>
<% cache ["v1", model] do %>
<%= render model %>
<% end %>
<% end %>
This will render the partial _model.html.erb and cache the result. For details about the cache mechanisms in Rails, I suggest reading Rails Guides.
A more aggressive caching strategy would be to cache all the models on a single page. That could be done by setting the current page to an instance variable in the controller:
def index
#page = params[:page]
#models = ...
end
Now in your template you can include the page in the composite cache key:
<% cache ["v1", cache_key_for(#models), #page] do %>
<% #models.each do |model| %>
<%= render model %>
<% end %>
<% end %>
<% end %>
cache_key_for is a helper that computes a cache key for a set of models. It could be defined as:
def cache_key_for(models)
"#{models.count}-{models.map(&:updated_at).max.utc.to_s(:number)}"
end

Related

Rails 6: Render a partial if only I'm using a method from my controller

I have a Users controller, with index, show, edit and destroy methods. In my layouts/ folder, I have a general-purpose user.html.erb layout that renders some partials. These partials are of course producing errors due some of the info isn't available, like #user.name, for example. I've tried to render that partial always when I'm in a def show state, something like:
<% if Users.show %>
<% render "shared/asides/users" %>
<% else %>
Other partials
<% end %>
I've tried several ways and I always get errors. I feel totally lost even trying to find out this on the Rails documentation nothing seems to be indicated there too.
Your problem is, as you say, you're trying to display things associated with a user, like #user.name, but there is no #user.
So why not check for #user before showing the partial? Or if you have a collection of users, I'm guessing #users?
<% if #users %>
<%= render "shared/asides/users" %>
<% else %>
<%= Do something else %>
<% end %>
Of maybe a bit neater:
<%= render (#users ? path/to/partial_a.html.erb : path/to/partial_b.html.erb) %>
You can make a special layout for your action. Then, at the end of action add layout to render.
def show
...
render layout: "custom_layout"
end

rails4 caching naming conventions

I have a rails 4 app. I have to differentiate the different cache keys somehow but don't know the naming conventions.
FIRST EXAMPLE:
I have a task model with index, completed_tasks and incoming_tasks actions. A have the same instance name (#tasks) though because of the pagination.
At the moment the cache-keys are named like below. My questions: 1. Is the cache-key structure good enough? 2. Is it important in which order I put the parts of the key in the array? For example [#tasks.map(&:id), #tasks.map(&:updated_at).max, 'completed-tasks'] is better than ['completed-tasks', #tasks.map(&:id), #tasks.map(&:updated_at).max]?
completed_tasks.html.erb
<% cache([#tasks.map(&:id), #tasks.map(&:updated_at).max, 'completed-tasks']) do %>
<%= render #tasks %>
<% end %>
tasks.html.erb
<% cache([#tasks.map(&:id), #tasks.map(&:updated_at).max]) do %>
<%= render #tasks %>
<% end %>
incoming_tasks.html.erb
<% cache([#tasks.map(&:id), #tasks.map(&:updated_at).max, 'incoming-tasks']) do %>
<%= render #tasks %>
<% end %>
SECOND EXAMPLE:
I also have problem with the naming conventions of the russian-doll-caching:
products/index.html.erb
<% cache([#products.map(&:id), #products.map(&:updated_at).max]) do %>
<%= render #products %>
<% end %>
_product.html.erb
<% cache(product) do %>
<%= product.name %>
....
<% end %>
Is this version good enough or I always should put some string in both the outer and inner caching key array to avoid problems with similarly named cache-keys on other pages. For instance I plan to put <% cache(#product) do %> on the profile#show page which would be exactly the same like the inner caching in my example. If the key has to be different what the rails convention is to name the inner an outer cache keys?
First, according to Russian Doll Caching article, I think it's not necessary to set the cache_key on your own, you could just leave it to rails, it generates cache_key auto. For example, the cache_key of #tasks = Task.incoming should differ from #tasks = Task.completed with something like views/task/1-20160330214154/task/2-20160330214154/d5f56b3fdb0dbaf184cc7ff72208195e and views/task/3-20160330214154/task/4-20160330214154/84cc7ff72208195ed5f56b3fdb0dbaf1
cache [#tasks, 'incoming_tasks'] do
...
end
Second, As for the namespace, though the template digest will be the same but the #tasks digest will be different. So it seems to be okay without namespace in this case.
cache #tasks do
...
end
Third, when it comes to namespace, I prefer prefix rather than suffix. i.e.
cache ['incoming_tasks', #tasks] do
...
end
As the second example, I think this would do just fine.
<% cache #product do # first layer cache %>
<% cache #products do # second layer cache %>
<%= render #products %>
<% end %>
<% cache #product do # second layer cache %>
<%= product.name %>
....
<% end %>
<% end %>
The caching key for app/views/products/show.html.erb will be something like views/product/123-20160310191209/707c67b2d9fb66ab41d93cb120b61f46. That last bit is a MD5 of the template file itself and all of its dependencies. It'll change if you change either the template or any of the dependencies, and thus allow the cache to expire automatically.
For further reading: https://github.com/rails/cache_digests
It's best practice to always put a string at the end. It really just needs to be something that makes sense to you.

Correct way of rails caching complex query

I am new to caching and I'm not sure what my best course of action is.
I want to cache a part of my view that relies on a complex query. The query looks something like:
#sessions_next_week = group_by_wday(LittleClassSession.location_only([1,2]).age_range_only(age_from, age_to).supports_dropins_only(support).approved_users_only.next_week)
Above you'll see a number of scopes and methods called. The view renders an instance variable named #sessions_next_week like so:
<% #sessions_next_week.each do |wday, lcs| %>
<h3><%= wday %></h3>
<%= render partial: 'table_head' %>
<% lcs.each do |s| %>
<%= render partial: 'table_row', :locals => {:s => s, :show_day => true} %>
<% end %>
<%= render partial: 'table_foot' %>
<% end %>
As you can see, #sessions_next_week is iterated through, and its children are iterated through. Given this, and given the nature of the query results in the instance variable, I'm not sure where to implement the caching. In the model? In the view?
So my questions are:
Do I need model caching or can I do this in the view?
What's the correct implementation?
The solution is to simply add two character:
#sessions_next_week ||= group_by_wday(LittleClassSession.location_only([1,2]).age_range_only(age_from, age_to).supports_dropins_only(support).approved_users_only.next_week)
This is called memoization, and you can look it up. Here's one source: http://www.justinweiss.com/articles/4-simple-memoization-patterns-in-ruby-and-one-gem/

Cleaning up view ruby logic and separating concerns into model/controller

I want to display a random assortment of 6 tools from my database on my home page. I have created a Pages controller with a home action.
This is my Pages controller:
class PagesController < ApplicationController
def home
#tools = Tool.all
end
end
Then in my home.html.erb view I use the .sample method to grab random tools from my database as such(I repeat this 6 times using tool1, tool2, tool3, etc variables for each):
<% tool1 = #tools.sample %>
<%= image_tag tool1.tool_image.url(:medium) %>
<%= tool1.name %>
<%= tool1.description %>
I am wondering if there is a better way to do this. It seems that I have logic in my view and there must be a way to move that logic somewhere else? My model, controller, etc. How would one go about cleaning this code up so that it's good rails code? Or maybe this is good rails code and I just don't know it since I am a beginner.
Your controller doesn't need to extract everything from the tools_table, so I'd first remove the .all. Your example makes it seem like you just need 6 random objects from the database, here's one way to do that:
class PagesController < ApplicationController
def home
#tools = Tool.order("RANDOM()").first(6)
end
end
Then in your view you can just loop through those:
<% #tools.each do |tool| %>
<%= image_tag tool.tool_image.url(:medium) %>
<%= tool.name %>
<%= tool.description %>
<% end %>
In addition to Anthony's answer.
To clear up the view with some rails magic you can also add a partial to your app/views/tools called:
_tool.html.erb
Looking like:
<%= image_tag tool.tool_image.url(:medium) %>
<%= tool.name %>
<%= tool.description %>
And then change your view to
<%= render #tools %>
And Rails will know what to do if #tools is a collection of tools 😄

When doing fragment caching, should we cache active record queries as well?

If one is fragment caching a part of the page that has content loaded from models in the controller. Should these queries also be cached?
Does this mean there will be two types of caching: fragment and active support caching for two different types of data?
For example. In the view I could have:
<% cache 'videos_and_photos', :expires_in => 24.hours do %>
<div id="videos">
<% #videos.each do |video| %>
...
<% end %>
</div>
<div id="photos">
<% #photos.each do |photo| %>
...
<% end %>
</div>
<% end %>
and in the controller:
Rails.cache.fetch('videos', :expires_in => 24.hours) do
#videos = Video.where(...)
end
Rails.cache.fetch('photos', :expires_in => 24.hours) do
#photos = Photo.where(...)
end
My only grip with this is, if one cache expires first, then the data will display inconsistently. Is there a better way to go about this?
Normally one should not use business logic or queries in views, but in this case it is possible to make an exception. Just define a special method for your query, for instance Video.your_method, and use it in the view. This seems to be the cleanest way to do it:
<% cache 'videos_and_photos', :expires_in => 24.hours do %>
<div id="videos">
<% Video.your_method.each do |video| %>
...
<% end %>
</div>
Otherwise you are caching data which belongs together in two different places, which may lead to unpredictable results.
I have created a gem to avoid having to query the database if a cache exists.
https://github.com/rovermicrover/FlagpoleSitta
It stores all the database calls in Procs, and then only calls them if the cache doesn't exist. It then invalidates all related caches when an object gets updated. I got the idea from the following stackoverflow post.
Best way to combine fragment and object caching for memcached and Rails
While my gem might be over kill, and I still considered it in beta, the over all strat from the above stack overflow post should help.

Resources