I have a collection of invoices. One of the attributes is exchange_rate (is used to calculate the currency from US dollars for Mexican pesos). I need to create a warning if even ONE of the records doesn't have an exchange_rate set.
I could check if the exchange_rate of a record in a collection is blank like this...
<% is_blank = false %>
<% #invoices.each do |invoice| %>
<% if invoice.exchange_rate.blank? %>
<% is_blank = true %>
<% end %>
<% end %>
<% if is_blank %>
shoot warning: all of the invoices must have an exchange rate in order
to calculate pesos total
<% end %>
What is a more Railsy way of writing the above?
Simply like this, using the Enumerable#any? method:
<% if #invoices.any? { |i| i.exchange_rate.blank? } %>
shoot warning: all of the invoices must have an exchange rate in order
to calculate pesos total
<% end %>
Related
I'm using a conditional to check if two values exists. I want to override the first conditional with the second one. Most likely if the second one exists then the first will, if that makes sense. Not the issue i'm having is using two different call methods.
What I have so far:
<% #data.each do |i| %>
<% if i.stock.present? %>
In Stock
<% elsif i.sold.present? %>
Sold
<% else %>
n/a
<% end %>
<% end %>
So, if in stock display 'In Stock', if sold display 'Sold'.
I think I have a solution, but feel free to let me know if there is a better way. This is using the unless statement and seems to display what I need.
<% #data.each do |i| %>
<% unless i.stock.present? %>
In Stock
<% else if i.sold.present? %>
Sold
<% else %>
n/a
<% end %>
<% end %>
<% end %>
The following loop goes through the sales column and lists all 4 existing product values, like 19.99 19.99 3.99 3.99 to the corresponding user id.
<% #sales.each_with_index do |sale, index| %>
<% if current_user.id == sale.user_id %>
<% price = Warehouse.where(:product => sale.product).pluck(:mrr) %>
<%= value = price.split(',').join('.').to_f %>
<% else %>
<% end %>
Now I want to save the results/values into a new global variable and add up each out of "value". So the result of 19.99 19.99 3.99 3.99 should be 47.96.
I'm completely lost. Any Ideas?
You could do something like this:
<% total = 0 %>
<% #sales.each_with_index do |sale, index| %>
<% if current_user.id == sale.user_id %>
<% price = Warehouse.where(:product => sale.product).pluck(:mrr) %>
<%= value = price.split(',').join('.').to_f %>
<% total += value %>
<% end %>
<% end %>
<%= "Total is #{total}" %>
It is highly questionable to have code like this in the view though. You could get prices and calculate totals in your controller instead.
Also note that you are missing an end. I changed the unneeded else to an end.
In your controller you can create a instant variable prefixed by # so it can be used throughout your view
For example in your controller
#total_value = 0
And in your view
<%#sales.each_with_index do |sale, index| %>
<% if current_user.id == sale.user_id %>
<% price = Warehouse.where(:product => sale.product).pluck(:mrr) %>
<%= value = price.split(',').join('.').to_f %>
<% #total_value += value %>
<% else %>
<% end %>
You shouldn't add that kind of logic in your view. Create a view object class (that the controller instantiates) too handle all of this. You also probably can do something like:
user.sales.each do |sale|
total += find_price(sale)
# do more stuff
end
If you are asking 'if current_user.id == sale.user_id' then you most likely doing it wrong.
In that view object you could have a hash that has all the prices you want to show and iterate over that in your view.
I'm new to rails and I'm trying to build a view that will list the parents and related children
Ex:
Passport has many Visas
I want to list information about the passport and the visas that the passport has.
So I have
<% #passport_list.each do |passport| %>
# passportFields
<% passport.visas.each do |visa| %>
<%= t.text_field :visa_type %>
<% end %>
<% end %>
I'm getting the error
undefined method `visa_type' for #Passport:0x000000091b8b28
It looks like rails is trying to find the property visa_type for passport, instead of in visa. How does the scope work within each? Can I force it to access visa_type from visa?
I think you're looking for the fields_for form helper. This will allow you to create fields for the relevant visa attributes. Replace your code sample with the following, and you should be all set.
<% #passport_list.each do |passport| %>
# passportFields
<% t.fields_for :visas do |visa_fields| %>
<%= visa_fields.text_field :visa_type %>
<% end %>
<% end %>
You can also iterate over the list as follows:
<% #passport_list.each do |passport| %>
# passportFields
<% passport.visas.each do |visa| %>
<% t.fields_for :visas do |visa_fields| %>
<%= visa_fields.text_field :visa_type %>
<% end %>
<% end %>
<% end %>
For more information on fields_for, check out the link I added above, and to customize further for your use case, check out the "One-to-many" section.
IMO you should always handle the null case of an object.
Something like this if you use rails (present? is a Rails function)...
<% if #passport_list.present? %>
<% #passport_list.each do |passport| %>
passportFields
<% passport.visas.each do |visa| %>
<%= t.text_field :visa_type %>
<%end%>
<%end%>
<% else %>
<p>Nothing to see here</p>
<% end %>
However if your #passport_list is backed by an ActiveRecord Query, you can handle this in the model/helper/controller by returning the .none query on the model. Note that this differs from an empty array because it is an ActiveRecord Scope, so you can chain AR queries onto it
# scope on AR model
def self.awesomeville
where(country_of_origin: "awesomeville")
end
# method queried in controller
#passport_list = Passport.all
if #passport_list.present?
#passport_list
else
Passport.none
end
# additional filtering in view is now possible without fear of NoMethodError
#passport_list.awesomeville
Whereas a ruby Array would raise an error as it would respond to the Array methods.
New to Rails! I have products which are associated to a campaign. Each #product has a .price and .orders_count associated to it.
What I wanted to do is multiply the .price with .orders_count for each #product, and add them all up to get a total cost for the #campaign.
Being new to Rails, wasn't sure how to do the write syntax and had the following in the view. This does the first piece, but does not add them all up at the end. Thanks for the help!
<% #products.each do |p| %>
<% if p.orders_count? %>
<%= (number_to_currency((p.price) * p.orders_count)) %>
<% end %>
<% end %>
Try setting a variable to keep track of the sum of all products, and add the product to it each time through the loop. Try this:
<% sum = 0 %>
<% #products.each do |p| %>
<% if p.orders_count? %>
<% product = (number_to_currency((p.price) * p.orders_count)) %>
<% sum += product %>
<%= product %>
<% end %>
<% end %>
The sum is: <%= sum %>
I have a rails table called Movies. Movies are being collected and saved from an API which means that some movies may have a release_date and some may not.
All Movies are being displayed on the home page and they are sorted by {|t| - t.release_date.strftime("%Y%m%d").to_i}
<% #movies.sort_by{|t| - t.release_date.strftime("%Y%m%d").to_i}.each do |movie| %>
<% movie.title %>
<% movie.release_date.strftime("%Y") %>
<% end %>
So this code works fine but only as long as the returned movies have a release date. If they don't have a release date assigned, it gives me the following error.
ActionView::Template::Error (undefined method `strftime' for nil:NilClass):
But im only getting this error if the movie has no release_date.
So how can i add an exception to only display films WITH a release_date, where using strftime would no longer be a problem.
I've tried
<% unless movie.release_date.blank? %>
<% #movies.sort_by{|t| - t.release_date.strftime("%Y%m%d").to_i}.each do |movie| %>
<% #movie.title %>
<% #movie.release_date.strftime("%Y") %>
<% end %>
<% end %>
But that doesn't work as it gives an undefined method for 'movie'
You should be able to use reject to reject nil release_date like follows:
<% #movies.reject{ |m| m.release_date.nil? } %>
Another problem is you are using the variable movie as instance variable #movie within your each block.
Try:
<% #movies.reject{ |m| m.release_date.nil? }.sort_by{|t| - t.release_date.strftime("%Y%m%d").to_i}.each do |movie| %>
<% movie.title %>
<% movie.release_date.strftime("%Y") %>
<% end %>
Update:
And yes, as pointed by #NicolasGarnil in his answer, it's better to do these in SQL side than in ruby side. Select only the required records and let database do the sorting. So you could update your code to be something like:
In controller:
#movies = Movie.where('release_date is not null').order('release_date desc');
Then in your view:
<% #movies.each do |movie| %>
<% movie.title %>
<% movie.release_date.strftime("%Y") %>
<% end %>
For performance reasons you should not be using ruby to sort your records. This should be done at a database level.
You should first ensure that the release_date values are persisted in an appropriate format and then just use Movie.order("release_date desc"). Records with null values will be placed at the end of the results.