Class: Html2rss::Html::Navigator
- Inherits:
-
Object
- Object
- Html2rss::Html::Navigator
- Defined in:
- lib/html2rss/html/navigator.rb,
lib/html2rss/html/navigator/text_extractor.rb
Overview
Navigator owns DOM chrome recognition and node traversal helpers.
Defined Under Namespace
Classes: TextExtractor
Constant Summary collapse
- HEADING_TAGS =
Heading tags used to prioritize title extraction and container assessment.
%w[h1 h2 h3 h4 h5 h6].freeze
- IGNORED_CONTAINER_TAGS =
Element tags that indicate ignored DOM chrome when found in a container path.
%w[nav footer header svg script style].to_set.freeze
- CLUSTER_EXCLUDED_TAGS =
Layout roots and chrome tags excluded from class-clustering candidate nodes.
Set['html', 'body', 'nav', 'footer', 'header', 'svg', 'script', 'style'].freeze
- UTILITY_LANDMARK_TAGS =
Ancestor tags that usually indicate navigation/utility regions inside a content container.
%w[nav aside footer menu].to_set.freeze
- CARD_WALK_STOP_TAGS =
Immediate parent walk stops here — not a usable article card.
(UTILITY_LANDMARK_TAGS | IGNORED_CONTAINER_TAGS | %w[html body]).freeze
- WRAPPING_ANCHOR_CHILD_TAGS =
Inner tags that mean a wrapping is a card, not a span-styled list link.
(HEADING_TAGS + %w[p]).freeze
- MAIN_ANCHOR_SELECTOR =
Anchor selector used to identify the canonical article link element.
begin buf = +'a[href]:not([href=""])' %w[# javascript: mailto: tel: file:// sms: data:].each do |prefix| buf << %[:not([href^="#{prefix}"])] end buf.freeze end
Class Method Summary collapse
-
.descendant_of?(child_node, parent_node) ⇒ Boolean
Returns true if child_node is a descendant of parent_node.
-
.extract_visible_text(tag, separator: ' ', exclude_nodes: nil) ⇒ String?
Extracts visible text from a given node and its children.
-
.find_closest_selector_upwards(current_tag, selector) ⇒ Nokogiri::XML::Node?
Think of it as
css_upwardsmethod. -
.find_tag_in_ancestors(current_tag, tag_name) ⇒ Nokogiri::XML::Node?
Searches for the closest parent that matches the given tag name.
-
.ignored_container_path?(node, cache = nil) ⇒ Boolean
rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity.
-
.main_anchor_for(article_tag) ⇒ Nokogiri::XML::Node?
First eligible descendant anchor.
-
.parent_until_condition(node, condition) ⇒ Nokogiri::XML::Node?
Returns the first parent that satisfies the condition.
-
.usable_card_parent?(node) ⇒ Boolean
Immediate parent is a usable article card (not html/body/landmark chrome).
Class Method Details
.descendant_of?(child_node, parent_node) ⇒ Boolean
Returns true if child_node is a descendant of parent_node. Walks up using parent pointers to avoid NodeSet allocations.
152 153 154 155 156 157 158 159 160 |
# File 'lib/html2rss/html/navigator.rb', line 152 def descendant_of?(child_node, parent_node) curr = child_node.respond_to?(:parent) ? child_node.parent : nil while curr return true if curr == parent_node curr = curr.respond_to?(:parent) ? curr.parent : nil end false end |
.extract_visible_text(tag, separator: ' ', exclude_nodes: nil) ⇒ String?
Extracts visible text from a given node and its children.
43 44 45 |
# File 'lib/html2rss/html/navigator.rb', line 43 def extract_visible_text(tag, separator: ' ', exclude_nodes: nil) TextExtractor.call(tag, separator:, exclude_nodes:) end |
.find_closest_selector_upwards(current_tag, selector) ⇒ Nokogiri::XML::Node?
Think of it as css_upwards method.
It searches for the closest parent that matches the given selector.
122 123 124 125 126 127 128 129 130 131 |
# File 'lib/html2rss/html/navigator.rb', line 122 def find_closest_selector_upwards(current_tag, selector) while current_tag found = current_tag.at_css(selector) return found if found return nil unless current_tag.respond_to?(:parent) current_tag = current_tag.parent end end |
.find_tag_in_ancestors(current_tag, tag_name) ⇒ Nokogiri::XML::Node?
Searches for the closest parent that matches the given tag name.
139 140 141 142 143 |
# File 'lib/html2rss/html/navigator.rb', line 139 def find_tag_in_ancestors(current_tag, tag_name) return current_tag if current_tag.name == tag_name current_tag.ancestors(tag_name).first end |
.ignored_container_path?(node, cache = nil) ⇒ Boolean
rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/html2rss/html/navigator.rb', line 61 def ignored_container_path?(node, cache = nil) return cache[node] if cache&.key?(node) curr = node visited = [] is_ignored = false while curr.respond_to?(:parent) && curr if cache&.key?(curr) is_ignored = cache[curr] break end if IGNORED_CONTAINER_TAGS.include?(curr.name) is_ignored = true break end visited << curr curr = curr.parent end visited.each { |n| cache[n] = is_ignored } if cache is_ignored end |
.main_anchor_for(article_tag) ⇒ Nokogiri::XML::Node?
Returns first eligible descendant anchor.
50 51 52 53 54 |
# File 'lib/html2rss/html/navigator.rb', line 50 def main_anchor_for(article_tag) return article_tag if article_tag.name == 'a' && article_tag.matches?(MAIN_ANCHOR_SELECTOR) article_tag.at_css(MAIN_ANCHOR_SELECTOR) end |
.parent_until_condition(node, condition) ⇒ Nokogiri::XML::Node?
Returns the first parent that satisfies the condition. If the condition is met, it returns the node itself.
95 96 97 98 99 100 101 |
# File 'lib/html2rss/html/navigator.rb', line 95 def parent_until_condition(node, condition) while node && !node.document? && node.name != 'html' return node if condition.call(node) node = node.parent end end |
.usable_card_parent?(node) ⇒ Boolean
Immediate parent is a usable article card (not html/body/landmark chrome).
108 109 110 111 112 113 |
# File 'lib/html2rss/html/navigator.rb', line 108 def usable_card_parent?(node) return false unless node return false if node.respond_to?(:document?) && node.document? !CARD_WALK_STOP_TAGS.include?(node.name.to_s) end |