Module: Abqari

Defined in:
lib/abqari/version.rb,
lib/abqari.rb,
lib/abqari/cli.rb,
lib/abqari/log.rb,
lib/abqari/feed.rb,
lib/abqari/page.rb,
lib/abqari/site.rb,
lib/abqari/audit.rb,
lib/abqari/folio.rb,
lib/abqari/hooks.rb,
lib/abqari/robots.rb,
lib/abqari/search.rb,
lib/abqari/server.rb,
lib/abqari/cli/new.rb,
lib/abqari/helpers.rb,
lib/abqari/sitemap.rb,
lib/abqari/indieweb.rb,
lib/abqari/llms_txt.rb,
lib/abqari/manifest.rb,
lib/abqari/minifier.rb,
lib/abqari/renderer.rb,
lib/abqari/audit/seo.rb,
lib/abqari/cli/theme.rb,
lib/abqari/http_safe.rb,
lib/abqari/importers.rb,
lib/abqari/page/path.rb,
lib/abqari/paginator.rb,
lib/abqari/path_safe.rb,
lib/abqari/redirects.rb,
lib/abqari/audit/a11y.rb,
lib/abqari/audit/rule.rb,
lib/abqari/audit/links.rb,
lib/abqari/page/bundle.rb,
lib/abqari/page/parser.rb,
lib/abqari/site_config.rb,
lib/abqari/site_loader.rb,
lib/abqari/syndication.rb,
lib/abqari/theme_fonts.rb,
lib/abqari/headers_file.rb,
lib/abqari/security_txt.rb,
lib/abqari/audit/content.rb,
lib/abqari/audit/privacy.rb,
lib/abqari/bundle_assets.rb,
lib/abqari/erbi_template.rb,
lib/abqari/font_pipeline.rb,
lib/abqari/importers/cli.rb,
lib/abqari/taxonomy_page.rb,
lib/abqari/asset_pipeline.rb,
lib/abqari/image_pipeline.rb,
lib/abqari/importers/base.rb,
lib/abqari/render_context.rb,
lib/abqari/visualizations.rb,
lib/abqari/artifact_writer.rb,
lib/abqari/importers/ghost.rb,
lib/abqari/paginated_shard.rb,
lib/abqari/publisher_pages.rb,
lib/abqari/theme_bootstrap.rb,
lib/abqari/importers/jekyll.rb,
lib/abqari/page/incremental.rb,
lib/abqari/plausible_script.rb,
lib/abqari/audit/performance.rb,
lib/abqari/audit/dependencies.rb,
lib/abqari/importers/substack.rb,
lib/abqari/og_image_generator.rb,
lib/abqari/related_index_page.rb,
lib/abqari/syntax_highlighter.rb,
lib/abqari/publication_normaliser.rb,
lib/abqari/speculation_rules_guard.rb,
lib/abqari/importers/support/report.rb,
lib/abqari/render_context/cover_helpers.rb,
lib/abqari/render_context/picture_helpers.rb,
lib/abqari/importers/support/slug_normalizer.rb,
lib/abqari/importers/support/html_to_markdown.rb,
lib/abqari/importers/support/image_downloader.rb,
lib/abqari/render_context/publication_helpers.rb

Overview

The version constant lives alone, in a file with NO requires, because abqari.gemspec loads it to read Abqari::VERSION.

The gemspec used to require_relative 'lib/abqari' instead, which pulls in the whole engine — and with it erubi, commonmarker, rouge and webrick. That works locally (those gems are already installed) but breaks any git or path source: Bundler evaluates the gemspec BEFORE installing dependencies, so the requires blow up with "cannot load such file -- erubi". Published gems are immune (their spec is pre-serialized into the .gem), which is exactly why the bug only surfaced on a clean CI/deploy builder.

Keep this file dependency-free.

Defined Under Namespace

Modules: CLI, Helpers, Hooks, HttpSafe, Importers, Indieweb, Log, Minifier, PathSafe, PlausibleScript, PluginLoader, PublicationNormaliser, SiteConfig, SpeculationRulesGuard, Syndication, ThemeBootstrap, ThemeFonts Classes: ArtifactWriter, AssetPipeline, Audit, BundleAssets, ErbiTemplate, Feed, Folio, FontPipeline, HeadersFile, ImagePipeline, LlmsTxt, Manifest, OgImageGenerator, Page, PaginatedShard, Paginator, PublisherIndexPage, PublisherPages, PublisherShowPage, Redirects, RelatedIndexPage, RenderContext, Renderer, Robots, Search, SecurityTxt, Server, Site, SiteLoader, Sitemap, SyntaxHighlighter, TaxonomyPage, UserError, Visualizations, VizPage

Constant Summary collapse

ROOT =
File.expand_path('..', __dir__)
YAML_PERMITTED_CLASSES =

Classes the engine accepts from any YAML input — site config, data files, frontmatter, Folio cache, audit whitelists. Date + Time so naked dates like date: 2026-06-15 and ISO timestamps round-trip. Anything else (Symbol, ranges, arbitrary classes) raises Psych::DisallowedClass before any deserialised object touches the engine. Keep this set minimal — adding a class here is the security-sensitive equivalent of widening a sandbox.

[Date, Time].freeze
BUILD_OK_SENTINEL =

Marker file written by Site#build at the end of a successful build. Deployers (bin/deploy, CI pipelines that rsync _site/) refuse to ship without it; the test harness uses it as the fixture-intact sentinel. Single source of truth so the three readers stay in lockstep.

'.abqari-build-ok'
FRONTMATTER_RE =

Jekyll-style YAML frontmatter — opening ---, body, closing ---, then everything after.

Match groups:

[1] = the YAML body between the `---` markers
[2] = everything after the closing `---\n` (page body / layout
    markup / whatever follows)

The YAML group is OPTIONAL ((?:.*?\n)?) so an EMPTY frontmatter block (---\n---\n) still matches — group [1] is "" (→ {} after safe_load), not treated as "no frontmatter" (which rendered the two --- lines as visible body text).

Used in three places — Page (content files), RenderContext (layout files chaining via layout:), and Renderer (parent-layout lookup for the dep-mtime walker). Centralised here so the three callers stay in lockstep; previously the regex was duplicated and one copy had drifted (no body capture group), which made consolidation easy to put off.

/\A---\s*\n((?:.*?\n)?)---\s*\n(.*)/m.freeze
VERSION =
'1.1.0'

Class Method Summary collapse

Class Method Details

.atomic_write(path, content) ⇒ Object

Atomic file write: write to a sibling tempfile, fsync, then rename into place. POSIX guarantees the rename is atomic on the same filesystem, so a reader either sees the previous full contents or the new full contents — never a half-written buffer.

Used for state files we re-read on subsequent builds: the POSSE sent log, the webmention sender log, the webmention cache, the etag pointer. Without atomicity, a SIGINT or runner timeout mid-write leaves a truncated YAML / JSON file and the next build either crashes on parse or — worse for the POSSE case — treats every prior send as un-sent and re-posts everything to Mastodon + Bluesky.



77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/abqari.rb', line 77

def self.atomic_write(path, content)
  dir  = File.dirname(path)
  base = File.basename(path)
  tmp  = File.join(dir, ".#{base}.#{Process.pid}.tmp")
  File.open(tmp, 'w') do |f|
    f.write(content)
    f.flush
    f.fsync rescue nil
  end
  File.rename(tmp, path)
ensure
  File.unlink(tmp) if tmp && File.exist?(tmp) rescue nil
end

.debug?Boolean

Returns:

  • (Boolean)


45
46
47
# File 'lib/abqari.rb', line 45

def self.debug?
  %w[1 true yes].include?(ENV['ABQARI_DEBUG'].to_s.strip.downcase)
end

.run_cliObject

Wrap a CLI entry point so failures are presented rather than dumped.

A malformed config/site.yml used to greet the user with ten frames of Psych internals; the useful part (file, line, column) was there, just buried at the top. Same for a typo'd flag. Set ABQARI_DEBUG=1 to get the backtrace back when a UserError is hiding something real.

Interrupt is handled here too: Ctrl-C during a build printed a stack trace through the render pool, which reads like a crash rather than "you pressed Ctrl-C". Exit 130 is the shell convention for SIGINT.



33
34
35
36
37
38
39
40
41
42
43
# File 'lib/abqari.rb', line 33

def self.run_cli
  yield
rescue UserError => e
  warn e.message
  warn '' if debug?
  warn e.backtrace.join("\n") if debug?
  exit 1
rescue Interrupt
  warn "\nInterrupted."
  exit 130
end