bunny_farm 0.1.2

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 (106) hide show
  1. checksums.yaml +7 -0
  2. data/.envrc +1 -0
  3. data/.github/workflows/docs.yml +38 -0
  4. data/.gitignore +11 -0
  5. data/.travis.yml +3 -0
  6. data/CHANGELOG.md +61 -0
  7. data/COMMITS.md +196 -0
  8. data/Gemfile +4 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +330 -0
  11. data/Rakefile +9 -0
  12. data/bunny_farm.gemspec +30 -0
  13. data/config/bunny.yml.erb +29 -0
  14. data/config/bunny_test.yml.erb +29 -0
  15. data/config/hipchat.yml.erb +12 -0
  16. data/docs/api/configuration.md +9 -0
  17. data/docs/api/consumer.md +8 -0
  18. data/docs/api/message-class.md +419 -0
  19. data/docs/api/publisher.md +9 -0
  20. data/docs/architecture/integration.md +8 -0
  21. data/docs/architecture/message-flow.md +11 -0
  22. data/docs/architecture/overview.md +448 -0
  23. data/docs/architecture/scaling.md +8 -0
  24. data/docs/assets/actions_dsl_flow.svg +109 -0
  25. data/docs/assets/architecture_overview.svg +152 -0
  26. data/docs/assets/best_practices_patterns.svg +203 -0
  27. data/docs/assets/bunny_farm_logo.png +0 -0
  28. data/docs/assets/configuration_api_methods.svg +104 -0
  29. data/docs/assets/configuration_flow.svg +130 -0
  30. data/docs/assets/configuration_hierarchy.svg +70 -0
  31. data/docs/assets/data_processing_pipeline.svg +131 -0
  32. data/docs/assets/debugging_monitoring.svg +165 -0
  33. data/docs/assets/ecommerce_example_flow.svg +145 -0
  34. data/docs/assets/email_campaign_example.svg +127 -0
  35. data/docs/assets/environment_variables_map.svg +78 -0
  36. data/docs/assets/error_handling_flow.svg +114 -0
  37. data/docs/assets/favicon.ico +1 -0
  38. data/docs/assets/fields_dsl_structure.svg +89 -0
  39. data/docs/assets/instance_methods_lifecycle.svg +137 -0
  40. data/docs/assets/integration_patterns.svg +207 -0
  41. data/docs/assets/json_serialization_flow.svg +153 -0
  42. data/docs/assets/logo.svg +4 -0
  43. data/docs/assets/message_api_overview.svg +126 -0
  44. data/docs/assets/message_encapsulation.svg +113 -0
  45. data/docs/assets/message_lifecycle.svg +110 -0
  46. data/docs/assets/message_structure.svg +138 -0
  47. data/docs/assets/publisher_consumer_api.svg +120 -0
  48. data/docs/assets/scaling_deployment_patterns.svg +195 -0
  49. data/docs/assets/smart_routing_diagram.svg +131 -0
  50. data/docs/assets/system_architecture_overview.svg +155 -0
  51. data/docs/assets/task_scheduling_flow.svg +139 -0
  52. data/docs/assets/testing_strategies.svg +146 -0
  53. data/docs/assets/workflow_patterns.svg +183 -0
  54. data/docs/assets/yaml_config_structure.svg +72 -0
  55. data/docs/configuration/environment-variables.md +14 -0
  56. data/docs/configuration/overview.md +373 -0
  57. data/docs/configuration/programmatic-setup.md +10 -0
  58. data/docs/configuration/yaml-configuration.md +12 -0
  59. data/docs/core-features/configuration.md +528 -0
  60. data/docs/core-features/error-handling.md +82 -0
  61. data/docs/core-features/json-serialization.md +545 -0
  62. data/docs/core-features/message-design.md +406 -0
  63. data/docs/core-features/smart-routing.md +467 -0
  64. data/docs/core-features/task-scheduling.md +67 -0
  65. data/docs/core-features/workflow-support.md +112 -0
  66. data/docs/development/contributing.md +345 -0
  67. data/docs/development/roadmap.md +9 -0
  68. data/docs/development/testing.md +14 -0
  69. data/docs/examples/order-processing.md +10 -0
  70. data/docs/examples/overview.md +269 -0
  71. data/docs/examples/real-world.md +8 -0
  72. data/docs/examples/simple-producer-consumer.md +15 -0
  73. data/docs/examples/task-scheduler.md +9 -0
  74. data/docs/getting-started/basic-concepts.md +274 -0
  75. data/docs/getting-started/installation.md +122 -0
  76. data/docs/getting-started/quick-start.md +158 -0
  77. data/docs/index.md +106 -0
  78. data/docs/message-structure/actions-dsl.md +163 -0
  79. data/docs/message-structure/fields-dsl.md +146 -0
  80. data/docs/message-structure/instance-methods.md +115 -0
  81. data/docs/message-structure/overview.md +211 -0
  82. data/examples/README.md +212 -0
  83. data/examples/consumer.rb +41 -0
  84. data/examples/images/message_flow.svg +87 -0
  85. data/examples/images/order_workflow.svg +122 -0
  86. data/examples/images/producer_consumer.svg +96 -0
  87. data/examples/images/task_scheduler.svg +140 -0
  88. data/examples/order_processor.rb +238 -0
  89. data/examples/producer.rb +60 -0
  90. data/examples/simple_message.rb +43 -0
  91. data/examples/task_scheduler.rb +263 -0
  92. data/images/architecture_overview.svg +152 -0
  93. data/images/bunny_farm_logo.png +0 -0
  94. data/images/configuration_flow.svg +130 -0
  95. data/images/message_structure.svg +138 -0
  96. data/lib/bunny_farm/.irbrc +7 -0
  97. data/lib/bunny_farm/generic_consumer.rb +12 -0
  98. data/lib/bunny_farm/hash_ext.rb +37 -0
  99. data/lib/bunny_farm/init_bunny.rb +137 -0
  100. data/lib/bunny_farm/init_hipchat.rb +49 -0
  101. data/lib/bunny_farm/message.rb +218 -0
  102. data/lib/bunny_farm/message_elements.rb +25 -0
  103. data/lib/bunny_farm/version.rb +3 -0
  104. data/lib/bunny_farm.rb +9 -0
  105. data/mkdocs.yml +148 -0
  106. metadata +244 -0
@@ -0,0 +1,12 @@
1
+ # YAML Configuration
2
+
3
+ Using YAML files for structured configuration.
4
+
5
+ ## Basic Structure
6
+ ```yaml
7
+ development:
8
+ host: localhost
9
+ port: 5672
10
+ user: guest
11
+ pass: guest
12
+ ```
@@ -0,0 +1,528 @@
1
+ # Configuration
2
+
3
+ BunnyFarm provides flexible configuration options that adapt to different environments and deployment scenarios. This page covers configuration as a core feature that enables the framework's adaptability.
4
+
5
+ ## Configuration Philosophy
6
+
7
+ BunnyFarm follows the principle of **sensible defaults with easy customization**:
8
+
9
+ - **Zero configuration** - Works out of the box for development
10
+ - **Environment aware** - Adapts to different deployment contexts
11
+ - **Override hierarchy** - Multiple configuration layers with clear precedence
12
+ - **Runtime flexibility** - Can be configured programmatically or declaratively
13
+
14
+ ## Configuration as a Feature
15
+
16
+ ### Environment Adaptation
17
+
18
+ BunnyFarm automatically adapts to different environments:
19
+
20
+ ```ruby
21
+ # Development - uses localhost defaults
22
+ BunnyFarm.config { env 'development' }
23
+
24
+ # Production - uses environment variables or config files
25
+ BunnyFarm.config do
26
+ env 'production'
27
+ bunny_file 'config/production.yml'
28
+ end
29
+
30
+ # Testing - isolated configuration
31
+ BunnyFarm.config do
32
+ env 'test'
33
+ exchange_name 'test_exchange'
34
+ queue_name 'test_queue'
35
+ end
36
+ ```
37
+
38
+ ### Dynamic Configuration
39
+
40
+ Configuration can adapt to runtime conditions:
41
+
42
+ ```ruby
43
+ BunnyFarm.config do
44
+ # Dynamic host selection
45
+ host case ENV['REGION']
46
+ when 'us-east' then 'amqp-us-east.example.com'
47
+ when 'eu-west' then 'amqp-eu-west.example.com'
48
+ else 'localhost'
49
+ end
50
+
51
+ # Scale queue count based on load
52
+ queue_name "orders_#{ENV['INSTANCE_ID'] || 'default'}"
53
+
54
+ # Conditional features
55
+ if ENV['ENABLE_MONITORING'] == 'true'
56
+ monitoring_enabled true
57
+ metrics_endpoint 'http://prometheus:9090'
58
+ end
59
+ end
60
+ ```
61
+
62
+ ## Configuration Methods
63
+
64
+ ### 1. Environment Variables
65
+
66
+ The most common production configuration method:
67
+
68
+ ```bash
69
+ # Connection settings
70
+ export AMQP_HOST=amqp.example.com
71
+ export AMQP_PORT=5672
72
+ export AMQP_USER=app_user
73
+ export AMQP_PASS=secret_password
74
+ export AMQP_VHOST=production
75
+
76
+ # Application settings
77
+ export AMQP_EXCHANGE=production_exchange
78
+ export AMQP_QUEUE=production_queue
79
+ export AMQP_ROUTING_KEY='#'
80
+ export AMQP_APP_NAME=order_processor
81
+ ```
82
+
83
+ **Usage:**
84
+ ```ruby
85
+ # Automatically uses environment variables
86
+ BunnyFarm.config
87
+ ```
88
+
89
+ ### 2. YAML Configuration Files
90
+
91
+ Structured configuration with ERB templating:
92
+
93
+ **config/bunny.yml.erb:**
94
+ ```yaml
95
+ defaults: &defaults
96
+ host: <%= ENV['AMQP_HOST'] || 'localhost' %>
97
+ port: <%= ENV['AMQP_PORT'] || 5672 %>
98
+ user: <%= ENV['AMQP_USER'] || 'guest' %>
99
+ pass: <%= ENV['AMQP_PASS'] || 'guest' %>
100
+ vhost: <%= ENV['AMQP_VHOST'] || '/' %>
101
+
102
+ development:
103
+ <<: *defaults
104
+ exchange_name: dev_exchange
105
+ queue_name: dev_queue
106
+
107
+ staging:
108
+ <<: *defaults
109
+ host: staging-amqp.example.com
110
+ exchange_name: staging_exchange
111
+
112
+ production:
113
+ <<: *defaults
114
+ host: <%= ENV.fetch('PRODUCTION_AMQP_HOST') %>
115
+ exchange_name: production_exchange
116
+ connection_pool_size: 10
117
+ ```
118
+
119
+ **Usage:**
120
+ ```ruby
121
+ BunnyFarm.config do
122
+ env Rails.env
123
+ bunny_file 'config/bunny.yml.erb'
124
+ end
125
+ ```
126
+
127
+ ### 3. Programmatic Configuration
128
+
129
+ Ruby code-based configuration:
130
+
131
+ ```ruby
132
+ BunnyFarm.config do
133
+ # Basic settings
134
+ app_id 'order_processor'
135
+ env Rails.env
136
+
137
+ # Connection settings
138
+ host 'amqp.example.com'
139
+ port 5672
140
+ vhost '/orders'
141
+
142
+ # Queue configuration
143
+ exchange_name 'order_exchange'
144
+ queue_name 'order_processing'
145
+ routing_key 'OrderMessage.*'
146
+
147
+ # Advanced options
148
+ connection_pool_size 5
149
+ heartbeat_interval 30
150
+
151
+ # Message options
152
+ message_options do
153
+ persistent true
154
+ mandatory false
155
+ end
156
+
157
+ # Queue options
158
+ queue_options do
159
+ durable true
160
+ auto_delete false
161
+ arguments({
162
+ 'x-message-ttl' => 300_000, # 5 minutes
163
+ 'x-max-length' => 10_000
164
+ })
165
+ end
166
+ end
167
+ ```
168
+
169
+ ## Configuration Patterns
170
+
171
+ ### Multi-Environment Setup
172
+
173
+ **Rails Application:**
174
+ ```ruby
175
+ # config/environments/development.rb
176
+ BunnyFarm.config do
177
+ env 'development'
178
+ log_level :debug
179
+ end
180
+
181
+ # config/environments/staging.rb
182
+ BunnyFarm.config do
183
+ env 'staging'
184
+ bunny_file Rails.root.join('config', 'rabbitmq.yml')
185
+ log_level :info
186
+ end
187
+
188
+ # config/environments/production.rb
189
+ BunnyFarm.config do
190
+ env 'production'
191
+ bunny_file Rails.root.join('config', 'rabbitmq.yml')
192
+ log_level :warn
193
+
194
+ # Production-specific settings
195
+ connection_pool_size 10
196
+ heartbeat_interval 60
197
+ connection_timeout 30
198
+ end
199
+ ```
200
+
201
+ ### Docker Configuration
202
+
203
+ **docker-compose.yml:**
204
+ ```yaml
205
+ version: '3.8'
206
+ services:
207
+ app:
208
+ build: .
209
+ environment:
210
+ AMQP_HOST: rabbitmq
211
+ AMQP_USER: app
212
+ AMQP_PASS: ${RABBITMQ_PASSWORD}
213
+ AMQP_EXCHANGE: ${APP_EXCHANGE:-app_exchange}
214
+ AMQP_QUEUE: ${APP_QUEUE:-app_queue}
215
+ RAILS_ENV: production
216
+ depends_on:
217
+ - rabbitmq
218
+
219
+ rabbitmq:
220
+ image: rabbitmq:3-management
221
+ environment:
222
+ RABBITMQ_DEFAULT_USER: app
223
+ RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASSWORD}
224
+ ```
225
+
226
+ ### Kubernetes Configuration
227
+
228
+ **ConfigMap:**
229
+ ```yaml
230
+ apiVersion: v1
231
+ kind: ConfigMap
232
+ metadata:
233
+ name: bunny-farm-config
234
+ data:
235
+ AMQP_HOST: "rabbitmq-service"
236
+ AMQP_EXCHANGE: "production_exchange"
237
+ AMQP_QUEUE: "production_queue"
238
+ AMQP_APP_NAME: "order-processor"
239
+ ```
240
+
241
+ **Secret:**
242
+ ```yaml
243
+ apiVersion: v1
244
+ kind: Secret
245
+ metadata:
246
+ name: bunny-farm-secret
247
+ type: Opaque
248
+ data:
249
+ AMQP_USER: YXBwX3VzZXI= # base64 encoded
250
+ AMQP_PASS: c2VjcmV0X3Bhc3M= # base64 encoded
251
+ ```
252
+
253
+ **Deployment:**
254
+ ```yaml
255
+ apiVersion: apps/v1
256
+ kind: Deployment
257
+ metadata:
258
+ name: order-processor
259
+ spec:
260
+ template:
261
+ spec:
262
+ containers:
263
+ - name: app
264
+ image: order-processor:latest
265
+ envFrom:
266
+ - configMapRef:
267
+ name: bunny-farm-config
268
+ - secretRef:
269
+ name: bunny-farm-secret
270
+ ```
271
+
272
+ ## Advanced Configuration Features
273
+
274
+ ### Configuration Validation
275
+
276
+ ```ruby
277
+ BunnyFarm.config do
278
+ # Validate required settings
279
+ required_env_vars = %w[AMQP_HOST AMQP_USER AMQP_PASS]
280
+ missing = required_env_vars.select { |var| ENV[var].nil? }
281
+
282
+ raise "Missing required environment variables: #{missing.join(', ')}" if missing.any?
283
+
284
+ # Set configuration
285
+ host ENV['AMQP_HOST']
286
+ user ENV['AMQP_USER']
287
+ pass ENV['AMQP_PASS']
288
+ end
289
+ ```
290
+
291
+ ### Conditional Configuration
292
+
293
+ ```ruby
294
+ BunnyFarm.config do
295
+ # Base configuration
296
+ app_id 'message_processor'
297
+
298
+ # Environment-specific
299
+ case ENV['RAILS_ENV']
300
+ when 'development'
301
+ log_level :debug
302
+ queue_name 'dev_queue'
303
+ when 'test'
304
+ log_level :fatal
305
+ queue_name "test_queue_#{ENV['TEST_ENV_NUMBER']}"
306
+ when 'production'
307
+ log_level :info
308
+ connection_pool_size 20
309
+ queue_name 'prod_queue'
310
+
311
+ # Enable monitoring in production
312
+ monitoring_enabled true
313
+ health_check_port 8080
314
+ end
315
+
316
+ # Feature flags
317
+ if ENV['ENABLE_DEAD_LETTERS'] == 'true'
318
+ dead_letter_exchange 'failed_messages'
319
+ dead_letter_routing_key 'failed.#'
320
+ end
321
+ end
322
+ ```
323
+
324
+ ### Runtime Configuration Updates
325
+
326
+ ```ruby
327
+ class ConfigurableWorker
328
+ def initialize
329
+ @config_check_interval = 60 # Check every minute
330
+ @last_config_check = Time.current
331
+ setup_configuration
332
+ end
333
+
334
+ def process_messages
335
+ loop do
336
+ check_configuration_updates if should_check_config?
337
+
338
+ BunnyFarm.manage_single_message
339
+ end
340
+ end
341
+
342
+ private
343
+
344
+ def should_check_config?
345
+ Time.current - @last_config_check > @config_check_interval
346
+ end
347
+
348
+ def check_configuration_updates
349
+ new_config = load_config_from_source
350
+
351
+ if config_changed?(new_config)
352
+ logger.info "Configuration updated, reloading..."
353
+ update_configuration(new_config)
354
+ end
355
+
356
+ @last_config_check = Time.current
357
+ end
358
+
359
+ def config_changed?(new_config)
360
+ @current_config_hash != new_config.hash
361
+ end
362
+
363
+ def update_configuration(new_config)
364
+ BunnyFarm.reconfigure(new_config)
365
+ @current_config_hash = new_config.hash
366
+ end
367
+ end
368
+ ```
369
+
370
+ ## Configuration Security
371
+
372
+ ### Secret Management
373
+
374
+ Never store secrets in code or version control:
375
+
376
+ ```ruby
377
+ # Good: Use environment variables or secret management
378
+ BunnyFarm.config do
379
+ user ENV['AMQP_USER']
380
+ pass ENV['AMQP_PASS'] # From Kubernetes secret, HashiCorp Vault, etc.
381
+ end
382
+
383
+ # Avoid: Hardcoded secrets
384
+ BunnyFarm.config do
385
+ user 'admin'
386
+ pass 'secret123' # Don't do this!
387
+ end
388
+ ```
389
+
390
+ ### Encrypted Configuration
391
+
392
+ For sensitive configuration files:
393
+
394
+ ```ruby
395
+ require 'openssl'
396
+
397
+ class EncryptedConfig
398
+ def self.load(file_path, key)
399
+ encrypted_data = File.read(file_path)
400
+ cipher = OpenSSL::Cipher.new('AES-256-CBC')
401
+ cipher.decrypt
402
+ cipher.key = key
403
+
404
+ decrypted_data = cipher.update(encrypted_data) + cipher.final
405
+ YAML.safe_load(decrypted_data)
406
+ end
407
+ end
408
+
409
+ # Usage
410
+ config_key = ENV['CONFIG_ENCRYPTION_KEY']
411
+ config = EncryptedConfig.load('config/encrypted_bunny.yml', config_key)
412
+
413
+ BunnyFarm.config do
414
+ host config['host']
415
+ user config['user']
416
+ pass config['pass']
417
+ end
418
+ ```
419
+
420
+ ## Monitoring Configuration
421
+
422
+ ### Health Checks
423
+
424
+ ```ruby
425
+ BunnyFarm.config do
426
+ # Enable health check endpoint
427
+ health_check_enabled true
428
+ health_check_port 8080
429
+ health_check_path '/health'
430
+ end
431
+
432
+ # Health check responds with:
433
+ # GET /health
434
+ # {
435
+ # "status": "healthy",
436
+ # "rabbitmq_connection": "ok",
437
+ # "queue_depth": 42,
438
+ # "last_message_processed": "2024-01-15T10:30:00Z"
439
+ # }
440
+ ```
441
+
442
+ ### Metrics and Logging
443
+
444
+ ```ruby
445
+ BunnyFarm.config do
446
+ # Detailed logging
447
+ log_level ENV['LOG_LEVEL']&.downcase&.to_sym || :info
448
+ log_format :json # For structured logging
449
+
450
+ # Metrics
451
+ metrics_enabled true
452
+ metrics_port 9090
453
+ metrics_path '/metrics'
454
+
455
+ # Custom metric tags
456
+ metric_tags({
457
+ environment: ENV['RAILS_ENV'],
458
+ region: ENV['AWS_REGION'],
459
+ instance: ENV['HOSTNAME']
460
+ })
461
+ end
462
+ ```
463
+
464
+ ## Best Practices
465
+
466
+ ### 1. Layer Configuration Appropriately
467
+
468
+ ```ruby
469
+ # Layer 1: Sensible defaults
470
+ BunnyFarm.config do
471
+ host 'localhost'
472
+ port 5672
473
+ heartbeat_interval 30
474
+ end
475
+
476
+ # Layer 2: Environment-specific overrides
477
+ BunnyFarm.config do
478
+ bunny_file "config/#{Rails.env}.yml"
479
+ end
480
+
481
+ # Layer 3: Runtime overrides
482
+ BunnyFarm.config do
483
+ host ENV['AMQP_HOST'] if ENV['AMQP_HOST']
484
+ log_level ENV['LOG_LEVEL'].to_sym if ENV['LOG_LEVEL']
485
+ end
486
+ ```
487
+
488
+ ### 2. Validate Critical Configuration
489
+
490
+ ```ruby
491
+ BunnyFarm.config do
492
+ host ENV.fetch('AMQP_HOST') # Will raise if missing
493
+
494
+ port_value = ENV['AMQP_PORT']&.to_i || 5672
495
+ raise "Invalid port: #{port_value}" unless (1..65535).include?(port_value)
496
+ port port_value
497
+ end
498
+ ```
499
+
500
+ ### 3. Document Configuration Options
501
+
502
+ ```ruby
503
+ # config/bunny_farm.rb
504
+ BunnyFarm.config do
505
+ # Connection settings - required in production
506
+ host ENV['AMQP_HOST'] # RabbitMQ server hostname
507
+ port ENV['AMQP_PORT']&.to_i # RabbitMQ server port (default: 5672)
508
+ vhost ENV['AMQP_VHOST'] # Virtual host (default: '/')
509
+
510
+ # Authentication - use secrets in production
511
+ user ENV['AMQP_USER'] # Username (default: 'guest')
512
+ pass ENV['AMQP_PASS'] # Password (default: 'guest')
513
+
514
+ # Message routing
515
+ exchange_name ENV['AMQP_EXCHANGE'] || 'app_exchange'
516
+ queue_name ENV['AMQP_QUEUE'] || 'app_queue'
517
+ routing_key ENV['AMQP_ROUTING_KEY'] || '#'
518
+ end
519
+ ```
520
+
521
+ ## Next Steps
522
+
523
+ Configuration mastery enables:
524
+
525
+ - **[Environment-specific deployment](../architecture/overview.md)**
526
+ - **[Performance tuning](../architecture/scaling.md)** through configuration
527
+ - **[Security hardening](../development/contributing.md)** with proper secret management
528
+ - **[Monitoring and observability](error-handling.md)** through configuration
@@ -0,0 +1,82 @@
1
+ # Error Handling
2
+
3
+ BunnyFarm provides comprehensive error handling mechanisms that help build resilient message processing systems with proper failure management and recovery strategies.
4
+
5
+ ![Error Handling Flow](../assets/error_handling_flow.svg)
6
+
7
+ ## Built-in Error Tracking
8
+
9
+ ```ruby
10
+ class OrderMessage < BunnyFarm::Message
11
+ def process
12
+ validate_order
13
+ return unless successful?
14
+
15
+ process_payment
16
+ return unless successful?
17
+
18
+ fulfill_order
19
+ return unless successful?
20
+
21
+ success!
22
+ successful?
23
+ end
24
+
25
+ private
26
+
27
+ def validate_order
28
+ failure("Order ID required") unless @items[:order_id]
29
+ failure("Customer email required") unless @items[:customer_email]
30
+
31
+ success! if errors.empty?
32
+ end
33
+ end
34
+ ```
35
+
36
+ ## Error Recovery Patterns
37
+
38
+ ### Retry with Exponential Backoff
39
+
40
+ ```ruby
41
+ def process_with_retry
42
+ attempt = @items[:attempt] || 1
43
+
44
+ begin
45
+ risky_operation
46
+ success!
47
+ rescue RetryableError => e
48
+ if attempt < 3
49
+ delay = 2 ** attempt
50
+ retry_message = self.class.new(@items)
51
+ retry_message[:attempt] = attempt + 1
52
+ retry_message.publish_delayed('process_with_retry', delay: delay)
53
+ success! # Don't fail original message
54
+ else
55
+ failure("Failed after #{attempt} attempts: #{e.message}")
56
+ end
57
+ end
58
+
59
+ successful?
60
+ end
61
+ ```
62
+
63
+ ### Dead Letter Handling
64
+
65
+ ```ruby
66
+ class FailedMessage < BunnyFarm::Message
67
+ actions :investigate, :retry, :archive
68
+
69
+ def investigate
70
+ puts "Investigating failed message: #{@items[:original_error]}"
71
+ success!
72
+ end
73
+ end
74
+ ```
75
+
76
+ ## Best Practices
77
+
78
+ 1. Always handle exceptions gracefully
79
+ 2. Provide meaningful error messages
80
+ 3. Implement retry logic for transient failures
81
+ 4. Use dead letter queues for persistent failures
82
+ 5. Log errors for debugging and monitoring