Class: BetterService::Services::Base

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Concerns::Serviceable::Authorizable

#authorize!

Methods included from Concerns::Serviceable::Transactional

prepended

Methods included from Concerns::Instrumentation

#build_completion_payload, #build_failure_payload, #build_result_failure_payload, #build_start_payload, #extract_user_id_from_instance, #instrumentation_enabled?, prepended, #publish_cache_hit, #publish_cache_miss

Constructor Details

#initialize(user, params: {}) ⇒ Base

Returns a new instance of Base.



38
39
40
41
42
43
44
# File 'lib/better_service/services/base.rb', line 38

def initialize(user, params: {})
  validate_user_presence!(user) unless self.class._allow_nil_user
  validate_schema_presence!
  @user = user
  @params = safe_params_to_hash(params)
  validate_params!
end

Instance Attribute Details

#paramsObject (readonly)

Returns the value of attribute params.



36
37
38
# File 'lib/better_service/services/base.rb', line 36

def params
  @params
end

#userObject (readonly)

Returns the value of attribute user.



36
37
38
# File 'lib/better_service/services/base.rb', line 36

def user
  @user
end

Class Method Details

.allow_nil_user(value = true) ⇒ void

This method returns an undefined value.

Configure whether this service allows nil user

Examples:

Allow nil user

class PublicService < BetterService::Services::Base
  allow_nil_user true
end

Require user (default)

class PrivateService < BetterService::Services::Base
  allow_nil_user false
end

Parameters:

  • value (Boolean) (defaults to: true)

    Whether to allow nil user (default: true)



62
63
64
# File 'lib/better_service/services/base.rb', line 62

def self.allow_nil_user(value = true)
  self._allow_nil_user = value
end

.auto_invalidate_cache(enabled = true) ⇒ void

This method returns an undefined value.

Configure automatic cache invalidation after write operations

Examples:

Enable automatic cache invalidation (default for Create/Update/Destroy)

class Products::CreateService < CreateService
  cache_contexts :products, :category_products
  # Cache is automatically invalidated after successful create
end

Disable automatic cache invalidation

class Products::CreateService < CreateService
  auto_invalidate_cache false

  process_with do |data|
    product = Product.create!(params)
    invalidate_cache_for(user) if should_invalidate?  # Manual control
    { resource: product }
  end
end

Parameters:

  • enabled (Boolean) (defaults to: true)

    Whether to automatically invalidate cache (default: true)



121
122
123
# File 'lib/better_service/services/base.rb', line 121

def self.auto_invalidate_cache(enabled = true)
  self._auto_invalidate_cache = enabled
end

.performed_action(name) ⇒ void

This method returns an undefined value.

Configure the action name for metadata tracking

Examples:

Custom action service

class Order::ApproveService < Order::BaseService
  performed_action :approve
end

CRUD service with standard action

class Product::CreateService < Product::BaseService
  performed_action :created
end

Parameters:

  • name (Symbol, String)

    The action name (e.g., :publish, :approve)



80
81
82
# File 'lib/better_service/services/base.rb', line 80

def self.performed_action(name)
  self._action_name = name.to_sym
end

.process_with(&block) ⇒ Object



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

def self.process_with(&block)
  self._process_block = block
end

.respond_with(&block) ⇒ Object



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

def self.respond_with(&block)
  self._respond_block = block
end

.search_with(&block) ⇒ Object



84
85
86
# File 'lib/better_service/services/base.rb', line 84

def self.search_with(&block)
  self._search_block = block
end

.transform_with(&block) ⇒ Object



92
93
94
# File 'lib/better_service/services/base.rb', line 92

def self.transform_with(&block)
  self._transform_block = block
end

Instance Method Details

#callBetterService::Result

Main entry point - executes the 5-phase flow

Examples:

Success

result = ProductService.new(user, params: params).call
result.success? # => true
result.resource.persisted? # => true

Using destructuring

product, meta = ProductService.new(user, params: params).call
meta[:success] # => true

Validation failure (returns object with errors)

result = ProductService.new(user, params: invalid_params).call
result.failure? # => true
result.validation_errors # => { name: ["can't be blank"] }
result.resource.errors.any? # => true (object still available for form re-render)

Returns:

  • (BetterService::Result)

    Result wrapper containing resource and metadata

    • result.resource: The resource (single AR model, array of models, or nil on error)

    • result.meta: Hash with success status, action, message, and validation errors if any

    • Supports destructuring: ‘resource, meta = service.call`



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/better_service/services/base.rb', line 146

def call
  # Validation already raises ValidationError in initialize
  # Authorization already raises AuthorizationError
  authorize!

  data = search
  processed = process(data)

  # Auto-invalidate cache after write operations if configured
  if should_auto_invalidate_cache?
    invalidate_cache_for(user)
  end

  transformed = transform(processed)
  result = respond(transformed)

  # Build Result response
  build_result_response(result)
rescue Errors::Runtime::ValidationError => e
  # Schema validation errors (from initialize) - no object available
  wrap_response(nil, (e))
rescue Errors::Runtime::AuthorizationError => e
  # Authorization errors - no object available
  wrap_response(nil, (e))
rescue Errors::Runtime::ResourceNotFoundError => e
  # Resource not found (BetterService error) - no object available
  wrap_response(nil, (e))
rescue ActiveRecord::RecordNotFound => e
  # Resource not found (ActiveRecord error) - no object available
  wrap_response(nil, (e))
rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotSaved => e
  # AR validation errors - return the object with errors for form re-render
  wrap_response(e.record, (e))
rescue StandardError => e
  # Unexpected errors
  wrap_response(nil, (e))
end