Good afternoon. I am new to ruby and trying to build my first application.
I am using sqlite database and rails 5.0.
I have a model called Person that has the first name, last name and date of birth as attributes.
On the page where I list people I want to add the age of the people and obtain an average of the ages of the people
My controller looks like this:
before_action :set_persona, only: %i[ show edit update destroy ]
# GET /personas or /personas.json
def index
#persona = Persona.order("cast(strftime('%m', fecha_nacimiento) as integer)")
end
And my view like this
<table>
<thead>
<tr>
<th>Nombre</th>
<th>Apellido</th>
<th>Fecha nacimiento</th>
<th>Dni</th>
<th>Edad</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% #persona.each do |persona| %>
<tr>
<td><%= persona.nombre %></td>
<td><%= persona.apellido %></td>
<td><%= persona.fecha_nacimiento %></td>
<td><%= persona.dni %></td>
<td><%= Time.now.year - persona.fecha_nacimiento.year %></td>
<td><%= link_to 'Detail', persona %></td>
<td><%= link_to 'Edit', edit_persona_path(persona) %></td>
</tr>
<% end %>
</tbody>
</table>
<p>El promedio de edad de las personas es: </p>
Since I don't have a field in the database called "age" I can't understand how I can achieve the result.
The objective would be to iterate through each of the people and divide it by the length of it, or is there an easier way?
Please excuse my ignorance, thank you very much in advance.
What you want to do is select your calculated column and give it an alias:
def index
#persona = Persona.select(
Persona.arel_table[Arel.star], # personas.*
"cast(strftime('%m', fecha_nacimiento) as integer) as age"
)
.order(age: :desc)
end
Any columns you select will be available in the resulting model instances as attributes:
<table>
<thead>
<tr>
<th>Nombre</th>
<th>Apellido</th>
<th>Fecha nacimiento</th>
<th>Dni</th>
<th>Edad</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% #persona.each do |persona| %>
<tr>
<td><%= persona.nombre %></td>
<td><%= persona.apellido %></td>
<td><%= persona.fecha_nacimiento %></td>
<td><%= persona.dni %></td>
<td><%= persona.age %></td>
<td><%= link_to 'Detail', persona %></td>
<td><%= link_to 'Edit', edit_persona_path(persona) %></td>
</tr>
<% end %>
</tbody>
</table>
The easiest way to implement what you're asking is to do the operation within the view. This kind of breaks MVC but it's the fastest.
<% edades = 0 %>
<% #persona.each do |persona| %>
<% edades += Time.now.year - persona.fecha_nacimiento.year %>
<!-- ... the rest of the view code -->
<% end %>
<% average = edades.to_d / #persona.length # need to typecast to decimal so you can have fractionals %>
To do this in an MVC manner, you will have to do the computation in the controller.
def index
#persona = Persona.order("cast(strftime('%m', fecha_nacimiento) as integer)")
#average = #persona.collect { |p| Time.now.year - p.fecha_nacimiento.year }.sum / #persona.length.to_d
end
It would be easier to implement an age method in Person model so you can just call the method instead.
class Person
def edad
Time.now.year - fecha_nacimiento.year
end
end
and lastly, the computation for the age is more complex than just current year less birthday year. We use this function (taken from https://stackoverflow.com/a/2357790/365218) to calculate for the age.
def age(dob)
now = Time.now.utc.to_date
now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1)
end
Related
I'm working on a rails app for my expenses.
I have 3 models like : user, spending and currency.
I can create a spending via form, I have a title, description, amount, currency_id, user_id. All the relations are made and working.
I use my show.html.erb from the user controller to display all the expenses from the current_user. That works fine. I then I grouped the expenses together and sort them by date. I did actually 2 groups, the first one which is today and the rest. So on the display it shows me a list of the expenses I created Today (first group) and then a list of month with the expenses from a previous date (second group). For now it shows this :
Today
Expense 1...
Expense 2...
January
...
...
...
December
...
...
etc...
I would like to add the year as well next to the month so that it shows up like this :
Today
...
...
January 2018
...
...
December 2017
...
...
I addition to that I'd like this month and year to be a link so that when you click on it you go to a new page that show all the expenses for this specific month only.
Thats about it. Ah yes, I use will_paginate as well.
Now what I did is this, lets start with my UsersController :
class UsersController < ApplicationController
def show
#user_spendings = #current_user.spendings.all.order('date DESC').paginate(:page => params[:page], :per_page => 10)
#Retrives all messages and divides into two groups todays messages and other messages
#grouped_spendings = #user_spendings.group_by{ |t| t.date.to_date == DateTime.now.to_date }
if #user_spendings.present?
#Create month wise groups of messages
#month_wise_sorted_spendings = #user_spendings.group_by{ |t| t.date.month }
end
end
And my show view is a little bit longer (I didn't paste the will_paginate) :
<% if #grouped_spendings.present? && #grouped_spendings[true].present? %>
<table>
<thead>
<tr>
<th>Title</th>
<th>Description</th>
<th>Amount</th>
<th>Currency</th>
<th>Created</th>
<th>Actions</th>
</tr>
</thead>
<h3>Today</h3>
<tbody>
<% #grouped_spendings[true].each do |spending| %>
<tr>
<td><%= spending.title %></td>
<td><%= spending.description %></td>
<td><%= '%.02f' % spending.amount %></td>
<td><%= spending.currency.symb %></td>
<td><%= spending.date.strftime('%d %B %Y') %></td>
</tr>
</tbody>
<% end %>
</table>
<% end %>
<% if #month_wise_sorted_spendings.present? %>
<% #month_wise_sorted_spendings.each do |hash_elements|%>
<table>
<thead>
<tr>
<th>Title</th>
<th>Description</th>
<th>Amount</th>
<th>Currency</th>
<th>Created</th>
<th>Actions</th>
</tr>
</thead>
<h3><%= Date::MONTHNAMES[hash_elements.first] %></h3>
<tbody>
<% hash_elements.last.each do |spending| %>
<tr>
<td><%= spending.title %></td>
<td><%= spending.description %></td>
<td><%= '%.02f' % spending.amount %></td>
<td><%= spending.currency.symb %></td>
<td><%= spending.date.strftime('%d %B %Y') %></td>
</tr>
</tbody>
<% end %>
</table>
<% end %>
<% end %>
Sorry for that giant code I hope your not blind...
How could I manage to get the month and the year and to make it a link after that. I tried already a few things such as modify this particular line in the show : <%= Date::MONTHNAMES[hash_elements.first] %>. The thing I got is that Date::MONTHNAMES changes the actual month number into the month name and hash_elements.first gives me the actual month number where hash_elements.last gives me all the informations about my expense.
Then I was like, ok, maybe I should go to the controller and add the year over there in this variable #month_wise_sorted_spendings = #user_spendings.group_by{ |t| t.date.month }. This group_by only groups the month for now so I try to add t.date.year, but then in the view I can't use Date::MONTHNAMES. So no success here...
Well I'm a bit lost, any help or hint would be appreciated.
Thanks alot
Ok guys I got everything working !
Maybe some of you want to see how it looks like so there we go
def show
#user_spendings = #current_user.spendings.all.order('date DESC').paginate(:page => params[:page], :per_page => 15)
#Retrives all messages and divides into two groups todays messages and other messages
#grouped_spendings = #user_spendings.group_by{ |t| t.date.to_date == DateTime.now.to_date }
if #user_spendings.present?
#Create month wise groups of messages
#month_wise_sorted_spendings = #user_spendings.group_by{ |t| (Date::MONTHNAMES[t.date.month] + " " + t.date.year.to_s) }
end
end
def detail
if params[:month]
#date = Date.parse("#{params[:month]}") # to get the first day of the month
#user_spendings_month = #current_user.spendings.order('date DESC').paginate(:page => params[:page], :per_page => 30).where(:date => #date..#date.end_of_month) # get posts for the month
#user_amount = Currency.joins(:spendings).
select(:symb, 'SUM(spendings.amount) AS amount').
where(spendings: { id: #user_spendings_month.map(&:id) }).
group(:symb)
else
#user_spendings_month = #current_user.spendings.all.order('date DESC')
end
respond_to do |format|
format.html
format.pdf {
render template: 'views/users/detail', pdf: "eXpenses #{params[:month]}", file: "#{Rails.root}/app/views/users/detail.pdf.erb" }
end
end
I changed the #month_wise_sorted_spendings variable to match my need : the month by name and the year. I also added the def detail as I want to be able to link_to all my months/year to go to a other page and see specific data about this month.
<% if #grouped_spendings.present? && #grouped_spendings[true].present? %>
<table class= "table table-striped table-bordered table-responsive">
<thead>
<tr>
<th>Title</th>
<th>Description</th>
<th>Amount</th>
<th>Currency</th>
<th>Created</th>
<th>Actions</th>
</tr>
</thead>
<h3 class="btn btn-secondary col-1">Today</h3>
</br></br>
<tbody>
<% #grouped_spendings[true].each do |spending| %>
<tr>
<td><%= spending.title %></td>
<td><%= spending.description %></td>
<td><%= '%.02f' % spending.amount %></td>
<td><%= spending.currency.symb %></td>
<td><%= spending.date.strftime('%d %B %Y') %></td>
</tr>
</tbody>
<% end %>
</table>
<% end %>
<% if #month_wise_sorted_spendings.present? %>
<% #month_wise_sorted_spendings.each do |month,spending|%>
<table class= "table table-striped table-bordered table-responsive">
<thead>
<tr>
<th>Title</th>
<th>Description</th>
<th>Amount</th>
<th>Currency</th>
<th>Created</th>
<th>Actions</th>
</tr>
</thead>
</br>
<% date_in_link = " #{month}" %>
<p><%= link_to user_detail_path(#current_user, :month => month), class: "btn btn-secondary col-1" do %>
<i class="fa fa-link"></i>
<%= date_in_link %>
<% end %>
<%= link_to user_detail_path(#current_user, :month => month, format: "pdf"), class: "btn btn-secondary col-1" do %>
<i class="fa fa-file-pdf-o"></i>
PDF
<% end %></p>
</br>
<tbody>
<% spending.each do |spending| %>
<tr>
<td><%= spending.title %></td>
<td><%= spending.description %></td>
<td><%= '%.02f' % spending.amount %></td>
<td><%= spending.currency.symb %></td>
<td><%= spending.date.strftime('%d %B %Y') %></td>
</tr>
</tbody>
<% end %>
</table>
<% end %>
<% end %>
</div>
I use a month param to get the date in the URL and use into the new page via the #user_spendings_month and the #page.
Next to the month/year links I added a link to print a pdf about those infos, I thought it can be handy. I use the same variables I use in the show view in the pdf. Tada ! Just great !
I hope it is clear enough if you don't get something, well just write a comment !
I'm creating a rails app for my expenses. I have several models like user, spending, currency. I put relations between them, everything works. I can add a new expense that is shown on a index (all the expenses from all users) and I decided to loop through the expenses on the user show page, where the current_user see his own expenses.
I also grouped the expenses by date (month and year) and I have a "Today" group, which shows all the expenses entered on the day. It looks like this in the controller :
def show
#user_spendings = #current_user.spendings.all.order('date DESC').paginate(:page => params[:page], :per_page =>
10)
#Retrives all messages and divides into two groups todays messages and other messages
#grouped_spendings = #user_spendings.group_by{ |t| t.date.to_date == DateTime.now.to_date }
if #user_spendings.present?
#Create month wise groups of messages
#month_wise_sorted_spendings = #user_spendings.group_by{ |t| (Date::MONTHNAMES[t.date.month] + " " + t.date.year.to_s) }
end
end
This is my view :
<% if #grouped_spendings.present? && #grouped_spendings[true].present? %>
<table>
<thead>
<tr>
<th>Title</th>
<th>Description</th>
<th>Amount</th>
<th>Currency</th>
<th>Created</th>
<th>Actions</th>
</tr>
</thead>
<h3>Today</h3>
<tbody>
<% #grouped_spendings[true].each do |spending| %>
<tr>
<td><%= spending.title %></td>
<td><%= spending.description %></td>
<td><%= '%.02f' % spending.amount %></td>
<td><%= spending.currency.symb %></td>
<td><%= spending.date.strftime('%d %B %Y') %></td>
</tr>
</tbody>
<% end %>
</table>
<% end %>
<% if #month_wise_sorted_spendings.present? %>
<% #month_wise_sorted_spendings.each do |hash_elements|%>
<table>
<thead>
<tr>
<th>Title</th>
<th>Description</th>
<th>Amount</th>
<th>Currency</th>
<th>Created</th>
<th>Actions</th>
</tr>
</thead>
<% date_in_link = "#{hash_elements.first}" %>
<h3><%= link_to(date_in_link, detail_result_path) %></h3>
<tbody>
<% hash_elements.last.each do |spending| %>
<tr>
<td><%= spending.title %></td>
<td><%= spending.description %></td>
<td><%= '%.02f' % spending.amount %></td>
<td><%= spending.currency.symb %></td>
<td><%= spending.date.strftime('%d %B %Y') %></td>
</tr>
</tbody>
<% end %>
</table>
<% end %>
<% end %>
I would like to create a link on the month and year and open a page where all the informations are looped again but only for this specific month of the year.
I already achieved the linking with this :
<% date_in_link = "#{hash_elements.first}" %>
<h3><%= link_to(date_in_link, detail_result_path) %></h3>
To avoid mixing the controllers etc... I decided to create new controller called detail and a result.html.erb page where I will loop the expenses about the specific month and year.
Which way should I take with this ? Is it a good option to do a new controller ? And how can I get the infos back according to the date on my new page ?
I thought about passing some params via the link_to but I'm not sure it's the best solution. Maybe there is something easier but I don't know or see it.
If anyone can help would be great !
Thanks alot
I found the answer but it's on a other post I did as I had an other problem before this one. So the link is here
Feel free to ask question if any
I created this code that pulls the information I need.
def index
#votes = Vote.all
#originalitysum = Vote.group(:widget_id).sum(:originality)
end
It returns a hash:
{188=>5, 160=>2}
I now need to match the key to the widget_id and return the value. I.E:
If the widget_id is 188 return 5.
<% #votes.group(:widget_id).each do |vote| %>
<tr>
<td><%= vote.widget.name %></td>
<td><%= vote.widget.store %></td>
<td><%= %></td> <!-- This needs to be the total Originality -->
<td><%= vote.interest %></td>
<td><%= vote.rating %></td>
</tr>
<% end %>
I'm open to changing this if some other way makes more sense.
You can get the originality sum with #originalitysum[vote.widget.id]
Figured it out.
Controller
def index
#votes = Vote.all
#originalitysum = Vote.select([:widget_id, :originality]).group('widget_id').sum(:originality)
#votecount = Vote.group(:widget_id).count(:originality)
#votes = Vote.select(:widget_id,
"SUM(originality) as originality_sum",
"SUM(interest) as interest_sum",
"SUM(rating) as rating_sum").group(:widget_id).order("rating_sum DESC")
end
The view
<% #votes.group(:burger_id).each do |vote| %>
<tr>
<td><%= vote.widget.name %></td>
<td><%= vote.widget.store %></td>
<td><%= vote.originality_sum %></td>
<td><%= vote.interest_sum %></td>
<td><%= vote.rating_sum %></td>
</tr>
<% end %>
Thanks to this answer in this link, I was able to parse it together.
Group by a column and then get the sum of another column in Rails
The added bonus is that it allowed me to easily sum the other columns as well.
I am displaying a list of user in my table (no pagination). User contains id, name, age, status, location. I want to allow the list to be filtered by age or status. How do I do this in ruby rails ? I tried using filterrific but stuck since last two days here
Id name age status location
1 xz 22 single ca
2 yy 23 married ma
view
<table>
<tr>
<th>id</th>
<th>name</th>
<th>age</th>
<th>status</th>
<th>location</th>
</tr>
<% items.each do |item| %>
<tr>
<td><%= item.id %></td>
<td><%= item.name %></td>
<td><%= item.age %></td>
<td><%= item.status %></td>
<td><%= item.lcation %></td>
</tr>
<% end %>
</table>
controller
def index
#items = User.all
end
I think, this cast can help you perfect without any gems.
I am building an html table that should include name, rating1, rating2, and rating3. rating 1-3 come from different models than name.
resources :names do
resource :rat1,:rat2,:rat3
end
Inside of my html table I'd like to include the ratings from within each of these tables but I would like to automatically skip over or ignore tables that are nil. This is because :names may only have a :rat1 and not a :rat2 or :rat3. My view should look something like this.
<table>
<thead>Name</thead>
<thead>Rating 1</thead>
<thead>Rating 2</thead>
<thead>Rating 3</thead>
<% #names.each do |name| %>
<tr>
<td><%= name.nametext %></td>
<td><%= name.rat1.rating %></td>
<td><%= name.rat2.rating %></td>
<td><%= name.rat3.rating %></td>
</tr>
<% end %>
</table>
Except that if name.rat1 is nil it will either a.) replace the value with N/A OR b.) it will leave this field blank and move on to the next.
What is the cleanest way to do this?
::UPDATE::
So my issue is that the name.rat1 is nil and the name.rat1.rating is an undefined method of a nil class so both of these options will throw the same undefined method of a nil class error regardless of the || or helper method. At least thats what my current tests are showing. Any other options? or different workarounds? I'd like to avoid having to put a validation loop like this for every rat1-3
<% unless name.rat1.nil? %>
<%= name.rat1.rating %>
<% end %>
There has to be a simpler way.
I would probably create a helper method in names_helper.rb
def show_rating(rating)
if rating.present?
rating
else
"default value"
end
end
Then use it in the view:
<%= show_rating name.rat1.rating %>
OFFTOPIC Your table structure is wrong. It should have <thead><tr><th>Name</th><th>Rating1</th>..so on..</tr></thead>
So, in your case you can use the condition while rendering the rating values as:
<table>
<thead>
<tr>
<th>Name</th>
<th>Rating 1</th>
<th>Rating 2</th>
<th>Rating 3</th>
</tr>
</thead>
<tbody>
<% #names.each do |name| %>
<tr>
<td><%= name.nametext %></td>
<td><%= name.rat1.rating || 'N/A' %></td>
<td><%= name.rat2.rating || 'N/A' %></td>
<td><%= name.rat3.rating || 'N/A' %></td>
</tr>
<% end %>
</tbody>
</table>