One of the most common reasons my web application fails is because a user sometimes lacks a certain attribute that a view expects it to have. For instance, most users in my application have an education (school, degree, etc.) entry in our system, but some users do not. Assuming my view looks something like this:
<% #educations.each do |education| %>
<%= education.school %>
<%= education.degree %>
<% end %>
I want to avoid "Pokemon" exception handling and feel that there has to be a better way around dealing with a "undefined method `degree' for nil:NilClass" error in the case that a user does not have an education entry in our database. This just seems like an ugly/tedious fix:
<% #educations.each do |education| %>
<% if education.school %>
<%= education.school %>
<% end %>
<% if education.degree %>
<%= education.degree %>
<% end %>
<% end %>
Any input is appreciated. Thank you!
As long as you know the first object you're working on won't be nil, the easiest way is to just do this:
- #educations.each do |education|
= education.try :school
= education.try :degree
The #try method is pretty handy. You can also call .to_s on anything you think might be nil, Ie:
- #educations.each do |education|
= education.school.to_s
= education.degree.to_s
This will convert nils to an empty string. This isn't as useful in the view IMO, but comes in handy a lot of times if you have input that is expecting to be a string and might be empty. Ie a method like:
def put_in_parenthesis(string)
"(" + string.to_s + ")"
end
You have a couple of options here.
The easiest to implement is the try method. It is used like so:
<%= education.try( :degree ) %>
The problem is that try() is viewed as a bit of an anti-pattern. As the reference indicates, you can achieve similar functionality with something like:
<%= education && education.degree %>
This isn't really a lot different, intellectually, in my opinion. A popular way of handling this a little more cleanly is the Null Object pattern, which is basically an object with defined neutral ("null") behavior.
Related
I am building a rails app where I have a museums page which has a feature where it displays the museum with the most exhibits. The problem is that when there are no exhibits added to the db it gives an undefined method 'museum_name'. So the problem I have is I am not sure what would be the best way to make a check that would still allow me to access the page if there are zero exhibits?
Museums controller:
def index
#museums = Museum.all
most_exhibits = Exhibit.most_exhibits
most_exhibits.each do |museum|
#top_museum = MuseumsHelper.get_museum_name(museum.museum_id)[0]
end
Helper class method being used:
def self.get_museum_name(museum_id)
Museum.where(id: museum_id)
end
Display in views:
<%= #top_museum.museum_name %>
The best way to do it depends on how you want it to be. I think the ideal solution for yours is to check if/else then show the content accordingly:
<% if #top_museum.present? %>
<%= #top_museum.museum_name %>
<% else %>
<span>Nothing to display</span>
<% end %>
Or using try <%= #top_museum.try(:museum_name) %> or if you have ruby 2.3.0 or newer you can use safe navigation operator <%= #top_museum&.museum_name %> (Read more).
You could use try in your helper, that way it tries the query, if it fails then it returns nil
def self.get_museum_name(museum_id)
Museum.try(where(id: museum_id))
end
Ref: https://apidock.com/rails/v3.2.1/Object/try
I have an instance variable #tally_property, and if there are photos on that object I would like to cycle through the photos and show them.
So my code snippet looks like this:
<% if #tally_property.photos.present? %>
<% #tally_property.photos.each_with_index do |photo, index| %>
The issue is that based on the above, if #tally_property is nil, then the entire first line throws an error.
So is there a 'nil' check I can do that isn't bulky, i.e. I don't want to do if #tally_property.nil?, on both the primary object and the association, and is elegant and ruby & rails-esque?
I would use the safe navigation operator (&.) and write something like this:
<% #tally_property&.photos&.each_with_index do |photo, index| %>
...
<% end %>
In Ruby 2.3.0+ you can use the safe navigation operator:
#tally_property&.photos
ActiveSupport has a .try method that can be used to the same end in older versions of ruby:
#tally_property.try(:photos)
You can add a simple conditional to be able to safely iterate through the collection:
<% (#tally_property.try(:photos)||[]).each_with_index do |photo, index| %>
<% end %>
Rails 4 adds ActiveRecord::Relation#none and a change in behaviour so that associations always return a ActiveRecord::Relation. So its perfectly acceptable to write:
<% #tally_property.try(:photos).try(:each_with_index) do |photo, index| %>
<% end %>
After upgrading your app. Or you can use a partial and render:
<%= render partial: 'photos', collection: #tally_property.photos if #tally_property %>
Which removes the need for writing the iteration.
Use && (or and, they each have their sweetspot).
Taking it out of Erb for a moment, I would generally write something like this:
if #tally_property and #tally_property.photos.present?
Depending on photos I might use:
if #tally_property and #tally_property.photos
or perhaps:
if #tally_property and not #tally_property.photos.empty?
Sometimes I'll use a temporary variable:
if (photos = #tally_property && #tally_property.photos)
photos.each #…
That kind of thing.
I would recommend this episode of Ruby Tapas, And/Or for a longer (but still quick) look at it.
One more way, just select all photos connected to this tally_property:
example how it might be:
Photo.joins(:tally_property).each_with_index do |photo, index|
I want to implement a search functionality in my Rails app by using the pg_search gem. I've set up everything like it says in the documentation. Then I've set up a search controller with a show action:
def show
#pg_search_documents = PgSearch.multisearch(search_params)
end
The search itself works but I have a really annoying problem in my view. Whatever I do, it always outputs an array of PgSearch::Document objects. Even when I only write this in my view:
<%= #pg_search_documents.each do |document| %>
<% end %>
I get this (I've shortened it):
[#<PgSearch::Document id: 2, content: "…", searchable_id: 28, searchable_type: "Vessel">, #<PgSearch::Document id: 3, content: "…", searchable_id: 27, searchable_type: "Vessel">]
I know that pg_search sets up a polymorphic association which I've never dealt with before — could that be the problem?
Thanks in advance
<%= #pg_search_documents.each do |document| %>
<% end %>
This is a classic error, one I remember being puzzled over when I first started learning Rails. The mistake is using <%= %> with each. The return value of each is the array that you're iterating over (in this case, #pg_search_documents), and by using <%=, you're telling Rails to create a string from that array and insert it into your view. That generally isn't what you want: you want the view to be generated by the code inside the block you're passing to each.
Use <% #pg_search_documents.each do |document| %> instead (omitting the =) and you'll avoid the dump of the array's content.
You may also need to use .searchable as #blelump suggests, but I wanted to answer the other half of your question, as it's a common pitfall.
To get back to the original source model, searchable call is needed on these search result records, e.g:
<% #pg_search_documents.each do |document| %>
<%= document.searchable %>
<% end %>
You can also switch back to the source model within your controller, e.g:
#pg_search_documents = PgSearch.multisearch(search_params).collect(&:searchable)
Then, the #pg_search_documents will contain Vessel elements.
I've got a Rails 3 application where I'm using quite a few conditional statements to change the design of the page. What is the best practice for keeping the logic out of the view for having such drastic amounts of conditionals?
Hypothetical Example:
<% unless #ethos.blank? %>
<%= unless #work.nil? do %>
<%= link_to "Add Work", work_path %>
<% end %>
<%= #ethos.tagline %>
<% end %>
I've got many more conditionals inside of other conditionals. What is the best way to manage this inside of one view?
You should avoid complex conditionals (and most conditionals) in views. Extract them to a Helper, or better yet, to some kind of "presenter" so that you can work with a receiver instead of those "global looking/feeling helpers"
SomeHelper
module SomeHelper
def work_link
(#ethos.present? && #work) ? link_to("Add Work", work_path) : nil
end
end
View
<%= work_link %>
<%= #ethos.tagline if #ethos.present? %>
If #ethos is likely to be nil as opposed to an empty [] array, you could instead use:
<%= #ethos.try :tagline %>
Also note that in your original view <%= unless #work.nil? do %> should have been using a - and not a =.
Oh, and I encourage you to use HAML over ERB. With HAML, the view looks like this (easier to read, isn't it) :
= work_link %>
= #ethos. try :tagline
Your original view would look like this in HAML (remember, avoid conditionals in views as much as possible!)
- unless #ethos.blank?
- unless #work.nil? do
= link_to "Add Work", work_path
= #ethos.tagline
If the code works, what is your concern? Is it aesthetics or are you having difficulty reasoning about what the code is doing because there is so much nesting?.
The simplest solution is probably just to move the conditionals inline.
<%= link_to("Add Work", work_path) if #ethos.present? && #work %>
<%= #ethos.tagline if #ethos.present? %>
This will improve readability (and therefore maintainability), though it may not go far enough to keep the Rails purists happy. Zabba's answer presents several great choices that go further down the rabbit hole.
Model Member belongs_to Discipline, i.e. user can have a discipline but it is optional.
I'm wondering what would be the idiomatic Rails way to show member's discipline when I show the user details.
My first approach
<b>Discipline:</b>
<%=h #member.discipline.name %>
works otherwise fine but fails with NoMethodError if member's discipline is Nil. In that case, I'd like to have nothing there.
A couple of alternatives I have are
Define method Member:discipline_name that returns "" if member doesn't have a discipline
Restrict output with if;
The alternative with "if" would be something like:
<b>Discipline:</b>
<% if #member.discipline %>
<%=h #member.discipline.name %>
<% end %>
This isn't a big decision to make but I'd like to know if there's an "idiomatic way" to do this or some helper/something or something else to consider.
br, Touko
In my opinion the idiomatic way would be:
<b>Discipline:</b>
<%=h #member.discipline.name if #member.displince.present? %>
Actually, developing Wukerplank's answer further, following seems to work fine and be pretty concise:
<b>Discipline:</b>
<%=h #member.discipline.name if #member.discipline %>
If you need to display anything in case of non-existence, you can use the ternery operator:
<%= condition ? statement true : statement false %>
<%= #member.discipline ? #member.discipline.name : 'none' %>