Defining a method to select values from a hash


#1

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:

atd.check(“I’m realy hating this wether”)
=> [{“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. :confused:

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.
:slight_smile:


#2

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.