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