Optimize IF clause with block in Rails - ruby-on-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.

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

Rails write variable in view without using <%= %>

Let's say I've got the variable #var. Usually I would use <%= #var %> to write it into the view. Now I want to call a module method from within my view which internally decides to write the content of #var or not.
Inside the module I can't use <%= %>. How can I print the content of #var from within the module? The method would be called like this: <% my_method %>. Thanks
Update
Thanks for the answers so far. Maybe I should say more about my initial problem to be more clear. Sorry if I wasn't clear enough.
At first I used the <%= %> tag like this:
def print_if_present(var)
var ? var : ""
end
<%= print_if_present var %>
But then, when the var was nil, I got "" as output, which took space in the view. How can I prevent this behavior?
I assume that your module is actualy the view helper. If is that so, simply return var.
def my_method
if my_condition
#var
else # else clause is optional
#other_var
end
end
Note that the else clause is optional. If you want to write something or nothing, you can simply use the if. This is so because if the if is not executed and there is no else, it will return nil, that will be casted to an empty string in your template. Just to ilustrate,
if true
1
end
=> 1 #return if last value
if false
1
end
=> nil # return nil because there is no else block
Since you still want to print the return of your method on your template, you need to keep the equal sign:
<%= my_method %>
The best way to do this is to have your method return the string and use <%= ... %> as in fotanus’ answer, but in Rails if you really need to write output directly from a helper you could use the concat method:
The preferred method of outputting text in your views is to use the <%= “text” %> eRuby syntax. The regular puts and print methods do not operate as expected in an eRuby code block. If you absolutely must output text within a non-output code block (i.e., <% %>), you can use the concat method.
So you can define a helper like this:
def my_method
if some_condition
concat "Something or other"
else
concat "Something else"
end
end
And then use it in a non-output block:
<% my_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 3 tag_list.each

I have a problem trying to make a list from a acts_as_taggable_on tag_list
I have tag list array, and I want to list it so im trying this:
<%= proyects.tag_list.each do |tagsx| %>
* <%= tagsx %> <br>
<% end %>
And I get the list im looking for, but also the whole array again...
When it renders, looks like this..
* AJAX
* Rails
* Heroku
* Prototype
AJAX, Rails, Heroku, Prototype
Any ideas on getting rid of the last line?
Or do you guys know a more efficient way of achieving this?
Thanks in advance.
Change this:
<%= proyects.tag_list.each do |tagsx| %>
to this:
<% proyects.tag_list.each do |tagsx| %>
You don't want to output the return value of the .each call, just the elements of the array. Calling Array#each with a block returns the array (as you are):
each {|item| block } → ary
each → an_enumerator
Calls block once for each element in self, passing that element as a parameter.
If no block is given, an enumerator is returned instead.
and that's were the comma delimited list is coming from.
because you have a typo in your code :-)
<%- proyects.tag_list.each do |tagsx| %>
* <%= tagsx %> <br>
<% end %>
see the difference?
no '=' after the first % sign
%= means that the result of a Ruby expression is returned to the view
%- means that the Ruby expression is evaluated, but no result is returned
The code in your question gets "proyects.tag_list" , executes the loop, during which it prints out the individual tags, and then returns the whole array to the view because of the '='

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