Module: Abqari::Hooks

Defined in:
lib/abqari/hooks.rb

Overview

Plugin hook registry. Plugins register named-event callbacks; the engine invokes them at known points during the build.

Two flavours:

- `register(event, &block)` + `run(event, ...)` — event-style.
Every registered callback is called for its side effect; the
return value is ignored.

- `register(event, &block)` + `run_filter(event, value, ...)` —
filter-style. The value is threaded through every callback;
each callback returns the (possibly modified) value, and the
final result is what the engine consumes downstream.

Events exposed by the engine (see docs/plugins.md for the canonical list):

Lifecycle:
:before_build         (site)            — Site#build entry
:after_load_pages     (site)            — after `load_pages`, before generators
:after_generate       (site)            — after `generate_all`
:before_render_page   (site, page)      — per page, before its render call
:after_build          (site)            — Site#build exit (success or failure)

Filters:
:post_process         (html, site, page) -> html
:resolved_frontmatter (fm, page)         -> fm

Thread safety: registration and run iterate over a snapshot of the callback list, so registering inside a hook callback is safe (the new callback fires on the next event, not the current one).

Class Method Summary collapse

Class Method Details

.clear!Object

Reset the registry. Used in tests and any caller that wants a fresh hook table — the engine itself doesn't currently clear between rebuilds (plugins survive incremental rebuilds, matching Jekyll's _plugins/ semantics).



83
84
85
# File 'lib/abqari/hooks.rb', line 83

def clear!
  @mutex.synchronize { @hooks = Hash.new { |h, k| h[k] = [] } }
end

.count(event) ⇒ Object



93
94
95
# File 'lib/abqari/hooks.rb', line 93

def count(event)
  @mutex.synchronize { @hooks[event].size }
end

.register(event, &block) ⇒ Object

Register a callback for event. Returns the block for chaining.

Raises:

  • (ArgumentError)


41
42
43
44
45
46
# File 'lib/abqari/hooks.rb', line 41

def register(event, &block)
  raise ArgumentError, "Hooks.register requires a block (event: #{event.inspect})" unless block

  @mutex.synchronize { @hooks[event] << block }
  block
end

.registered_eventsObject

Read-only snapshot of the current registry — useful in tests and for bin/audit style introspection.



89
90
91
# File 'lib/abqari/hooks.rb', line 89

def registered_events
  @mutex.synchronize { @hooks.keys.dup }
end

.run(event, *args) ⇒ Object

Run every callback registered for event. Each return value is discarded; use run_filter if you need to transform a value.



50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/abqari/hooks.rb', line 50

def run(event, *args)
  snapshot = @mutex.synchronize { @hooks[event].dup }
  snapshot.each do |hook|
    hook.call(*args)
  rescue StandardError => e
    Log.error "plugin hook #{event} crashed: #{e.class}: #{e.message}"
    e.backtrace&.first(5)&.each { |line| Log.error "  #{line}" }
    # Re-raise — a misbehaving plugin should fail the build, not
    # silently produce wrong output. Operators can debug from
    # the logged backtrace.
    raise
  end
end

.run_filter(event, value, *args) ⇒ Object

Thread value through every callback registered for event. Each callback receives (value, *args) and returns the new value. Final value flows back to the caller.



67
68
69
70
71
72
73
74
75
76
77
# File 'lib/abqari/hooks.rb', line 67

def run_filter(event, value, *args)
  snapshot = @mutex.synchronize { @hooks[event].dup }
  snapshot.each do |hook|
    value = hook.call(value, *args)
  rescue StandardError => e
    Log.error "plugin filter #{event} crashed: #{e.class}: #{e.message}"
    e.backtrace&.first(5)&.each { |line| Log.error "  #{line}" }
    raise
  end
  value
end