简体   繁体   中英

Cleaning up bloated views

I am fairly new to rails, so my tendency is to bloat the views. rack-mini-profiler shows 2/3 of my load time and 1/2 my sql calls from rendering my views. I'm looking for some advice on how to clean this up, as well as some good resources for coding apps more efficiently.

I have Users with Trips(:city_id, :start_date, :end_date) these Trips belongs_to a City which belongs_to a Country.

controller

# this grabs topics that are concidered genral
@feed = Topic.where(:city_id => current_user.trips.pluck(:city_id), :general_topic => true).uniq
# topics that are specific to a users trip time range
current_user.trips.each do |trip|
    @feed += Topic.where("(general_topic = ? AND city_id = ?) AND ((start_date BETWEEN ? AND ? OR end_date BETWEEN ? AND ?) OR (start_date <= ? AND end_date >= ?))", false, trip.city_id, trip.start_date, trip.end_date, trip.start_date, trip.end_date, trip.start_date, trip.end_date).uniq
end

view

<% @feed.each do |topic| %>
<% if topic.general_topic %>
    <div>
        <div>
            <h3>
                <%= link_to(topic) do %>    
                    <%= topic.title %>  
                <% end %>
            </h3>
        </div>
        <div>
        <p><%= time_ago_in_words(topic.created_at) %> by <%= link_to topic.user.name, "#" %> about <%= topic.city.name %>, <%= topic.city.country.name %></p>
        </div>
        <div>
            <p><%= topic.comments.size %> comments 
                <% if topic.comments.any? %>
                (<%= time_ago_in_words(topic.comments.last.updated_at) %> ago)
                <% end %></p>
        </div>      
    </div>      
<% else %>
    <div>
        <div>
            <h3>
                <%= link_to(topic) do %>    
                    <%= topic.title %>  
                <% end %>
            </h3>
        </div>
        <div>
        <p><%= time_ago_in_words(topic.created_at) %> by <%= link_to topic.user.name, "#" %> in <%= topic.city.name %>, <%= topic.city.country.name %> <%= topic.start_date.strftime("%m/%d") %>-<%= topic.end_date.strftime("%m/%d") %></p>
        </div>
        <div>
            <p><%= topic.comments.size %> comments 
                <% if topic.comments.any? %>
                (<%= time_ago_in_words(topic.comments.last.updated_at) %> ago)
                <% end %></p>
        </div>  
    </div>      
<% end %>       
<% end %>
</div> <!-- end discussions -->

I know this is pretty horrific. But what I want the topic to look something like this:

Topic.Title
topic.created_at "by" topic.user.name "about" topic.city.name "," topic.city.country.name
topic.comments.count (topic.comment.last.created_at)

What should I start looking into in order to get some logic into the controller or model? currently my Topic model is just:

class Topic < ActiveRecord::Base
belongs_to :user
belongs_to :trip
belongs_to :city
has_many :comments, :class_name => 'Comment', :foreign_key => :topic_id
default_scope { order("created_at DESC")}

There's a few things you can do, for speed and readability.

For speed, eager-load your data in the controller, using includes . Eg, when you load the topics you can load the associated comments too:

@feed = Topic.where(:city_id => current_user.trips.pluck(:city_id), :general_topic => true).includes(:comments)

see Rails 4: How to use includes() with where() to retrieve associated objects

Generally, you should minimise the amount of database-calling you do in your views: when it's done in the controller, it's all done in one place and is easy to read and maintain.

For readability, you can DRY ("don't repeat yourself") up your code. Look at the contents of the if and else blocks: they are almost identical: you show an extra line if topic isn't .general_topic. Why repeat so much identical stuff? You could rewrite it thus:

<% @feed.each do |topic| %>
  <div>
    <div>
      <h3>
        <%= link_to(topic) do %>    
          <%= topic.title %>  
        <% end %>
      </h3>
    </div>
    <div>
      <p>
        <%= time_ago_in_words(topic.created_at) %> by <%= link_to topic.user.name, "#" %> about <%= topic.city.name %>, <%= topic.city.country.name %>
        <% unless topic.general_topic %>
          <%= topic.start_date.strftime("%m/%d") %>-<%= topic.end_date.strftime("%m/%d") %>
        <% end %>        
      </p>
    </div>
    <div>
      <p><%= topic.comments.size %> comments 
        <% if last = topic.comments.last %>
          (<%= time_ago_in_words(last.updated_at) %> ago)
        <% end %>
      </p>
    </div>      
  </div>  
<% end %>    

Note that i removed topic.comments.any? and replaced it with some code to get the last one, and test if it exists. This is syntactic sugar (once the eager loading is happening) but reads more clearly in my opinion.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM