On 16.04.2011 16:51, Iaki Baz C. wrote:
2011/4/15 Kevin M.[email protected]:
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
some that you might not want users to be able to invoke.
and each SIP request header
(i.e. “From: sip:[email protected]”) 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
(efficient) header field access through [] using header name in any case
spelling.
header original name) as a field in the hash entry value, so instead
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
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 = Struct.new name, values
DUMMY = HdrInfo[nil, [].freeze].freeze
LT = “\r\n”.freeze
def self.parse(io)
hdr = {}
io.each_line LT do |l|
case l
when /^([^:]+:\s*(.*)$/
# too simplistic parsing!
hdr[$1] = $2.split(/,/).each(&:strip!)
when /^$/
break
else
raise "Not a header line: %p" % l
end
end
new(hdr)
end
def initialize(headers)
@hdr = {}
# assume hdr is String and values is parsed
headers.each do |hdr, values|
@hdr[normalize(hdr)] = 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]/ =~ 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