Parsing XML and how to handle nested Arrays and Hashes - ruby

I'm using this instance variable:
#response = HTTParty.get("http://www.bart.gov/dev/eta/bart_eta.xml")
I'm trying to parse the xml using rails 3.2:
<% #response.each do |r| %>
<% r.each do |root| %>
<%= root.class %>
<% end %>
<% end %>
The output is
String Hash
I get "String Hash" for "root.class". I don't understand how it can be "String Hash," I would like to implement another "each" method to go deeper in the xml layers.
What does "String Hash" mean?

Your #response object is of the type HTTParty::Response.
It looks like it's wrapping an array with two values in it: the first value is a String, "root", and the second value is a Hash.
Since you have no line breaks in your ERB code, as you iterate through the array it is printing out String and Hash on the same line.
Try using root.inspect to dig deeper into what values you're actually iterating through.

Related

Iterate over a singleton or multiple of a returned DataMapper class

I have the following code
if admin_authorized?
#allschools = School.all
elsif faculty_authorized?
#allschools = School.get(Faculty.get(session[:faculty_id]))
end
I am getting this error
undefined method `each' for #<School:0x007f88a1360318>
#allschools will either be an array of schools or a singleton school. I need to iterate over this list in the view, like so:
<% #allschools.each do |s| %>
<%= f_optionselected(s.id.to_s, params[:school], s.name) %>
<% end %>
I've tried to do if statements with #allschools.count, #allschools.typeof(Array), ....
Should I use two different variables and a PHP type isset() statment to determine which block to display? Or is there a way to iterate over the statement in some Ruby way?
School.all returns an ActiveRecord::Relation and you can iterate through it, as you do with each. I think School.get returns a single School instance, right? Try to wrap it into an Array:
#allschools = [School.get(Faculty.get(session[:faculty_id]))]

Undefined method each for nil:NilClass in erb template

I'm trying to loop through and array and have checked to make sure it is being passed correctly to the template, yet am hitting the error.
The template:
<% if defined?(source[:blacklist]) %>
"blacklist": [
<% source[:blacklist].each do |listed| %>
"<%= listed %>"
<% end %>
],
<% end %>
output of source[:blacklist] when no loop:
"[\"/var/log/httpd/access.log*\", \"/var/log/httpd/error.log*\", \"/var/log/httpd/ssl_request_log\", \"/var/log/httpd/access_log\", \"/var/log/httpd/error_log\"]"
error:
undefined method `each' for nil:NilClass
Your array is not present, if it was it would actually be a string.
Using defined?(source[:blacklist]) presents an issue as it will return true if it's not an array, e.g. defined? nil is "nil" which is truthy.
Step 1: convert to array
If you cannot change how the data is being generated, parse it into a Ruby array, defaulting to an empty array if there is an unexpected data or none at all.
blacklist = source[:blacklist].gsub(/(\­[\"|\"\])/­, '').s­plit('", "') || []
Step 2: loop if the array exists and with content
<% if not blacklist.empty? %>
If source[:blacklist] is nil then defined? source[:blacklist] will return the string "method" and that's "truthy".
So it will fall through and you'll get the error you see... Undefined method each for nil:NilClass
When you say "output of source with no loop"... is that in the controller or did you check the output in the erb file? It could be the value's not being passed to the erb, and you typically want to use instance variables to pass data to html.erb files.
Also, you should note that the output you show for source[:blacklist] is actually a string, not an array, so even if it was passed you would still get an error on the each method.

Array of Ruby objects returning strings on each method. Why?

Useful additional info: I am using the decent_exposure gem so this might be the issue - correcting the code below:
expose(:get_filter_tags) do
if params[:filter_tag_names]
filter_tag_names = Array(params[:filter_tag_names].split(" "))
filter_tags = Array.new
filter_tag_names.each do |f|
t = Tag.find_by_name(f)
filter_tags << t
end
end
end
So, something funny happens when I call this in the view:
query string ?utf8=✓&filter_tag_names=test
<% get_filter_tags.each do |ft| %>
<%= ft.name %>
<% end %>
Error message: undefined method `name' for "test":String
Why is this trying to call name on a string not a Tag object? If I put the following in the view, and have jut one filter_tag_names item
def getfiltertag
Tag.find_by_name(params[:filter_tag_names])
end
#view
<%= getfiltertag.name %>
query string: ?utf8=✓&filter=test
like above then I can call name just fine, so obviously I am doing something wrong to get an array of strings instead of objects. I just don't know what. Any suggestions?
Your problem is that each returns self — so if you write filter_tag_names.each, it returns filter_tag_names. You could fix this by explicitly returning filter_tags, but more idiomatically, you could just rewrite it as:
expose(:get_filter_tags) do
if params[:filter_tag_names]
filter_tag_names = Array(params[:filter_tag_names].split(" "))
filter_tag_names.map {|f| Tag.find_by_name(f) }
end
end
Just as an aside, this method will return nil if there aren't any filter tag names. You may want to do that, or you might want to return an empty collection to avoid exceptions in the calling code.

Rails 3 refactoring issue

The following view code generates a series of links with totals (as expected):
<% #jobs.group_by(&:employer_name).sort.each do |employer, jobs| %>
<%= link_to employer, jobs_path() %> <%= "(#{jobs.length})" %>
<% end %>
However, when I refactor the view's code and move the logic to a helper, the code doesn't work as expect.
view:
<%= employer_filter(#jobs_clone) %>
helper:
def employer_filter(jobs)
jobs.group_by(&:employer_name).sort.each do |employer,jobs|
link_to employer, jobs_path()
end
end
The following output is generated:
<Job:0x10342e628>#<Job:0x10342e588>#<Job:0x10342e2e0>Employer A#<Job:0x10342e1c8>Employer B#<Job:0x10342e0d8>Employer C#<Job:0x10342ded0>Employer D#
What am I not understanding? At first blush, the code seems to be equivalent.
In the first example, it is directly outputting to erb, in the second example it is returning the result of that method.
Try this:
def employer_filter(jobs)
employer_filter = ""
jobs.group_by(&:employer_name).sort.each do |employer,jobs|
employer_filter += link_to(employer, jobs_path())
end
employer_filter
end
Then call it like this in the view:
raw(employer_filter(jobs))
Also note the use of "raw". Once you move generation of a string out of the template you need to tell rails that you don't want it html escaped.
For extra credit, you could use the "inject" command instead of explicitly building the string, but I am lazy and wanted to give you what I know would work w/o testing.
This syntax worked as I hoped it would:
def employer_filter(jobs_clone)
jobs_clone.group_by(&:employer_name).sort.collect { |group,items|
link_to( group, jobs_path() ) + " (#{items.length})"
}.join(' | ').html_safe
end

Word Count with Ruby

I am trying to figure out a way to count a words in a particular string that contains html.
Example String:
<p>Hello World</p>
Is there a way in Ruby to count the words in between the p tags? Or any tag for that matter?
Examples:
<p>Hello World</p>
<h2>Hello World</h2>
<li>Hello World</li>
Thanks in advance!
Edit (here is my working code)
Controller:
class DashboardController < ApplicationController
def index
#pages = Page.find(:all)
#word_count = []
end
end
View:
<% #pages.each do |page| %>
<% page.current_state.elements.each do |el| %>
<% #count = Hpricot(el.description).inner_text.split.uniq.size %>
<% #word_count << #count %>
<% end %>
<li><strong>Page Name: <%= page.slug %> (Word Count: <%= #word_count.inject(0){|sum,n| sum+n } %>)</strong></li>
<% end %>
Here's how you can do it:
require 'hpricot'
content = "<p>Hello World...."
doc = Hpricot(content)
doc.inner_text.split.uniq
Will give you:
[
[0] "Hello",
[1] "World"
]
(sidenote: the output is formatted with awesome_print that I warmly recommend)
Sure
Use Nokogiri to parse the HTML/XML and XPath to find the element and its text value.
Split on whitespace to count the words
You'll want to use something like Hpricot to remove the HTML, then it's just a case of counting words in plain text.
Here is an example of stripping the HTML: http://underpantsgnome.com/2007/01/20/hpricot-scrub/
First start with something able to parse HTML like Hpricot, then use simple regular expression to do what you want (you can merely split over spaces and then count for example)

Resources