Class: Dommy::Headers

Inherits:
Object
  • Object
show all
Includes:
Bridge::Methods
Defined in:
lib/dommy/fetch.rb

Overview

WHATWG Headers. Names are stored lowercased and compared case-insensitively; iteration (keys/values/entries/forEach) is sorted by name with duplicate values combined by ", " (the spec's "sort and combine" output). new Headers(init) fills from a record (Hash → set per key), a sequence (Array of [name, value] pairs → append), or another Headers instance.

Constant Summary collapse

HEADER_NAME =

RFC 7230 token — a valid header name (one or more of these bytes).

/\A[!#$%&'*+\-.^_`|~0-9A-Za-z]+\z/.freeze
HTTP_WHITESPACE =

Leading/trailing HTTP whitespace (tab or space) trimmed from a value.

/\A[\t ]+|[\t ]+\z/.freeze

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Bridge::Methods

included

Constructor Details

#initialize(init = nil) ⇒ Headers

Returns a new instance of Headers.



1128
1129
1130
1131
1132
# File 'lib/dommy/fetch.rb', line 1128

def initialize(init = nil)
  @list = [] # ordered [lowercased name, value] pairs (duplicates allowed)
  @guard = :none # :none (mutable) or :immutable (Response.error/redirect)
  fill(init)
end

Class Method Details

.canonical(name) ⇒ Object

RFC 7230 Title-Case form of a header name. Retained as a public helper; the Headers store itself is lowercased per the WHATWG spec.



1235
1236
1237
# File 'lib/dommy/fetch.rb', line 1235

def self.canonical(name)
  name.split("-").map(&:capitalize).join("-")
end

Instance Method Details

#__internal_raw_pairs__Object

Internal: a copy of the raw [name, value] pairs — lets one Headers be filled from another without losing duplicates or split Set-Cookie values.



1166
1167
1168
# File 'lib/dommy/fetch.rb', line 1166

def __internal_raw_pairs__
  @list.map(&:dup)
end

#__js_call__(method, args) ⇒ Object



1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
# File 'lib/dommy/fetch.rb', line 1189

def __js_call__(method, args)
  case method
  when "set"
    set_value(args[0], args[1])
    nil
  when "append"
    append_value(args[0], args[1])
    nil
  when "delete"
    ensure_mutable!
    name = validate_name!(args[0])
    @list.reject! { |n, _| n == name }
    nil
  when "get"
    get_combined(validate_name!(args[0]))
  when "has"
    name = validate_name!(args[0])
    @list.any? { |n, _| n == name }
  when "keys"
    sort_and_combine.map(&:first)
  when "values"
    sort_and_combine.map(&:last)
  when "entries"
    sort_and_combine
  when "getSetCookie"
    # WHATWG: Set-Cookie's individual values, in insertion order (never
    # combined, unlike every other header).
    @list.select { |n, _| n == "set-cookie" }.map(&:last)
  when "forEach"
    # WHATWG: forEach(callback) — callback(value, key, headers), in the
    # sort-and-combine order. `self` is the third argument so
    # `(_, _, h) => h.get(...)` works the same as in a browser.
    cb = args[0]
    sort_and_combine.each do |k, v|
      if cb.respond_to?(:__js_call__)
        cb.__js_call__("call", [v, k, self])
      elsif cb.respond_to?(:call)
        cb.call(v, k, self)
      end
    end
    nil
  end
end

#__js_get__(_key) ⇒ Object



1179
1180
1181
# File 'lib/dommy/fetch.rb', line 1179

def __js_get__(_key)
  Bridge::ABSENT # Headers exposes only methods; any property read is absent
end

#__js_set__(_key, _value) ⇒ Object



1183
1184
1185
# File 'lib/dommy/fetch.rb', line 1183

def __js_set__(_key, _value)
  Bridge::UNHANDLED
end

#fill(init) ⇒ Object

WHATWG "fill": a record (Hash) sets each key; a sequence (Array) appends each [name, value] pair (a non-2-element member is a TypeError); another Headers is copied pair-for-pair.



1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
# File 'lib/dommy/fetch.rb', line 1146

def fill(init)
  case init
  when Headers
    init.__internal_raw_pairs__.each { |name, value| append_value(name, value) }
  when Array
    init.each do |pair|
      unless pair.is_a?(Array) && pair.length == 2
        raise Bridge::TypeError,
          "Failed to construct 'Headers': The provided value cannot be converted to a sequence of [name, value] pairs."
      end
      append_value(pair[0], pair[1])
    end
  when Hash
    init.each { |name, value| set_value(name, value) }
  end
  nil
end

#make_immutable!Object

WHATWG: mark these headers immutable — the guard Response.error() / Response.redirect() give their headers. Any later set/append/delete then raises a TypeError. (Other guards — request/request-no-cors/response forbidden-name filtering — are out of scope: fetch here is stubbed.)



1138
1139
1140
1141
# File 'lib/dommy/fetch.rb', line 1138

def make_immutable!
  @guard = :immutable
  self
end

#to_hObject

A plain Hash of name => combined value (duplicate names combined by ", "; Set-Cookie collapses to its combined value — use getSetCookie for the split list). For callers that want a simple record.



1173
1174
1175
1176
1177
# File 'lib/dommy/fetch.rb', line 1173

def to_h
  sort_and_combine.each_with_object({}) do |(name, value), out|
    out[name] = out.key?(name) ? "#{out[name]}, #{value}" : value
  end
end