Forum: Ruby Is there a better way to do this?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Paul M. (Guest)
on 2009-04-21 23:01
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://pqmf.com
Robert D. (Guest)
on 2009-04-21 23:44
(Received via mailing list)
On Tue, Apr 21, 2009 at 9:01 PM, Paul M. <removed_email_address@domain.invalid>
wrote:

>  class Hash
>    def 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


--
Si tu veux construire un bateau ...
Ne rassemble pas des hommes pour aller chercher du bois, préparer des
outils, répartir les tâches, alléger le travail… mais enseigne aux
gens la nostalgie de l’infini de la mer.

If you want to build a ship, don’t herd people together to collect
wood and don’t assign them tasks and work, but rather teach them to
long for the endless immensity of the sea.
Paul M. (Guest)
on 2009-04-21 23:54
Robert D. 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
matt neuburg (Guest)
on 2009-04-21 23:56
(Received via mailing list)
Paul M. <removed_email_address@domain.invalid> wrote:

> or alternatively
>
> 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.
Paul M. (Guest)
on 2009-04-22 00:36
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.
Robert K. (Guest)
on 2009-04-22 01:41
(Received via mailing list)
On 21.04.2009 21:01, Paul M. 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.

> entries.XmlSimple(in_file)
>     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 D. (Guest)
on 2009-04-22 01:51
(Received via mailing list)
On Tue, Apr 21, 2009 at 11:40 PM, Robert K.
<removed_email_address@domain.invalid> 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
Paul M. (Guest)
on 2009-04-22 02:26
Robert K. 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://pqmf.com
Paul M. (Guest)
on 2009-04-22 02:57
Robert D. wrote:
> On Tue, Apr 21, 2009 at 11:40 PM, Robert K.
> <removed_email_address@domain.invalid> 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
Robert K. (Guest)
on 2009-04-22 10:25
(Received via mailing list)
On 22.04.2009 00:26, Paul M. wrote:
> Robert K. 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 D. (Guest)
on 2009-04-22 13:09
(Received via mailing list)
On Wed, Apr 22, 2009 at 12:57 AM, Paul M. <removed_email_address@domain.invalid>
wrote:
>>     fetch( sym ) { fetch( sym.to_s ) { super } }
>  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

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-forum.com/.
>
>



--
Si tu veux construire un bateau ...
Ne rassemble pas des hommes pour aller chercher du bois, préparer des
outils, répartir les tâches, alléger le travail… mais enseigne aux
gens la nostalgie de l’infini de la mer.

If you want to build a ship, don’t herd people together to collect
wood and don’t assign them tasks and work, but rather teach them to
long for the endless immensity of the sea.
This topic is locked and can not be replied to.