[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Is there a better way to do this?

Paul Mckibbin

4/21/2009 7:02:00 PM

I recently had code which needed to work with the same structure twice,
once using the . nomenclature and once with an index into a hash.
i.e. in one case I had

entries=@log.entry

which I would interrogate with:

entries.timings.connect.first_byte

or alternatively

entries.XmlSimple(in_file)

which I would interrogate with:

entries['timings']['connect']['first_byte'].

Rather than write two blocks for code, one for the in-memory, I decided
to override the base structure that XmlSimple used, but I found out that
it was Hash. So the following was what I came up with:

class Hash
def method_missing(sym,*args,&blk)
return self[sym] if self.keys.include?(sym)
return self[sym.to_s] if self.keys.include?(sym.to_s)
super
end
end

which works for me, but I was wondering if there is a better way to do
this than calling self.keys.include? to determine if the key exists
without triggering any unexpected side effects.

Mac
http:...
--
Posted via http://www.ruby-....

10 Answers

Robert Dober

4/21/2009 7:44:00 PM

0

On Tue, Apr 21, 2009 at 9:01 PM, Paul Mckibbin <pmckibbin@gmail.com> wrote:

> =A0class Hash
> =A0 =A0def method_missing(sym,*args,&blk)
A time bomb!
But I might do the same and ...
... pay the same price debugging.

Just my 0.02$

Cheers
Robert


--=20
Si tu veux construire un bateau ...
Ne rassemble pas des hommes pour aller chercher du bois, pr=E9parer des
outils, r=E9partir les t=E2ches, all=E9ger le travail=85 mais enseigne aux
gens la nostalgie de l=92infini de la mer.

If you want to build a ship, don=92t herd people together to collect
wood and don=92t assign them tasks and work, but rather teach them to
long for the endless immensity of the sea.

--
Antoine de Saint-Exup=E9ry

matt

4/21/2009 7:51:00 PM

0

Paul Mckibbin <pmckibbin@gmail.com> wrote:

> I recently had code which needed to work with the same structure twice,
> once using the . nomenclature and once with an index into a hash.
> i.e. in one case I had
>
> entries=@log.entry
>
> which I would interrogate with:
>
> entries.timings.connect.first_byte
>
> or alternatively
>
> entries.XmlSimple(in_file)
>
> which I would interrogate with:
>
> entries['timings']['connect']['first_byte'].
>
> Rather than write two blocks for code, one for the in-memory, I decided
> to override the base structure that XmlSimple used, but I found out that
> it was Hash. So the following was what I came up with:
>
> class Hash
> def method_missing(sym,*args,&blk)
> return self[sym] if self.keys.include?(sym)
> return self[sym.to_s] if self.keys.include?(sym.to_s)
> super
> end
> end
>
> which works for me, but I was wondering if there is a better way to do
> this than calling self.keys.include? to determine if the key exists
> without triggering any unexpected side effects.

I would have thought that key? is the basic form here.

Also, just a thought, you might look at ostruct which plays off the very
same impulse you're having (i.e. to talk to hash as if it were a struct
with accessors). The code might give you some ideas...

m.

--
matt neuburg, phd = matt@tidbits.com, http://www.tidbits...
Leopard - http://www.takecontrolbooks.com/leopard-custom...
AppleScript - http://www.amazon.com/gp/product/...
Read TidBITS! It's free and smart. http://www.t...

Paul Mckibbin

4/21/2009 7:55:00 PM

0

Robert Dober wrote:
> A time bomb!
> But I might do the same and ...
> ... pay the same price debugging.
>
> Just my 0.02$

Thanks Robert,

I agree it is, but I didn't have the time to spend working out a more
elegant way of handling the metaprogramming goodness that would be
required to perform the same function in a safer way. I think this
protects me well enough from Hashes with block initialization, and
extant methods.

Waiting for the ba-boom

Mac
--
Posted via http://www.ruby-....

Paul Mckibbin

4/21/2009 8:36:00 PM

0

matt neuburg wrote:
> I would have thought that key? is the basic form here.

absolutely right. I'd forgotten about that method.

>
> Also, just a thought, you might look at ostruct which plays off the very
> same impulse you're having (i.e. to talk to hash as if it were a struct
> with accessors). The code might give you some ideas...
>
thanks matt, I'll take a look

> m.
--
Posted via http://www.ruby-....

Robert Klemme

4/21/2009 9:36:00 PM

0

On 21.04.2009 21:01, Paul Mckibbin wrote:
> I recently had code which needed to work with the same structure twice,
> once using the . nomenclature and once with an index into a hash.

If I understand the rest of your post correctly it is not exactly the
same structure but rather similarly structured data in two different
data structures (custom classes and XML DOM).

The fix that I propose is to not have two data structures storing the
same data. If you have classes already for storing all this, I'd
probably write bit of code that builds the structure using an XML push
or pull parser.

> i.e. in one case I had
>
> entries=@log.entry
>
> which I would interrogate with:
>
> entries.timings.connect.first_byte
>
> or alternatively
>
> entries.XmlSimple(in_file)
>
> which I would interrogate with:
>
> entries['timings']['connect']['first_byte'].
>
> Rather than write two blocks for code, one for the in-memory, I decided
> to override the base structure that XmlSimple used, but I found out that
> it was Hash. So the following was what I came up with:
>
> class Hash
> def method_missing(sym,*args,&blk)
> return self[sym] if self.keys.include?(sym)
> return self[sym.to_s] if self.keys.include?(sym.to_s)
> super
> end
> end

Usually nil is returned for absent keys so you can do

def method_missing(sym,*args,&blk)
self[sym] || self[sym.to_s] || super
end

> which works for me, but I was wondering if there is a better way to do
> this than calling self.keys.include? to determine if the key exists
> without triggering any unexpected side effects.

Kind regards

robert

Robert Dober

4/21/2009 9:51:00 PM

0

On Tue, Apr 21, 2009 at 11:40 PM, Robert Klemme
<shortcutter@googlemail.com> wrote:
> On 21.04.2009 21:01, Paul Mckibbin wrote:
>>
>> I recently had code which needed to work with the same structure twice,
>> once using the . nomenclature and once with an index into a hash.
>
> If I understand the rest of your post correctly it is not exactly the sam=
e
> structure but rather similarly structured data in two different data
> structures (custom classes and XML DOM).
>
> The fix that I propose is to not have two data structures storing the sam=
e
> data. =A0If you have classes already for storing all this, I'd probably w=
rite
> bit of code that builds the structure using an XML push or pull parser.
>
>> i.e. in one case I had
>>
>> entries=3D@log.entry
>>
>> which I would interrogate with:
>>
>> entries.timings.connect.first_byte
>>
>> or alternatively
>>
>> entries.XmlSimple(in_file)
>>
>> which I would interrogate with:
>>
>> entries['timings']['connect']['first_byte'].
>>
>> Rather than write two blocks for code, one for the in-memory, I decided
>> to override the base structure that XmlSimple used, but I found out that
>> it was Hash. So the following was what I came up with:
>>
>> =A0class Hash
>> =A0 =A0def method_missing(sym,*args,&blk)
>> =A0 =A0 return self[sym] if self.keys.include?(sym)
>> =A0 =A0 return self[sym.to_s] if self.keys.include?(sym.to_s)
>> =A0 =A0 super
>> =A0 =A0end
>> =A0end
>
> Usually nil is returned for absent keys so you can do
>
> def method_missing(sym,*args,&blk)
> =A0self[sym] || self[sym.to_s] || super
the following line will work for all potential hash values

fetch( sym ) { fetch( sym.to_s ) { super } }

HTH
Robert

Paul Mckibbin

4/21/2009 10:27:00 PM

0

Robert Klemme wrote:
> If I understand the rest of your post correctly it is not exactly the
> same structure but rather similarly structured data in two different
> data structures (custom classes and XML DOM).

True, but it is a 3rd party object library that I don't have access to
other than through OLE calls or file storage.

>
> The fix that I propose is to not have two data structures storing the
> same data. If you have classes already for storing all this, I'd
> probably write bit of code that builds the structure using an XML push
> or pull parser.

I wish that were the case, but 3rd party non-GPL'd code. :(

>
> Usually nil is returned for absent keys so you can do
>

I was attempting to avoid the case when it isn't.....


> def method_missing(sym,*args,&blk)
> self[sym] || self[sym.to_s] || super
> end
>

.... this will trigger the creation and return of a memoized object,
which is precisely a side-effect I want to avoid.

For example:

test=Hash.new {|k,v| k[v]=v.to_s*3} => {}
test[:one] => "oneoneone"
test[:two] => "twotwotwo"
test.three
NoMethodError: undefined method `three' for {:one=>"oneoneone",
:two=>"twotwotwo"}:Hash
from (irb):15

# cool and desired

class Hash
def method_missing(sym,*args,&blk)
return self[sym] if self.key?(sym)
return self[sym.to_s] if self.key?(sym.to_s)
super
end
end
=> nil

test.one => "oneoneone"
test.two => "twotwotwo"
test.three
NoMethodError: undefined method `three' for {:one=>"oneoneone",
:two=>"twotwotwo"}:Hash
from (irb):20:in `method_missing'
from (irb):25

#Same error message also cool and desired

test[:three] => "threethreethree"
test.three => "threethreethree"


#Redefine with your more compact code, which isn't performing the key?
check

class Hash
def method_missing(sym,*args,&blk)
self[sym] || self[sym.to_s] || super
end
end
=> nil

test.four => "fourfourfour"

#Not my expected result. I would expect an error from that method call,
but thanks for the effort.

Mac
http:...
--
Posted via http://www.ruby-....

Paul Mckibbin

4/21/2009 10:57:00 PM

0

Robert Dober wrote:
> On Tue, Apr 21, 2009 at 11:40 PM, Robert Klemme
> <shortcutter@googlemail.com> wrote:
>> data. �If you have classes already for storing all this, I'd probably write
>>> or alternatively
>>>
>> def method_missing(sym,*args,&blk)
>> �self[sym] || self[sym.to_s] || super
> the following line will work for all potential hash values
>
> fetch( sym ) { fetch( sym.to_s ) { super } }
>
> HTH
> Robert

Cool and will generate the same error type, I've used a modification of
it to make the stack look the same, and give a debugging hint if it does
go wrong....


begin
fetch( sym ) { fetch( sym.to_s ) { super }}
rescue
raise NoMethodError,"undefined method #{sym} for
#{self.inspect}:#{self.class} overridden by Mac's redefinition of
Hash::method_missing in #{__FILE__}"
end

Thanks again,

Mac
--
Posted via http://www.ruby-....

Robert Klemme

4/22/2009 6:23:00 AM

0

On 22.04.2009 00:26, Paul Mckibbin wrote:
> Robert Klemme wrote:
>> If I understand the rest of your post correctly it is not exactly the
>> same structure but rather similarly structured data in two different
>> data structures (custom classes and XML DOM).
>
> True, but it is a 3rd party object library that I don't have access to
> other than through OLE calls or file storage.

I see. But: if file storage means XML then you can still create a data
structure which responds to the same set of methods as the OLE version.

An alternative approach to modifying Hash is to wrap the whole beast in
something that exhibits the same interface as the OLE version.

require 'delegate'

class HashWrap < SimpleDelegator
def method_missing(s,*a,&b)
key = s.to_s

if a.empty? and __getobj__.key? key
res = __getobj__[key]
res = self.class.new(res) if Hash === res
res
else
super
end
end
end

h = {"foo" => {"x"=>456}, "bar" => 2}
s = HashWrap.new h

p s.foo
p s.foo.x
p s.bar
p s.not_there


Cheers

robert

Robert Dober

4/22/2009 9:09:00 AM

0

On Wed, Apr 22, 2009 at 12:57 AM, Paul Mckibbin <pmckibbin@gmail.com> wrote=
:
> Robert Dober wrote:
>> On Tue, Apr 21, 2009 at 11:40 PM, Robert Klemme
>> <shortcutter@googlemail.com> wrote:
>>> data. =EF=BF=BDIf you have classes already for storing all this, I'd pr=
obably write
>>>> or alternatively
>>>>
>>> def method_missing(sym,*args,&blk)
>>> =EF=BF=BDself[sym] || self[sym.to_s] || super
>> the following line will work for all potential hash values
>>
>> =C2=A0 =C2=A0 fetch( sym ) { fetch( sym.to_s ) { super } }
>>
>> HTH
>> Robert
>
> Cool and will generate the same error type, I've used a modification of
> it to make the stack look the same, and give a debugging hint if it does
> go wrong....
>
>
> begin
> =C2=A0fetch( sym ) { fetch( sym.to_s ) { super }}
> rescue
> =C2=A0raise NoMethodError,"undefined method #{sym} for
> #{self.inspect}:#{self.class} overridden by Mac's redefinition of
> Hash::method_missing in #{__FILE__}"
> end

Hmm unless I am missing something here there is no need to raise
NoMethodError with super just to
rescue it immediately
What about

def method_missing sym
fetch( sym ) do
fetch( sym.to_s) do
raise NoMethodError,"undefined method #{sym} for
#{self.inspect}:#{self.class} overridden by Mac's redefinition of
Hash::method_missing in #{__FILE__}"
end
end
end

Robert
>
> Thanks again,
>
> Mac
> --
> Posted via http://www.ruby-....
>
>



--=20
Si tu veux construire un bateau ...
Ne rassemble pas des hommes pour aller chercher du bois, pr=C3=A9parer des
outils, r=C3=A9partir les t=C3=A2ches, all=C3=A9ger le travail=E2=80=A6 mai=
s enseigne aux
gens la nostalgie de l=E2=80=99infini de la mer.

If you want to build a ship, don=E2=80=99t herd people together to collect
wood and don=E2=80=99t assign them tasks and work, but rather teach them to
long for the endless immensity of the sea.

--
Antoine de Saint-Exup=C3=A9ry