Class: Seo::ArticleLinkAuditor
- Inherits:
-
BaseService
- Object
- BaseService
- Seo::ArticleLinkAuditor
- Defined in:
- app/services/seo/article_link_auditor.rb
Overview
Unified per-article link auditor that combines internal and external link checking.
Extracts all links from an article's HTML content, validates internal links
via InternalLinkValidator and external links via LinkAnalyzer, and returns
a unified result. Optionally persists the audit result as a JSONB column
on the article for fast re-display without re-scanning.
Usage:
result = Seo::ArticleLinkAuditor.new.audit(article)
result.internal_broken # => [{ path:, suggestion: }, ...]
result.external_broken # => [{ href:, status:, redirect: }, ...]
result.all_valid? # => true/false
Persist result for cache (reads without re-audit)
Seo::ArticleLinkAuditor.new.audit_and_persist!(article)
Defined Under Namespace
Classes: AuditResult
Instance Method Summary collapse
-
#audit(article, skip_http_ping: false, skip_external: false) ⇒ AuditResult
Run a full audit on the given article (internal + external links).
-
#audit_and_persist!(article, skip_external: false) ⇒ AuditResult
Run audit and persist the result to the article's link_audit_result column.
Methods inherited from BaseService
#initialize, #log_debug, #log_error, #log_info, #log_warning, #logger, #options, #process, #tagged_logger
Constructor Details
This class inherits a constructor from BaseService
Instance Method Details
#audit(article, skip_http_ping: false, skip_external: false) ⇒ AuditResult
Run a full audit on the given article (internal + external links).
60 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 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'app/services/seo/article_link_auditor.rb', line 60 def audit(article, skip_http_ping: false, skip_external: false) html = active_html(article) return AuditResult.new if html.blank? # Internal link validation internal_result = Seo::InternalLinkValidator.new.process(html, skip_http_ping: skip_http_ping) internal_broken = internal_result.broken_links.map do |bl| { path: bl.path, href: bl.href, suggestion: bl.suggestion } end # External link validation external_broken = [] external_checked = 0 unless skip_external analyzer = Seo::LinkAnalyzer.new link_analysis_result = analyzer.process(html) if link_analysis_result.status == :ok link_analysis_result.link_analysis.each do |href, check| external_checked += 1 status_code = check[:result].to_i next if status_code.in?(200..399) external_broken << { href: href, status: status_code, redirect: check[:location] } end end end AuditResult.new( internal_checked: internal_result.checked_count, internal_broken: internal_broken, external_checked: external_checked, external_broken: external_broken ) end |
#audit_and_persist!(article, skip_external: false) ⇒ AuditResult
Run audit and persist the result to the article's link_audit_result column.
Also upserts the editorial link graph as a side effect.
108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'app/services/seo/article_link_auditor.rb', line 108 def audit_and_persist!(article, skip_external: false) result = audit(article, skip_external: skip_external) begin article.update_column(:link_audit_result, result.to_h) if article.respond_to?(:link_audit_result) Seo::InternalLinkValidator.upsert_editorial_links!(article) rescue StandardError => e Rails.logger.warn "[ArticleLinkAuditor] Failed to persist audit for Article##{article.id}: #{e.}" end result end |