Conditional link_to_if name parameter always evaluated? - ruby-on-rails

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

Related

Optimize IF clause with block in Rails

I have something like:
<%= "<p class='...'>#{product.value}</p>".html_safe if product.value %>
So I want to show the value wrapped in some html if it exists. Problem is that value is a method that requires some calculations and the way above for every product value is calculated two times which doubles my page loading time.
Any way to optimize this?
For readability reasons I would be very explicit:
<% if (calculated_value = product.value) %>
<p class='...'><%= calculated_value %></p>
<& end %>
I think the following should work:
<%= product.value.then { |v| content_tag(:p, v, class: '...') if v } %>
The value method is only called once on the product object. After that, it is passed to a block then that will define what is the result of the overall expression. Without calling again the value method, inside the block we determine what is the value that should be returned (if any).
Note: The generation of the HTML object has been replaced by an invocation of the content_tag helper method.

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

Rails: undefined method error not fixable with attr_accessor

I have created a very simple model called Discussion and one of the columns is a boolean called resolved. The idea being that once a discussion item has been resolved that value is set to true.
On the edit form, I tried to put in some logic based on the value of that field.
<%= form_for(#discussion) do |d| %>
...
<% if d.resolved == "true" %>
<p>The discussion is resolved</p>
<% else %>
<p>The discussion is not resolved</p>
<% end %>
<% end %>
However, I'm getting an error message
undefined method `resolved' for #<ActionView::Helpers::FormBuilder:0x00000101674678>
I tried adding an attr_accessor line to my model but that didn't do anything for me either. I'm not sure what I have to do to fix this. I'm pretty new to rails, so I'm sure that whatever the problem is it's probably pretty simple to fix, but I just don't get it. Thanks.
Because d represent an instance of the form builder, you want
<% if #discussion.resolved %>
If resolved is represented as a "boolean" in ActiveRecord.
every boolean column represents as predicate, so you can use:
if #discussion.resolved?
...
end
What you're looking for is the resolved? method.
<% if #discussion.resolved? %>
which is auto-generated for boolean columns.

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: Fields_for without a scope?

Is there a way to use fields_for with in a form without having a scope?
For example:
<% fields_for "user[]" do |x|
<%= x.text_field :name %>
<% end %>
Without the user model being loaded in memory?
I got it working using territory[user][][name], but I would like to keep it in ERB.
I think the answer would be 'no', since those form_for and fields_for would try to determine default value from that given instance variable.
However, I think if you want to lower memory usage from loading that model, you might try to create a mock-up model to return nil values, and create a instance object from that one instead.
is there any specific reason you need to use form_for specifically? Its really designed to be used with an instantiated model object.
Alternatively, why don't you just use the regular form helper tags. You can define it as follows:
<%form_tag :my_form do %>
<%= text_field_tag :foo, :bar %>
<%end%>
You may want to check the documentation for action view to see how it all works.

Resources