Class: Servus::Base Abstract

Inherits:
Object
  • Object
show all
Includes:
Events::Emitter, Guards, Support::Errors, Support::Rescuer
Defined in:
lib/servus/base.rb

Overview

This class is abstract.

Subclass and implement initialize and call methods to create a service

Base class for all service objects in the Servus framework.

This class provides the foundational functionality for implementing the Service Object pattern, including automatic validation, logging, benchmarking, and error handling.

Examples:

Creating a basic service

class Services::ProcessPayment::Service < Servus::Base
  def initialize(user:, amount:, payment_method:)
    @user = user
    @amount = amount
    @payment_method = payment_method
  end

  def call
    return failure("Invalid amount") if @amount <= 0

    transaction = charge_payment
    success({ transaction_id: transaction.id })
  end

  private

  def charge_payment
    # Payment processing logic
  end
end

Using a service

result = Services::ProcessPayment::Service.call(
  user: current_user,
  amount: 100,
  payment_method: "credit_card"
)

if result.success?
  puts "Transaction ID: #{result.data[:transaction_id]}"
else
  puts "Error: #{result.error.message}"
end

See Also:

Constant Summary collapse

Logger =

Support class aliases

Servus::Support::Logger
Emitter =
Servus::Events::Emitter
Response =
Servus::Support::Response
Validator =
Servus::Support::Validator

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Guards

load_defaults

Methods included from Events::Emitter

#emit_events_for, emit_result_events!

Methods included from Support::Rescuer

included

Class Attribute Details

.arguments_schemaHash? (readonly)

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 the arguments schema defined via the schema DSL method.

Returns:

  • (Hash, nil)

    the arguments schema or nil if not defined



257
258
259
# File 'lib/servus/base.rb', line 257

def arguments_schema
  @arguments_schema
end

.result_schemaHash? (readonly)

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 the result schema defined via the schema DSL method.

Returns:

  • (Hash, nil)

    the result schema or nil if not defined



263
264
265
# File 'lib/servus/base.rb', line 263

def result_schema
  @result_schema
end

Class Method Details

.after_call(result, instance) ⇒ 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.

Executes post-call hooks including result validation and event emission.

This method is automatically called after service execution completes and handles:

  • Validating the result data against RESULT_SCHEMA (if defined)
  • Emitting events declared with the emits DSL

Parameters:

Raises:



293
294
295
296
# File 'lib/servus/base.rb', line 293

def after_call(result, instance)
  Validator.validate_result!(self, result)
  Emitter.emit_result_events!(instance, result)
end

.before_call(args) ⇒ 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.

Executes pre-call hooks including logging and argument validation.

This method is automatically called before service execution and handles:

  • Logging the service call with arguments
  • Validating arguments against ARGUMENTS_SCHEMA (if defined)

Parameters:

  • args (Hash)

    keyword arguments being passed to the service

Raises:



276
277
278
279
# File 'lib/servus/base.rb', line 276

def before_call(args)
  Logger.log_call(self, args)
  Validator.validate_arguments!(self, args)
end

.benchmark(**_args) ⇒ Servus::Support::Response

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.

Measures service execution time and logs the result.

This method wraps the service execution to capture timing metrics. The duration is logged along with the success/failure status of the service.

Parameters:

  • _args (Hash)

    keyword arguments (unused, kept for method signature compatibility)

Yield Returns:

Returns:



308
309
310
311
312
313
314
315
316
# File 'lib/servus/base.rb', line 308

def benchmark(**_args)
  start_time = Time.now.utc
  result = yield
  duration = Time.now.utc - start_time

  Logger.log_result(self, result, duration)

  result
end

.call(**args) ⇒ Servus::Support::Response

Executes the service with automatic validation, logging, and benchmarking.

This is the primary entry point for executing services. It handles the complete service lifecycle including:

  • Input argument validation against schema
  • Service instantiation
  • Execution timing/benchmarking
  • Result validation against schema
  • Automatic logging of calls, results, and errors

rubocop:disable Metrics/MethodLength

Examples:

Successful execution

result = MyService.call(user_id: 123, amount: 50)
result.success? # => true
result.data # => { transaction_id: "abc123" }

Failed execution

result = MyService.call(user_id: 123, amount: -10)
result.success? # => false
result.error.message # => "Amount must be positive"

Parameters:

  • args (Hash)

    keyword arguments passed to the service's initialize method

Returns:

Raises:

See Also:

  • #initialize
  • #call


185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/servus/base.rb', line 185

def call(**args)
  before_call(args)

  instance = new(**args)

  # Wrap execution in catch block to handle guard failures
  result = catch(:guard_failure) do
    benchmark(**args) { instance.call }
  end

  # If result is a GuardError, a guard failed - wrap in failure Response
  result = Response.new(false, nil, result) if result.is_a?(Servus::Support::Errors::GuardError)

  after_call(result, instance)

  result
rescue Servus::Support::Errors::ValidationError => e
  Logger.log_validation_error(self, e)
  raise e
rescue StandardError => e
  Logger.log_exception(self, e)
  raise e
end

.schema(arguments: nil, result: nil) ⇒ void

This method returns an undefined value.

Defines schema validation rules for the service's arguments and/or result.

This method provides a clean DSL for specifying JSON schemas that will be used to validate service inputs and outputs. Schemas defined via this method take precedence over ARGUMENTS_SCHEMA and RESULT_SCHEMA constants. The next major version will deprecate those constants in favor of this DSL.

Examples:

Defining both arguments and result schemas

class ProcessPayment::Service < Servus::Base
  schema(
    arguments: {
      type: 'object',
      required: ['user_id', 'amount'],
      properties: {
        user_id: { type: 'integer' },
        amount: { type: 'number', minimum: 0.01 }
      }
    },
    result: {
      type: 'object',
      required: ['transaction_id'],
      properties: {
        transaction_id: { type: 'string' }
      }
    }
  )
end

Defining only arguments schema

class SendEmail::Service < Servus::Base
  schema arguments: { type: 'object', required: ['email', 'subject'] }
end

Parameters:

  • arguments (Hash, nil) (defaults to: nil)

    JSON schema for validating service arguments

  • result (Hash, nil) (defaults to: nil)

    JSON schema for validating service result data

See Also:



248
249
250
251
# File 'lib/servus/base.rb', line 248

def schema(arguments: nil, result: nil)
  @arguments_schema = arguments.with_indifferent_access if arguments
  @result_schema    = result.with_indifferent_access    if result
end

Instance Method Details

#error!(message = nil, type: Servus::Support::Errors::ServiceError) ⇒ void

Note:

Prefer #failure for expected error conditions. Use this for exceptional cases.

This method returns an undefined value.

Logs an error and raises an exception, halting service execution.

Use this method when you need to immediately halt execution with an exception rather than returning a failure response. The error is automatically logged before the exception is raised.

Examples:

Raising an error with custom message

def call
  error!("Critical system failure") if system_down?
end

Raising with specific error type

def call
  error!("Unauthorized access", type: Servus::Support::Errors::UnauthorizedError)
end

Parameters:

  • message (String, nil) (defaults to: nil)

    error message for the exception (uses default if nil)

  • type (Class) (defaults to: Servus::Support::Errors::ServiceError)

    error class to raise (must inherit from ServiceError)

Raises:

See Also:



143
144
145
146
147
148
149
150
151
# File 'lib/servus/base.rb', line 143

def error!(message = nil, type: Servus::Support::Errors::ServiceError)
  error = type.new(message)
  Logger.log_exception(self.class, error)

  # Emit error! events before raising
  emit_events_for(:error!, Response.new(false, nil, error))

  raise type, message
end

#failure(message = nil, type: Servus::Support::Errors::ServiceError) ⇒ Servus::Support::Response

Creates a failure response with an error.

Use this method to return failure results from your service's call method. The failure is logged automatically and returns a response containing the error.

Examples:

Using default error type with custom message

def call
  return failure("User not found") unless user_exists?
  # ...
end

Using custom error type

def call
  return failure("Invalid payment", type: Servus::Support::Errors::BadRequestError)
  # ...
end

Using error type's default message

def call
  return failure(type: Servus::Support::Errors::NotFoundError)
  # Uses "Not found" as the message
end

Parameters:

  • message (String, nil) (defaults to: nil)

    custom error message (uses error type's default if nil)

  • type (Class) (defaults to: Servus::Support::Errors::ServiceError)

    error class to instantiate (must inherit from ServiceError)

Returns:

See Also:



115
116
117
118
# File 'lib/servus/base.rb', line 115

def failure(message = nil, type: Servus::Support::Errors::ServiceError)
  error = type.new(message)
  Response.new(false, nil, error)
end

#success(data) ⇒ Servus::Support::Response

Creates a successful response with the provided data.

Use this method to return successful results from your service's call method. The data will be validated against the RESULT_SCHEMA if one is defined.

Examples:

Returning simple data

def call
  success({ user_id: 123, status: "active" })
end

Returning nil for operations without data

def call
  perform_action
  success(nil)
end

Parameters:

  • data (Object)

    the data to return in the response (typically a Hash)

Returns:

See Also:



81
82
83
# File 'lib/servus/base.rb', line 81

def success(data)
  Response.new(true, data, nil)
end