What's the difference between these two ways of using ruby blocks? - ruby-on-rails

This is question is added to my last one.
The problem in my last question was solved by changing the following code:
<p><%= each(2,16,3){|x| x } %></p>
to
<p>
<% each(2,16,3) do |x| %>
<%= x %>
<% end %>
<p>
But I still don't know the difference between the one line style delimited by {} and 3 lines styles by using do and end tag

The first version:
<p><%= each(2,16,3){|x| x } %></p>
takes the return value of the entire each method call and tries to output it. The second version:
<p>
<% each(2,16,3) do |x| %>
<%= x %>
<% end %>
<p>
takes each individual item one at a time and outputs it (since you are evaluating the output inside the block). The actual return value of the each method is not used.
As mentioned by others, this only matters when you need to do some sort of output/calculation inside the block, which each value yielded to the block; the rest is just semantics. The following are the same:
evens = (0..10).to_a.delete_if { |value| value.odd? }
and
evens = (0..10).to_a.delete_if do |value|
value.odd?
end

Related

Rails different output if block is given inline or over multiple lines

I'm having trouble finding out what I did wrong or how to achieve the same result.
What I'm trying to do is to print the array #attributes in a rails view.
This is not working:
<% #attributes.each do |element| %>
<%= puts element.to_s %>
<% end %>
But this is:
<%= #attributes.each { |element| puts element.to_s }%>
I've also played around with putting everything in the first statement in <%= %> without any success.
Aren't those two statements exactly the same?
Also, if you could help me out - how would you iterate over the array and insert a new line after each item?
Thanks in advance!
Alex
puts does not actually output to the ERB buffer. It outputs to STDOUT and returns nil. If you want to iterate through the records and output you would do it by:
<% #attributes.each do |element| %>
<%= element.to_s %>
<% end %>
The <%= %> ERB tags output the return value of the expression into the buffer. If you wanted to do this with a non-printing ERB expression you would need to use concat:
<% #attributes.each { |element| concat element.to_s } %>
Aren't those two statements exactly the same?
No. The output is actually the same as:
<% #attributes.each do |element| %>
<%= nil %>
<% end %>
And
<%= #attributes %>
Rails uses the ERB templates to render the views or say generate html documents for the browsers amongst others.
ERBcopies the text portions of the template directly to the generated document, and only processes code that is identified by markers. There are mostly two types of markers <% %> and <%= %>.
A tag with an equals sign indicates that the enclosed code is an expression and that the renderer should substitute the code element with the result of the code (as a string) when it renders the template.
Tags without the equals sign denote that the enclosed code is a scriptlet. Each scriptlet is caught and executed, and the final result of the code is then injected into the output at the point of the scriptlet.
<% #attributes.each do |element| %>
<%= puts element.to_s %>
<% end %>
With your example above you're telling the renderer to put whatever is the result of the expression in the second line, which is the output of the element.
Whereas here <%= #attributes.each { |element| puts element.to_s }%> you are asking rendered to put the #attributes object/variable along with the individual output of whatever is inside in #attributes variable which explains the difference in your output results.
PS you can avoid both puts and to_s from <%= puts element.to_s %> because whatever is inside <%= %> gets to the HTML document as a string itself.

Fixnum.times { image_tag('path_to_file') } returns the fixnum

I was trying to refactor my erb code for rating stars into one line and came across this, is it possible to achieve the results another way?
So currently it's like this
<% rating.times do %>
<%= image_tag 'rating_star.png' %>
<% end %>
Ideally I'd like to reduce this to
<%= rating.times { image_tag 'rating_star.png' } %>
This returns the value of rating (0, 1, 2, 3, 4 or 5), if I change <%= to <% nothing is rendered. Is there any way to do this?
You can use concat:
<% rating.times { concat image_tag('rating_star.png') } %>
But <%= ... %> within a loop is cleaner IMO.
You need #map to return an array of all image_tags you wish.
Then #join the result to get a string.
Lastly, you need to tell this string is valid html with #html_safe.
<%= rating.times.map{ image_tag 'rating_star.png' }.join.html_safe %>
The difference between <% %> and <%= %> is that the former is simply evaluated, but with the latter the result is rendered. You have two things going on in your code snippet: the control part (the loop), and the rendering part (the image tag). Since you want only the image tag part rendered, you want to surround only that code with <=% %>, and the rest with <% %>.
So while this would work:
<%= rating.times { %> <%= image_tag 'rating_star.png' %> <% } %>
I find it much less readable than the 3-line version.

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

Rails 2: How can I get the return object of a method and print it on a view?

I developed hours ago a fuzzy method on store_controller.rb. This method, named fuzzy, gets the product quantity as argument, then, with if statements, checks the value and returns an object named disp. I call this method on the view index.html.erb.
Now, in the view, I inserted a line that checks the value of disp. By the value of disp, the view must print out a different image that displays the available quantities of product, for example, available or not available. But, inserting this line, I got an error: undefined local variable or method 'disp' for #<ActionView::Base:0x6dc1864>
This is the code of the view index.html.erb:
<% for product in #products -%>
<div class="entry">
<%= image_tag(product.image_url) %>
<h3><%=h product.title %></h3>
<%= product.descr %>
<br /><div class="disp">
<p><% fuzzy(product.quantity) %><% if disp == 0 %><%= image_tag("nodisp.png", :border => "0") %><% end %><% if disp == 1 %><%= image_tag("disp.png", :border => "0") %><% end %></p>
</div><br />
<p><span class="price"><%= number_to_currency(product.price, :unit => "€") %></span></p>
<% form_remote_tag :url => { :action => :add_to_cart, :id => product } do %>
<%= submit_tag "add to cart!" %>
<% end %>
</div>
<% end %>
Please notice the call of method fuzzy. This method is inserted in store_controller.rb:
def fuzzy(q)
disp = (q-5)/20
if q > 9 then disp = 1 end
if q < 7 then disp = 0 end
return disp
end
The question is: When I execute the code in the view, calling fuzzy method, how I can get the result disp of the method, and, with if statements, diplay it on the view? Is it possible?
I'm going to address the issue you have, and the solution.
The issue is that you are expecting access to the variable disp in your view, however this variable is out of scope after the function fuzzy is done executing. If this doesn't make sense, I would read about scope in programming.
The solution is that your method returns a value(and in Ruby you don't actually need to explicity state return in this case becauase the last executed line is returned) so now you need to assign a variable with the value of what was returned from that fuzzy call.
The solution(for your code to work) is to set a variable to the return value like so:
<% disp = fuzzy(product.quantity) %>
Keep in mind that this disp is a different one from what you created in your function fuzzy. They are in two different scopes.
Since you are already returning the value that you need from the fuzzy method,
you can replace the <% fuzzy(product.quantity) %> with <% disp = fuzzy(product.quantity) %> to set a variable named disp to the return value of the fuzzy method. And that should work with the rest of your code.

rails - when using Group_by - How to get an Index?

I have the following:
sets = DataSet.all.group_by{ |data| [data.project_id, "-", data.thread_id].join(" ") }
<% sets.each do |range, datas| %>
<p><%= range %>:</p>
<% datas.each do |data| %>
<%=data%>
<p>Last Post<%= data.last.created_at %></p>
<% end %>
<% end %>
Problem is that I need an index. So i updated the above with:
<% sets.each_with_index do |range, datas, i| %>
<p><%= range %>:</p>
<% datas.each do |data| %>
<%= i %>
<%=data%>
<p>Last Post<%= data.last.created_at %></p>
<% end %>
<% end %>
That then breaks, with the error: undefined method `last' for 0:Fixnum
Ideas? thank you
The issue you observe is because of the way parameters are assigned to the block. In your second example, you will observe that range contains an array containing a single range and the matching datas, the datas variable contains the index and i is always nil.
This is because ruby "unsplats" arrays if it is the only parameter to the block. If you have more than one type (in this case an array and an integer), you must hint ruby on what it should do. The simplest way is to use parentheses.
<% sets.each_with_index do |(range, datas), i| %>
...
<% end %>
That way, ruby will know what you mean and split the array up into range and datas. This is actually a feature of ruby's assignment operator in conjunction with the comma operator. It works like this
my_array = [1, 2]
(x, y) = my_array
p x # prints 1
p y # prints 2

Resources