Module: HaveAPI::Hooks

Defined in:
lib/haveapi/hooks.rb

Overview

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.

Usage

Register hooks

class MyClass include Hookable

has_hook :myhook
end

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])

Class Method Summary collapse

Class Method Details

.call_for(klass, name, where = nil, args: [], initial: {}) ⇒ Object

Call all blocks that are connected to hook in klass with name. klass may be a class name or an object instance. If where is set, the blocks are executed in it with instance_exec. args is an array of arguments given to all blocks. The first argument to all block is always a return value from previous block or initial, which defaults to an empty hash.

Blocks are executed one by one in the order they were connected. Blocks must return a hash, that is then passed to the next block and the return value from the last block is returned to the caller.

A block may decide that no further blocks should be executed. In such a case it calls Hooks.stop with the return value. It is then returned to the caller immediately.



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/haveapi/hooks.rb', line 101

def self.call_for(klass, name, where = nil, args: [], initial: {})
  classified = hook_classify(klass)

  catch(:stop) do
    return initial unless @hooks[classified]
    hooks = @hooks[classified][name][:listeners]
    return initial unless hooks

    hooks.each do |hook|
      if where
        ret = where.instance_exec(initial, *args, &hook)
      else
        ret = hook.call(initial, *args)
      end

      initial.update(ret) if ret
    end

    initial
  end
end

.connect_hook(klass, name, &block) ⇒ Object

Connect class hook defined in klass with name to block. klass is a class name.



70
71
72
# File 'lib/haveapi/hooks.rb', line 70

def self.connect_hook(klass, name, &block)
  @hooks[hook_classify(klass)][name][:listeners] << block
end

.connect_instance_hook(klass, name, &block) ⇒ Object

Connect instance hook from instance klass with name to block.



75
76
77
78
79
80
81
82
83
84
85
# File 'lib/haveapi/hooks.rb', line 75

def self.connect_instance_hook(klass, name, &block)
  unless @hooks[klass]
    @hooks[klass] = {}

    @hooks[klass.class].each do |k, v|
      @hooks[klass][k] = {listeners: []}
    end
  end

  @hooks[klass][name][:listeners] << block
end

.hook_classify(klass) ⇒ Object



123
124
125
# File 'lib/haveapi/hooks.rb', line 123

def self.hook_classify(klass)
  klass.is_a?(String) ? Object.const_get(klass) : klass
end

.hooksObject



64
65
66
# File 'lib/haveapi/hooks.rb', line 64

def self.hooks
  @hooks
end

.register_hook(klass, name, opts = {}) ⇒ Object

Register a hook defined by klass with name. klass is an instance of Class, that is class name, not it's instance. opts is a hash and can have following keys:

- desc - why this hook exists, when it's called
- context - the context in which given blocks are called
- args - hash of block arguments
- initial - hash of initial values
- ret - hash of return values


55
56
57
58
59
60
61
62
# File 'lib/haveapi/hooks.rb', line 55

def self.register_hook(klass, name, opts = {})
  classified = hook_classify(klass)
  opts[:listeners] = []

  @hooks ||= {}
  @hooks[classified] ||= {}
  @hooks[classified][name] = opts
end

.stop(ret) ⇒ Object



127
128
129
# File 'lib/haveapi/hooks.rb', line 127

def self.stop(ret)
  throw(ret)
end