Class: Zenrows::Hooks

Inherits:
Object
  • Object
show all
Includes:
MonitorMixin
Defined in:
lib/zenrows/hooks.rb,
lib/zenrows/hooks/context.rb,
lib/zenrows/hooks/log_subscriber.rb

Overview

Thread-safe hook registry for request lifecycle events

Manages registration and execution of callbacks for HTTP request events. Supports both block-based callbacks and subscriber objects.

Examples:

Register a block callback

hooks = Zenrows::Hooks.new
hooks.register(:on_response) { |response, context| puts response.status }

Register a subscriber object

class MySubscriber
  def on_response(response, context)
    puts response.status
  end
end
hooks.add_subscriber(MySubscriber.new)

Author:

  • Ernest Bursa

Since:

  • 0.3.0

Defined Under Namespace

Classes: Context, LogSubscriber

Constant Summary collapse

EVENTS =

Available hook events

Since:

  • 0.3.0

i[before_request after_request on_response on_error around_request].freeze

Instance Method Summary collapse

Constructor Details

#initializeHooks

Returns a new instance of Hooks.

Since:

  • 0.3.0



32
33
34
35
36
# File 'lib/zenrows/hooks.rb', line 32

def initialize
  super # Initialize MonitorMixin
  @callbacks = Hash.new { |h, k| h[k] = [] }
  @subscribers = []
end

Instance Method Details

#add_subscriber(subscriber) ⇒ self

Add a subscriber object that responds to hook methods

Subscriber objects can implement any of the hook methods:

  • before_request(context)
  • after_request(context)
  • on_response(response, context)
  • on_error(error, context)
  • around_request(context, &block)

Examples:

class MetricsSubscriber
  def on_response(response, context)
    StatsD.increment('requests')
  end
end
hooks.add_subscriber(MetricsSubscriber.new)

Parameters:

  • subscriber (Object)

    Object responding to one or more hook methods

Returns:

  • (self)

    Returns self for chaining

Since:

  • 0.3.0



79
80
81
82
83
84
85
# File 'lib/zenrows/hooks.rb', line 79

def add_subscriber(subscriber)
  unless EVENTS.any? { |e| subscriber.respond_to?(e) }
    warn "ZenRows: Subscriber #{subscriber.class} doesn't respond to any hook events"
  end
  synchronize { @subscribers << subscriber }
  self
end

#callbacks_for(event) ⇒ Array (protected)

Get callbacks for a specific event (for merging)

Parameters:

  • event (Symbol)

    Event name

Returns:

  • (Array)

    Copy of callbacks for the event

Since:

  • 0.3.0



172
173
174
# File 'lib/zenrows/hooks.rb', line 172

def callbacks_for(event)
  synchronize { @callbacks[event].dup }
end

#dupHooks

Duplicate the hooks registry

Returns:

  • (Hooks)

    New hooks registry with copied callbacks

Since:

  • 0.3.0



157
158
159
160
161
162
163
164
# File 'lib/zenrows/hooks.rb', line 157

def dup
  copy = Hooks.new
  synchronize do
    EVENTS.each { |e| @callbacks[e].each { |h| copy.register(e, h) } }
    @subscribers.each { |s| copy.add_subscriber(s) }
  end
  copy
end

#empty?Boolean

Check if any hooks are registered

Returns:

  • (Boolean)

    true if no hooks registered

Since:

  • 0.3.0



132
133
134
# File 'lib/zenrows/hooks.rb', line 132

def empty?
  synchronize { @callbacks.values.all?(&:empty?) && @subscribers.empty? }
end

#merge(other) ⇒ self

Merge hooks from another registry

Used for per-client hooks that inherit from global hooks.

Parameters:

  • other (Hooks, nil)

    Another hooks registry to merge

Returns:

  • (self)

Since:

  • 0.3.0



142
143
144
145
146
147
148
149
150
151
152
# File 'lib/zenrows/hooks.rb', line 142

def merge(other)
  return self unless other

  synchronize do
    EVENTS.each do |event|
      @callbacks[event].concat(other.callbacks_for(event))
    end
    @subscribers.concat(other.subscribers_list)
  end
  self
end

#register(event, callable = nil) { ... } ⇒ self

Register a callback for an event

Examples:

With block

hooks.register(:on_response) { |response, ctx| log(response) }

With callable

hooks.register(:on_response, ->(response, ctx) { log(response) })

Parameters:

  • event (Symbol)

    Event name (:before_request, :after_request, :on_response, :on_error, :around_request)

  • callable (#call, nil) (defaults to: nil)

    Callable object (proc, lambda, or object responding to #call)

Yields:

  • Block to execute when event fires

Returns:

  • (self)

    Returns self for chaining

Raises:

  • (ArgumentError)

    if event is unknown or handler doesn't respond to #call

Since:

  • 0.3.0



51
52
53
54
55
56
57
58
# File 'lib/zenrows/hooks.rb', line 51

def register(event, callable = nil, &block)
  validate_event!(event)
  handler = callable || block
  raise ArgumentError, "Handler must respond to #call" unless handler.respond_to?(:call)

  synchronize { @callbacks[event] << handler }
  self
end

#run(event, *args) ⇒ void

This method returns an undefined value.

Run callbacks for an event

Parameters:

  • event (Symbol)

    Event name

  • args (Array)

    Arguments to pass to callbacks

Since:

  • 0.3.0



92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/zenrows/hooks.rb', line 92

def run(event, *args)
  handlers, subscribers = synchronize do
    [@callbacks[event].dup, @subscribers.dup]
  end

  # Run registered callbacks
  handlers.each { |h| h.call(*args) }

  # Run subscriber methods
  subscribers.each do |sub|
    sub.public_send(event, *args) if sub.respond_to?(event)
  end
end

#run_around(context) { ... } ⇒ Object

Run around callbacks (wrapping)

Executes a chain of around handlers, each wrapping the next. If no around handlers exist, simply yields to the block.

Parameters:

  • context (Hash)

    Request context

Yields:

  • Block to wrap (the actual HTTP request)

Returns:

  • (Object)

    Result of the wrapped block

Since:

  • 0.3.0



114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/zenrows/hooks.rb', line 114

def run_around(context, &block)
  handlers, subscribers = synchronize do
    [@callbacks[:around_request].dup, @subscribers.dup]
  end

  # Build chain of around handlers
  chain = handlers + subscribers.select { |s| s.respond_to?(:around_request) }

  if chain.empty?
    block.call
  else
    execute_chain(chain, context, &block)
  end
end

#subscribers_listArray (protected)

Get subscribers list (for merging)

Returns:

  • (Array)

    Copy of subscribers

Since:

  • 0.3.0



179
180
181
# File 'lib/zenrows/hooks.rb', line 179

def subscribers_list
  synchronize { @subscribers.dup }
end