lib/protocol/http/headers.rb in protocol-http-0.2.0 vs lib/protocol/http/headers.rb in protocol-http-0.3.0

- old
+ new

@@ -18,11 +18,13 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. module Protocol module HTTP + # Headers are an array of key-value pairs. Some header keys represent multiple values. class Headers + # Split by commas. class Split < Array COMMA = /\s*,\s*/ def initialize(value) super(value.split(COMMA)) @@ -35,10 +37,11 @@ def to_s join(", ") end end + # Split by newline charaters. class Multiple < Array def initialize(value) super() self << value @@ -61,24 +64,26 @@ end if indexed @indexed = indexed.dup else - @indexed = self.to_h + @indexed = nil end end def dup self.class.new(@fields, @indexed) end + # An array of `[key, value]` pairs. attr :fields def freeze return if frozen? - @indexed = to_h + # Generate @indexed + self.to_h super end def empty? @@ -91,25 +96,26 @@ def include? key self[key] != nil end - def slice!(keys) - _, @fields = @fields.partition do |field| + def extract(keys) + deleted, @fields = @fields.partition do |field| keys.include?(field.first.downcase) end - keys.each do |key| - @indexed.delete(key) + if @indexed + keys.each do |key| + @indexed.delete(key) + end end - return self + return deleted end - def slice(keys) - self.dup.slice!(keys) - end + # This is deprecated. + alias slice! extract def add(key, value) self[key] = value end @@ -127,11 +133,13 @@ # Append the value to the given key. Some values can be appended multiple times, others can only be set once. # @param key [String] The header key. # @param value The header value. def []= key, value - merge_into(@indexed, key.downcase, value) + if @indexed + merge_into(@indexed, key.downcase, value) + end @fields << [key, value] end MERGE_POLICY = { @@ -162,41 +170,56 @@ 'proxy-authenticate' => Multiple }.tap{|hash| hash.default = Split} # Delete all headers with the given key, and return the merged value. def delete(key) - _, @fields = @fields.partition do |field| + deleted, @fields = @fields.partition do |field| field.first.downcase == key end - return @indexed.delete(key) + if @indexed + return @indexed.delete(key) + elsif policy = MERGE_POLICY[key] + (key, value), *tail = deleted + merged = policy.new(value) + + tail.each{|k,v| merged << v} + + return merged + else + key, value = deleted.last + return value + end end protected def merge_into(hash, key, value) if policy = MERGE_POLICY[key] if current_value = hash[key] current_value << value else hash[key] = policy.new(value) end else - raise ArgumentError, "Header #{key} can only be set once!" if hash.include?(key) - # We can't merge these, we only expose the last one set. hash[key] = value end end def [] key - @indexed[key] + to_h[key] end + # A hash table of `{key, policy[key].map(values)}` def to_h - @fields.inject({}) do |hash, (key, value)| + @indexed ||= @fields.inject({}) do |hash, (key, value)| merge_into(hash, key.downcase, value) hash end + end + + def inspect + "#<#{self.class} #{@fields.inspect}>" end def == other if other.is_a? Hash to_h == other