Module: Bridgetown::Hooks

Defined in:
lib/bridgetown-core/hooks.rb

Defined Under Namespace

Classes: HookRegistration

Constant Summary collapse

DEFAULT_PRIORITY =
20
PRIORITY_MAP =
{
  low: 10,
  normal: 20,
  high: 30,
}.freeze
NotAvailable =
Class.new(RuntimeError)
Uncallable =
Class.new(RuntimeError)

Class Method Summary collapse

Class Method Details

.clear_reloadable_hooksObject

Clear all hooks marked as reloadable from the registry



106
107
108
109
110
111
112
# File 'lib/bridgetown-core/hooks.rb', line 106

def self.clear_reloadable_hooks
  Bridgetown.logger.debug("Clearing reloadable hooks")

  @registry.each_value do |hooks|
    hooks.delete_if(&:reloadable)
  end
end

.prioritized_hooks(hooks) ⇒ Object

Sort registered hooks according to priority and load order

Parameters:



40
41
42
43
# File 'lib/bridgetown-core/hooks.rb', line 40

def self.prioritized_hooks(hooks)
  grouped_hooks = hooks.group_by(&:priority)
  grouped_hooks.keys.sort.reverse.map { |priority| grouped_hooks[priority] }.flatten
end

.priority_value(priority) ⇒ Object



31
32
33
34
35
# File 'lib/bridgetown-core/hooks.rb', line 31

def self.priority_value(priority)
  return priority if priority.is_a?(Integer)

  PRIORITY_MAP[priority] || DEFAULT_PRIORITY
end

.register(owners, event, priority: DEFAULT_PRIORITY, reloadable: true) {|obj| ... } ⇒ Object

Register one or more hooks which may be triggered later for a particular event

Parameters:

  • owners (Symbol, Array<Symbol>)

    name of the owner (:site, :resource, etc.)

  • event (Symbol)

    name of the event (:pre_read, :post_render, etc.)

  • priority (Integer, Symbol) (defaults to: DEFAULT_PRIORITY)

    either :low, :normal, or :high, or an integer. Default is normal (20)

  • reloadable (Boolean) (defaults to: true)

    whether the hook should be removed prior to a site reload. Default is true.

Yields:

  • the block will be called when the event is triggered. Typically it receives at least one argument.

Yield Parameters:

  • obj

    the object which triggered the event hook



56
57
58
59
60
# File 'lib/bridgetown-core/hooks.rb', line 56

def self.register(owners, event, priority: DEFAULT_PRIORITY, reloadable: true, &block)
  Array(owners).each do |owner|
    register_one(owner, event, priority:, reloadable:, &block)
  end
end

.register_one(owner, event, priority: DEFAULT_PRIORITY, reloadable: true) {|obj| ... } ⇒ Proc

Register a hook which may be triggered later for a particular event

Parameters:

  • owner (Symbol)

    name of the owner (:site, :resource, etc.)

  • event (Symbol)

    name of the event (:pre_read, :post_render, etc.)

  • priority (Integer, Symbol) (defaults to: DEFAULT_PRIORITY)

    either :low, :normal, or :high, or an integer. Default is normal (20)

  • reloadable (Boolean) (defaults to: true)

    whether the hook should be removed prior to a site reload. Default is true.

Yields:

  • the block will be called when the event is triggered. Typically it receives at least one argument.

Yield Parameters:

  • obj

    the object which triggered the event hook

Returns:

  • (Proc)

    the block that was pased in

Raises:



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/bridgetown-core/hooks.rb', line 74

def self.register_one(owner, event, priority: DEFAULT_PRIORITY, reloadable: true, &block)
  @registry[owner] ||= []

  raise Uncallable, "Hooks must respond to :call" unless block.respond_to? :call

  @registry[owner] << HookRegistration.new(
    owner:,
    event:,
    priority: priority_value(priority),
    reloadable:,
    block:
  )
  if ENV["BRIDGETOWN_LOG_LEVEL"] == "debug"
    if Bridgetown.respond_to?(:logger)
      Bridgetown.logger.debug("Registering hook:", @registry[owner].last.to_s)
    else
      p "Registering hook:", @registry[owner].last.to_s
    end
  end

  block
end

.remove_hook(owner, block) ⇒ Object

Delete a previously-registered hook

Parameters:

  • owners (Symbol)

    name of the owner (:site, :resource, etc.)

  • block (Proc)

    the exact block used originally to register the hook



101
102
103
# File 'lib/bridgetown-core/hooks.rb', line 101

def self.remove_hook(owner, block)
  @registry[owner].delete_if { |item| item.block == block }
end

.trigger(owner, event, *args) ⇒ Object

Trigger all registered hooks for a particular owner and event. Any arguments after the initial two will be directly passed along to the hooks.

Parameters:

  • owner (Symbol)

    name of the owner (:site, :resource, etc.)

  • event (Symbol)

    name of the event (:pre_read, :post_render, etc.)



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/bridgetown-core/hooks.rb', line 119

def self.trigger(owner, event, *args) # rubocop:disable Metrics/CyclomaticComplexity
  # proceed only if there are hooks to call
  hooks = @registry[owner]&.select { |item| item.event == event }
  return if hooks.nil? || hooks.empty?

  prioritized_hooks(hooks).each do |hook|
    if ENV["BRIDGETOWN_LOG_LEVEL"] == "debug"
      hook_info = args[0].respond_to?(:relative_path) ? args[0].relative_path : hook.block
      Bridgetown.logger.debug("Triggering hook:", "#{owner}:#{event} for #{hook_info}")
    end
    hook.block.call(*args)
  end

  true
end