Simple Rails Conditional Statement - ruby-on-rails

Pretty simple I'm sure... but I can't figure out why it wouldn't work.
<tr<% if film.viewed == true %> class="viewed"<% end %>>
The film.viewed is a boolean but its not rendering the class if its true. Probably a syntax error. Any help? Also is there an easier way to write this without opening and closing? I tried using:
<tr<% if film.viewed == true puts class=\"viewed\" end %>>
Again probably a syntax error. I'm coming from PHP so I'm still learning.
Thanks.

Your best bet would be something like the following...
<tr <%= 'class="viewed"' if film.viewed? -%>>
All boolean ActiveRecord columns get a question mark method that will return a true or false. As MySQL stores booleans as 1/0, I usually use the question mark method just to be safe.

I think this is easer to read:
<%= content_tag(:tr, "", :class => film.viewed? ? "viewed" : nil) %>

I'm guessing film.viewed isn't true, but another value.
try something to the effect of
<tr <%= "class='viewed'" if film.viewed %> >

<tr <%= film.viewed ? 'class="viewed"' : 'class="notviewed" %> >
So, some notes.
To do a full if ... end you would have needed a ";" or newline before the end.
Do you actually have a local variable or parameter called film?
You need <%= and not just <% in order to interpolate the final result of your template code.
I imagine the erb template driver calls the to_s method on whatever result it gets, and since nil.to_s returns "" then you are safe with a stand-alone false if statement, which has the effect as an expression of returning nil. But somehow having the expression always return a value one would be willing to see interpolated would seem to make sense.

I would rather do something like this.
<tr class="<%= film.viewed ? "viewed" : "" -%>"></tr>

Related

Conditional link_to_if name parameter always evaluated?

I had an impression that link_to_if should work with this code without any problem:
<%= link_to_if locker.student, locker.student.fullname, locker.student do %>
<div>more complicated</div>
<% end %>
I am getting a no method error on "fullname".
So, my impression was that when student exists, link will be created, otherwise block will be rendered.
However, it seems like name parameter is always executed, regardless the if condition, so it simply breaks when there is no student on locker.
Is this true?
If yes (weird), how can i do something like this? What i want is to avoid a standard if.else.
Thx
Yes its always executed: its an argument of the function.
You could do:
<%= link_to_if locker.student, locker.student.try(:fullname), locker.student do %>
<div>more complicated</div>
<% end %>
Or use a standard if wrapping the link_to
Elegant ways to avoid these problems are null objects and/or decorators

If, else dynamic statement logic

Can someone explain the logic behind this code?(This is the correct code btw)
<% if #request.query['first_name'] && !#request.query['first_name'].empty? %>
Welcome! <%= #request.query['first_name'] %>
<% else %>
Hi! What is your name?
<% end %>
My intuition is to write the following instead:
<% if #request.query.inspect['first_name'].empty? %>
Hi! What is your name?
<% else %>
Welcome! <%= #request.query.inspect['first_name'] %>
<% end %>
I am trying to have a user form where people can input their names, when there is no input yet the text above the form says "Hi! What is your name?" when there is an input it has a message saying "Welcome! *User_name*"
The first block of code is not intuitive to me, the second one would make more sense.. ANy advice on how to understand the code?
Your intuition is correct, though you need an alternative to empty?. Rails adds a few different methods you can use:
blank? returns true if the receiver is nil, an empty array, string, or hash, or a string with only whitespace.
present? returns true if blank? is false. So your condition could be:
<% if #request.query['first_name'].present? %>
Welcome...
(I find it's always more intuitive to start with the positive condition - it would work just as well to check blank?).
Edit: It's pretty likely you can skip the query method entirely if all you expect there is either a string or nil. Just use:
<% if #request.query['first_name'] %>
You need to check if it's nil before you can check if its empty, because you are checking a Hash#empty?
irb(main):001:0> nil.empty?
NoMethodError: undefined method `empty?' for nil:NilClass
from (irb):1
irb(main):002:0> {}.empty?
=> true
The code checks for hash key existence, then check if the value of the hash is present. This action can be done in one check using:
#request.query.try(:[], 'first_name').empty?
You can avoid the first condition inside the if statement by transforimng nil into an empty string. I don't know if that is what you meant to do but you almost had.
First, you shouldn't call inspect in the hash because it will transform the entire thing into a 'complex' string. What you want to do turn only the value inside the first_name option, because in that case if the name exists it will still be the same, and if it doesn't, it will be turned into "nil".
Secondly, the method inspect isn't the best choice here, because the returned string will never be empty, given that nil.inspect => "nil". What you should use is the method to_s, wich will behave like this when applied to nil: nil.to_s => "".
Finally, you could update your code to:
<% if #request.query['first_name'].to_s.empty? %>
Hi! What is your name?
<% else %>
Welcome! <%= #request.query['first_name'] %>
<% end %>

Count Method on Undefined Active Record Array #objects.count

Is there a way to write a clean if nil then in a view. Assuming my lack of ruby is biting me here. Example,
If object nil, then return, nothing found
have
<%= #objects.count if #objects %>
want something like this
<%= #objects.count if #objects then "nothing found" %>
There are many ways to write something like this.
Something simple would be:
<% if #objects %>
<%= #objects.count %>
<% else %>
nothing found
<% end %>
If you get into a slightly more complex conditional I would suggest moving the logic into a helper and call it from the view. ex:
<%= count_for(#object) %>
Here's a good solution for you:
<%= "nothing found" unless #objects.try(:length).to_i > 0 %>
One of the issues is that you can't run count on a nil object. Therefore you need to use Rails' super handy .try() method to return nil when #objects = nil, rather than NoMethodError.
Next issue: You can't make a comparison between nil and a number using > so you need to convert the results of #objects.length to an integer which will return 0 for nil.
Lastly, try calling length rather than count. This will avoid running any extra queries when #objects is defined.
Avoids: SELECT COUNT(*) FROM 'objects'
Also if you want to display the count using this one-liner technique you can simply write up a shorthand if/else statement as follows:
<%= #objects.try(:length).to_i > 0 ? #objects.length : "nothing found" %>
One last option:
Use the pluralize method, which can handle a nil count:
Showing <%= pluralize( #objects.try(:length), 'object' ) %>
Sorry, I know this is pretty late, but hopefully helpful for someone else!

Rails View : Idiomatic way to show description for an association object that can be null?

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' %>

How to have an optional local variable in a partial template in rails?

I was thinking that at the top of my partial I would have something like this
<% optional_width = default_value unless (defined? optional_width)
But I've had inconsistent results with this, I'm thinking this is not a good way to do this. What is the "correct" way to do this in rails?
Read the Passing local variables to sub templates section in the ActionView::Base docs
Basically it says you should use this pattern:
<% if local_assigns.has_key? :headline %>
Headline: <%= headline %>
<% end %>
For you, this might translate to something like:
<div style="width: <%= local_assigns.has_key?(:optional_width) ? optional_width : 500 %>px;">
<!-- filler -->
</div>
important!
According to the docs
Testing using defined? headline will not work. This is an implementation restriction.
Although not exactly equivalent to your code, that's usually done with || operator.
<% optional_width ||= default_value %>
This is equivalent to optional_width = optional_width || default_value. Due to shot-circuit evaluation, if optional_with is "true", i.e. it's defined, and not nil, the right-hand part becomes equal to it, and default_value is not even computed. Otherwise, right-hand part would be equal to default_value. That's essentially what you want to do.
Ok, I admit that it may not work for partial's locals. The particular situation I can imagine is that if in first render call the optional_width variable was set to some value, and in the consequent call to render it is not mentioned at all while keeping its value from the first run. Can't do such a check right now, though.

Resources