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.
- checksums.yaml +7 -0
- data/.envrc +1 -0
- data/.github/workflows/docs.yml +38 -0
- data/.gitignore +11 -0
- data/.travis.yml +3 -0
- data/CHANGELOG.md +61 -0
- data/COMMITS.md +196 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +330 -0
- data/Rakefile +9 -0
- data/bunny_farm.gemspec +30 -0
- data/config/bunny.yml.erb +29 -0
- data/config/bunny_test.yml.erb +29 -0
- data/config/hipchat.yml.erb +12 -0
- data/docs/api/configuration.md +9 -0
- data/docs/api/consumer.md +8 -0
- data/docs/api/message-class.md +419 -0
- data/docs/api/publisher.md +9 -0
- data/docs/architecture/integration.md +8 -0
- data/docs/architecture/message-flow.md +11 -0
- data/docs/architecture/overview.md +448 -0
- data/docs/architecture/scaling.md +8 -0
- data/docs/assets/actions_dsl_flow.svg +109 -0
- data/docs/assets/architecture_overview.svg +152 -0
- data/docs/assets/best_practices_patterns.svg +203 -0
- data/docs/assets/bunny_farm_logo.png +0 -0
- data/docs/assets/configuration_api_methods.svg +104 -0
- data/docs/assets/configuration_flow.svg +130 -0
- data/docs/assets/configuration_hierarchy.svg +70 -0
- data/docs/assets/data_processing_pipeline.svg +131 -0
- data/docs/assets/debugging_monitoring.svg +165 -0
- data/docs/assets/ecommerce_example_flow.svg +145 -0
- data/docs/assets/email_campaign_example.svg +127 -0
- data/docs/assets/environment_variables_map.svg +78 -0
- data/docs/assets/error_handling_flow.svg +114 -0
- data/docs/assets/favicon.ico +1 -0
- data/docs/assets/fields_dsl_structure.svg +89 -0
- data/docs/assets/instance_methods_lifecycle.svg +137 -0
- data/docs/assets/integration_patterns.svg +207 -0
- data/docs/assets/json_serialization_flow.svg +153 -0
- data/docs/assets/logo.svg +4 -0
- data/docs/assets/message_api_overview.svg +126 -0
- data/docs/assets/message_encapsulation.svg +113 -0
- data/docs/assets/message_lifecycle.svg +110 -0
- data/docs/assets/message_structure.svg +138 -0
- data/docs/assets/publisher_consumer_api.svg +120 -0
- data/docs/assets/scaling_deployment_patterns.svg +195 -0
- data/docs/assets/smart_routing_diagram.svg +131 -0
- data/docs/assets/system_architecture_overview.svg +155 -0
- data/docs/assets/task_scheduling_flow.svg +139 -0
- data/docs/assets/testing_strategies.svg +146 -0
- data/docs/assets/workflow_patterns.svg +183 -0
- data/docs/assets/yaml_config_structure.svg +72 -0
- data/docs/configuration/environment-variables.md +14 -0
- data/docs/configuration/overview.md +373 -0
- data/docs/configuration/programmatic-setup.md +10 -0
- data/docs/configuration/yaml-configuration.md +12 -0
- data/docs/core-features/configuration.md +528 -0
- data/docs/core-features/error-handling.md +82 -0
- data/docs/core-features/json-serialization.md +545 -0
- data/docs/core-features/message-design.md +406 -0
- data/docs/core-features/smart-routing.md +467 -0
- data/docs/core-features/task-scheduling.md +67 -0
- data/docs/core-features/workflow-support.md +112 -0
- data/docs/development/contributing.md +345 -0
- data/docs/development/roadmap.md +9 -0
- data/docs/development/testing.md +14 -0
- data/docs/examples/order-processing.md +10 -0
- data/docs/examples/overview.md +269 -0
- data/docs/examples/real-world.md +8 -0
- data/docs/examples/simple-producer-consumer.md +15 -0
- data/docs/examples/task-scheduler.md +9 -0
- data/docs/getting-started/basic-concepts.md +274 -0
- data/docs/getting-started/installation.md +122 -0
- data/docs/getting-started/quick-start.md +158 -0
- data/docs/index.md +106 -0
- data/docs/message-structure/actions-dsl.md +163 -0
- data/docs/message-structure/fields-dsl.md +146 -0
- data/docs/message-structure/instance-methods.md +115 -0
- data/docs/message-structure/overview.md +211 -0
- data/examples/README.md +212 -0
- data/examples/consumer.rb +41 -0
- data/examples/images/message_flow.svg +87 -0
- data/examples/images/order_workflow.svg +122 -0
- data/examples/images/producer_consumer.svg +96 -0
- data/examples/images/task_scheduler.svg +140 -0
- data/examples/order_processor.rb +238 -0
- data/examples/producer.rb +60 -0
- data/examples/simple_message.rb +43 -0
- data/examples/task_scheduler.rb +263 -0
- data/images/architecture_overview.svg +152 -0
- data/images/bunny_farm_logo.png +0 -0
- data/images/configuration_flow.svg +130 -0
- data/images/message_structure.svg +138 -0
- data/lib/bunny_farm/.irbrc +7 -0
- data/lib/bunny_farm/generic_consumer.rb +12 -0
- data/lib/bunny_farm/hash_ext.rb +37 -0
- data/lib/bunny_farm/init_bunny.rb +137 -0
- data/lib/bunny_farm/init_hipchat.rb +49 -0
- data/lib/bunny_farm/message.rb +218 -0
- data/lib/bunny_farm/message_elements.rb +25 -0
- data/lib/bunny_farm/version.rb +3 -0
- data/lib/bunny_farm.rb +9 -0
- data/mkdocs.yml +148 -0
- metadata +244 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
<svg width="800" height="550" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<!-- Background transparent -->
|
|
3
|
+
<rect width="800" height="550" fill="transparent"/>
|
|
4
|
+
|
|
5
|
+
<!-- Title -->
|
|
6
|
+
<text x="400" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="bold" fill="#e0e0e0">
|
|
7
|
+
Task Scheduler with Retry Logic
|
|
8
|
+
</text>
|
|
9
|
+
|
|
10
|
+
<!-- Scheduler (Producer) -->
|
|
11
|
+
<g>
|
|
12
|
+
<rect x="50" y="70" width="150" height="100" rx="10" fill="#2d3748" stroke="#9f7aea" stroke-width="2"/>
|
|
13
|
+
<text x="125" y="95" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" fill="#9f7aea">SCHEDULER</text>
|
|
14
|
+
<text x="125" y="115" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">Creates tasks with</text>
|
|
15
|
+
<text x="125" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">scheduled times</text>
|
|
16
|
+
<text x="125" y="145" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">ā° schedule_for</text>
|
|
17
|
+
<text x="125" y="160" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">š retry_count: 0</text>
|
|
18
|
+
</g>
|
|
19
|
+
|
|
20
|
+
<!-- Worker (Consumer) -->
|
|
21
|
+
<g>
|
|
22
|
+
<rect x="600" y="70" width="150" height="100" rx="10" fill="#2d3748" stroke="#ed8936" stroke-width="2"/>
|
|
23
|
+
<text x="675" y="95" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" fill="#ed8936">WORKER</text>
|
|
24
|
+
<text x="675" y="115" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">Executes scheduled</text>
|
|
25
|
+
<text x="675" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">tasks at right time</text>
|
|
26
|
+
<text x="675" y="145" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">ā” Execute actions</text>
|
|
27
|
+
<text x="675" y="160" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">š Handle retries</text>
|
|
28
|
+
</g>
|
|
29
|
+
|
|
30
|
+
<!-- Task Types -->
|
|
31
|
+
<g>
|
|
32
|
+
<rect x="250" y="70" width="300" height="100" rx="10" fill="#1a202c" stroke="#4a5568" stroke-width="1"/>
|
|
33
|
+
<text x="400" y="95" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" fill="#68d391">Task Types</text>
|
|
34
|
+
|
|
35
|
+
<g>
|
|
36
|
+
<rect x="270" y="110" width="60" height="25" rx="3" fill="#4299e1" stroke="#4299e1" stroke-width="1"/>
|
|
37
|
+
<text x="300" y="125" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#1a202c">š§ Email</text>
|
|
38
|
+
|
|
39
|
+
<rect x="340" y="110" width="60" height="25" rx="3" fill="#68d391" stroke="#68d391" stroke-width="1"/>
|
|
40
|
+
<text x="370" y="125" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#1a202c">š¾ Backup</text>
|
|
41
|
+
|
|
42
|
+
<rect x="410" y="110" width="60" height="25" rx="3" fill="#ed8936" stroke="#ed8936" stroke-width="1"/>
|
|
43
|
+
<text x="440" y="125" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#1a202c">š Report</text>
|
|
44
|
+
|
|
45
|
+
<rect x="480" y="110" width="60" height="25" rx="3" fill="#9f7aea" stroke="#9f7aea" stroke-width="1"/>
|
|
46
|
+
<text x="510" y="125" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#1a202c">š§¹ Cleanup</text>
|
|
47
|
+
</g>
|
|
48
|
+
|
|
49
|
+
<text x="400" y="155" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#9ca3af">Different task types with specific logic</text>
|
|
50
|
+
</g>
|
|
51
|
+
|
|
52
|
+
<!-- Execution Flow -->
|
|
53
|
+
<g>
|
|
54
|
+
<text x="400" y="210" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="#f6e05e">Execution Flow</text>
|
|
55
|
+
|
|
56
|
+
<!-- Execute -->
|
|
57
|
+
<rect x="100" y="230" width="100" height="60" rx="8" fill="#2d3748" stroke="#68d391" stroke-width="2"/>
|
|
58
|
+
<text x="150" y="250" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#68d391">EXECUTE</text>
|
|
59
|
+
<text x="150" y="265" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">Check timing</text>
|
|
60
|
+
<text x="150" y="280" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">Run task logic</text>
|
|
61
|
+
|
|
62
|
+
<!-- Success -->
|
|
63
|
+
<rect x="250" y="230" width="100" height="60" rx="8" fill="#2d3748" stroke="#68d391" stroke-width="2"/>
|
|
64
|
+
<text x="300" y="250" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#68d391">ā
SUCCESS</text>
|
|
65
|
+
<text x="300" y="265" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">Task completed</text>
|
|
66
|
+
<text x="300" y="280" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">Send notifications</text>
|
|
67
|
+
|
|
68
|
+
<!-- Retry -->
|
|
69
|
+
<rect x="450" y="230" width="100" height="60" rx="8" fill="#2d3748" stroke="#f6e05e" stroke-width="2"/>
|
|
70
|
+
<text x="500" y="250" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#f6e05e">š RETRY</text>
|
|
71
|
+
<text x="500" y="265" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">Exponential</text>
|
|
72
|
+
<text x="500" y="280" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">backoff delay</text>
|
|
73
|
+
|
|
74
|
+
<!-- Failure -->
|
|
75
|
+
<rect x="600" y="230" width="100" height="60" rx="8" fill="#2d3748" stroke="#e53e3e" stroke-width="2"/>
|
|
76
|
+
<text x="650" y="250" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#e53e3e">ā FAILURE</text>
|
|
77
|
+
<text x="650" y="265" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">Max retries</text>
|
|
78
|
+
<text x="650" y="280" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#e0e0e0">Send alerts</text>
|
|
79
|
+
</g>
|
|
80
|
+
|
|
81
|
+
<!-- Flow Arrows -->
|
|
82
|
+
<defs>
|
|
83
|
+
<marker id="arrow-green-2" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
|
84
|
+
<polygon points="0 0, 10 3.5, 0 7" fill="#68d391"/>
|
|
85
|
+
</marker>
|
|
86
|
+
<marker id="arrow-yellow" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
|
87
|
+
<polygon points="0 0, 10 3.5, 0 7" fill="#f6e05e"/>
|
|
88
|
+
</marker>
|
|
89
|
+
<marker id="arrow-red" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
|
90
|
+
<polygon points="0 0, 10 3.5, 0 7" fill="#e53e3e"/>
|
|
91
|
+
</marker>
|
|
92
|
+
</defs>
|
|
93
|
+
|
|
94
|
+
<!-- Execute to Success -->
|
|
95
|
+
<line x1="200" y1="250" x2="250" y2="250" stroke="#68d391" stroke-width="2" marker-end="url(#arrow-green-2)"/>
|
|
96
|
+
<text x="225" y="245" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#68d391">success</text>
|
|
97
|
+
|
|
98
|
+
<!-- Execute to Retry -->
|
|
99
|
+
<line x1="200" y1="270" x2="450" y2="270" stroke="#f6e05e" stroke-width="2" marker-end="url(#arrow-yellow)"/>
|
|
100
|
+
<text x="325" y="285" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#f6e05e">error (retry < 3)</text>
|
|
101
|
+
|
|
102
|
+
<!-- Retry back to Execute -->
|
|
103
|
+
<path d="M 500 230 Q 400 190 150 230" stroke="#f6e05e" stroke-width="2" fill="none" marker-end="url(#arrow-yellow)"/>
|
|
104
|
+
<text x="350" y="205" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#f6e05e">retry after delay</text>
|
|
105
|
+
|
|
106
|
+
<!-- Retry to Failure -->
|
|
107
|
+
<line x1="550" y1="260" x2="600" y2="260" stroke="#e53e3e" stroke-width="2" marker-end="url(#arrow-red)"/>
|
|
108
|
+
<text x="575" y="255" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#e53e3e">max retries</text>
|
|
109
|
+
|
|
110
|
+
<!-- Retry Logic Details -->
|
|
111
|
+
<g>
|
|
112
|
+
<rect x="50" y="330" width="700" height="120" rx="10" fill="#2d3748" stroke="#f6e05e" stroke-width="2"/>
|
|
113
|
+
<text x="400" y="355" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="#f6e05e">Retry Logic with Exponential Backoff</text>
|
|
114
|
+
|
|
115
|
+
<g>
|
|
116
|
+
<text x="70" y="380" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#68d391">Attempt 1:</text>
|
|
117
|
+
<text x="140" y="380" font-family="Arial, sans-serif" font-size="12" fill="#e0e0e0">Execute immediately ā Fail ā Wait 2¹ = 2 seconds</text>
|
|
118
|
+
|
|
119
|
+
<text x="70" y="400" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#f6e05e">Attempt 2:</text>
|
|
120
|
+
<text x="140" y="400" font-family="Arial, sans-serif" font-size="12" fill="#e0e0e0">Execute after delay ā Fail ā Wait 2² = 4 seconds</text>
|
|
121
|
+
|
|
122
|
+
<text x="70" y="420" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#ed8936">Attempt 3:</text>
|
|
123
|
+
<text x="140" y="420" font-family="Arial, sans-serif" font-size="12" fill="#e0e0e0">Execute after delay ā Fail ā Give up, send alert</text>
|
|
124
|
+
</g>
|
|
125
|
+
</g>
|
|
126
|
+
|
|
127
|
+
<!-- Code Example -->
|
|
128
|
+
<g>
|
|
129
|
+
<rect x="50" y="470" width="700" height="70" rx="10" fill="#1a202c" stroke="#4a5568" stroke-width="1"/>
|
|
130
|
+
<text x="400" y="490" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#9ca3af">Task Definition Example</text>
|
|
131
|
+
|
|
132
|
+
<text x="60" y="510" font-family="monospace" font-size="10" fill="#f6e05e">task_type:</text>
|
|
133
|
+
<text x="120" y="510" font-family="monospace" font-size="10" fill="#e0e0e0">"email_reminder"</text>
|
|
134
|
+
<text x="250" y="510" font-family="monospace" font-size="10" fill="#f6e05e">scheduled_for:</text>
|
|
135
|
+
<text x="340" y="510" font-family="monospace" font-size="10" fill="#e0e0e0">"2024-01-01 14:30:00"</text>
|
|
136
|
+
|
|
137
|
+
<text x="60" y="525" font-family="monospace" font-size="10" fill="#f6e05e">payload:</text>
|
|
138
|
+
<text x="110" y="525" font-family="monospace" font-size="10" fill="#e0e0e0">{ recipient: "user@example.com", subject: "Meeting reminder" }</text>
|
|
139
|
+
</g>
|
|
140
|
+
</svg>
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# Order processing example with multiple actions and error handling
|
|
3
|
+
|
|
4
|
+
require 'bundler/setup'
|
|
5
|
+
require 'bunny_farm'
|
|
6
|
+
require 'json'
|
|
7
|
+
|
|
8
|
+
# Example of a more complex message class for order processing
|
|
9
|
+
class OrderMessage < BunnyFarm::Message
|
|
10
|
+
# Define fields including nested structures
|
|
11
|
+
fields :order_id, :total_amount, :status,
|
|
12
|
+
{ customer: [:name, :email, :phone] },
|
|
13
|
+
{ items: [:product_id, :quantity, :price] },
|
|
14
|
+
:created_at
|
|
15
|
+
|
|
16
|
+
# Define multiple actions for different stages of order processing
|
|
17
|
+
actions :validate, :process_payment, :ship, :cancel
|
|
18
|
+
|
|
19
|
+
def validate(*args)
|
|
20
|
+
puts "\nš Validating order ##{@items[:order_id]}"
|
|
21
|
+
|
|
22
|
+
# Check required fields
|
|
23
|
+
if @items[:customer][:email].nil? || @items[:customer][:email].empty?
|
|
24
|
+
failure("Customer email is required")
|
|
25
|
+
return
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
if @items[:total_amount].to_f <= 0
|
|
29
|
+
failure("Invalid order amount: #{@items[:total_amount]}")
|
|
30
|
+
return
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Simulate validation
|
|
34
|
+
sleep 0.5
|
|
35
|
+
|
|
36
|
+
puts " ā
Order validation passed"
|
|
37
|
+
puts " Customer: #{@items[:customer][:name]} (#{@items[:customer][:email]})"
|
|
38
|
+
puts " Amount: $#{'%.2f' % @items[:total_amount]}"
|
|
39
|
+
|
|
40
|
+
# Update status
|
|
41
|
+
@items[:status] = 'validated'
|
|
42
|
+
|
|
43
|
+
# Publish next step
|
|
44
|
+
publish('process_payment')
|
|
45
|
+
|
|
46
|
+
success!
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def process_payment(*args)
|
|
50
|
+
puts "\nš³ Processing payment for order ##{@items[:order_id]}"
|
|
51
|
+
|
|
52
|
+
# Simulate payment processing
|
|
53
|
+
sleep 1
|
|
54
|
+
|
|
55
|
+
# Randomly simulate payment success/failure for demo
|
|
56
|
+
if rand > 0.2 # 80% success rate
|
|
57
|
+
puts " ā
Payment successful: $#{'%.2f' % @items[:total_amount]}"
|
|
58
|
+
@items[:status] = 'paid'
|
|
59
|
+
|
|
60
|
+
# Publish next step
|
|
61
|
+
publish('ship')
|
|
62
|
+
success!
|
|
63
|
+
else
|
|
64
|
+
puts " ā Payment failed - insufficient funds"
|
|
65
|
+
@items[:status] = 'payment_failed'
|
|
66
|
+
|
|
67
|
+
# Publish cancellation
|
|
68
|
+
publish('cancel')
|
|
69
|
+
failure("Payment processing failed")
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def ship(*args)
|
|
74
|
+
puts "\nš¦ Shipping order ##{@items[:order_id]}"
|
|
75
|
+
puts " To: #{@items[:customer][:name]}"
|
|
76
|
+
puts " Email: #{@items[:customer][:email]}"
|
|
77
|
+
|
|
78
|
+
# Simulate shipping process
|
|
79
|
+
sleep 0.5
|
|
80
|
+
|
|
81
|
+
tracking_number = "TRK#{rand(100000..999999)}"
|
|
82
|
+
puts " š Tracking number: #{tracking_number}"
|
|
83
|
+
|
|
84
|
+
@items[:status] = 'shipped'
|
|
85
|
+
@items[:tracking_number] = tracking_number
|
|
86
|
+
|
|
87
|
+
# Send notification email (simulated)
|
|
88
|
+
send_notification('shipped')
|
|
89
|
+
|
|
90
|
+
success!
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def cancel(*args)
|
|
94
|
+
puts "\nā Cancelling order ##{@items[:order_id]}"
|
|
95
|
+
puts " Reason: #{@errors.last if @errors}"
|
|
96
|
+
|
|
97
|
+
@items[:status] = 'cancelled'
|
|
98
|
+
|
|
99
|
+
# Send cancellation email (simulated)
|
|
100
|
+
send_notification('cancelled')
|
|
101
|
+
|
|
102
|
+
success! # Even cancellation is handled successfully
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
private
|
|
106
|
+
|
|
107
|
+
def send_notification(type)
|
|
108
|
+
puts " š§ Sending #{type} notification to #{@items[:customer][:email]}"
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Producer/Consumer combo for demonstration
|
|
113
|
+
if __FILE__ == $0
|
|
114
|
+
require 'optparse'
|
|
115
|
+
|
|
116
|
+
mode = nil
|
|
117
|
+
OptionParser.new do |opts|
|
|
118
|
+
opts.banner = "Usage: #{$0} [options]"
|
|
119
|
+
opts.on("-p", "--producer", "Run as producer") { mode = :producer }
|
|
120
|
+
opts.on("-c", "--consumer", "Run as consumer") { mode = :consumer }
|
|
121
|
+
opts.on("-h", "--help", "Show this help") do
|
|
122
|
+
puts opts
|
|
123
|
+
exit
|
|
124
|
+
end
|
|
125
|
+
end.parse!
|
|
126
|
+
|
|
127
|
+
if mode.nil?
|
|
128
|
+
puts "Please specify --producer or --consumer mode"
|
|
129
|
+
exit 1
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Configure BunnyFarm
|
|
133
|
+
BunnyFarm.config do
|
|
134
|
+
env 'development'
|
|
135
|
+
app_id mode == :producer ? 'order_producer' : 'order_consumer'
|
|
136
|
+
|
|
137
|
+
if mode == :consumer
|
|
138
|
+
# Configure to receive all OrderMessage actions
|
|
139
|
+
bunny Hashie::Mash.new(
|
|
140
|
+
YAML.load(
|
|
141
|
+
ERB.new(
|
|
142
|
+
File.read(
|
|
143
|
+
File.join(BunnyFarm::CONFIG.config_dir, BunnyFarm::CONFIG.bunny_file)
|
|
144
|
+
)
|
|
145
|
+
).result, aliases: true
|
|
146
|
+
)[BunnyFarm::CONFIG.env].merge(
|
|
147
|
+
'routing_key' => 'OrderMessage.*'
|
|
148
|
+
)
|
|
149
|
+
)
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
if mode == :producer
|
|
154
|
+
puts "Order Processing Producer"
|
|
155
|
+
puts "=" * 40
|
|
156
|
+
|
|
157
|
+
# Create sample orders
|
|
158
|
+
orders = [
|
|
159
|
+
{
|
|
160
|
+
order_id: "ORD-#{rand(1000..9999)}",
|
|
161
|
+
total_amount: 99.99,
|
|
162
|
+
status: 'new',
|
|
163
|
+
customer: {
|
|
164
|
+
name: 'John Doe',
|
|
165
|
+
email: 'john@example.com',
|
|
166
|
+
phone: '+1-555-0123'
|
|
167
|
+
},
|
|
168
|
+
items: [
|
|
169
|
+
{ product_id: 'PROD-001', quantity: 2, price: 49.99 }
|
|
170
|
+
],
|
|
171
|
+
created_at: Time.now.to_s
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
order_id: "ORD-#{rand(1000..9999)}",
|
|
175
|
+
total_amount: 149.50,
|
|
176
|
+
status: 'new',
|
|
177
|
+
customer: {
|
|
178
|
+
name: 'Jane Smith',
|
|
179
|
+
email: 'jane@example.com',
|
|
180
|
+
phone: '+1-555-0124'
|
|
181
|
+
},
|
|
182
|
+
items: [
|
|
183
|
+
{ product_id: 'PROD-002', quantity: 1, price: 149.50 }
|
|
184
|
+
],
|
|
185
|
+
created_at: Time.now.to_s
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
order_id: "ORD-#{rand(1000..9999)}",
|
|
189
|
+
total_amount: 0, # This will fail validation
|
|
190
|
+
status: 'new',
|
|
191
|
+
customer: {
|
|
192
|
+
name: 'Bad Order',
|
|
193
|
+
email: '', # This will also fail
|
|
194
|
+
phone: ''
|
|
195
|
+
},
|
|
196
|
+
items: [],
|
|
197
|
+
created_at: Time.now.to_s
|
|
198
|
+
}
|
|
199
|
+
]
|
|
200
|
+
|
|
201
|
+
orders.each do |order_data|
|
|
202
|
+
msg = OrderMessage.new
|
|
203
|
+
|
|
204
|
+
# Populate message fields
|
|
205
|
+
order_data.each do |key, value|
|
|
206
|
+
msg[key] = value
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Start the order processing workflow
|
|
210
|
+
if msg.publish('validate')
|
|
211
|
+
puts "ā
Sent order #{order_data[:order_id]} for validation"
|
|
212
|
+
else
|
|
213
|
+
puts "ā Failed to send order: #{msg.errors.join(', ')}"
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
sleep 1
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
puts "\nš¬ All orders sent for processing!"
|
|
220
|
+
|
|
221
|
+
else # consumer mode
|
|
222
|
+
puts "Order Processing Consumer"
|
|
223
|
+
puts "=" * 40
|
|
224
|
+
puts "Waiting for order messages..."
|
|
225
|
+
puts "Press Ctrl+C to exit"
|
|
226
|
+
|
|
227
|
+
begin
|
|
228
|
+
BunnyFarm.manage(false)
|
|
229
|
+
rescue Interrupt
|
|
230
|
+
puts "\n\nShutting down order processor..."
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# Clean up
|
|
235
|
+
if BunnyFarm::CONFIG.connection && BunnyFarm::CONFIG.connection.open?
|
|
236
|
+
BunnyFarm::CONFIG.connection.close
|
|
237
|
+
end
|
|
238
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# Producer example - sends messages to RabbitMQ
|
|
3
|
+
|
|
4
|
+
require 'bundler/setup'
|
|
5
|
+
require 'bunny_farm'
|
|
6
|
+
require_relative 'simple_message'
|
|
7
|
+
|
|
8
|
+
# Configure BunnyFarm
|
|
9
|
+
BunnyFarm.config do
|
|
10
|
+
env 'development'
|
|
11
|
+
app_id 'greeting_producer'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
puts "BunnyFarm Producer Example"
|
|
15
|
+
puts "=" * 40
|
|
16
|
+
puts "Sending messages to RabbitMQ..."
|
|
17
|
+
puts "Queue: #{BunnyFarm::CONFIG.bunny.queue_name}"
|
|
18
|
+
puts "Exchange: #{BunnyFarm::CONFIG.bunny.exchange_name}"
|
|
19
|
+
puts "=" * 40
|
|
20
|
+
|
|
21
|
+
# Create and send hello messages
|
|
22
|
+
3.times do |i|
|
|
23
|
+
msg = GreetingMessage.new
|
|
24
|
+
msg[:name] = "User #{i + 1}"
|
|
25
|
+
msg[:greeting_type] = 'hello'
|
|
26
|
+
msg[:timestamp] = Time.now.to_s
|
|
27
|
+
|
|
28
|
+
if msg.publish('say_hello')
|
|
29
|
+
puts "ā
Sent hello message for User #{i + 1}"
|
|
30
|
+
else
|
|
31
|
+
puts "ā Failed to send hello message: #{msg.errors.join(', ')}"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
sleep 0.5
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Create and send goodbye messages
|
|
38
|
+
2.times do |i|
|
|
39
|
+
msg = GreetingMessage.new
|
|
40
|
+
msg[:name] = "Guest #{i + 1}"
|
|
41
|
+
msg[:greeting_type] = 'goodbye'
|
|
42
|
+
msg[:timestamp] = Time.now.to_s
|
|
43
|
+
|
|
44
|
+
if msg.publish('say_goodbye')
|
|
45
|
+
puts "ā
Sent goodbye message for Guest #{i + 1}"
|
|
46
|
+
else
|
|
47
|
+
puts "ā Failed to send goodbye message: #{msg.errors.join(', ')}"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
sleep 0.5
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
puts "\nš¬ All messages sent!"
|
|
54
|
+
puts "Run consumer.rb in another terminal to process them."
|
|
55
|
+
|
|
56
|
+
# Clean up
|
|
57
|
+
if BunnyFarm::CONFIG.connection && BunnyFarm::CONFIG.connection.open?
|
|
58
|
+
BunnyFarm::CONFIG.connection.close
|
|
59
|
+
puts "Connection closed."
|
|
60
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# Simple message class example
|
|
3
|
+
|
|
4
|
+
require 'bundler/setup'
|
|
5
|
+
require 'bunny_farm'
|
|
6
|
+
|
|
7
|
+
# Define a simple message class for processing greetings
|
|
8
|
+
class GreetingMessage < BunnyFarm::Message
|
|
9
|
+
# Define the fields this message expects
|
|
10
|
+
fields :name, :greeting_type, :timestamp
|
|
11
|
+
|
|
12
|
+
# Define the actions this message can handle
|
|
13
|
+
actions :say_hello, :say_goodbye
|
|
14
|
+
|
|
15
|
+
def say_hello(*args)
|
|
16
|
+
puts "š Hello, #{@items[:name]}!"
|
|
17
|
+
puts "Received at: #{@items[:timestamp]}"
|
|
18
|
+
|
|
19
|
+
# Simulate some processing
|
|
20
|
+
sleep 0.5
|
|
21
|
+
|
|
22
|
+
# Mark as successful
|
|
23
|
+
success!
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def say_goodbye(*args)
|
|
27
|
+
puts "š Goodbye, #{@items[:name]}!"
|
|
28
|
+
puts "Until next time at: #{@items[:timestamp]}"
|
|
29
|
+
|
|
30
|
+
# Simulate some processing
|
|
31
|
+
sleep 0.5
|
|
32
|
+
|
|
33
|
+
# Mark as successful
|
|
34
|
+
success!
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
if __FILE__ == $0
|
|
39
|
+
puts "Simple Message Class Example"
|
|
40
|
+
puts "=" * 40
|
|
41
|
+
puts "This file defines the GreetingMessage class."
|
|
42
|
+
puts "Use producer.rb to send messages and consumer.rb to process them."
|
|
43
|
+
end
|