Class: Dommy::URL

Inherits:
Object
  • Object
show all
Defined in:
lib/dommy/url.rb

Overview

‘URL` — WHATWG-style URL parsing. Public API mirrors the JS class:

u = Dommy::URL.new("https://x.test/a/b?k=v#h")
u.protocol  # "https:"
u.host      # "x.test"
u.pathname  # "/a/b"
u.search    # "?k=v"
u.hash      # "#h"
u.search_params.get("k")  # "v"

Construction with a base URL is supported for relative inputs:

Dommy::URL.new("/a", "https://x.test").href
  # => "https://x.test/a"

Internally backed by Ruby’s URI library — good enough for the common test cases. Edge cases that URI rejects raise ‘DOMException::SyntaxError` (called `TypeError` in JS but Dommy uses the closest WHATWG name).

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(input, base = nil) ⇒ URL

Returns a new instance of URL.



68
69
70
71
72
# File 'lib/dommy/url.rb', line 68

def initialize(input, base = nil)
  raw = parse_with_base(input, base)
  @uri = raw
  @search_params = URLSearchParams.new(raw.query.to_s, owner: self)
end

Instance Attribute Details

#search_paramsObject (readonly)

Returns the value of attribute search_params.



66
67
68
# File 'lib/dommy/url.rb', line 66

def search_params
  @search_params
end

Class Method Details

.__reset_blob_urls__Object

Test seam: drop all registered blob URLs.



61
62
63
# File 'lib/dommy/url.rb', line 61

def __reset_blob_urls__
  @blob_urls.clear
end

.__resolve_blob_url__(url) ⇒ Object

Resolve a blob: URL back to its Blob, or nil if revoked / unknown. Internal — used by fetch / XHR implementations that load blob URLs.



56
57
58
# File 'lib/dommy/url.rb', line 56

def __resolve_blob_url__(url)
  @blob_urls[url.to_s]
end

.create_object_url(blob) ⇒ Object Also known as: createObjectURL

Create a unique blob: URL that resolves back to ‘blob` via `URL.resolve_blob_url(url)`. Returns nil for non-Blob input.



34
35
36
37
38
39
40
41
# File 'lib/dommy/url.rb', line 34

def create_object_url(blob)
  return nil unless blob.is_a?(Blob)

  id = "%032x" % rand(2 ** 128)
  url = "blob:dommy/#{id}"
  @blob_urls[url] = blob
  url
end

.revoke_object_url(url) ⇒ Object Also known as: revokeObjectURL

Revoke a previously-created blob URL. No-op for unknown URLs, matching the spec.



47
48
49
50
# File 'lib/dommy/url.rb', line 47

def revoke_object_url(url)
  @blob_urls.delete(url.to_s)
  nil
end

Instance Method Details

#__js_call__(method, _args) ⇒ Object



246
247
248
249
250
251
# File 'lib/dommy/url.rb', line 246

def __js_call__(method, _args)
  case method
  when "toString", "toJSON"
    href
  end
end

#__js_get__(key) ⇒ Object



190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/dommy/url.rb', line 190

def __js_get__(key)
  case key
  when "href"
    href
  when "protocol"
    protocol
  when "host"
    host
  when "hostname"
    hostname
  when "port"
    port
  when "pathname"
    pathname
  when "search"
    search
  when "hash"
    hash
  when "origin"
    origin
  when "username"
    username
  when "password"
    password
  when "searchParams"
    @search_params
  end
end

#__js_set__(key, value) ⇒ Object



219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/dommy/url.rb', line 219

def __js_set__(key, value)
  case key
  when "href"
    self.href = value
  when "protocol"
    self.protocol = value
  when "host"
    self.host = value
  when "hostname"
    self.hostname = value
  when "port"
    self.port = value
  when "pathname"
    self.pathname = value
  when "search"
    self.search = value
  when "hash"
    self.hash = value
  when "username"
    self.username = value
  when "password"
    self.password = value
  end

  nil
end

#__notify_params_changed__Object

Called by URLSearchParams when it mutates; we need to keep the underlying URI’s query string in sync so subsequent ‘href` is accurate.



256
257
258
# File 'lib/dommy/url.rb', line 256

def __notify_params_changed__
  sync_uri_query
end

#hashObject



148
149
150
151
# File 'lib/dommy/url.rb', line 148

def hash
  f = @uri.fragment.to_s
  f.empty? ? "" : "##{f}"
end

#hash=(value) ⇒ Object



153
154
155
156
# File 'lib/dommy/url.rb', line 153

def hash=(value)
  f = value.to_s.sub(/^#/, "")
  @uri.fragment = f.empty? ? nil : f
end

#hostObject



94
95
96
97
98
99
100
101
# File 'lib/dommy/url.rb', line 94

def host
  port = @uri.port
  default = @uri.default_port
  hostpart = @uri.host.to_s
  return hostpart if port.nil? || port == default

  "#{hostpart}:#{port}"
end

#host=(value) ⇒ Object



103
104
105
106
107
# File 'lib/dommy/url.rb', line 103

def host=(value)
  h, p = value.to_s.split(":", 2)
  @uri.host = h
  @uri.port = p.to_i if p
end

#hostnameObject



109
110
111
# File 'lib/dommy/url.rb', line 109

def hostname
  @uri.host.to_s
end

#hostname=(value) ⇒ Object



113
114
115
# File 'lib/dommy/url.rb', line 113

def hostname=(value)
  @uri.host = value.to_s
end

#hrefObject



74
75
76
# File 'lib/dommy/url.rb', line 74

def href
  build_href
end

#href=(value) ⇒ Object



78
79
80
81
82
83
# File 'lib/dommy/url.rb', line 78

def href=(value)
  raw = parse_with_base(value.to_s, nil)
  @uri = raw
  @search_params.__replace__(raw.query.to_s)
  build_href
end

#originObject



158
159
160
161
162
163
# File 'lib/dommy/url.rb', line 158

def origin
  return "null" unless @uri.scheme && @uri.host

  port_part = (@uri.port && @uri.port != @uri.default_port) ? ":#{@uri.port}" : ""
  "#{@uri.scheme}://#{@uri.host}#{port_part}"
end

#passwordObject



173
174
175
# File 'lib/dommy/url.rb', line 173

def password
  @uri.password.to_s
end

#password=(value) ⇒ Object



177
178
179
# File 'lib/dommy/url.rb', line 177

def password=(value)
  @uri.password = value.to_s.empty? ? nil : value.to_s
end

#pathnameObject



127
128
129
# File 'lib/dommy/url.rb', line 127

def pathname
  @uri.path.to_s
end

#pathname=(value) ⇒ Object



131
132
133
134
135
# File 'lib/dommy/url.rb', line 131

def pathname=(value)
  v = value.to_s
  v = "/#{v}" if !v.start_with?("/") && !v.empty?
  @uri.path = v
end

#portObject



117
118
119
120
121
# File 'lib/dommy/url.rb', line 117

def port
  return "" if @uri.port.nil? || @uri.port == @uri.default_port

  @uri.port.to_s
end

#port=(value) ⇒ Object



123
124
125
# File 'lib/dommy/url.rb', line 123

def port=(value)
  @uri.port = value.to_s.empty? ? nil : value.to_i
end

#protocolObject



85
86
87
# File 'lib/dommy/url.rb', line 85

def protocol
  @uri.scheme ? "#{@uri.scheme}:" : ""
end

#protocol=(value) ⇒ Object



89
90
91
92
# File 'lib/dommy/url.rb', line 89

def protocol=(value)
  s = value.to_s.sub(/:$/, "")
  @uri.scheme = s
end

#searchObject



137
138
139
140
# File 'lib/dommy/url.rb', line 137

def search
  q = @search_params.to_s
  q.empty? ? "" : "?#{q}"
end

#search=(value) ⇒ Object



142
143
144
145
146
# File 'lib/dommy/url.rb', line 142

def search=(value)
  q = value.to_s.sub(/^\?/, "")
  @search_params.__replace__(q)
  sync_uri_query
end

#to_json(*_args) ⇒ Object



185
186
187
188
# File 'lib/dommy/url.rb', line 185

def to_json(*_args)
  # match JSON.stringify(url) -> "\"<href>\""
  href.inspect
end

#to_sObject



181
182
183
# File 'lib/dommy/url.rb', line 181

def to_s
  href
end

#usernameObject



165
166
167
# File 'lib/dommy/url.rb', line 165

def username
  @uri.user.to_s
end

#username=(value) ⇒ Object



169
170
171
# File 'lib/dommy/url.rb', line 169

def username=(value)
  @uri.user = value.to_s.empty? ? nil : value.to_s
end