Ruby on Rails: get collection of attributes for a model - ruby-on-rails

I have a model with a lot of attributes, and have build a series of pages to collect all the relevant data. In the last page, I want to show the user all the collected data.
I could create this page by manually typing all the labels and values for each attribute, but I expect that this kind of tedious and repetitive task has already been solved by someone so that in 3-4 lines of code.
At this stage I am only prototyping so this doesn't need to look good.
Anyone has any suggestions as to how to quickly print on the screen all attributes of a model?
I was thinking something like this:
If #my_data_model is the instance variable of which I want to print the attributes, then:
<%= show_attributes #my_data_model %>
would output the attribute values with their labels.
Thanks in anticipation.

I am doing that for one of my projects like this:
First I define an array of the columns I don't want like the timestamp columns:
<% #rejects = ["id", "created_at", "updated_at" %>
Then from the object I remove those columns;
<% #columns = Patient.column_names.reject { |c| #rejects.include?(c) } %>
Then I iterate through the column_names and print out the entered information:
<h2>Is the following information correct?</h2>
<div class="checks">
<h3>Patient details</h3>
<% #columns.each_with_index do |c, i| %>
<p id="p<%= i %>" class="check">
<span class="title"><%= c %>:</span>
<span class="value"><%= #patient[i] %></span>
<span class="valid">
<img src="../../images/icons/tick.png" alt="green tick">
</span>
</p>
<% end %>
</div>
Hope this helps!

I have been using this as a generic show view for inheritated_resources gem.
%h2= resource_class.model_name.human
%table
- resource_class.column_names.each do |column_name|
%tr{ :class => (cycle "odd", "even") }
%td= resource_class.human_attribute_name(column_name)
- if resource[column_name].respond_to?(:strftime)
%td= l resource.send(column_name)
- else
%td= resource.send(column_name)
There resource_class returns the current model class and resource the current instance of it.

Thank you all,
I build a solution based on your recommendations like this:
<% #rejects = ["_id", "created_at", "updated_at"] %>
<% #columns = Agency.column_names - #rejects %>
<% #columns.each_with_index do |c, i| %>
<p id="p<%= i %>" class="check">
<span class="title"><%= c %>:</span>
<span class="value"><%= #agency.send(c) %></span>
</p>
<% end %>
Using <%= #patient[i] %> didn't work for me, probably because I am using Mongomapper as my ORM.

Related

Rails 4, how to ordering using custom method

I want to order the Conversation model, using a custom method.
I found some solution:
How do you order by a custom model method that has no attribute in SQL?
and
http://awaxman11.github.io/blog/2013/10/11/sorting-a-rails-resource-based-on-a-calculated-value/ ,
but Conversation order have priority.
First- answer_percent desc,
second- order to last_answer time
(using custom model method last_answered_to_i ).
last_answered_to_i method source:
def last_answered_to_i
if Conversation.where(company_id: self.id, is_answered: true).present?
last_conversation = Conversation.where(company_id: self.id, is_answered: true).first
if last_conversation.answered_at.blank? || last_conversation.asked_at.blank?
minutes = (Time.now- last_conversation.updated_at)/1.minutes
else
minutes = (last_conversation.answered_at - last_conversation.asked_at)/1.minutes
end
minutes.to_i
else
nil
end
end
after ordering I want add pagination using kaminari gem.
#lists = Company.searchable.order("answer_percent desc").page(params[:page]).per(20)
How do I order by column and custom method and add pagination?
I think the answer depends on what you want to see in the view because some of the problem could actually be solved in how you call #lists there. Also, some of the links you found make sorting by a model method sound more difficult than it is.
In your case, you can sort your conversations by a custom method like so:
Conversation.all.sort_by(&:custom_method)
Or specifically:
Conversation.all.sort_by(&:last_answered_to_i)
Specifically, you cannot use SQL to sort or order by something not in the actual database, so you use the Ruby sort_by method. For more info on the ampersand, see this post.
For your actual view, I'm not sure really how you want to organize it. I recently did something where I needed to group my resource by another resource called "categories", and then sort the original resource by "netvotes" which was a custom model method, then order by name. I did it by:
Ordering by name in the controller: #resources = Resource.order(:name)
Grouping by category in the outer loop of the view: <% #resources.group_by(&:category).each do |category, resources| %>
Then sorting the resources by votes in the partial for resources: <%= render resources.sort_by(&:netvotes).reverse %>
The view is a bit confusing, so here is the full view loop in index.html.erb:
<% #resources.group_by(&:category).each do |category, resources| %>
<div class="well">
<h3 class="brand-text"><%= category.name %></h3>
<%= render resources.sort_by(&:netvotes).reverse %>
</div>
<% end %>
And here is the _resource.html.erb partial:
<div class="row resource">
<div class="col-sm-2 text-center">
<div class="vote-box">
<%= link_to fa_icon('chevron-up lg'), upvote_resource_path(resource), method: :put %><br>
<%= resource.netvotes %><br>
<%= link_to fa_icon('chevron-down lg'), downvote_resource_path(resource), method: :put %>
</div>
</div>
<div class="col-sm-10">
<%= link_to resource.name, resource.link, target: "_blank" %>
<p><%= resource.notes %></p>
</div>
</div>
I hope that helps you think through some more ways to address your problem.

Poor Performance in Rails App

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.

combining 2 tables, looping through the results and displaying items from each

So I'm trying to combine two tables and show the results in order of the start_date.
I've tried a few things but because its technically a nested loop its giving me double results for each item.
The code i currently have is as follows
<% #subcategory = Subcategory.all %>
<% #product = Product.all %>
<% (#product + #subcategory).each do |product, subcategory|%>
<% if product.display_on_home_page and !product.is_highlight_product and !(product == '..') or subcategory.
display_on_home_page and !subcategory.is_highlight_product and !(subcategory == '..')%>
<div class="column_entry">
<%= link_to image_tag(subcategory.image_attachment.url(:normal_page_size)), subcategories_content_url(subcategory.id), :controller=>'subcategories' %>
</div>
<% end %>
<% if product.price_from %>
<div class="column_entry">
<div class="product_special">
<span class="a">From Only</span>
<span class="b"><%= number_to_currency(product.price,:unit=>'€') %></span>
</div>
<%= link_to image_tag(product.product_image.url(:normal_page_size)), products_content_url(product.id), :controller=>'products' %>
</div>
<% else %>
<div class="column_entry">
<div class="product_special">
<span class="a">Only</span>
<span class="b"><%= number_to_currency(product.price,:unit=>'€') %></span>
</div>
<%= link_to image_tag(product.product_image.url(:normal_page_size)), products_content_url(product.id), :controller=>'products' %>
</div>
<% end %>
<% end %>
I know this is quite a long an complex statement, its supposed to loop through all of the subcategories and all of the products and display the images, there are also two different ways of displaying the price based on a boolean that says whether the price is a specific amount or it starts from a given price.
at the moment its reading through the loop but its giving me the error
undefined method `is_highlight_product' for nil:NilClass
since this is the first column in the table that is referenced and its breaking here I think that there must be some conflict in its ability to see the information stored in the table.
I'm still quite new to ruby on rails so any help or even just a nudge in the right direction would be very much appreciated.
If you would like more information just ask in the comments and I'll put it up as fast as I can.
The problem here is, when you do something like this:
(#product + #subcategory).each do |product, subcategory|
The local variable product will iterate firstly through products, then through subcategories, and the local variable subcategory will always be nil.
What you can do, a dirty way - check
if product.is_a?(Product)
# do your things
elsif product.is_a?(Subcategory)
# do other things
end

Change Text Validation Rails 3

I would like to to do the following, any ideas
I have a Products model with two fields, both strings.
Name
Position
I would like on the index pages for the products to display the Name in Red if the Positions field is blank in the database.
Thanks in advance
Create helper method to check if object is blank?
def set_css_class(object, css_class)
" #{css_class}" if object.blank?
end
Call it in your View:
<div class="name <%= set_css_class(#poroduct.position, 'red') %>">
<%= #product.name %>
</div>
<span class="<%= product.position.nil? ? "red" : "blue" %>">
<%= product.name %>
</span>
Update:
Let's say you have code like you provided in comment, modify it to:
<td>
<%= link_to admin_printer_path(printer), :class => 'ico' do %>
<b<%= ' class="error"' unless printer.position? %>><%= printer.name %></b>
<% end %>
</td>
and in css file/section in header add (or modify according to your structure):
.error { color: red; }

Sort or order error messages in error_messages (Rails)

Is there any (simple) way to get some control of the order in which a model's errors appear in the view? Ordering the rules does not seem to help whatsoever.
Use error_message_on instead of error_messages to get the message for an individual attribute.
<div class="errorMessages">
<% %{name title description}.each do |att| %>
<%= f.error_message_on att, :css_class => "error" %>
<% end %>
</div>
In 2.3.6 validation messages will show in order you declared them in code
link
Here's an answer (for my own notes, basically) using Baldu's answer. This puts the attribute_names in alpha order:
<% if #model.errors.length>0 %>
<div class="errorExplanation">
<h3>There were problems with the following fields:</h3><ul>
<% #model.attribute_names.each do |attribute| %>
<% if !#model.errors[attribute].blank? %>
<li><%= f.error_message_on attribute, Model.human_attribute_name(attribute)+ " ", :style=>"display:inline" %></li>
<% end %>
</ul>
<% end %>
</div>
<% end %>
Of course you can parametrize this further as a partial, for instance. I'll probably do that :)
I had the same prob in groovy in rails (grails). changing it in the code does not change anything, the above answer doe not work for me either. This is how I end up solving my prob. Team created a custom tag for sorting / ordering grails error msgs.
def renderOrderedErrors = { attrs, body ->
def bean = attrs.bean
def fields = attrs.fields
fields.each { out << g.renderErrors(bean: bean, field:it) }
}
and this is how you use it:
<g:if test="${totalRating.hasErrors() || rating.hasErrors()}">
<div class="errors">
<g:if test="${totalRating.hasErrors()}"><g:renderOrderedErrors bean="${totalRating}" as="list" fields="${['totalEffectiveDate','awardedDisability']}"/></g:if>
<g:if test="${rating.hasErrors()}"><g:renderOrderedErrors bean="${rating}" as="list" fields="${['ratingStatus','ratingIssue','disability','effectiveDate','ratingType','socDate','nodDate','ssocDate','form9Date','six46Date','remandDate']}"/></g:if>//this is the way you want to order the fields in the form
</div>
</g:if>

Resources