Unexpected Hash Output - ruby

I've created a hash through a loop that looks like this.
<% ship_hash = {} %>
<% #order_preview.fedex_rates.each do |rate| %>
<% if rate.service_name == "FedEx Ground Home Delivery" || rate.service_name == "FedEx 2 Day" || rate.service_name == "FedEx Standard Overnight" %>
<% ship_hash["#{rate.service_name}"] = "#{number_to_currency(rate.price.to_f / 100)}" %>
<% end %>
<% end %>
<% #order_preview.usps_rates.each do |rate| %>
<% if rate.service_name == "USPS Priority Mail 1-Day" %>
<% ship_hash["#{rate.service_name}"] = "#{number_to_currency(rate.price.to_f / 100)}" %>
<% end %>
<% end %>
What I am trying to do is simple. I want to get the output of the keys and values through a block.
>> ship_hash
=> {"FedEx Ground Home Delivery"=>"$9.78", "FedEx 2 Day"=>"$20.59", "FedEx Standard Overnight"=>"$33.78", "USPS Priority Mail 1-Day"=>"$5.60"}
Ok, that's what I expected...
>> ship_hash.each { |key, value| puts "#{key}: #{value}" }
I get
=> {"FedEx Ground Home Delivery"=>"$9.78", "FedEx 2 Day"=>"$20.59", "FedEx Standard Overnight"=>"$33.78", "USPS Priority Mail 1-Day"=>"$5.60"}
When I thought I would get something like this
FedEx Ground Home Delivery: $9.78...
On the RubyMonk primer page here is why I am confused. I don't get the output I expect!!
I am running the commands from better_errors after I <%=raise %> after the code generates hash.
I must be missing something, this is basic and unexpected... Feel free to suggest alternate titles to help future confundos ...
Update
Well it sounds like a live shell isn't the place to run these commands, as it only returns the hash itself. What I am trying to do in practicality is populate a f.select field with both the key and value of the hashes. Not to change the question too much, but How could I populate the option of a select field so it displays the key and values next to eachother?

If you go in irb (or rails console) and type the following:
ship_hash = {"FedEx Ground Home Delivery"=>"$9.78", "FedEx 2 Day"=>"$20.59", "FedEx Standard Overnight"=>"$33.78", "USPS Priority Mail 1-Day"=>"$5.60"}
ship_hash.each { |key, value| puts "#{key}: #{value}" }
You'll see the following output:
FedEx Ground Home Delivery: $9.78
FedEx 2 Day: $20.59
FedEx Standard Overnight: $33.78
USPS Priority Mail 1-Day: $5.60
=> {"FedEx Ground Home Delivery"=>"$9.78", "FedEx 2 Day"=>"$20.59", "FedEx Standard Overnight"=>"$33.78", "USPS Priority Mail 1-Day"=>"$5.60"}
What's happening is that ruby is printing to the console the expected output, and then returning the hash itself as the return value.
So you should see the output that you expected in your rails logs. However, the html being displayed is the return value of your each statement, which is just the hash itself.
What you want to do is something like this:
<ul>
<% ship_hash.each do |key, value| %>
<li><%= key %>: <%= value %></li>
<% end %>
</ul>
This will output the key value pairs of the hash in the form of an unordered list.

Related

Retrieve data using for loop in Ruby

I have a question about using foreach loops in Ruby.
I want to display documents and am using a foreach loop in order to display these documents. It returns an error with the i variable inside of data["response"]["docs"][i]["topic"] which is a JSON string I am iterating over.
I do not understand why that is. Can anyone tell me what I am doing wrong?
If I simply do data["response"]["docs"][0]["topic"] it works fine but not with the i. Why is that?
<%
(0..10).each do |i|
%>
<%= i %> <br/>
<%= data["response"]["docs"][i]["topic"] %>
<%
end
%>
My question is, how many items are there in data["response"]["docs"]? Are there exactly 11? Either way I would use the following code instead:
<% data["response"]["docs"].each_with_index do |item, index| %>
<%= index %>
<br/>
<%= item["topic"] %>
<% end %>
This iterates over the data["response"]["docs"] no matter how many there are (whether is is 1 doc or 20 docs) and stores the value in the variable named item. The each_with_index function gives you the index as well, stored in index, so you can display it later. If you only want the first 11 use:
<% data["response"]["docs"].first(11).each_with_index do |item, index| %>
This will grab a maximum of 11 doc items.
It's hard to tell what might be going wrong because you haven't posted the error, but if you're using a 10-element array, you want to do:
(0..9).each do |i|
With 0-based indexes, you should only use the range from 0-9, rather than 0-10. You may be getting an error because you're trying to access an element that isn't there (i.e. at index 10).
Even better is:
<% data["response"]["docs"].each do |document| %>
<%= document["topic"] %>
<% end %>
or if you need to print the index:
<% data["response"]["docs"].each_with_index do |document, index| %>
<%= index %> <br/>
<%= document["topic"] %>
<% end %>

Redmine modification for issue number

I would like to hide the issue number on the issue list.
I use "Redmine 2.3.1" and the file to achieve that is this file: ./app/views/issues/_list.html.erb
It contains this line:
<%= raw query.inline_columns.map {|column| "<td class=\"#{column.css_classes}\">#{column_content(column, issue)}</td>"}.join%>
And the array query.inline_columns contains the id for example. I thought modifying this array would help me with my problem, but the changes aren't saved.
This is what I tried:
<%= #query.inline_columns.inspect %>
=> [#<QueryColumn:0x00000003607928 #name=:id ... and so on>]
<% #query.inline_columns.delete_at(0) %>
=>
<%= #query.inline_columns.inspect %>
=> [#<QueryColumn:0x00000003607928 #name=:id ... and so on>]
Why was the delete_at useless? And how would I modify this array or hide the number?
A few things to note:
#query.inline_columns might not an array. It may quack like an Array, but it's not.
#query.inline_columns.delete_at(0) should have returned something. Did it return and you ommited?
And last, but not least, the most probable guess I can make:
#query.inline_columns fetches something, converts it to an array and dups it before returning. The only way you can do something like that would be:
some_columns = #query.inline_columns
some_columns.delete_at(0)
some_columns
This is my solution now:
<% if #project.id == 1 %>
<% #qcols = query.inline_columns.reject{ |col| col.name.to_s == 'id' } %>
<% else %>
<% #qcols = query.inline_columns.each %>
<% end %>
So I took advantage of the reject method. And then I work with the #qcols.
Posted on behalf of OP.

In a Ruby each block, how do I do something to the last record in the array within the block?

Say I have a block like this:
<% #help_sections.each do |section| %>
<li><%= section.name %></li>
<% end %>
But on the last record returned, I want to do something else, e.g. applying a class to the li that's there:
<li class="last"><%= section.name %></li>
How do I do that in the most DRY way?
Thanks.
Edit1:
I imagine I would simply use an if statement and the last ruby method, but not sure how to do that within the block? I know that if I just wanted the last element in that array, I could just do #help_sections.last, but that doesn't make sense within the confines of a Ruby block.
The most DRY way is to use CSS instead. Instead of e.g. this:
li.last { color: red; }
..and then cluttering up your markup with an extra CSS class, just use the :last-child pseudoselector, i.e.:
li:last-child { color: red; }
Then you don't have to change anything in your view. This is supported in all modern browsers including IE9.
Try each_with_index:
<% #help_sections.each do |section, index| %>
<li <%= "class='last'" if index == (#help_sections.length-1) %>><%= section.name %></li>
<% end %>
DRY is a good idea in general, but don't kill yourself to keep from repeating a li.
<% #help_sections[0...-1].each do |section| %>
<li><%= section.name %></li>
<% end %>
<li class="last"><%= #help_sections.last.name %></li>
You could either do something like
<% #help_sections.count.times do |i| %>
<%= #help_sections[i].name %>
<%= do_something if #help_sections.count == i - 1 %>
<% end %>
This old answer might help (Tell the end of a .each loop in ruby). Basically, you can use:
<% #help_sections.each_with_index do |section, index| %>
<% if index == #help_sections.size - 1 %>
<li class="last">
<% else %>
<li>
<% end %>
<%= section.name %></li>
<% end %>
If you use each_with_index instead of plain each, the block will also be passed the index of the current element within the collection. You can then compare that to #help_sections.length.
E.g.
<% #help_sections.each_with_index do |section, i| %>
<li<% concat " class='last'" if i == #help_sections.length - 1 %>><%= section.name %></li>
<% end %>
In general cases not covered by smart CSS selectors, you can define a convenient helper method, which would provide each iterated element with its context within the collection. Like this one:
def each_with_context enum
length = enum.count
enum.each_with_index do |elem, i|
context = {
:first => i == 0,
:last => i == length - 1,
:even => i.even?,
:odd => i.odd?,
:middle => i == length / 2
}
yield elem, context
end
end
And then use it within HAML view like this:
-each_with_context(#help_sections) do |section, context|
%li{:class => context[:last] ? 'last' : nil}
=section.name

persistent counter in Ruby/Rails for each

I have the following code that gives me a bunch of locations and an index for each location.
<%= paginate #locations %>
<% #locations.each_with_index do |location, index| %>
<h1><%= index + 1 %></h1>
<h2><%= location.name %></h2>
<% end %>
Everything works fine, I get all the elements in the right order with the right index (palace hast the most visits, then bar, etc.):
1 Palace
2 Bar
3 Cinema
4 Restaurant
5 Ballon
I use Kaminari for pagination and if I klick on the second page to show the next 5 locations, the index starts again at 1, but it should give me 6.
next page (should continue the index with 6)
1 Arena (should be 6)
2 Stadion (should be 7)
and so on ...
So how do I get a persistent counter/index which continues on the next page?
Thanks in advance
Update
I came up with the following, works great so far:
<% if params[:page].nil? || params[:page] == "0" || params[:page] == "1" %>
<% x = 0 %>
<% else %>
<% page = params[:page].to_i - 1 %>
<% x = page * 10 %>
<% end %>
<% #locations.each_with_index do |location, index| %>
<%= index + x + 1 %>
...
each_with_index is a plain ruby method (Enumerable I think) that doesn't know anything about the pagination structure. You may have to provide a additional calculation, unless Kaminari provides something (I haven't use Kaminari yet)
If this were will_paginate, I'd do something like this, and maybe it would work for you too:
<h1><%= index + 1 + ((params[:page] || 0 ) * #items_per_page ) %></h1>
(you have to set #items_per_page yourself)

Ruby On Rails XML to View

I have a controller with an API request showing all my Google Docs.
feed = client.get('http://docs.google.com/feeds/documents/private/full').to_xml
feed.elements.each('entry') do |entry|
puts 'title: ' + entry.elements['title'].text
puts 'type: ' + entry.elements['category'].attribute('label').value
puts 'updated: ' + entry.elements['updated'].text
puts 'id: ' + entry.elements['id'].text
# Extract the href value from each <atom:link>
links = {}
entry.elements.each('link') do |link|
links[link.attribute('rel').value] = link.attribute('href').value
end
puts links.to_s
end
So, I can see the results in my console but how do I get them into my view?
I tried with something like this, but that doesn't work (I changed my variable in the controller to an accessor of course)
<% feed.elements.each('entry') do
|entry| %> <%
entry.elements['title'].text %> <%
end %>
First, in your controller, make feed an instance variable. IE: it should be:
#feed = client.get..... instead of feed = client.get....
If that doesn't fix it... I don't know your API for sure, but I suspect you may need to be using:
<% #feed.elements.each('entry') do |entry| %> <% entry['title'] %> <% end %>
Note: entry['title'] instead of entry.elements['title'].text
What your current code indicates is that the feed is structured like this:
feed.elements[0].elements['attr'].text, when it's probably just feed.elements[0]['attr']
Does that make sense? Try that and see what happens.
If that doesn't work, just put: debug(#feed) in your view and copy and paste it to the end of your question. That'll help us figure out the right way to access this info.
Problem solved. Because I use 'puts' in the controller to show the content of the feed in the console I also have to change that for the view. Of course, puts is equal to <%= ... %>.
<ul>
<% #feed.elements.each('entry') do |entry| %>
<li><%= 'title: ' + entry.elements['title'].text %></li>
<% end %>
</ul>

Resources