undefined method `strftime' for nil:NilClass - shows sometimes - ruby-on-rails

I'm building a simple app for displaying movies using themoviedb gem. However, when I try to do a search query, it displays the error I mentioned. Now, that doesn't happen every time - only in certain cases (e.g. Matrix works fine, but Fight Club shows the error)
Here's my code:
<% #movie.each do |movie| %>
<%= link_to movie_path(movie.id) do %>
<%= image_tag("#{#configuration.base_url}w154#{movie.poster_path}") if movie.poster_path %>
<% end %>
<div class="moviesindex">
<%= link_to movie.title, movie_path(movie.id) %>
(<%= movie.release_date.to_date.strftime("%Y") %>) <br />
</div>
<% end %>

Be forgiving for realease_date if movie.release_date can be nil at times using try:
<%= movie.release_date.try(:year) %>
This will give you release year if release_date is valid and gives you nil if release_date is nil.
Since release_date, I assume is already either a Date or DateTime or ActiveSupport::TimeWithZone, the to_date is not necessary. Also I think date.year is more cleaner than date.strftime("%Y").

Related

Rails for Date format issue

I am trying to output just the date on the generated line, but when I execute this code, which I am sure is not elegant at all, it gives me undefined method 'strftime' for nil:NilClass
<% #person.subordinates.each do |sub| %>
<tr>
<td>
<% if sub.position == 'alumni' %>
<p>
<%= link_to(sekret_person_path(sub.position, sub.user_name)) do %>
<strong><%= sub.fname %> <%= sub.lname %>,</strong>
<% end %> <%= sub.startdate.strftime("%Y") %> - <%= sub.graddate.strftime("%Y") %>
</p>
<% end %>
</td>
</tr>
<% end %>
Further up in my code, I am using #person.startdate.strftime("%m/%d/%Y") without any issue, but I think my issue is that I am using the sub call, which is grabbing any people that are flagged as sub to this person.
Your error says that one of your attributes is nil. The sytax is correct. Try debugging with:
#person.subordinates.select { |sub| sub.startdate.nil? || sub.graddate.nil? }
The elements that are retrieved on this line will be the ones that are giving you errors, because you are trying to run nil.strftime('%Y'), which makes sense.
You should use a condition on your .erb to stop it, like:
<%= sub.startdate && sub.startdate.strftime("%Y") %> - <%= sub.graddate && sub.graddate.strftime("%Y") %>
It is not an error with Rails.
strftime is being called on nil object. Make sure you are calling strftime on a date, time, or datetime object.
Refer to strftime on rails apidock - https://apidock.com/ruby/DateTime/strftime
Another way to solve this that will guard against nils would be:
<%= sub.startdate&.strftime("%Y") %> - <%= sub.graddate&.strftime("%Y") %>
That way you don't have to add a conditional to make sure that any method/attribute in the chain exists prior to calling them. This works because of the safe navigation operator, which is explained in depth here: https://rubyinrails.com/2017/11/17/safe-navigation-operator-ampersand-dot-in-ruby/

Is there a way to get the console to ignore certain errors?

Say I have an instance variable #n, and I'm calling <%= #n.title %> in my view.
If #n equals a valid record, then this will print normally. But if #n is blank or invalid, then the entire page will show an error message, because of this one little line.
Is there a way to get #n.title to just print nil if #n is nil or invalid?
I'm looking for a way to do this without conditional statements. For example, if I wanted to print
<%= #v1.title %>,<%= #v2.title %>,<%= #v3.title %>,<%= #v4.title %>,
if I wanted to use conditionals to print without errors, it would require 12 lines of code:
<% if #v1 %>
<%= #v1.title %>,
<% end %>
<% if #v2 %>
<%= #v2.title %>,
<% end %>
<% elsif #v3 %>
<%= #v3.title %>,
<% end %>
<% elsif #v4 %>
<%= #v4.title %>,
<% end %>
It seems a shame to use 12 lines on this. It would be nice to be able to accomplish the error-handling right when printing.
You can totally do this easily with the try() method. I use it all the time.
<%= #n.try( :title ) %>
That will return nil if #n is nil or if the title method doesn't exist on #n.
You can also chain them together like this:
#n.try( :title ).try( :to_s )
Or even use it on a hash:
#n.try( :[], 'name' ) # Which is the same as #n['name']
See http://api.rubyonrails.org/classes/Object.html#method-i-try
EDIT (Jan 11, 2016)
You can now use the "safe navigation operator" as of Ruby 2.3.0.
#n&.title&.to_s
As well as the Array#dig and Hash#dig methods introduced in Ruby 2.3.0.
hash = { 'name' => 'bob' }
hash.dig( 'name' ) # Which is the safe way to do hash['name']
You can add some logic to your view that differentiates between development (where some errors can be ignored) and production environments (where errors should cause your app to fail in an obvious and ugly manner). Ruby's nil has a "falsey" nature, so you can use that concept to your benefit as well.
<% if Rails.env.development? %>
<% if #n %>
<%= #n.title %>
<% else %>
<%= nil %>
<% end %>
<% else %>
<%= #n.title %>
<% end %>

Displaying images from associated models

I'm trying to get my miniatures view to display all associated photos from the collections model.
My sample miniature has two photos but the following code gives an error "undefined method `photo' for #".
<% #miniature.collections(:photo).each do |photo| %>
<%= image_tag #miniature.collections.photo.url(:medium) %>
<% end %>
I think that the relationships are all correct though because rails console works fine with them and the following code shows the first image, twice.
<% #miniature.collections(:photo).each do |photo| %>
<%= image_tag #miniature.collections.first.photo.url(:medium) %>
<% end %>
Equally I can swap out first for last and show the 2nd image twice. What am I doing wrong?
Without knowing a huge amount about your associations, I'm relatively sure you want to use the instance variable that you instantiated in the each loop. That would look like something to the effect of:
<% #miniature.collections(:photo).each do |collection| %>
<% if !collection.photo.url(:medium).nil? %>
<%= image_tag collection.photo.url(:medium) %>
<% end %>
<% end %>
Using #miniature.collections.first.photo.url(:medium) would display the first image of the collection once for each photo in the collection. Likely this is not what you want.

Rails .each except if

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.

Question with .find and a return value

I'm fairly new to rails (only about a month's experience), so this may be trivial. In my app, if I call
<%= Group.find(:all).each do |g| %>
<p><%= g.name %></p>
<%= end %>
it prints out all of the groups' names correctly. However, afterwards, it returns all of them (with the hexcodes, and stuff). I figure that's because .find returns everything you have it iterate over. Anyways - on to my question: Is .find the wrong method, or how do I go about iterating over every Group, without returning them afterwards?
I would appreciate any help or insight you all have.
Thanks!
I'm guessing you're doing something like
<%= Group.find(:all).each do |g| %>
<p><%= g.name %></p>
<%= end %>
That would print the return value of the whole statement. Instead, do this
<% Group.find(:all).each do |g| %>
<p><%= g.name %></p>
<% end %>
That wouldn't print the returned value.
Sidenote: Group.find(:all) is the same as Group.all

Resources