Class: Operandi::Base

Inherits:
Object
  • Object
show all
Extended by:
CallbackDsl
Includes:
Callbacks, Concerns::Execution, Concerns::ParentService, Concerns::StateManagement, Dsl::ArgumentsDsl, Dsl::OutputsDsl, Dsl::StepsDsl
Defined in:
lib/operandi/base.rb

Overview

Base class for building service objects with arguments, outputs, and steps.

Examples:

Basic service

class CreateUser < Operandi::Base
  arg :name, type: String
  arg :email, type: String

  output :user, type: User

  step :create_user

  private

  def create_user
    self.user = User.create!(name: name, email: email)
  end
end

result = CreateUser.run(name: "John", email: "john@example.com")
result.success? # => true
result.user     # => #<User id: 1, name: "John">

Constant Summary

Constants included from Callbacks

Callbacks::EVENTS

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from CallbackDsl

after_service_run, after_step_run, all_callbacks_for, around_service_run, around_step_run, before_service_run, before_step_run, callbacks_for, on_service_failure, on_service_success, on_step_crash, on_step_failure, on_step_success

Methods included from Dsl::StepsDsl

included

Methods included from Dsl::OutputsDsl

included

Methods included from Dsl::ArgumentsDsl

included

Methods included from Callbacks

#run_callbacks

Constructor Details

#initialize(args = {}, config = {}, parent_service = nil) ⇒ Base

Initialize a new service instance.

Parameters:

  • args (Hash) (defaults to: {})

    arguments to pass to the service

  • config (Hash) (defaults to: {})

    runtime configuration overrides

  • parent_service (Base, nil) (defaults to: nil)

    parent service for nested calls



71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/operandi/base.rb', line 71

def initialize(args = {}, config = {}, parent_service = nil)
  @config = Operandi.config.merge(self.class.class_config || {}).merge(config)
  @parent_service = parent_service

  @output = Collection::Base.new(self, CollectionTypes::OUTPUTS)
  @arg = Collection::Base.new(self, CollectionTypes::ARGUMENTS, args.dup)

  @stopped = false
  @launched_steps = []

  initialize_errors
  initialize_warnings
end

Class Attribute Details

.class_configHash?

Returns class-level configuration options.

Returns:

  • (Hash, nil)

    class-level configuration options



177
178
179
# File 'lib/operandi/base.rb', line 177

def class_config
  @class_config
end

Instance Attribute Details

#argCollection::Base (readonly)

Returns collection of argument values.

Returns:



55
56
57
# File 'lib/operandi/base.rb', line 55

def arg
  @arg
end

#errorsMessages (readonly)

Returns collection of error messages.

Returns:

  • (Messages)

    collection of error messages



61
62
63
# File 'lib/operandi/base.rb', line 61

def errors
  @errors
end

#outputCollection::Base (readonly)

Returns collection of output values.

Returns:



58
59
60
# File 'lib/operandi/base.rb', line 58

def output
  @output
end

#warningsMessages (readonly)

Returns collection of warning messages.

Returns:

  • (Messages)

    collection of warning messages



64
65
66
# File 'lib/operandi/base.rb', line 64

def warnings
  @warnings
end

Class Method Details

.config(config = {}) ⇒ Hash

Set class-level configuration for this service.

Parameters:

  • config (Hash) (defaults to: {})

    configuration options

Returns:

  • (Hash)

    the configuration hash



183
184
185
# File 'lib/operandi/base.rb', line 183

def config(config = {})
  self.class_config = config
end

.run(**kwargs) ⇒ Base

Run the service and return the result.

Examples:

result = MyService.run(name: "test")
result.success? # => true

Parameters:

  • kwargs (Hash)

    keyword arguments matching service arguments

Returns:

  • (Base)

    the executed service instance



195
196
197
# File 'lib/operandi/base.rb', line 195

def run(**kwargs)
  new(kwargs).tap(&:call)
end

.run!(**kwargs) ⇒ Base

Run the service and raise an error if it fails.

Examples:

MyService.run!(name: "test") # raises if service fails

Parameters:

  • kwargs (Hash)

    keyword arguments matching service arguments

Returns:

  • (Base)

    the executed service instance

Raises:



207
208
209
# File 'lib/operandi/base.rb', line 207

def run!(**kwargs)
  new(kwargs, { raise_on_error: true }).tap(&:call)
end

.with(service_or_config, config = {}) ⇒ BaseWithContext

Create a context for running the service with a parent service or config.

Examples:

With parent service

ChildService.with(self).run(data: value)

With configuration

MyService.with(use_transactions: false).run(name: "test")

Parameters:

  • service_or_config (Base, Hash)

    parent service or configuration hash

  • config (Hash) (defaults to: {})

    configuration hash (when first param is a service)

Returns:



222
223
224
225
226
227
# File 'lib/operandi/base.rb', line 222

def with(service_or_config, config = {})
  service = service_or_config.is_a?(Hash) ? nil : service_or_config
  config = service_or_config unless service

  BaseWithContext.new(self, service, config.dup)
end

Instance Method Details

#callvoid

This method returns an undefined value.

Execute the service steps.

Raises:

  • (StandardError)

    re-raises any exception after running always steps



160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/operandi/base.rb', line 160

def call
  load_defaults_and_validate

  run_callbacks(:before_service_run, self)

  run_callbacks(:around_service_run, self) do
    execute_service
  end

  run_service_result_callbacks
rescue StandardError => e
  run_steps_with_always
  raise e
end

#errors?Boolean

Check if the service has any errors.

Returns:

  • (Boolean)

    true if errors collection is not empty



103
104
105
# File 'lib/operandi/base.rb', line 103

def errors?
  @errors.any?
end

#fail!(message) ⇒ void

This method returns an undefined value.

Add an error to the :base key.

Parameters:

  • message (String)

    the error message



141
142
143
# File 'lib/operandi/base.rb', line 141

def fail!(message)
  errors.add(:base, message)
end

#fail_immediately!(message) ⇒ void

This method returns an undefined value.

Add an error and stop execution immediately, causing transaction rollback. Steps marked with always: true will still run after this method is called.

Parameters:

  • message (String)

    the error message

Raises:

  • (FailExecution)

    always raises to halt execution and rollback



151
152
153
154
# File 'lib/operandi/base.rb', line 151

def fail_immediately!(message)
  errors.add(:base, message, rollback: false)
  raise Operandi::FailExecution
end

#failed?Boolean

Check if the service completed with errors.

Returns:

  • (Boolean)

    true if any errors were added



96
97
98
# File 'lib/operandi/base.rb', line 96

def failed?
  errors?
end

#stop!Boolean

Stop executing remaining steps after the current step completes.

Returns:

  • (Boolean)

    true



117
118
119
# File 'lib/operandi/base.rb', line 117

def stop!
  @stopped = true
end

#stop_immediately!void

This method returns an undefined value.

Stop execution immediately, skipping any remaining code in the current step.

Raises:



132
133
134
135
# File 'lib/operandi/base.rb', line 132

def stop_immediately!
  @stopped = true
  raise Operandi::StopExecution
end

#stopped?Boolean

Check if the service has been stopped.

Returns:

  • (Boolean)

    true if stop! was called



124
125
126
# File 'lib/operandi/base.rb', line 124

def stopped?
  @stopped
end

#success?Boolean Also known as: successful?

Check if the service completed without errors.

Returns:

  • (Boolean)

    true if no errors were added



88
89
90
# File 'lib/operandi/base.rb', line 88

def success?
  !errors?
end

#warnings?Boolean

Check if the service has any warnings.

Returns:

  • (Boolean)

    true if warnings collection is not empty



110
111
112
# File 'lib/operandi/base.rb', line 110

def warnings?
  @warnings.any?
end