better_service 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +1321 -0
  4. data/Rakefile +15 -0
  5. data/lib/better_service/cache_service.rb +310 -0
  6. data/lib/better_service/concerns/instrumentation.rb +242 -0
  7. data/lib/better_service/concerns/serviceable/authorizable.rb +106 -0
  8. data/lib/better_service/concerns/serviceable/cacheable.rb +97 -0
  9. data/lib/better_service/concerns/serviceable/messageable.rb +30 -0
  10. data/lib/better_service/concerns/serviceable/presentable.rb +66 -0
  11. data/lib/better_service/concerns/serviceable/transactional.rb +51 -0
  12. data/lib/better_service/concerns/serviceable/validatable.rb +58 -0
  13. data/lib/better_service/concerns/serviceable/viewable.rb +49 -0
  14. data/lib/better_service/concerns/serviceable.rb +12 -0
  15. data/lib/better_service/concerns/workflowable/callbacks.rb +116 -0
  16. data/lib/better_service/concerns/workflowable/context.rb +108 -0
  17. data/lib/better_service/concerns/workflowable/step.rb +141 -0
  18. data/lib/better_service/concerns/workflowable.rb +12 -0
  19. data/lib/better_service/configuration.rb +113 -0
  20. data/lib/better_service/errors/better_service_error.rb +271 -0
  21. data/lib/better_service/errors/configuration/configuration_error.rb +21 -0
  22. data/lib/better_service/errors/configuration/invalid_configuration_error.rb +28 -0
  23. data/lib/better_service/errors/configuration/invalid_schema_error.rb +28 -0
  24. data/lib/better_service/errors/configuration/nil_user_error.rb +37 -0
  25. data/lib/better_service/errors/configuration/schema_required_error.rb +29 -0
  26. data/lib/better_service/errors/runtime/authorization_error.rb +38 -0
  27. data/lib/better_service/errors/runtime/database_error.rb +38 -0
  28. data/lib/better_service/errors/runtime/execution_error.rb +27 -0
  29. data/lib/better_service/errors/runtime/resource_not_found_error.rb +38 -0
  30. data/lib/better_service/errors/runtime/runtime_error.rb +22 -0
  31. data/lib/better_service/errors/runtime/transaction_error.rb +34 -0
  32. data/lib/better_service/errors/runtime/validation_error.rb +42 -0
  33. data/lib/better_service/errors/workflowable/configuration/duplicate_step_error.rb +27 -0
  34. data/lib/better_service/errors/workflowable/configuration/invalid_step_error.rb +12 -0
  35. data/lib/better_service/errors/workflowable/configuration/step_not_found_error.rb +29 -0
  36. data/lib/better_service/errors/workflowable/configuration/workflow_configuration_error.rb +24 -0
  37. data/lib/better_service/errors/workflowable/runtime/rollback_error.rb +46 -0
  38. data/lib/better_service/errors/workflowable/runtime/step_execution_error.rb +47 -0
  39. data/lib/better_service/errors/workflowable/runtime/workflow_execution_error.rb +40 -0
  40. data/lib/better_service/errors/workflowable/runtime/workflow_runtime_error.rb +25 -0
  41. data/lib/better_service/railtie.rb +6 -0
  42. data/lib/better_service/services/action_service.rb +60 -0
  43. data/lib/better_service/services/base.rb +249 -0
  44. data/lib/better_service/services/create_service.rb +60 -0
  45. data/lib/better_service/services/destroy_service.rb +57 -0
  46. data/lib/better_service/services/index_service.rb +56 -0
  47. data/lib/better_service/services/show_service.rb +44 -0
  48. data/lib/better_service/services/update_service.rb +58 -0
  49. data/lib/better_service/subscribers/log_subscriber.rb +131 -0
  50. data/lib/better_service/subscribers/stats_subscriber.rb +208 -0
  51. data/lib/better_service/version.rb +3 -0
  52. data/lib/better_service/workflows/base.rb +106 -0
  53. data/lib/better_service/workflows/dsl.rb +59 -0
  54. data/lib/better_service/workflows/execution.rb +89 -0
  55. data/lib/better_service/workflows/result_builder.rb +67 -0
  56. data/lib/better_service/workflows/rollback_support.rb +44 -0
  57. data/lib/better_service/workflows/transaction_support.rb +32 -0
  58. data/lib/better_service.rb +28 -0
  59. data/lib/generators/serviceable/action_generator.rb +29 -0
  60. data/lib/generators/serviceable/create_generator.rb +27 -0
  61. data/lib/generators/serviceable/destroy_generator.rb +27 -0
  62. data/lib/generators/serviceable/index_generator.rb +27 -0
  63. data/lib/generators/serviceable/scaffold_generator.rb +70 -0
  64. data/lib/generators/serviceable/show_generator.rb +27 -0
  65. data/lib/generators/serviceable/templates/action_service.rb.tt +42 -0
  66. data/lib/generators/serviceable/templates/create_service.rb.tt +33 -0
  67. data/lib/generators/serviceable/templates/destroy_service.rb.tt +40 -0
  68. data/lib/generators/serviceable/templates/index_service.rb.tt +54 -0
  69. data/lib/generators/serviceable/templates/service_test.rb.tt +23 -0
  70. data/lib/generators/serviceable/templates/show_service.rb.tt +37 -0
  71. data/lib/generators/serviceable/templates/update_service.rb.tt +50 -0
  72. data/lib/generators/serviceable/update_generator.rb +27 -0
  73. data/lib/generators/workflowable/WORKFLOW_README +27 -0
  74. data/lib/generators/workflowable/templates/workflow.rb.tt +72 -0
  75. data/lib/generators/workflowable/templates/workflow_test.rb.tt +62 -0
  76. data/lib/generators/workflowable/workflow_generator.rb +60 -0
  77. data/lib/tasks/better_service_tasks.rake +4 -0
  78. metadata +180 -0
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterService
4
+ module Workflowable
5
+ # Step - Represents a single step in a workflow pipeline
6
+ #
7
+ # Each step wraps a service class and defines how data flows into it,
8
+ # whether it's optional, conditional execution, and rollback behavior.
9
+ #
10
+ # Example:
11
+ # step = Step.new(
12
+ # name: :create_order,
13
+ # service_class: Order::CreateService,
14
+ # input: ->(ctx) { { items: ctx.cart_items } },
15
+ # optional: false,
16
+ # condition: ->(ctx) { ctx.cart_items.any? },
17
+ # rollback: ->(ctx) { ctx.order.destroy! }
18
+ # )
19
+ #
20
+ # step.call(context, user, params)
21
+ class Step
22
+ attr_reader :name, :service_class, :input_mapper, :optional, :condition, :rollback_block
23
+
24
+ def initialize(name:, service_class:, input: nil, optional: false, condition: nil, rollback: nil)
25
+ @name = name
26
+ @service_class = service_class
27
+ @input_mapper = input
28
+ @optional = optional
29
+ @condition = condition
30
+ @rollback_block = rollback
31
+ end
32
+
33
+ # Execute the step
34
+ #
35
+ # @param context [Context] The workflow context
36
+ # @param user [Object] The current user
37
+ # @param params [Hash] Base params for the workflow
38
+ # @return [Hash] Service result
39
+ def call(context, user, base_params = {})
40
+ # Check if step should be skipped due to condition
41
+ if should_skip?(context)
42
+ return {
43
+ success: true,
44
+ skipped: true,
45
+ message: "Step #{name} skipped due to condition"
46
+ }
47
+ end
48
+
49
+ # Build input params for the service
50
+ service_params = build_params(context, base_params)
51
+
52
+ # Call the service
53
+ result = service_class.new(user, params: service_params).call
54
+
55
+ # Store result in context if successful
56
+ if result[:success]
57
+ store_result_in_context(context, result)
58
+ elsif optional
59
+ # If step is optional and failed, continue but log the failure
60
+ context.add(:"#{name}_error", result[:errors])
61
+ return {
62
+ success: true,
63
+ optional_failure: true,
64
+ message: "Optional step #{name} failed but continuing",
65
+ errors: result[:errors]
66
+ }
67
+ end
68
+
69
+ result
70
+ rescue StandardError => e
71
+ # If step is optional, swallow the error and continue
72
+ if optional
73
+ context.add(:"#{name}_error", e.message)
74
+ {
75
+ success: true,
76
+ optional_failure: true,
77
+ message: "Optional step #{name} raised error but continuing",
78
+ error: e.message
79
+ }
80
+ else
81
+ raise e
82
+ end
83
+ end
84
+
85
+ # Execute rollback for this step
86
+ #
87
+ # @param context [Context] The workflow context
88
+ # @raise [StandardError] If rollback fails (caught and wrapped by workflow)
89
+ def rollback(context)
90
+ return unless rollback_block
91
+
92
+ if rollback_block.is_a?(Proc)
93
+ context.instance_exec(context, &rollback_block)
94
+ else
95
+ rollback_block.call(context)
96
+ end
97
+ # Note: Exceptions are propagated to workflow which wraps them in RollbackError
98
+ end
99
+
100
+ private
101
+
102
+ # Check if step should be skipped
103
+ def should_skip?(context)
104
+ return false unless condition
105
+
106
+ if condition.is_a?(Proc)
107
+ !context.instance_exec(context, &condition)
108
+ else
109
+ !condition.call(context)
110
+ end
111
+ end
112
+
113
+ # Build params for service from context
114
+ def build_params(context, base_params)
115
+ if input_mapper
116
+ if input_mapper.is_a?(Proc)
117
+ context.instance_exec(context, &input_mapper)
118
+ else
119
+ input_mapper.call(context)
120
+ end
121
+ else
122
+ base_params
123
+ end
124
+ end
125
+
126
+ # Store successful result data in context
127
+ def store_result_in_context(context, result)
128
+ # Store resource if present
129
+ context.add(name, result[:resource]) if result.key?(:resource)
130
+
131
+ # Store items if present
132
+ context.add(name, result[:items]) if result.key?(:items)
133
+
134
+ # If neither resource nor items, store the whole result
135
+ if !result.key?(:resource) && !result.key?(:items)
136
+ context.add(name, result)
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterService
4
+ module Concerns
5
+ # Workflowable - Namespace for all workflow-related concerns
6
+ #
7
+ # This module groups all concerns that are specific to BetterService workflows,
8
+ # such as lifecycle callbacks, step execution, context management, etc.
9
+ module Workflowable
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterService
4
+ # Configuration - Centralized configuration for BetterService
5
+ #
6
+ # Provides configuration options for various BetterService features.
7
+ #
8
+ # @example Configure in initializer
9
+ # # config/initializers/better_service.rb
10
+ # BetterService.configure do |config|
11
+ # config.instrumentation_enabled = true
12
+ # config.instrumentation_include_args = false
13
+ # config.instrumentation_excluded_services = ["HealthCheckService"]
14
+ # end
15
+ class Configuration
16
+ # Enable/disable instrumentation globally
17
+ #
18
+ # When disabled, no events will be published for any service.
19
+ #
20
+ # @return [Boolean] Default: true
21
+ attr_accessor :instrumentation_enabled
22
+
23
+ # Include service arguments in event payloads
24
+ #
25
+ # When enabled, args and kwargs are included in event payloads.
26
+ # Disable if arguments contain sensitive data (passwords, tokens, etc.)
27
+ #
28
+ # @return [Boolean] Default: true
29
+ attr_accessor :instrumentation_include_args
30
+
31
+ # Include service result in event payloads
32
+ #
33
+ # When enabled, the service result is included in completion events.
34
+ # Disable to reduce payload size or protect sensitive return values.
35
+ #
36
+ # @return [Boolean] Default: false
37
+ attr_accessor :instrumentation_include_result
38
+
39
+ # List of service class names to exclude from instrumentation
40
+ #
41
+ # Services in this list will not publish any events.
42
+ # Useful for high-frequency services that would generate too many events.
43
+ #
44
+ # @return [Array<String>] Default: []
45
+ attr_accessor :instrumentation_excluded_services
46
+
47
+ # Enable/disable built-in log subscriber
48
+ #
49
+ # When enabled, all service events are logged to Rails.logger.
50
+ #
51
+ # @return [Boolean] Default: false
52
+ attr_accessor :log_subscriber_enabled
53
+
54
+ # Log level for built-in log subscriber
55
+ #
56
+ # Valid values: :debug, :info, :warn, :error
57
+ #
58
+ # @return [Symbol] Default: :info
59
+ attr_accessor :log_subscriber_level
60
+
61
+ # Enable/disable built-in stats subscriber
62
+ #
63
+ # When enabled, statistics are collected for all service executions.
64
+ #
65
+ # @return [Boolean] Default: false
66
+ attr_accessor :stats_subscriber_enabled
67
+
68
+ def initialize
69
+ # Instrumentation defaults
70
+ @instrumentation_enabled = true
71
+ @instrumentation_include_args = true
72
+ @instrumentation_include_result = false
73
+ @instrumentation_excluded_services = []
74
+
75
+ # Built-in subscribers defaults
76
+ @log_subscriber_enabled = false
77
+ @log_subscriber_level = :info
78
+ @stats_subscriber_enabled = false
79
+ end
80
+ end
81
+
82
+ class << self
83
+ # Get the global configuration object
84
+ #
85
+ # @return [BetterService::Configuration]
86
+ def configuration
87
+ @configuration ||= Configuration.new
88
+ end
89
+
90
+ # Configure BetterService
91
+ #
92
+ # @yield [Configuration] Configuration object
93
+ # @return [void]
94
+ #
95
+ # @example
96
+ # BetterService.configure do |config|
97
+ # config.instrumentation_enabled = true
98
+ # config.log_subscriber_enabled = true
99
+ # end
100
+ def configure
101
+ yield(configuration)
102
+ end
103
+
104
+ # Reset configuration to defaults
105
+ #
106
+ # Useful for testing.
107
+ #
108
+ # @return [void]
109
+ def reset_configuration!
110
+ @configuration = Configuration.new
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,271 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterService
4
+ # Base exception class for all BetterService errors
5
+ #
6
+ # This class provides rich error information including error codes, context,
7
+ # original errors, and structured metadata for debugging and logging.
8
+ #
9
+ # @example Basic usage
10
+ # raise BetterServiceError.new(
11
+ # "Something went wrong",
12
+ # code: :custom_error,
13
+ # context: { user_id: 123, action: "create" }
14
+ # )
15
+ #
16
+ # @example With original error
17
+ # begin
18
+ # dangerous_operation
19
+ # rescue StandardError => e
20
+ # raise BetterServiceError.new(
21
+ # "Operation failed",
22
+ # code: :operation_failed,
23
+ # original_error: e,
24
+ # context: { operation: "dangerous_operation" }
25
+ # )
26
+ # end
27
+ #
28
+ # @example Accessing error information
29
+ # begin
30
+ # service.call
31
+ # rescue BetterServiceError => e
32
+ # logger.error e.to_h
33
+ # # => {
34
+ # # error_class: "BetterService::Errors::Runtime::ValidationError",
35
+ # # message: "Validation failed",
36
+ # # code: :validation_failed,
37
+ # # timestamp: "2025-11-09T10:30:00Z",
38
+ # # context: { service: "UserService", validation_errors: {...} },
39
+ # # original_error: nil,
40
+ # # backtrace: [...]
41
+ # # }
42
+ # end
43
+ class BetterServiceError < StandardError
44
+ # @return [Symbol, nil] Error code for programmatic handling
45
+ attr_reader :code
46
+
47
+ # @return [Exception, nil] Original exception that caused this error
48
+ attr_reader :original_error
49
+
50
+ # @return [Hash] Additional context about the error
51
+ attr_reader :context
52
+
53
+ # @return [Time] When the error was raised
54
+ attr_reader :timestamp
55
+
56
+ # Initialize a new BetterService error
57
+ #
58
+ # @param message [String, nil] Human-readable error message
59
+ # @param code [Symbol, nil] Error code for programmatic handling
60
+ # @param original_error [Exception, nil] Original exception that caused this error
61
+ # @param context [Hash] Additional context about the error (service name, params, etc.)
62
+ def initialize(message = nil, code: nil, original_error: nil, context: {})
63
+ super(message)
64
+ @code = code
65
+ @original_error = original_error
66
+ @context = context || {}
67
+ @timestamp = Time.current
68
+ end
69
+
70
+ # Convert error to a structured hash
71
+ #
72
+ # @return [Hash] Hash representation with all error information
73
+ def to_h
74
+ {
75
+ error_class: self.class.name,
76
+ message: message,
77
+ code: code,
78
+ timestamp: timestamp.iso8601,
79
+ context: context,
80
+ original_error: original_error_info,
81
+ backtrace: backtrace&.first(10) || []
82
+ }
83
+ end
84
+
85
+ # Get detailed error message with context
86
+ #
87
+ # @return [String] Detailed message including context
88
+ def detailed_message
89
+ parts = [ message ]
90
+ parts << "Code: #{code}" if code
91
+ parts << "Context: #{context.inspect}" if context.any?
92
+ parts << "Original: #{original_error.class.name}: #{original_error.message}" if original_error
93
+ parts.join(" | ")
94
+ end
95
+
96
+ # Enhanced inspect for debugging
97
+ #
98
+ # @return [String] Detailed string representation
99
+ def inspect
100
+ "#<#{self.class.name}: #{detailed_message}>"
101
+ end
102
+
103
+ # Override backtrace to include original error's backtrace
104
+ #
105
+ # @return [Array<String>] Combined backtrace
106
+ def backtrace
107
+ trace = super || []
108
+
109
+ if original_error && original_error.backtrace
110
+ trace + [ "--- Original Error Backtrace ---" ] + original_error.backtrace
111
+ else
112
+ trace
113
+ end
114
+ end
115
+
116
+ private
117
+
118
+ # Get original error information as hash
119
+ #
120
+ # @return [Hash, nil] Original error details or nil
121
+ def original_error_info
122
+ return nil unless original_error
123
+
124
+ {
125
+ class: original_error.class.name,
126
+ message: original_error.message,
127
+ backtrace: original_error.backtrace&.first(5) || []
128
+ }
129
+ end
130
+ end
131
+
132
+ # Standard error codes used in BetterService responses
133
+ #
134
+ # These codes are used for both hash responses and exception codes,
135
+ # providing consistent error identification across the system.
136
+ #
137
+ # @example Using error codes in exceptions
138
+ # raise Errors::Runtime::ValidationError.new(
139
+ # "Validation failed",
140
+ # code: ErrorCodes::VALIDATION_FAILED,
141
+ # context: { validation_errors: {...} }
142
+ # )
143
+ #
144
+ # @example Handling errors by code
145
+ # begin
146
+ # service.call
147
+ # rescue BetterServiceError => e
148
+ # case e.code
149
+ # when ErrorCodes::VALIDATION_FAILED
150
+ # render json: { errors: e.context[:validation_errors] }, status: :unprocessable_entity
151
+ # when ErrorCodes::UNAUTHORIZED
152
+ # render json: { error: e.message }, status: :forbidden
153
+ # end
154
+ # end
155
+ module ErrorCodes
156
+ # ============================================
157
+ # BUSINESS LOGIC ERROR CODES
158
+ # ============================================
159
+
160
+ # Validation failed - input parameters are invalid
161
+ #
162
+ # Used when Dry::Schema validation fails.
163
+ # The error will include validation details in context.
164
+ #
165
+ # @example
166
+ # raise Errors::Runtime::ValidationError.new(
167
+ # "Validation failed",
168
+ # code: :validation_failed,
169
+ # context: {
170
+ # validation_errors: { email: ["is invalid"], age: ["must be greater than 18"] }
171
+ # }
172
+ # )
173
+ VALIDATION_FAILED = :validation_failed
174
+
175
+ # Authorization failed - user doesn't have permission
176
+ #
177
+ # Used when the authorize_with block returns false.
178
+ #
179
+ # @example
180
+ # raise Errors::Runtime::AuthorizationError.new(
181
+ # "Not authorized to perform this action",
182
+ # code: :unauthorized,
183
+ # context: { user_id: 123, action: "destroy" }
184
+ # )
185
+ UNAUTHORIZED = :unauthorized
186
+
187
+ # ============================================
188
+ # PROGRAMMING ERROR CODES
189
+ # ============================================
190
+
191
+ # Schema is missing or invalid
192
+ SCHEMA_REQUIRED = :schema_required
193
+
194
+ # Service configuration is invalid
195
+ CONFIGURATION_ERROR = :configuration_error
196
+
197
+ # ============================================
198
+ # RUNTIME ERROR CODES
199
+ # ============================================
200
+
201
+ # Unexpected error during service execution
202
+ EXECUTION_ERROR = :execution_error
203
+
204
+ # Required resource was not found
205
+ RESOURCE_NOT_FOUND = :resource_not_found
206
+
207
+ # Database transaction failed
208
+ TRANSACTION_ERROR = :transaction_error
209
+
210
+ # Database operation failed (validation errors, save failures)
211
+ DATABASE_ERROR = :database_error
212
+
213
+ # Workflow execution failed
214
+ WORKFLOW_FAILED = :workflow_failed
215
+
216
+ # Workflow step failed
217
+ STEP_FAILED = :step_failed
218
+
219
+ # Workflow rollback failed
220
+ ROLLBACK_FAILED = :rollback_failed
221
+ end
222
+ end
223
+
224
+ # Require all error classes
225
+ require_relative "configuration/configuration_error"
226
+ require_relative "configuration/schema_required_error"
227
+ require_relative "configuration/invalid_schema_error"
228
+ require_relative "configuration/invalid_configuration_error"
229
+ require_relative "configuration/nil_user_error"
230
+
231
+ require_relative "runtime/runtime_error"
232
+ require_relative "runtime/execution_error"
233
+ require_relative "runtime/transaction_error"
234
+ require_relative "runtime/resource_not_found_error"
235
+ require_relative "runtime/database_error"
236
+ require_relative "runtime/validation_error"
237
+ require_relative "runtime/authorization_error"
238
+
239
+ require_relative "workflowable/configuration/workflow_configuration_error"
240
+ require_relative "workflowable/configuration/step_not_found_error"
241
+ require_relative "workflowable/configuration/invalid_step_error"
242
+ require_relative "workflowable/configuration/duplicate_step_error"
243
+
244
+ require_relative "workflowable/runtime/workflow_runtime_error"
245
+ require_relative "workflowable/runtime/workflow_execution_error"
246
+ require_relative "workflowable/runtime/step_execution_error"
247
+ require_relative "workflowable/runtime/rollback_error"
248
+
249
+ # Namespace for all BetterService errors
250
+ module BetterService
251
+ module Errors
252
+ # Namespace for configuration/programming errors
253
+ module Configuration
254
+ end
255
+
256
+ # Namespace for runtime errors
257
+ module Runtime
258
+ end
259
+
260
+ # Namespace for workflow-related errors
261
+ module Workflowable
262
+ # Namespace for workflow configuration errors
263
+ module Configuration
264
+ end
265
+
266
+ # Namespace for workflow runtime errors
267
+ module Runtime
268
+ end
269
+ end
270
+ end
271
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterService
4
+ module Errors
5
+ module Configuration
6
+ # Base class for all configuration/programming errors in BetterService
7
+ #
8
+ # Configuration errors are raised when a service is incorrectly configured or used.
9
+ # These are programming errors that should be fixed during development.
10
+ #
11
+ # @example
12
+ # raise BetterService::Errors::Configuration::ConfigurationError.new(
13
+ # "Invalid service configuration",
14
+ # code: :configuration_error,
15
+ # context: { service: "MyService", issue: "missing required config" }
16
+ # )
17
+ class ConfigurationError < BetterServiceError
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterService
4
+ module Errors
5
+ module Configuration
6
+ # Raised when a service has invalid configuration settings
7
+ #
8
+ # This error is raised when configuration options are invalid or conflicting,
9
+ # such as invalid cache settings, presenter configurations, or workflow definitions.
10
+ #
11
+ # @example Invalid cache configuration
12
+ # class MyService < BetterService::Services::Base
13
+ # config do
14
+ # cache enabled: true, expires_in: "invalid" # Should be integer
15
+ # end
16
+ # end
17
+ #
18
+ # @example Invalid workflow step
19
+ # class MyWorkflow < BetterService::Workflow
20
+ # step :invalid,
21
+ # with: nil, # Missing service class
22
+ # input: -> (ctx) { ctx.data }
23
+ # end
24
+ class InvalidConfigurationError < ConfigurationError
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterService
4
+ module Errors
5
+ module Configuration
6
+ # Raised when a service schema definition is invalid
7
+ #
8
+ # This error is raised when the Dry::Schema definition contains syntax errors
9
+ # or invalid validation rules.
10
+ #
11
+ # @example Invalid schema definition
12
+ # class MyService < BetterService::Services::Base
13
+ # schema do
14
+ # required(:email).filled(:invalid_type) # Invalid type
15
+ # end
16
+ # end
17
+ #
18
+ # @example Schema with syntax error
19
+ # class MyService < BetterService::Services::Base
20
+ # schema do
21
+ # required(:name) # Missing predicate
22
+ # end
23
+ # end
24
+ class InvalidSchemaError < ConfigurationError
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterService
4
+ module Errors
5
+ module Configuration
6
+ # Raised when a service is initialized with nil user when user is required
7
+ #
8
+ # By default, all BetterService services require a user object. This error is raised
9
+ # during initialization if user is nil and `allow_nil_user` is not configured.
10
+ #
11
+ # @example Service called with nil user (will raise error)
12
+ # class MyService < BetterService::Services::Base
13
+ # schema do
14
+ # required(:name).filled(:string)
15
+ # end
16
+ # end
17
+ #
18
+ # MyService.new(nil, params: { name: "test" })
19
+ # # => raises NilUserError
20
+ #
21
+ # @example Allowing nil user
22
+ # class MyService < BetterService::Services::Base
23
+ # config do
24
+ # allow_nil_user true
25
+ # end
26
+ #
27
+ # schema do
28
+ # required(:name).filled(:string)
29
+ # end
30
+ # end
31
+ #
32
+ # MyService.new(nil, params: { name: "test" }) # OK
33
+ class NilUserError < ConfigurationError
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterService
4
+ module Errors
5
+ module Configuration
6
+ # Raised when a service is missing a required schema definition
7
+ #
8
+ # All BetterService services must define a schema block for parameter validation.
9
+ # This error is raised during service initialization if no schema is defined.
10
+ #
11
+ # @example Service without schema (will raise error)
12
+ # class MyService < BetterService::Services::Base
13
+ # # Missing: schema do ... end
14
+ # end
15
+ #
16
+ # MyService.new(user, params: {})
17
+ # # => raises SchemaRequiredError
18
+ #
19
+ # @example Correct usage with schema
20
+ # class MyService < BetterService::Services::Base
21
+ # schema do
22
+ # required(:name).filled(:string)
23
+ # end
24
+ # end
25
+ class SchemaRequiredError < ConfigurationError
26
+ end
27
+ end
28
+ end
29
+ end