cmdx 1.11.0 → 1.12.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4a9b1cdaccb648dcffd8c5b50417b2891d2c263f922884b4e8163aa8389a7602
4
- data.tar.gz: 7ab3292c7441d0a279c1609e3976c7599ae10ae4168b22aae027667578d0352a
3
+ metadata.gz: 5dffef0df3a8abb190fc0ca6a194383614fdd81f3eebf22ecab59d8ec047d1d7
4
+ data.tar.gz: 0f38ccaa12a07c41b64fb4fd3f0685bafb45b7de0b3830243f85a53de4d331c4
5
5
  SHA512:
6
- metadata.gz: a3c544688dd90eb3e117460f591cd1d85dd3590a1aae8c90e37930c2728ecab5b1ff8ccce06be2cb894627d5d7beca7edebfa313667879e863c0e5338dc7bc29
7
- data.tar.gz: a0b6e9c99fc46d3b4bbc627d9967acb8e39fe0fcf1ff51dcc3ab841acabc7b6d6e1bacf3cbaf4a08f0fc9381b409b2829d5f830ecadeefaab9d84647ce28c7cb
6
+ metadata.gz: 8649dadb0908d88f7441b80d6e3dce03e7f0d20095216d8ac24cc0bf8b1446f57b4552f021a324f47d860112e431b531695a5cd78e84a4b2cf0c3be0dff1224e
7
+ data.tar.gz: 5e88be6d5a3070d75abbbcb20e25d900db3a44f011f71ed55c61f748cda77e9be4297f12829ab9f28daac66a4b2c511725318ae4e1455793eef8b8f806c3864c
@@ -17,6 +17,11 @@ Reference the CMDx documentation in https://github.com/drexed/cmdx/blob/main/LLM
17
17
  - Ruby 3.4+
18
18
  - RSpec 3.1+
19
19
 
20
+ ## Development Guidelines
21
+ - Performance is critical - benchmark any changes that could affect speed
22
+ - Follow existing code patterns and conventions
23
+ - Maintain backward compatibility for public API
24
+
20
25
  ## Code Style and Structure
21
26
  - Write concise, idiomatic Ruby code with accurate examples
22
27
  - Follow Ruby conventions and best practices
@@ -24,6 +29,7 @@ Reference the CMDx documentation in https://github.com/drexed/cmdx/blob/main/LLM
24
29
  - Prefer iteration and modularization over code duplication
25
30
  - Use descriptive variable and method names (e.g., user_signed_in?, calculate_total)
26
31
  - Write comprehensive code documentation using the Yardoc format
32
+ - Minimize object allocations in hot paths
27
33
 
28
34
  ## Naming Conventions
29
35
  - Use snake_case for file names, method names, and variables
@@ -35,6 +41,7 @@ Reference the CMDx documentation in https://github.com/drexed/cmdx/blob/main/LLM
35
41
  - Use Ruby's expressive syntax (e.g., unless, ||=, &.)
36
42
  - Prefer double quotes for strings
37
43
  - Respect my Rubocop options
44
+ - Run `bundle exec rubocop .` before finalizing any code changes
38
45
 
39
46
  ## Performance Optimization
40
47
  - Use memoization for expensive operations
@@ -50,6 +57,7 @@ Reference the CMDx documentation in https://github.com/drexed/cmdx/blob/main/LLM
50
57
  - Don't test declarative configuration
51
58
  - Use appropriate matchers
52
59
  - Update tests and update Yardocs after you write code
60
+ - Run `bundle rspec .` before finalizing any code changes
53
61
 
54
62
  ## Documentation
55
63
  - Utilize the YARDoc format when documenting Ruby code
data/.yard-lint.yml ADDED
@@ -0,0 +1,174 @@
1
+ # YARD-Lint Configuration
2
+ # See https://github.com/mensfeld/yard-lint for documentation
3
+
4
+ # Global settings for all validators
5
+ AllValidators:
6
+ # YARD command-line options (applied to all validators by default)
7
+ YardOptions:
8
+ - --private
9
+ - --protected
10
+
11
+ # Global file exclusion patterns
12
+ Exclude:
13
+ - '\.git'
14
+ - 'vendor/**/*'
15
+ - 'node_modules/**/*'
16
+ - 'spec/**/*'
17
+ - 'test/**/*'
18
+
19
+ # Exit code behavior (error, warning, convention, never)
20
+ FailOnSeverity: warning
21
+
22
+ # Minimum documentation coverage percentage (0-100)
23
+ # Fails if coverage is below this threshold
24
+ # MinCoverage: 80.0
25
+
26
+ # Diff mode settings
27
+ DiffMode:
28
+ # Default base ref for --diff (auto-detects main/master if not specified)
29
+ DefaultBaseRef: ~
30
+
31
+ # Documentation validators
32
+ Documentation/UndocumentedObjects:
33
+ Description: 'Checks for classes, modules, and methods without documentation.'
34
+ Enabled: false
35
+ Severity: warning
36
+ ExcludedMethods:
37
+ - 'initialize/0' # Exclude parameter-less initialize
38
+ - '/^_/' # Exclude private methods (by convention)
39
+
40
+ Documentation/UndocumentedMethodArguments:
41
+ Description: 'Checks for method parameters without @param tags.'
42
+ Enabled: true
43
+ Severity: warning
44
+
45
+ Documentation/UndocumentedBooleanMethods:
46
+ Description: 'Checks that question mark methods document their boolean return.'
47
+ Enabled: true
48
+ Severity: warning
49
+
50
+ Documentation/UndocumentedOptions:
51
+ Description: 'Detects methods with options hash parameters but no @option tags.'
52
+ Enabled: true
53
+ Severity: warning
54
+
55
+ Documentation/MarkdownSyntax:
56
+ Description: 'Detects common markdown syntax errors in documentation.'
57
+ Enabled: true
58
+ Severity: warning
59
+
60
+ # Tags validators
61
+ Tags/Order:
62
+ Description: 'Enforces consistent ordering of YARD tags.'
63
+ Enabled: true
64
+ Severity: convention
65
+ EnforcedOrder:
66
+ - param
67
+ - option
68
+ - return
69
+ - raise
70
+ - example
71
+
72
+ Tags/InvalidTypes:
73
+ Description: 'Validates type definitions in @param, @return, @option tags.'
74
+ Enabled: true
75
+ Severity: warning
76
+ ValidatedTags:
77
+ - param
78
+ - option
79
+ - return
80
+
81
+ Tags/TypeSyntax:
82
+ Description: 'Validates YARD type syntax using YARD parser.'
83
+ Enabled: true
84
+ Severity: warning
85
+ ValidatedTags:
86
+ - param
87
+ - option
88
+ - return
89
+ - yieldreturn
90
+
91
+ Tags/MeaninglessTag:
92
+ Description: 'Detects @param/@option tags on classes, modules, or constants.'
93
+ Enabled: true
94
+ Severity: warning
95
+ CheckedTags:
96
+ - param
97
+ - option
98
+ InvalidObjectTypes:
99
+ - class
100
+ - module
101
+ - constant
102
+
103
+ Tags/CollectionType:
104
+ Description: 'Validates Hash collection syntax consistency.'
105
+ Enabled: true
106
+ Severity: convention
107
+ EnforcedStyle: long # 'long' for Hash{K => V} (YARD standard), 'short' for {K => V}
108
+ ValidatedTags:
109
+ - param
110
+ - option
111
+ - return
112
+ - yieldreturn
113
+
114
+ Tags/TagTypePosition:
115
+ Description: 'Validates type annotation position in tags.'
116
+ Enabled: true
117
+ Severity: convention
118
+ CheckedTags:
119
+ - param
120
+ - option
121
+ # EnforcedStyle: 'type_after_name' (YARD standard: @param name [Type])
122
+ # or 'type_first' (@param [Type] name)
123
+ EnforcedStyle: type_after_name
124
+
125
+ Tags/ApiTags:
126
+ Description: 'Enforces @api tags on public objects.'
127
+ Enabled: false # Opt-in validator
128
+ Severity: warning
129
+ AllowedApis:
130
+ - public
131
+ - private
132
+ - internal
133
+
134
+ Tags/OptionTags:
135
+ Description: 'Requires @option tags for methods with options parameters.'
136
+ Enabled: true
137
+ Severity: warning
138
+
139
+ # Warnings validators - catches YARD parser errors
140
+ Warnings/UnknownTag:
141
+ Description: 'Detects unknown YARD tags.'
142
+ Enabled: false
143
+ Severity: error
144
+
145
+ Warnings/UnknownDirective:
146
+ Description: 'Detects unknown YARD directives.'
147
+ Enabled: true
148
+ Severity: error
149
+
150
+ Warnings/InvalidTagFormat:
151
+ Description: 'Detects malformed tag syntax.'
152
+ Enabled: true
153
+ Severity: error
154
+
155
+ Warnings/InvalidDirectiveFormat:
156
+ Description: 'Detects malformed directive syntax.'
157
+ Enabled: true
158
+ Severity: error
159
+
160
+ Warnings/DuplicatedParameterName:
161
+ Description: 'Detects duplicate @param tags.'
162
+ Enabled: true
163
+ Severity: error
164
+
165
+ Warnings/UnknownParameterName:
166
+ Description: 'Detects @param tags for non-existent parameters.'
167
+ Enabled: true
168
+ Severity: error
169
+
170
+ # Semantic validators
171
+ Semantic/AbstractMethods:
172
+ Description: 'Ensures @abstract methods do not have real implementations.'
173
+ Enabled: true
174
+ Severity: warning
data/CHANGELOG.md CHANGED
@@ -6,6 +6,21 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
 
7
7
  ## [UNRELEASED]
8
8
 
9
+ ## [1.12.0] - 2025-12-18
10
+
11
+ ### Added
12
+ - Added active record database transaction example
13
+ - Added Sentry error tracking example
14
+ - Added Redis idempotency example
15
+ - Added Flipper feature flag example
16
+
17
+ ### Updated
18
+ - Remove `handle_*` methods and provide `on(*states_or_statuses)` method for more flexibility
19
+ - Optimize logging ancestor lookup
20
+ - Use chop instead of range for better string performance
21
+ - Update boolean coercion `TRUTHY` and `FALSEY` regexp to be case insensitive
22
+ - Improve YARD documentation with `yard-lint`
23
+
9
24
  ## [1.11.0] - 2025-11-08
10
25
 
11
26
  ### Updated
@@ -4,6 +4,10 @@ Attributes define your task's interface with automatic validation, type coercion
4
4
 
5
5
  ## Declarations
6
6
 
7
+ !!! warning "Important"
8
+
9
+ Attributes are order-dependent, so if you need to reference them as a source or use them in conditions, make sure they’re defined in the correct order.
10
+
7
11
  !!! tip
8
12
 
9
13
  Prefer using the `required` and `optional` alias for `attributes` for brevity and to clearly signal intent.
@@ -54,7 +58,7 @@ class PublishArticle < CMDx::Task
54
58
 
55
59
  # Conditionally required
56
60
  required :publisher, if: :magazine?
57
- required :approver, unless: proc { status == :published }
61
+ attribute :approver, required: true, unless: proc { status == :published }
58
62
 
59
63
  def work
60
64
  title #=> "Getting Started with Ruby"
@@ -14,10 +14,10 @@ class ProcessSubscription < CMDx::Task
14
14
  attribute :user_id, presence: true
15
15
 
16
16
  # String with length constraints
17
- attribute :preferences, length: { minimum: 10, maximum: 500 }
17
+ optional :preferences, length: { minimum: 10, maximum: 500 }
18
18
 
19
19
  # Numeric range validation
20
- attribute :tier_level, inclusion: { in: 1..5 }
20
+ required :tier_level, inclusion: { in: 1..5 }
21
21
 
22
22
  # Format validation for email
23
23
  attribute :contact_email, format: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
@@ -46,6 +46,42 @@ ProcessSubscription.execute(
46
46
 
47
47
  ### Common Options
48
48
 
49
+ ```ruby
50
+ class ProcessProduct < CMDx::Task
51
+ # Allow nil
52
+ attribute :tier_level, inclusion: {
53
+ in: 1..5,
54
+ allow_nil: true
55
+ }
56
+
57
+ # Conditionals
58
+ optional :contact_email, format: {
59
+ with: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i,
60
+ if: ->(value) { value.includes?("@") }
61
+ }
62
+ required :status, exclusion: {
63
+ in: %w[recalled archived],
64
+ unless: :product_sunsetted?
65
+ }
66
+
67
+ # Custom message
68
+ attribute :title, length: {
69
+ within: 5..100,
70
+ message: "must be in optimal size"
71
+ }
72
+
73
+ def work
74
+ # Your logic here...
75
+ end
76
+
77
+ private
78
+
79
+ def product_defunct?(value)
80
+ context.company.out_of_business? || value == "deprecated"
81
+ end
82
+ end
83
+ ```
84
+
49
85
  This list of options is available to all validators:
50
86
 
51
87
  | Option | Description |
@@ -261,10 +297,15 @@ Validation failures provide detailed, structured error messages:
261
297
 
262
298
  ```ruby
263
299
  class CreateProject < CMDx::Task
264
- attribute :project_name, presence: true, length: { minimum: 3, maximum: 50 }
265
- attribute :budget, numeric: { greater_than: 1000, less_than: 1000000 }
266
- attribute :priority, inclusion: { in: [:low, :medium, :high] }
267
- attribute :contact_email, format: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
300
+ attribute :project_name,
301
+ presence: true,
302
+ length: { minimum: 3, maximum: 50 }
303
+ optional :budget,
304
+ numeric: { greater_than: 1000, less_than: 1000000 }
305
+ required :priority,
306
+ inclusion: { in: [:low, :medium, :high] }
307
+ attribute :contact_email,
308
+ format: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
268
309
 
269
310
  def work
270
311
  # Your logic here...
@@ -126,19 +126,20 @@ result = BuildApplication.execute(version: "1.2.3")
126
126
 
127
127
  # Status-based handlers
128
128
  result
129
- .handle_success { |result| notify_deployment_ready(result) }
130
- .handle_failed { |result| handle_build_failure(result) }
131
- .handle_skipped { |result| log_skip_reason(result) }
129
+ .on(:success) { |result| notify_deployment_ready(result) }
130
+ .on(:failed) { |result| handle_build_failure(result) }
131
+ .on(:skipped) { |result| log_skip_reason(result) }
132
132
 
133
133
  # State-based handlers
134
134
  result
135
- .handle_complete { |result| update_build_status(result) }
136
- .handle_interrupted { |result| cleanup_partial_artifacts(result) }
135
+ .on(:complete) { |result| update_build_status(result) }
136
+ .on(:interrupted) { |result| cleanup_partial_artifacts(result) }
137
+ .on(:executed) { |result| alert_operations_team(result) } #=> .on(:complete, :interrupted)
137
138
 
138
139
  # Outcome-based handlers
139
140
  result
140
- .handle_good { |result| increment_success_counter(result) }
141
- .handle_bad { |result| alert_operations_team(result) }
141
+ .on(:good) { |result| increment_success_counter(result) } #=> .on(:success, :skipped)
142
+ .on(:bad) { |result| alert_operations_team(result) } #=> .on(:failed, :skipped)
142
143
  ```
143
144
 
144
145
  ## Pattern Matching
@@ -53,14 +53,14 @@ result.executed? #=> true (complete OR interrupted)
53
53
 
54
54
  ## Handlers
55
55
 
56
- Handle lifecycle events with state-based handlers. Use `handle_executed` for cleanup that runs regardless of outcome:
56
+ Handle lifecycle events with state-based handlers. Use `on(:executed)` for cleanup that runs regardless of outcome:
57
57
 
58
58
  ```ruby
59
59
  result = ProcessVideoUpload.execute
60
60
 
61
61
  # Individual state handlers
62
62
  result
63
- .handle_complete { |result| send_upload_notification(result) }
64
- .handle_interrupted { |result| cleanup_temp_files(result) }
65
- .handle_executed { |result| log_upload_metrics(result) }
63
+ .on(:complete) { |result| send_upload_notification(result) }
64
+ .on(:interrupted) { |result| cleanup_temp_files(result) }
65
+ .on(:executed) { |result| log_upload_metrics(result) } #=> .on(:complete, :interrupted)
66
66
  ```
@@ -47,19 +47,19 @@ result.bad? #=> true if skipped OR failed (not success)
47
47
 
48
48
  ## Handlers
49
49
 
50
- Branch business logic with status-based handlers. Use `handle_good` and `handle_bad` for success/skip vs failed outcomes:
50
+ Branch business logic with status-based handlers. Use `on(:good)` and `on(:bad)` for success/skip vs failed outcomes:
51
51
 
52
52
  ```ruby
53
53
  result = ProcessNotification.execute
54
54
 
55
55
  # Individual status handlers
56
56
  result
57
- .handle_success { |result| mark_notification_sent(result) }
58
- .handle_skipped { |result| log_notification_skipped(result) }
59
- .handle_failed { |result| queue_retry_notification(result) }
57
+ .on(:success) { |result| mark_notification_sent(result) }
58
+ .on(:skipped) { |result| log_notification_skipped(result) }
59
+ .on(:failed){ |result| queue_retry_notification(result) }
60
60
 
61
61
  # Outcome-based handlers
62
62
  result
63
- .handle_good { |result| update_message_stats(result) }
64
- .handle_bad { |result| track_delivery_failure(result) }
63
+ .on(:good) { |result| update_message_stats(result) } #=> .on(:success, :skipped)
64
+ .on(:bad) { |result| track_delivery_failure(result) } #=> .on(:failed, :skipped)
65
65
  ```
@@ -147,7 +147,11 @@ end
147
147
 
148
148
  ## Useful Examples
149
149
 
150
+ - [Active Record Database Transaction](https://github.com/drexed/cmdx/blob/main/examples/active_record_database_transaction.md)
150
151
  - [Active Record Query Tagging](https://github.com/drexed/cmdx/blob/main/examples/active_record_query_tagging.md)
152
+ - [Flipper Feature Flags](https://github.com/drexed/cmdx/blob/main/examples/flipper_feature_flags.md)
151
153
  - [Paper Trail Whatdunnit](https://github.com/drexed/cmdx/blob/main/examples/paper_trail_whatdunnit.md)
154
+ - [Redis Idempotency](https://github.com/drexed/cmdx/blob/main/examples/redis_idempotency.md)
155
+ - [Sentry Error Tracking](https://github.com/drexed/cmdx/blob/main/examples/sentry_error_tracking.md)
152
156
  - [Sidekiq Async Execution](https://github.com/drexed/cmdx/blob/main/examples/sidekiq_async_execution.md)
153
157
  - [Stoplight Circuit Breaker](https://github.com/drexed/cmdx/blob/main/examples/stoplight_circuit_breaker.md)
@@ -0,0 +1,27 @@
1
+ # Active Record Query Tagging
2
+
3
+ Wrap task or workflow execution in a database transaction. This is essential for data integrity when multiple steps modify the database.
4
+
5
+ ### Setup
6
+
7
+ ```ruby
8
+ # lib/cmdx_database_transaction_middleware.rb
9
+ class CmdxDatabaseTransactionMiddleware
10
+ def self.call(task, **options, &)
11
+ ActiveRecord::Base.transaction(requires_new: true, &)
12
+ end
13
+ end
14
+ ```
15
+
16
+ ### Usage
17
+
18
+ ```ruby
19
+ class MyTask < CMDx::Task
20
+ register :middleware, CmdxDatabaseTransactionMiddleware
21
+
22
+ def work
23
+ # Do work...
24
+ end
25
+
26
+ end
27
+ ```
@@ -0,0 +1,50 @@
1
+ # Flipper Feature Flags
2
+
3
+ Control task execution based on Flipper feature flags.
4
+
5
+ <https://github.com/flippercloud/flipper>
6
+
7
+ ### Setup
8
+
9
+ ```ruby
10
+ # lib/cmdx_flipper_middleware.rb
11
+ class CmdxFlipperMiddleware
12
+ def self.call(task, **options, &)
13
+ feature_name = options.fetch(:feature)
14
+ actor = options.fetch(:actor, -> { task.context[:user] })
15
+
16
+ # Resolve actor if it's a proc
17
+ actor = actor.call if actor.respond_to?(:call)
18
+
19
+ if Flipper.enabled?(feature_name, actor)
20
+ yield
21
+ else
22
+ # Option 1: Skip the task
23
+ task.skip!("Feature #{feature_name} is disabled")
24
+
25
+ # Option 2: Fail the task
26
+ # task.fail!("Feature #{feature_name} is disabled")
27
+ end
28
+ end
29
+ end
30
+ ```
31
+
32
+ ### Usage
33
+
34
+ ```ruby
35
+ class NewFeatureTask < CMDx::Task
36
+ # Execute only if :new_feature is enabled for the user in context
37
+ register :middleware, CmdxFlipperMiddleware,
38
+ feature: :new_feature
39
+
40
+ # Customize the actor resolution
41
+ register :middleware, CmdxFlipperMiddleware,
42
+ feature: :beta_access,
43
+ actor: -> { task.context[:company] }
44
+
45
+ def work
46
+ # ...
47
+ end
48
+ end
49
+ ```
50
+
@@ -0,0 +1,71 @@
1
+ # Redis Idempotency
2
+
3
+ Ensure tasks are executed exactly once using Redis to store execution state. This is critical for non-idempotent operations like charging a credit card or sending an email.
4
+
5
+ ### Setup
6
+
7
+ ```ruby
8
+ # lib/cmdx_redis_idempotency_middleware.rb
9
+ class CmdxRedisIdempotencyMiddleware
10
+ def self.call(task, **options, &block)
11
+ key = generate_key(task, options[:key])
12
+ ttl = options[:ttl] || 5.minutes.to_i
13
+
14
+ # Attempt to lock the key
15
+ if Redis.current.set(key, "processing", nx: true, ex: ttl)
16
+ begin
17
+ block.call.tap |result|
18
+ Redis.current.set(key, result.status, xx: true, ex: ttl)
19
+ end
20
+ rescue => e
21
+ Redis.current.del(key)
22
+ raise(e)
23
+ end
24
+ else
25
+ # Key exists, handle duplicate
26
+ status = Redis.current.get(key)
27
+
28
+ if status == "processing"
29
+ task.result.tap { |r| r.skip!("Duplicate request: currently processing", halt: true) }
30
+ else
31
+ task.result.tap { |r| r.skip!("Duplicate request: already processed (#{status})", halt: true) }
32
+ end
33
+ end
34
+ end
35
+
36
+ def self.generate_key(task, key_gen)
37
+ id = if key_gen.respond_to?(:call)
38
+ key_gen.call(task)
39
+ elsif key_gen.is_a?(Symbol)
40
+ task.send(key_gen)
41
+ else
42
+ task.context[:idempotency_key]
43
+ end
44
+
45
+ "cmdx:idempotency:#{task.class.name}:#{id}"
46
+ end
47
+ end
48
+ ```
49
+
50
+ ### Usage
51
+
52
+ ```ruby
53
+ class ChargeCustomer < CMDx::Task
54
+ # Use context[:payment_id] as the unique key
55
+ register :middleware, CmdxIdempotencyMiddleware,
56
+ key: ->(t) { t.context[:payment_id] }
57
+
58
+ def work
59
+ # Charge logic...
60
+ end
61
+ end
62
+
63
+ # First run: Executes
64
+ ChargeCustomer.call(payment_id: "123")
65
+ # => Success
66
+
67
+ # Second run: Skips
68
+ ChargeCustomer.call(payment_id: "123")
69
+ # => Skipped (reason: "Duplicate request: already processed (success)")
70
+ ```
71
+
@@ -0,0 +1,46 @@
1
+ # Sentry Error Tracking
2
+
3
+ Report unhandled exceptions and unexpected task failures to Sentry with detailed context.
4
+
5
+ <https://github.com/getsentry/sentry-ruby>
6
+
7
+ ### Setup
8
+
9
+ ```ruby
10
+ # lib/cmdx_sentry_middleware.rb
11
+ class CmdxSentryMiddleware
12
+ def self.call(task, **options, &)
13
+ Sentry.with_scope do |scope|
14
+ scope.set_tags(task: task.class.name)
15
+ scope.set_context(:user, Current.user.sentry_attributes)
16
+
17
+ yield.tap do |result|
18
+ # Optional: Report logical failures if needed
19
+ if Array(options[:report_on]).include?(result.status)
20
+ Sentry.capture_message("Task #{result.status}: #{result.reason}", level: :warning)
21
+ end
22
+ end
23
+ end
24
+ rescue => e
25
+ Sentry.capture_exception(e)
26
+ raise(e) # Re-raise to let the task handle the error or bubble up
27
+ end
28
+ end
29
+ ```
30
+
31
+ ### Usage
32
+
33
+ ```ruby
34
+ class ProcessPayment < CMDx::Task
35
+ # Report exceptions only
36
+ register :middleware, CmdxSentryMiddleware
37
+
38
+ # Report exceptions AND logical failures (result.failure?)
39
+ register :middleware, CmdxSentryMiddleware, report_on: %w[failed skipped]
40
+
41
+ def work
42
+ # ...
43
+ end
44
+ end
45
+ ```
46
+
@@ -112,6 +112,7 @@ module CMDx
112
112
  #
113
113
  # @param names [Array<Symbol, String>] The names of the attributes to create
114
114
  # @param options [Hash] Configuration options for the attributes
115
+ # @option options [Object] :* Any attribute configuration option
115
116
  #
116
117
  # @yield [self] Block to configure nested attributes
117
118
  #
@@ -137,6 +138,7 @@ module CMDx
137
138
  #
138
139
  # @param names [Array<Symbol, String>] The names of the attributes to create
139
140
  # @param options [Hash] Configuration options for the attributes
141
+ # @option options [Object] :* Any attribute configuration option
140
142
  #
141
143
  # @yield [self] Block to configure nested attributes
142
144
  #
@@ -154,6 +156,7 @@ module CMDx
154
156
  #
155
157
  # @param names [Array<Symbol, String>] The names of the attributes to create
156
158
  # @param options [Hash] Configuration options for the attributes
159
+ # @option options [Object] :* Any attribute configuration option
157
160
  #
158
161
  # @yield [self] Block to configure nested attributes
159
162
  #
@@ -238,6 +241,7 @@ module CMDx
238
241
  #
239
242
  # @param names [Array<Symbol, String>] The names of the child attributes
240
243
  # @param options [Hash] Configuration options for the child attributes
244
+ # @option options [Object] :* Any attribute configuration option
241
245
  #
242
246
  # @yield [self] Block to configure the child attributes
243
247
  #
@@ -257,6 +261,7 @@ module CMDx
257
261
  #
258
262
  # @param names [Array<Symbol, String>] The names of the optional child attributes
259
263
  # @param options [Hash] Configuration options for the child attributes
264
+ # @option options [Object] :* Any attribute configuration option
260
265
  #
261
266
  # @yield [self] Block to configure the child attributes
262
267
  #
@@ -274,6 +279,7 @@ module CMDx
274
279
  #
275
280
  # @param names [Array<Symbol, String>] The names of the required child attributes
276
281
  # @param options [Hash] Configuration options for the child attributes
282
+ # @option options [Object] :* Any attribute configuration option
277
283
  #
278
284
  # @yield [self] Block to configure the child attributes
279
285
  #