module HaveAPI::Hooks

All registered hooks and connected endpoints are stored in this module.

It supports connecting to both class and instance level hooks. Instance level hooks inherit all class registered hooks, but it is possible to connect to a specific instance and not for all instances of a class.

Hook definition contains additional information for as a documentation: description, context, arguments, return value.

Every hook can have multiple listeners. They are invoked in the order of registration. Instance-level listeners first, then class-level. Hooks are chained using the block’s first argument and return value. The first block to be executed gets the initial value, may make changes and returns it. The next block gets the return value of the previous block as its first argument, may make changes and returns it. Return value of the last block is returned to the caller of the hook.

Usage

Register hooks

class MyClass
  include Hookable

  has_hook :myhook,
           desc: 'Called when I want to',
           context: 'current',
           args: {
               a: 'integer',
               b: 'integer',
               c: 'integer',
           }
end

Not that the additional information is just optional. A list of defined hooks and their description is a part of the reference documentation generated by yard.

Class level hooks

# Connect hook
MyClass.connect_hook(:myhook) do |ret, a, b, c|
  # a = 1, b = 2, c = 3
  puts "Class hook!"
  ret
end

# Call hooks
MyClass.call_hooks(:myhook, args: [1, 2, 3])

Instance level hooks

# Create an instance of MyClass
my = MyClass.new

# Connect hook
my.connect_hook(:myhook) do |ret, a, b, c|
  # a = 1, b = 2, c = 3
  puts "Instance hook!"
  ret
end

# Call instance hooks
my.call_instance_hooks_for(:myhook, args: [1, 2, 3])
# Call class hooks
my.call_class_hooks_for(:myhook, args: [1, 2, 3])
# Call both instance and class hooks at once
my.call_hooks_for(:myhook, args: [1, 2, 3])

Chaining

5.times do |i|
  MyClass.connect_hook(:myhook) do |ret, a, b, c|
    ret[:counter] += i
    ret
  end
end

p MyClass.call_hooks(:myhook, args: [1, 2, 3], initial: {counter: 0})
=> {:counter=>5}