Module: Servus::Events::Emitter

Extended by:
ActiveSupport::Concern
Included in:
Base
Defined in:
lib/servus/events/emitter.rb

Overview

Provides event emission DSL for service objects.

This module adds the emits class method to services, allowing them to declare events that will be automatically emitted on success, failure, or error.

Examples:

Basic usage

class CreateUser < Servus::Base
  emits :user_created, on: :success
  emits :user_failed, on: :failure
end

Constant Summary collapse

EMISSION_TRIGGERS =

Triggers accepted by the emits DSL.

:success and :failure are selected from the service's result after call returns. :error! is fired by Base#error! immediately before it raises, so it never coincides with :failure.

Note the bang on :error! — it mirrors the method that triggers it.

%i[success failure error!].freeze

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.emit_result_events!(instance, result) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Emits events for a service result.

Called automatically after service execution completes. Determines the trigger type based on the result and emits all configured events.

Parameters:



36
37
38
39
# File 'lib/servus/events/emitter.rb', line 36

def self.emit_result_events!(instance, result)
  trigger = result.success? ? :success : :failure
  instance.send(:emit_events_for, trigger, result)
end

Instance Method Details

#build_event_payload(emission, result) ⇒ Hash

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Builds the event payload using the configured payload builder or defaults.

Parameters:

Returns:

  • (Hash)

    the event payload



236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'lib/servus/events/emitter.rb', line 236

def build_event_payload(emission, result)
  builder = emission[:payload_builder]

  if builder.is_a?(Proc)
    instance_exec(result, &builder)
  elsif builder.is_a?(Symbol)
    # Method-based payload builder
    send(builder, result)
  elsif result.success?
    # Default for success: return data
    result.data
  else
    # Default for failure/error: return error
    result.error
  end
end

#emission_condition_met?(emission, result) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns true when all declared conditions on the emission pass.

Parameters:

Returns:

  • (Boolean)


173
174
175
176
177
178
179
180
181
# File 'lib/servus/events/emitter.rb', line 173

def emission_condition_met?(emission, result)
  if_condition = emission[:if_condition]
  unless_condition = emission[:unless_condition]

  return false if if_condition && !evaluate_emission_condition(if_condition, result)
  return false if unless_condition && evaluate_emission_condition(unless_condition, result)

  true
end

#emit_events_for(trigger, result) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Emits events for a specific trigger with the given result.

Parameters:

  • trigger (Symbol)

    the trigger type (:success, :failure, :error!)

  • result (Servus::Support::Response)

    the service result



155
156
157
158
159
160
161
162
163
# File 'lib/servus/events/emitter.rb', line 155

def emit_events_for(trigger, result)
  self.class.emissions_for(trigger).each do |emission|
    next unless emission_condition_met?(emission, result)

    payload = build_event_payload(emission, result)
    validate_event_payload!(emission[:event_name], payload)
    Servus::Events::Bus.emit(emission[:event_name], payload)
  end
end

#evaluate_emission_condition(condition, result) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Evaluates a single emission condition — either a Proc/lambda or a Symbol method reference.

Both forms receive the result object so conditions can inspect result.data, result.error, result.success?, etc.

Parameters:

Returns:

  • (Object)

    truthy or falsy value



192
193
194
# File 'lib/servus/events/emitter.rb', line 192

def evaluate_emission_condition(condition, result)
  condition.is_a?(Proc) ? instance_exec(result, &condition) : send(condition, result)
end

#require_event_schema!(event_name) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Enforces Config#require_event_payload_schema for an event that has no Event class to carry a schema.

An unregistered event name is the one case where a payload cannot be validated at all, so it is exactly where the flag matters most. Skipping it here would mean the setting silently passed over the events furthest from having a contract.

Parameters:

  • event_name (Symbol)

    the event being emitted

Raises:



222
223
224
225
226
227
228
# File 'lib/servus/events/emitter.rb', line 222

def require_event_schema!(event_name)
  return unless Servus.config.require_event_payload_schema

  raise Servus::Support::Errors::SchemaRequiredError,
        "#{self.class} emits :#{event_name} but no Event class is registered for it — " \
        'schema missing! require_event_payload_schema is set to true.'
end

#validate_event_payload!(event_name, payload) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Validates the payload against the Event class's schema registered for the event.

Parameters:

  • event_name (Symbol)

    the event name

  • payload (Hash)

    the event payload

Raises:



203
204
205
206
207
208
# File 'lib/servus/events/emitter.rb', line 203

def validate_event_payload!(event_name, payload)
  event_class = Servus::Events::Bus.event_for(event_name)
  return require_event_schema!(event_name) unless event_class

  Servus::Support::Validator.validate_event_payload!(event_class, payload)
end