lib/moxml/node_set.rb in moxml-0.1.0 vs lib/moxml/node_set.rb in moxml-0.1.1
- old
+ new
@@ -1,268 +1,65 @@
-# lib/moxml/node_set.rb
+# frozen_string_literal: true
+
module Moxml
class NodeSet
include Enumerable
- attr_reader :native_nodes
+ attr_reader :nodes, :context
- def initialize(native_nodes = [])
- @native_nodes = Array(native_nodes)
+ def initialize(nodes, context)
+ @nodes = Array(nodes)
+ @context = context
end
def each
- return enum_for(:each) unless block_given?
- native_nodes.each { |node| yield Node.wrap(node) }
+ return to_enum(:each) unless block_given?
+
+ nodes.each { |node| yield Node.wrap(node, context) }
self
end
def [](index)
case index
when Integer
- Node.wrap(native_nodes[index])
+ Node.wrap(nodes[index], context)
when Range
- NodeSet.new(native_nodes[index])
+ NodeSet.new(nodes[index], context)
end
end
def first
- Node.wrap(native_nodes.first)
+ Node.wrap(nodes.first, context)
end
def last
- Node.wrap(native_nodes.last)
+ Node.wrap(nodes.last, context)
end
def empty?
- native_nodes.empty?
+ nodes.empty?
end
def size
- native_nodes.size
+ nodes.size
end
alias length size
def to_a
map { |node| node }
end
- def filter(selector)
- NodeSet.new(
- native_nodes.select { |node| Moxml.adapter.matches?(node, selector) }
- )
+ def +(other)
+ self.class.new(nodes + other.nodes, context)
end
- def remove
- each(&:remove)
- self
- end
-
def text
map(&:text).join
end
- def inner_html
- map(&:inner_html).join
- end
-
- def wrap(html_or_element)
- each do |node|
- wrapper = case html_or_element
- when String
- Document.parse("<div>#{html_or_element}</div>").root.children.first
- when Element
- html_or_element.dup
- else
- raise ArgumentError, "Expected String or Element"
- end
-
- node.add_previous_sibling(wrapper)
- wrapper.add_child(node)
- end
+ def remove
+ each(&:remove)
self
- end
-
- def add_class(names)
- each do |node|
- next unless node.is_a?(Element)
- current = (node["class"] || "").split(/\s+/)
- new_classes = names.is_a?(Array) ? names : names.split(/\s+/)
- node["class"] = (current + new_classes).uniq.join(" ")
- end
- self
- end
-
- def remove_class(names)
- each do |node|
- next unless node.is_a?(Element)
- current = (node["class"] || "").split(/\s+/)
- remove_classes = names.is_a?(Array) ? names : names.split(/\s+/)
- node["class"] = (current - remove_classes).join(" ")
- end
- self
- end
-
- def attr(name, value = nil)
- if value.nil?
- first&.[](name)
- else
- each { |node| node[name] = value if node.is_a?(Element) }
- self
- end
- end
-
- # Collection operations
- def +(other)
- NodeSet.new(native_nodes + other.native_nodes)
- end
-
- def -(other)
- NodeSet.new(native_nodes - other.native_nodes)
- end
-
- def &(other)
- NodeSet.new(native_nodes & other.native_nodes)
- end
-
- def |(other)
- NodeSet.new(native_nodes | other.native_nodes)
- end
-
- def uniq
- NodeSet.new(native_nodes.uniq)
- end
-
- def reverse
- NodeSet.new(native_nodes.reverse)
- end
-
- # Search and filtering
- def find_by_id(id)
- detect { |node| node.is_a?(Element) && node["id"] == id }
- end
-
- def find_by_class(class_name)
- select { |node| node.is_a?(Element) && (node["class"] || "").split(/\s+/).include?(class_name) }
- end
-
- def find_by_attribute(name, value = nil)
- select do |node|
- next unless node.is_a?(Element)
- if value.nil?
- node.attributes.key?(name)
- else
- node[name] == value
- end
- end
- end
-
- def of_type(type)
- select { |node| node.is_a?(type) }
- end
-
- # DOM Manipulation
- def before(node_or_nodes)
- each { |node| node.add_previous_sibling(node_or_nodes) }
- self
- end
-
- def after(node_or_nodes)
- each { |node| node.add_next_sibling(node_or_nodes) }
- self
- end
-
- def replace_with(node_or_nodes)
- each { |node| node.replace(node_or_nodes) }
- self
- end
-
- def wrap_all(wrapper)
- return self if empty?
-
- wrapper_node = case wrapper
- when String
- Document.parse(wrapper).root
- when Element
- wrapper
- else
- raise ArgumentError, "Expected String or Element"
- end
-
- first.add_previous_sibling(wrapper_node)
- wrapper_node.add_child(self)
- self
- end
-
- # Content manipulation
- def inner_text=(text)
- each { |node| node.inner_text = text }
- self
- end
-
- def inner_html=(html)
- each { |node| node.inner_html = html }
- self
- end
-
- # Attribute operations
- def toggle_class(names)
- names = names.split(/\s+/) if names.is_a?(String)
- each do |node|
- next unless node.is_a?(Element)
- current = (node["class"] || "").split(/\s+/)
- names.each do |name|
- if current.include?(name)
- current.delete(name)
- else
- current << name
- end
- end
- node["class"] = current.uniq.join(" ")
- end
- self
- end
-
- def has_class?(name)
- any? { |node| node.is_a?(Element) && (node["class"] || "").split(/\s+/).include?(name) }
- end
-
- def remove_attr(*attrs)
- each do |node|
- next unless node.is_a?(Element)
- attrs.each { |attr| node.remove_attribute(attr) }
- end
- self
- end
-
- # Position and hierarchy
- def parents
- NodeSet.new(
- map { |node| node.parent }.compact.uniq
- )
- end
-
- def children
- NodeSet.new(
- flat_map { |node| node.children.to_a }
- )
- end
-
- def siblings
- NodeSet.new(
- flat_map { |node| node.parent ? node.parent.children.reject { |sibling| sibling == node } : [] }
- ).uniq
- end
-
- def next
- NodeSet.new(
- map { |node| node.next_sibling }.compact
- )
- end
-
- def previous
- NodeSet.new(
- map { |node| node.previous_sibling }.compact
- )
end
end
end