Module: Dommy

Defined in:
lib/dommy/world.rb,
lib/dommy.rb,
lib/dommy/css.rb,
lib/dommy/url.rb,
lib/dommy/attr.rb,
lib/dommy/blob.rb,
lib/dommy/node.rb,
lib/dommy/event.rb,
lib/dommy/fetch.rb,
lib/dommy/bridge.rb,
lib/dommy/parser.rb,
lib/dommy/router.rb,
lib/dommy/element.rb,
lib/dommy/promise.rb,
lib/dommy/storage.rb,
lib/dommy/version.rb,
lib/dommy/document.rb,
lib/dommy/observer.rb,
lib/dommy/form_data.rb,
lib/dommy/navigator.rb,
lib/dommy/scheduler.rb,
lib/dommy/dom_parser.rb,
lib/dommy/shadow_root.rb,
lib/dommy/tree_walker.rb,
lib/dommy/test_helpers.rb,
lib/dommy/data_transfer.rb,
lib/dommy/dom_exception.rb,
lib/dommy/html_elements.rb,
lib/dommy/rspec/matchers.rb,
lib/dommy/custom_elements.rb,
lib/dommy/html_collection.rb,
lib/dommy/internal/cookie_jar.rb,
lib/dommy/minitest/assertions.rb,
lib/dommy/internal/dom_matching.rb,
lib/dommy/internal/node_traversal.rb,
lib/dommy/internal/observer_manager.rb,
lib/dommy/internal/observer_matcher.rb,
lib/dommy/internal/scope_resolution.rb,
lib/dommy/rspec/capy_style_matchers.rb,
lib/dommy/internal/node_wrapper_cache.rb,
lib/dommy/internal/mutation_coordinator.rb,
lib/dommy/internal/shadow_root_registry.rb,
lib/dommy/internal/template_content_registry.rb

Overview

Dommy — a happy-dom-style DOM polyfill in pure Ruby. Backbone is Nokogiri::HTML5 plus a small scheduler/event-loop layer.

Two views into the same objects:

- Public Ruby API (snake_case methods like `text_content`,
  `append_child`) for CRuby users writing tests against rendered
  HTML.
- `__js_get__` / `__js_set__` / `__js_call__` / `__js_new__`
  bridge protocol for JS bridge embedders — dispatches into the
  same underlying Ruby methods.

Defined Under Namespace

Modules: Bridge, EventTarget, Internal, Minitest, Node, NodeFilter, Parser, RSpec, TestHelpers, TreeTraversalCore Classes: AbortController, AbortSignal, Attr, Blob, CSSRule, CSSRuleList, CSSStyleSheet, CharacterDataNode, ClassList, Clipboard, CommentNode, CustomElementRegistry, CustomEvent, DOMException, DOMParser, DOMRect, DataTransfer, DatasetMap, Document, DocumentType, DragEvent, Element, ErrorValue, Event, FetchFn, File, FileList, FormData, Fragment, HTMLAnchorElement, HTMLAreaElement, HTMLAudioElement, HTMLBRElement, HTMLBaseElement, HTMLBodyElement, HTMLButtonElement, HTMLCollection, HTMLDataElement, HTMLDetailsElement, HTMLDialogElement, HTMLDivElement, HTMLElement, HTMLEmbedElement, HTMLFieldsetElement, HTMLFormElement, HTMLHRElement, HTMLHeadElement, HTMLHeadingElement, HTMLHtmlElement, HTMLIFrameElement, HTMLImageElement, HTMLInputElement, HTMLLIElement, HTMLLabelElement, HTMLLegendElement, HTMLLinkElement, HTMLMapElement, HTMLMediaElement, HTMLMetaElement, HTMLMeterElement, HTMLModElement, HTMLOListElement, HTMLObjectElement, HTMLOptGroupElement, HTMLOptionElement, HTMLOptionsCollection, HTMLOutputElement, HTMLParagraphElement, HTMLPictureElement, HTMLPreElement, HTMLProgressElement, HTMLQuoteElement, HTMLScriptElement, HTMLSelectElement, HTMLSlotElement, HTMLSourceElement, HTMLSpanElement, HTMLStyleElement, HTMLTableCaptionElement, HTMLTableCellElement, HTMLTableElement, HTMLTableRowElement, HTMLTableSectionElement, HTMLTemplateElement, HTMLTextAreaElement, HTMLTimeElement, HTMLTitleElement, HTMLTrackElement, HTMLUListElement, HTMLVideoElement, Headers, History, KeyboardEvent, LiveNodeList, Location, MouseEvent, MutationObserver, MutationRecord, NamedNodeMap, Navigator, NodeIterator, NodeList, PermissionStatus, Permissions, PromiseValue, Response, Scheduler, ShadowRoot, StandaloneEventTarget, Storage, StyleDeclaration, TextNode, TreeWalker, URL, URLSearchParams, Url, ValidityState, Window, XMLSerializer

Constant Summary collapse

VERSION =
"0.5.0"
HTML_ELEMENT_CLASSES =

Look up the subclass for a given HTML tag. Document#wrap_node consults this map; defaults to plain Element.

{
  "a" => HTMLAnchorElement,
  "form" => HTMLFormElement,
  "input" => HTMLInputElement,
  "button" => HTMLButtonElement,
  "img" => HTMLImageElement,
  "script" => HTMLScriptElement,
  "link" => HTMLLinkElement,
  "select" => HTMLSelectElement,
  "option" => HTMLOptionElement,
  "optgroup" => HTMLOptGroupElement,
  "textarea" => HTMLTextAreaElement,
  "label" => HTMLLabelElement,
  "fieldset" => HTMLFieldsetElement,
  "output" => HTMLOutputElement,
  "legend" => HTMLLegendElement,
  "slot" => HTMLSlotElement,
  "table" => HTMLTableElement,
  "thead" => HTMLTableSectionElement,
  "tbody" => HTMLTableSectionElement,
  "tfoot" => HTMLTableSectionElement,
  "tr" => HTMLTableRowElement,
  "td" => HTMLTableCellElement,
  "th" => HTMLTableCellElement,
  "caption" => HTMLTableCaptionElement,
  "dialog" => HTMLDialogElement,
  "details" => HTMLDetailsElement,
  "meter" => HTMLMeterElement,
  "progress" => HTMLProgressElement,
  "template" => HTMLTemplateElement,
  "audio" => HTMLAudioElement,
  "video" => HTMLVideoElement,
  "source" => HTMLSourceElement,
  "track" => HTMLTrackElement,
  "iframe" => HTMLIFrameElement,
  "picture" => HTMLPictureElement,
  "ol" => HTMLOListElement,
  "ul" => HTMLUListElement,
  "li" => HTMLLIElement,
  "time" => HTMLTimeElement,
  "data" => HTMLDataElement,
  "area" => HTMLAreaElement,
  "map" => HTMLMapElement,
  "object" => HTMLObjectElement,
  "embed" => HTMLEmbedElement,
  "base" => HTMLBaseElement,
  "meta" => HTMLMetaElement,
  "style" => HTMLStyleElement,
  "title" => HTMLTitleElement,
  "q" => HTMLQuoteElement,
  "blockquote" => HTMLQuoteElement,
  "ins" => HTMLModElement,
  "del" => HTMLModElement,
  "div" => HTMLDivElement,
  "span" => HTMLSpanElement,
  "p" => HTMLParagraphElement,
  "h1" => HTMLHeadingElement,
  "h2" => HTMLHeadingElement,
  "h3" => HTMLHeadingElement,
  "h4" => HTMLHeadingElement,
  "h5" => HTMLHeadingElement,
  "h6" => HTMLHeadingElement,
  "br" => HTMLBRElement,
  "hr" => HTMLHRElement,
  "pre" => HTMLPreElement,
  "body" => HTMLBodyElement,
  "head" => HTMLHeadElement,
  "html" => HTMLHtmlElement
}.freeze

Class Method Summary collapse

Class Method Details

.element_class_for(tag_name) ⇒ Object



4452
4453
4454
# File 'lib/dommy/html_elements.rb', line 4452

def self.element_class_for(tag_name)
  HTML_ELEMENT_CLASSES[tag_name.to_s.downcase] || Element
end

.new_windowObject

Build a fresh, empty Window (no host). Equivalent to opening a blank document.



58
59
60
# File 'lib/dommy.rb', line 58

def self.new_window
  Window.new
end

.parse(html) ⇒ Object

Parse an HTML string and return a fresh ‘Window`.

When the input starts with ‘<!doctype` or `<html>`, it is parsed as a full HTML document (preserving <head>, <title>, etc.). Otherwise the input is treated as a body fragment and inserted into a fresh document’s <body>.

The Window has no host (standalone CRuby usage); embedders that need bridge callbacks (e.g. a wasm host) pass a host instead.



45
46
47
48
49
50
51
52
53
54
# File 'lib/dommy.rb', line 45

def self.parse(html)
  s = html.to_s
  if s.match?(/\A\s*(<!doctype|<html\b)/i)
    Window.new(nil, nokogiri_doc: Nokogiri::HTML5(s))
  else
    window = Window.new
    window.document.body.inner_html = s
    window
  end
end

.structured_clone(value, memo = {}) ⇒ Object

‘structuredClone(value)` — deep clone for plain Ruby values and DOM nodes (via `cloneNode(true)`). Mirrors the JS structured-clone algorithm for the subset we support:

- primitives (String / Numeric / Boolean / nil)         → copied
- Array / Hash / Set                                    → deep-cloned
- DOM nodes (anything responding to `clone_node`)       → deep-cloned
- cyclic structures                                     → preserved
- Proc / Method / Class / Module / IO                   → DataCloneError

Symbols are passed through unchanged (Ruby has no ‘Symbol` clone concept; JS treats Symbols as DataCloneError, but Ruby code uses them as hash keys so this is the pragmatic choice).



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/dommy.rb', line 75

def self.structured_clone(value, memo = {})
  return memo[value.object_id] if memo.key?(value.object_id)

  case value
  when nil, true, false, Numeric, Symbol
    value
  when String
    value.dup
  when Array
    arr = []
    memo[value.object_id] = arr
    value.each { |v| arr << structured_clone(v, memo) }
    arr
  when Hash
    h = {}
    memo[value.object_id] = h
    value.each { |k, v| h[structured_clone(k, memo)] = structured_clone(v, memo) }
    h
  when Set
    s = Set.new
    memo[value.object_id] = s
    value.each { |v| s << structured_clone(v, memo) }
    s
  when Time
    value.dup
  when Regexp
    value.dup
  when Proc, Method, UnboundMethod, IO, Class, Module
    raise DOMException::DataCloneError, "#{value.class} cannot be cloned"
  else
    if value.respond_to?(:clone_node)
      value.clone_node(true)
    else
      # Fallback: rely on the object's own `dup` (which handles
      # custom classes the user might pass through).
      value.dup
    end
  end
end

.structuredClone(value) ⇒ Object

JS-style global alias for symmetry with ‘window.structuredClone(…)`.



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

def Dommy.structuredClone(value)
  structured_clone(value)
end