Defining a method to select values from a hash.

D

Danny Tatom

I'm terribly confused with this, and can't seem to figure it out (or
explain it).

Using HTTParty, I have a method that grabs some XML and returns it as a
hash:

def check(data)
@results = self.class.post('/checkDocument', :query => {:key =>
@key,
:data => data})
end

def errors
errors = @results['results']['error']
end

An example of the hash it returns:=> [{"suggestions"=>{"option"=>["really", "ready", "real", "relay",
"realty"]}, "precontext"=>"I'm", "type"=>"spelling", "string"=>"realy",
"description"=>"Spelling"}, {"suggestions"=>{"option"=>["weather",
"whether"]},
"url"=>"http://service.afterthedeadline.com/info.slp?text=wether",
"precontext"=>"this", "type"=>"spelling", "string"=>"wether",
"description"=>"Did you mean..."}]

Now calling the .errors method, I can get the string from each one like
this:
atd.errors.each { |error| p error['string'] }
"realy"
"wether"

What I'd like to do is write a method for string, so that
atd.errors.each { |error| p.error.string } would result in the same
thing. For the life of me, I can't figure out how to do that. :/

The other problem I'm having with the way I'm doing it now is that
calling .errors.each when there's only one error sent back gives me a
"can't convert String into Integer" error.

So, if anyone could point me in the right direction, I'd appreciate it.
:)
 
B

Brian Candler

You want foo['bar'] and foo.bar to be the same thing, like in
Javascript? In that case you need to modify Hash, not String. Below is
some code which does that.

---- 8< ----
# Extend a Hash with this module to get semantics of a Javascript
object:
# me.foo is the same as me['foo']
module JSObjectMixin
def method_missing(meth,*rest,&blk)
key = meth.to_s
if key[-1] == ?=
self[key[0..-2]] = rest.first
else
self[key]
end
end
end

def extend_jsobject(tree)
case tree
when Hash
tree.extend JSObjectMixin
tree.each do |k,v|
extend_jsobject(v)
end
when Array
tree.each do |v|
extend_jsobject(v)
end
end
end

errors = [{"suggestions"=>{"option"=>["really", "ready", "real",
"relay",
"realty"]}, "precontext"=>"I'm", "type"=>"spelling",
"string"=>"realy",
"description"=>"Spelling"}, {"suggestions"=>{"option"=>["weather",
"whether"]},
"url"=>"http://service.afterthedeadline.com/info.slp?text=wether",
"precontext"=>"this", "type"=>"spelling", "string"=>"wether",
"description"=>"Did you mean..."}]
extend_jsobject(errors)

errors.each { |error| p error['string'] }
errors.each { |error| p error.string }
---- 8< ----

Another option would be to walk your results structure and replace each
Hash with an OpenStruct (see ostruct.rb in the standard library).
However, that would _only_ allow error.string, not error['string']
The other problem I'm having with the way I'm doing it now is that
calling .errors.each when there's only one error sent back gives me a
"can't convert String into Integer" error.

Simple solution:

Array(atd.errors).each { ... }

If atd.errors is already an Array this does nothing; if it isn't, then
it is wrapped in a one-element array.

Better solution: If this is using XML::Simple internally, there's an
option you can give it to return arrays always, even when there's only a
single instance (or I believe you can tell it to do this for specific
tags). I can't remember offhand what that option is, but you should be
able to find it. Then see if the library you're using allows that option
to be passed through.

HTH,

Brian.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,981
Messages
2,570,187
Members
46,730
Latest member
AudryNolan

Latest Threads

Top