On 16.04.2011 16:51, I=F1aki Baz Castillo wrote:
> 2011/4/15 Kevin Mahler<kevin.mahler / yahoo.com>:
>> He has a key. It contains some
>> data. It's not necessarily true that he should duplicate that data in
>> the mapped-to values.
>
> To clarify, my exact case is the following:

Now it gets interesting. :-)

> I've coded a parser for SIP (similar to HTTP). The parser generates a
> Request object which inherits from Hash,

Usually it's better to use composition instead of inheritance to achieve =

this.  Now your SipRequest inherits *all* methods from Hash including=20
some that you might not want users to be able to invoke.

> and each SIP request header
> (i.e. "From: sip:alice / example.org") becomes an entry of the hash
> (Request object) as follows:
>
> - The key is "FROM" (capitalized).
> - The value is an Array of strings (a s header can have multiple values=
).
>
> I need to store the key capitalized for fastest lookup, but I also
> want to store the original header name (which could be "from", "From",
> "frOM" and so).

So, to sum it up: you want to have a class for SIP request which allows=20
(efficient) header field access through [] using header name in any case =

spelling.

> So my parser adds an instance variable @real_name within the header
> name string ("FROM").
>
> When I do the lookup of a header in the Request object, I would like
> also to retrieve the key's @real_name, but I've already understood
> that this is only possible if taint the key string before inserting it
> in the hash and use Hash#assoc. This solution is not good for
> performance.
>
> The solution suggested by Robert is adding such information (the
> header original name) as a field in the hash entry value, so instead
> of having:
>
>    request["FROM"]
>    =3D>  [ "sip:alice / xample.org ]
>
> I would end with something like:
>
>    request["FROM"]
>    =3D>  Struct ( "From", [ "sip:alice / xample.org ] )
>
> The problem this last suggestion introduces is that it breaks the
> existing API and makes more complext for a developer to handle the
> Request class (which should be as easy as handling a Hash).

Here's how I'd do it.  First, I would start with the interface, maybe=20
something like this

module SIP
   class Request
     def self.parse(io)
       # ...
     end

     # get a header field by symbol
     def [](header_name_sym)
     end

     # return the real name used
     def header_name(header_name_sym)
     end
   end
end

Then I'd think how I could make that API work properly.  For example two =

variants, error and default value:

module SIP
   class Request
     HdrInfo =3D Struct.new name, values
     DUMMY =3D HdrInfo[nil, [].freeze].freeze
     LT =3D "\r\n".freeze

     def self.parse(io)
       hdr =3D {}

       io.each_line LT do |l|
         case l
         when /^([^:]+:\s*(.*)$/
           # too simplistic parsing!
           hdr[$1] =3D $2.split(/,/).each(&:strip!)
         when /^$/
           break
         else
           raise "Not a header line: %p" % l
         end
       end

       new(hdr)
     end

     def initialize(headers)
       @hdr =3D {}

       # assume hdr is String and values is parsed
       headers.each do |hdr, values|
         @hdr[normalize(hdr)] =3D HdrInfo[hdr, values]
       end
     end

     # get a header field by symbol
     def [](header_name_sym)
       @hdr.fetch(normalize(header_name_sym)) do |k|
         DUMMY
       end.values
     end

     # return the real name used
     def header_name(header_name_sym)
       @hdr.fetch(normalize(header_name_sym)).do |k|
         raise ArgumentError,
           "Header not found %p" % header_name_sym
       end.name
     end

   private
     def normalize(h)
       /[A-Z]/ =3D~ h ? h.downcase : h).to_sym
     end
   end
end

Of course we could build the internal hash straight away during parsing. =

  The main focus of the example was how to use the header once parsed.

> Thanks to both for your comments.

You're welcome.

Kind regards

	robert

--=20
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/