Poor Performance in Rails App - ruby-on-rails

Looking for ways to improve the particularly bad performance I'm getting from my rails application. Here's the code from the page in question:
notifications_controller.rb
class NotificationsController < ApplicationController
def index
#questions = Question.all.order(:updated_at => :desc)
#users = User.all
#answers = Answer.all.order(:updated_at => :desc)
end
end
and here's the corresponding view. I know it's ugly but it's working.
<div>
<% if current_user %>
<div class="notifications-added col-md-8">
<h4 class="col-md-offset-2">Approvals & Answers</h4>
<span class="text-center">
<% current_user.questions.order(id: :desc).each do |question| %>
<% if question.approved == true %>
Your question, <%= link_to "#{question.title}", question_path(question) %>, has been <span class="notifications">approved.</span><br>
<% end %>
<% question.answers.each do |answer| %>
<%= answer.user.name %> <span class="notifications">added an answer</span> to your question, <%= link_to "#{question.title}", question_path(question) %>.<br>
<% end %>
<% end %>
</span>
</div>
<div class="notifications-voted col-md-4">
<h4 class="text-center">Votes</h4><span></span>
<% current_user.answers.order(updated_at: :desc).each do |answer| %>
<% #users.each do |user| %>
<% if user.voted_up_on? answer %>
<%= user.name %> <span class="notifications">upvoted</span> your answer to <%= link_to "#{Question.find(answer.question_id).title}", question_path(answer.question_id) %>.<br>
<% elsif user.voted_down_on? answer %>
<%= user.name %> <span class="notifications">downvoted</span> your answer to <%= link_to "#{Question.find(answer.question_id).title}", question_path(answer.question_id) %>. <br>
<% end %>
<% end %>
<% end %>
</div>
<% end %>
</div>
I think I'm just sorting too much. The page is taking a long time to load. What's the low hanging fruit for improving my performance? Any thoughts? Thanks in advance.

The first , you should move logic code in your view into model.
The second , use pluck method instead array active record objects. Array string are lightweight than array of active record. Right ?
The third , use slim template engine instead erb template engine.
The fourth , cache db.
The fiveth, use google PageSpeed plugin for google chrome to analytic what's slow.

Peter answer is correct, and I will add two things :
Eager loading
Doing this
current_user.questions.each do |question|
question.answers.each do |answer|
...
end
end
will generate a query for each question. Rails will load all the questions, then for each question load its associated answers (1 query + 1 query for any question).
If you replace the first line by
current_user.questions.include(:answer).each do |question|
Rails will load all the questions, then all the associated answers (2 queries).
Look at the log
Every information for any bad performance should be visible on logs in development mode. For example, if voted_up need to load any other models than answer, your query number will be too big.

Related

Strange output from rails each do

Rails each do method is acting strangely and I do not know why.
controller
def index
#fabric_guides = FabricGuide.with_attached_image.all.order(:name)
end
index.html.erb
<div class="guide-items">
<%= #fabric_guides.each do |fabric| %>
<div class="guide-container">
<%= link_to fabric_guide_path(slug: fabric.slug) do %>
<%= image_tag fabric.image if fabric.image.attached? %>
<% end %>
<div class="guide-info">
<p class="g-name">
<%= link_to fabric.name,
fabric_guide_path(slug: fabric.slug) %>
</p>
</div>
</div>
<% end %>
</div>
I have two FabricGuide records so I expect two "guide-container" but I get three. Or more precisely I get two guide containers and a third block of text containing all the content from the last FabricGuide record.
I have almost an identical setup for articles and have never encountered this problem. I'd happily share more information if needed. Thank you!
Please remove = equal sign from your each loop of view code
like below :-
<% #fabric_guides.each do |fabric| %>
...
...
<% end %>
you have used this <%= #fabric_guides.each do |fabric| %> in your view that's why it shows all record in DOM.
The expression for erb tags is <% %>
now if we want to print that tag too then we apply <%= %>

(Ruby on Rails) Displaying Posts Into Groups of 3

I'm using the following code to display posts to my users.
_feed.html.erb partial:
<% #posts_by_month.each do |monthname, posts| %>
<%= monthname %>
<ul>
<% posts.each do |post| %>
<li><%= post.created_at %></li>
<% end %>
</ul>
<% end %>
Controller:
def home
if logged_in?
#post = current_user.posts.build
#posts_by_month = current_user.feed.group_by { |post| post.created_at.strftime("%B") }
This renders my posts as follows:
Post 1
Post 2
Post 3
Post 4
I want to change it so that the posts are displayed like:
Post 1 Post 2 Post 3
Post 4 etc etc
etc
I've tried several approaches to this, including the in_groups_of(3) method however the way it is currently setup means nothing works. I feel like there is an obvious solution I'm missing - can anyone help?
[Edit to expand on the in_groups_of(3) error]
If I change line 4 in the _feed partial to:
<% posts.in_groups_of(3, false).each do |post| %>
It gives the error: undefined method `created_at' for #< Array:0xbb8f258 >
The #in_groups_of method returns an Array of Arrays each containing 3 Post objects.
So you now also need to iterate over the returned array that contains your three Posts, something like:
<% posts.in_groups_of(3, false).each do |post_group| %>
<% post_group.each do |post| %>
<li><%= post.created_at %></li>
<% end %>
<% end %>
You can use facets gem. This provides and each_by method. You can use each_by to create groups and iterate further on these groups.
Here is code snippet on how to use each_by
<div class = "small-9 columns vertical-border-left">
<%- #client.contact_details.each_by(3) do |contact_details| %>
<div class="row">
<%- contact_details.each do |contact| %>
<div class="small-3 columns small">
<div> <%= contact.contact_detail_type %> contact </div>
<div> <%= contact.contact_email %> </div>
<div> <%= contact.contact_phone %> </div>
</div>
<% end %>
</div>
<% end %>
</div>

Ruby on Rails how to get last query?

I'm having trouble trying to figure out when I reached the end of my query. So what I want to do is list all the records in my database that begin with the letter A which I got however I want to output a message if the query turns out blank. When I try I get a bunch of my custom messages even the query didn't turn out blank. Is there any way to tell if I've reached EOF in ruby on rails?
Sample
<div id = "content-A">
<p>A</p>
<% #animes.each do |anime| %>
<% if anime.aname.starts_with?('A') %>
<%= link_to anime.aname, {:action => 'list'} %>
<% else %>
<p>No anime listed in this Category :( </p>
<%end%>
<%end %>
</div>
I believe you want sth like:
<% animes_group = #animes.group_by {|anime| anime.aname.to_s[0].upcase}
('A'..'Z').each do |letter| %>
<div id="content-<%= letter %>">
<p><%= letter %></p>
<% if animes = animes_group[letter] %>
<% animes.each do |anime| %>
<%= link_to anime.aname, {:action => 'list'} %>
<% end %>
<% else %>
<p>No anime listed in this Category :( </p>
<%end%>
<% end %>
You should consider moving some of the logic to the controller here, however what is to be moved depends on many factors like whether #animes are being used anywhere else etc.

How do you implement archive menu for blog system into rails3 app?

When you go to blog page, you will see the archive list on the menu.
In most cases, it shows something like this
'Archive'
2012(78)
-December(1)
-November(5)
-October(10)
...
2011(215)
2010(365)
I'm confident to make blog posting system by using scaffold.
But I have no idea how to make this Archive:(
Anyone come up with good idea to implement this to app easily???
Need your help!!
<h3>Archives </h3>
<% if #posts.to_a.empty? %>
<div class="post">
<p>No articles found...</p>
</div>
<% else %>
<% current_month = 0 %>
<% current_year = 0 %>
<% for article in #posts %>
<% if (article.created_at.year != current_year)
current_year = article.created_at.year
%>
<h3 class="archiveyear"><%= article.created_at.year%></h3>
<% end %>
<% if (article.created_at.month != current_month || article.created_at.year != current_year)
current_month = article.created_at.month
current_year = article.created_at.year
%>
<h4 class="archivemonth"><%= (Date::MONTHNAMES[article.created_at.month]) %></h4>
<% end %>
<div class="archivepost">
<%= link_to article.title, article_path(article), :remote => true %> on <%= article.created_at.strftime('%A')%> - <%= article.created_at.strftime('%d') + "th"%>
</div>
<% end -%>
<%end %>
This may help you. I ve not included the number of counts in this code. Actually m figuring how to do it. If u can let me know.
Also in the controller ive done this.
#posts = Article.order("created_at DESC")
The #posts is an array so the items inside it ll be ordered and then i can fetch the records according to it ordering.
Thanks.

For the first x in array?

I imagine this has a rather simple answer
<% for user in #users %>
<li>
<%= link_to user.username, user %>
</li>
<% end %>
Taking my simple example above, how would I apply a class to my <li> to the first three instances returned?
Secondly, how could I just have the the second two items have a different class from the first one? as in 1..2
Either you could count manually (which is kinda ugly):
<% i = 0 %>
<% for user in #users %>
<li class=<%= (i < 3 ? "foo" : "bar") %>>
<%= link_to user.username, user %>
</li>
<% i = i.next %>
<% end %>
or use each_with_index
<% #users.each_with_index do |user, i| %>
<li class=<%= (i < 3 ? "foo" : "bar") %>>
<%= link_to user.username, user %>
</li>
<% end %>
Once you get to more complex things than i < 3 (like your 1..2 issue) you should think about a helper_method (in helpers) like class_by_position(pos) so that you can write
<li class=<%= class_by_position(i) %>>
The :first-child pseudoselector might be a better way to go, but you'll need to have a counter variable that keeps track of the iterations to do it your way.
Your question is a little vague. I can't tell if you want to stop processing the array after the first x, or not.
If you're just looking to stop after the first x items, or just looking for the 2nd and 3rd items the solution is to use a slice.
Example: just the first 3:
#user[0,3].each do |user|
... # only executed for user = #user[0],user = #user[1] and user = #user[3]
end
Example: just the second and third:
#user[1,2].each do |user|
... #only only executed for user = #user[1] and user = #user[3]
end
And here's a more specific answer to your question using these new concepts and the content_tag to programatically decide the class of the list item. If you're going to be doing this often, makes a great candidate for a function.
<% first = true %>
<% #user[0,2].each do |user| %>
<% content_tag :li,
:class => first ? "class-for-first-item" : "class-for-2nd-&-3rd" do %>
<%= link_to user.username, user %>
<% end %>
<% first = false %>
<% end %>
<!-- Now to do the rest of them:-->
<% #user[3,-1].each do |user| %>
<% content_tag :li, :class => "class-for-rest" do %>
<%= link_to user.username, user %>
<% end %>
<% end %>

Resources