smart_message 0.0.5 → 0.0.6

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.
@@ -0,0 +1,366 @@
1
+ #!/usr/bin/env ruby
2
+ # examples/08_entity_addressing.rb
3
+ #
4
+ # Demonstrates SmartMessage entity addressing capabilities including:
5
+ # - Point-to-point messaging with FROM/TO fields
6
+ # - Broadcast messaging (no TO field)
7
+ # - Request-reply patterns with REPLY_TO
8
+ # - Instance-level addressing overrides
9
+ # - Gateway patterns
10
+
11
+ require_relative '../lib/smart_message'
12
+
13
+ puts "šŸŽÆ SmartMessage Entity Addressing Demo"
14
+ puts "=" * 50
15
+
16
+ # Configure transport for demo
17
+ transport = SmartMessage::Transport.create(:stdout, loopback: true)
18
+ serializer = SmartMessage::Serializer::JSON.new
19
+
20
+ # =============================================================================
21
+ # Example 1: Point-to-Point Messaging
22
+ # =============================================================================
23
+
24
+ puts "\nšŸ“” Example 1: Point-to-Point Messaging"
25
+ puts "-" * 40
26
+
27
+ class OrderMessage < SmartMessage::Base
28
+ version 1
29
+ description "Direct order processing between order service and fulfillment"
30
+
31
+ # Point-to-point addressing
32
+ from 'order-service'
33
+ to 'fulfillment-service'
34
+ reply_to 'order-service'
35
+
36
+ property :order_id, required: true
37
+ property :customer_id, required: true
38
+ property :items, required: true
39
+ property :total_amount, required: true
40
+
41
+ config do
42
+ transport SmartMessage::Transport.create(:stdout, loopback: true)
43
+ serializer SmartMessage::Serializer::JSON.new
44
+ end
45
+
46
+ def self.process(header, payload)
47
+ data = JSON.parse(payload)
48
+ puts " šŸŽÆ FULFILLMENT SERVICE received order:"
49
+ puts " Order ID: #{data['order_id']}"
50
+ puts " From: #{header.from} → To: #{header.to}"
51
+ puts " Reply to: #{header.reply_to}"
52
+ puts " Customer: #{data['customer_id']}"
53
+ puts " Items: #{data['items'].join(', ')}"
54
+ puts " Total: $#{data['total_amount']}"
55
+ end
56
+ end
57
+
58
+ # Subscribe and publish point-to-point message
59
+ OrderMessage.subscribe
60
+
61
+ order = OrderMessage.new(
62
+ order_id: "ORD-2024-001",
63
+ customer_id: "CUST-12345",
64
+ items: ["Widget A", "Widget B", "Gadget C"],
65
+ total_amount: 299.99
66
+ )
67
+
68
+ puts "\nšŸ“¤ Publishing point-to-point order message..."
69
+ order.publish
70
+ sleep(0.2) # Allow time for handlers to process
71
+
72
+ # =============================================================================
73
+ # Example 2: Broadcast Messaging
74
+ # =============================================================================
75
+
76
+ puts "\nšŸ“» Example 2: Broadcast Messaging"
77
+ puts "-" * 40
78
+
79
+ class SystemAnnouncementMessage < SmartMessage::Base
80
+ version 1
81
+ description "System-wide announcements to all services"
82
+
83
+ # Broadcast addressing (no 'to' field)
84
+ from 'admin-service'
85
+ # No 'to' field = broadcast to all subscribers
86
+
87
+ property :message, required: true
88
+ property :priority, default: 'normal'
89
+ property :effective_time, required: true
90
+
91
+ config do
92
+ transport SmartMessage::Transport.create(:stdout, loopback: true)
93
+ serializer SmartMessage::Serializer::JSON.new
94
+ end
95
+
96
+ def self.process(header, payload)
97
+ data = JSON.parse(payload)
98
+ priority_icon = data['priority'] == 'high' ? '🚨' : 'šŸ“¢'
99
+ puts " #{priority_icon} ALL SERVICES received announcement:"
100
+ puts " From: #{header.from}"
101
+ puts " To: #{header.to.nil? ? 'ALL (broadcast)' : header.to}"
102
+ puts " Priority: #{data['priority'].upcase}"
103
+ puts " Message: #{data['message']}"
104
+ puts " Effective: #{data['effective_time']}"
105
+ end
106
+ end
107
+
108
+ # Subscribe and publish broadcast message
109
+ SystemAnnouncementMessage.subscribe
110
+
111
+ announcement = SystemAnnouncementMessage.new(
112
+ message: "System maintenance scheduled for tonight at 2:00 AM EST",
113
+ priority: 'high',
114
+ effective_time: '2024-12-20 02:00:00 EST'
115
+ )
116
+
117
+ puts "\nšŸ“¤ Publishing broadcast announcement..."
118
+ announcement.publish
119
+ sleep(0.2) # Allow time for handlers to process
120
+
121
+ # =============================================================================
122
+ # Example 3: Request-Reply Pattern
123
+ # =============================================================================
124
+
125
+ puts "\nšŸ”„ Example 3: Request-Reply Pattern"
126
+ puts "-" * 40
127
+
128
+ class UserLookupRequest < SmartMessage::Base
129
+ version 1
130
+ description "Request user information from user service"
131
+
132
+ from 'web-service'
133
+ to 'user-service'
134
+ reply_to 'web-service'
135
+
136
+ property :user_id, required: true
137
+ property :request_id, required: true
138
+ property :requested_fields, default: ['name', 'email']
139
+
140
+ config do
141
+ transport SmartMessage::Transport.create(:stdout, loopback: true)
142
+ serializer SmartMessage::Serializer::JSON.new
143
+ end
144
+
145
+ def self.process(header, payload)
146
+ data = JSON.parse(payload)
147
+ puts " šŸ” USER SERVICE received lookup request:"
148
+ puts " Request ID: #{data['request_id']}"
149
+ puts " User ID: #{data['user_id']}"
150
+ puts " From: #{header.from} → To: #{header.to}"
151
+ puts " Reply to: #{header.reply_to}"
152
+ puts " Fields: #{data['requested_fields'].join(', ')}"
153
+
154
+ # Simulate response (in real system, this would be a separate response message)
155
+ puts " ā†©ļø Simulated response would go to: #{header.reply_to}"
156
+ end
157
+ end
158
+
159
+ class UserLookupResponse < SmartMessage::Base
160
+ version 1
161
+ description "Response with user information"
162
+
163
+ from 'user-service'
164
+ # 'to' will be set to the original request's 'reply_to'
165
+
166
+ property :user_id, required: true
167
+ property :request_id, required: true
168
+ property :user_data
169
+ property :success, default: true
170
+ property :error_message
171
+
172
+ config do
173
+ transport SmartMessage::Transport.create(:stdout, loopback: true)
174
+ serializer SmartMessage::Serializer::JSON.new
175
+ end
176
+
177
+ def self.process(header, payload)
178
+ data = JSON.parse(payload)
179
+ puts " āœ… WEB SERVICE received lookup response:"
180
+ puts " Request ID: #{data['request_id']}"
181
+ puts " Success: #{data['success']}"
182
+ puts " User Data: #{data['user_data'] ? 'Present' : 'None'}"
183
+ puts " From: #{header.from} → To: #{header.to}"
184
+ end
185
+ end
186
+
187
+ # Subscribe to both request and response
188
+ UserLookupRequest.subscribe
189
+ UserLookupResponse.subscribe
190
+
191
+ # Send request
192
+ request = UserLookupRequest.new(
193
+ user_id: "USER-789",
194
+ request_id: SecureRandom.uuid,
195
+ requested_fields: ['name', 'email', 'last_login']
196
+ )
197
+
198
+ puts "\nšŸ“¤ Publishing user lookup request..."
199
+ request.publish
200
+ sleep(0.2) # Allow time for handlers to process
201
+
202
+ # Send simulated response
203
+ response = UserLookupResponse.new(
204
+ user_id: "USER-789",
205
+ request_id: request.request_id,
206
+ user_data: {
207
+ name: "Alice Johnson",
208
+ email: "alice@example.com",
209
+ last_login: "2024-12-19 14:30:00"
210
+ },
211
+ success: true
212
+ )
213
+ response.to('web-service') # Set reply destination
214
+
215
+ puts "\nšŸ“¤ Publishing user lookup response..."
216
+ response.publish
217
+ sleep(0.2) # Allow time for handlers to process
218
+
219
+ # =============================================================================
220
+ # Example 4: Instance-Level Addressing Override
221
+ # =============================================================================
222
+
223
+ puts "\nšŸ”§ Example 4: Instance-Level Addressing Override"
224
+ puts "-" * 40
225
+
226
+ class PaymentMessage < SmartMessage::Base
227
+ version 1
228
+ description "Payment processing with configurable routing"
229
+
230
+ # Default addressing
231
+ from 'payment-service'
232
+ to 'primary-bank-gateway'
233
+ reply_to 'payment-service'
234
+
235
+ property :payment_id, required: true
236
+ property :amount, required: true
237
+ property :account_id, required: true
238
+ property :payment_method, default: 'credit_card'
239
+
240
+ config do
241
+ transport SmartMessage::Transport.create(:stdout, loopback: true)
242
+ serializer SmartMessage::Serializer::JSON.new
243
+ end
244
+
245
+ def self.process(header, payload)
246
+ data = JSON.parse(payload)
247
+ gateway_icon = header.to.include?('backup') ? 'šŸ”„' : 'šŸ¦'
248
+ puts " #{gateway_icon} #{header.to.upcase} received payment:"
249
+ puts " Payment ID: #{data['payment_id']}"
250
+ puts " From: #{header.from} → To: #{header.to}"
251
+ puts " Amount: $#{data['amount']}"
252
+ puts " Account: #{data['account_id']}"
253
+ puts " Method: #{data['payment_method']}"
254
+ end
255
+ end
256
+
257
+ PaymentMessage.subscribe
258
+
259
+ # Normal payment using class defaults
260
+ normal_payment = PaymentMessage.new(
261
+ payment_id: "PAY-001",
262
+ amount: 150.00,
263
+ account_id: "ACCT-12345",
264
+ payment_method: 'credit_card'
265
+ )
266
+
267
+ puts "\nšŸ“¤ Publishing normal payment (using class defaults)..."
268
+ puts " Class FROM: #{PaymentMessage.from}"
269
+ puts " Class TO: #{PaymentMessage.to}"
270
+ normal_payment.publish
271
+ sleep(0.2) # Allow time for handlers to process
272
+
273
+ # Override addressing for backup gateway
274
+ backup_payment = PaymentMessage.new(
275
+ payment_id: "PAY-002",
276
+ amount: 75.50,
277
+ account_id: "ACCT-67890",
278
+ payment_method: 'debit_card'
279
+ )
280
+
281
+ # Override instance addressing
282
+ backup_payment.to('backup-bank-gateway')
283
+ backup_payment.reply_to('payment-backup-service')
284
+
285
+ puts "\nšŸ“¤ Publishing backup payment (with overrides)..."
286
+ puts " Instance FROM: #{backup_payment.from}"
287
+ puts " Instance TO: #{backup_payment.to}"
288
+ puts " Instance REPLY_TO: #{backup_payment.reply_to}"
289
+ backup_payment.publish
290
+ sleep(0.2) # Allow time for handlers to process
291
+
292
+ # =============================================================================
293
+ # Example 5: Gateway Pattern
294
+ # =============================================================================
295
+
296
+ puts "\nšŸŒ‰ Example 5: Gateway Pattern"
297
+ puts "-" * 40
298
+
299
+ class ExternalAPIMessage < SmartMessage::Base
300
+ version 1
301
+ description "External API integration message"
302
+
303
+ from 'api-gateway'
304
+ to 'external-partner-service'
305
+
306
+ property :api_call, required: true
307
+ property :payload_data, required: true
308
+ property :authentication_token
309
+ property :partner_id, required: true
310
+
311
+ config do
312
+ transport SmartMessage::Transport.create(:stdout, loopback: true)
313
+ serializer SmartMessage::Serializer::JSON.new
314
+ end
315
+
316
+ def self.process(header, payload)
317
+ data = JSON.parse(payload)
318
+ puts " 🌐 EXTERNAL PARTNER received API call:"
319
+ puts " API Call: #{data['api_call']}"
320
+ puts " From: #{header.from} → To: #{header.to}"
321
+ puts " Partner ID: #{data['partner_id']}"
322
+ puts " Has Auth Token: #{data['authentication_token'] ? 'Yes' : 'No'}"
323
+ puts " Payload Size: #{data['payload_data'].to_s.length} characters"
324
+ end
325
+ end
326
+
327
+ ExternalAPIMessage.subscribe
328
+
329
+ # Create internal message
330
+ internal_data = {
331
+ user_id: "USER-123",
332
+ action: "update_profile",
333
+ changes: { email: "newemail@example.com" }
334
+ }
335
+
336
+ # Transform for external API via gateway
337
+ external_message = ExternalAPIMessage.new(
338
+ api_call: "PUT /api/v1/users/USER-123",
339
+ payload_data: internal_data,
340
+ authentication_token: "Bearer abc123xyz789",
341
+ partner_id: "PARTNER-ALPHA"
342
+ )
343
+
344
+ # Gateway can override destination based on routing rules
345
+ if internal_data[:action] == "update_profile"
346
+ external_message.to('partner-alpha-profile-service')
347
+ else
348
+ external_message.to('partner-alpha-general-service')
349
+ end
350
+
351
+ puts "\nšŸ“¤ Publishing external API message via gateway..."
352
+ external_message.publish
353
+ sleep(0.2) # Allow time for handlers to process
354
+
355
+ # =============================================================================
356
+ # Summary
357
+ # =============================================================================
358
+
359
+ puts "\nšŸŽÆ Entity Addressing Summary"
360
+ puts "=" * 50
361
+ puts "āœ… Point-to-Point: FROM/TO specified for direct routing"
362
+ puts "āœ… Broadcast: FROM only, TO=nil for all subscribers"
363
+ puts "āœ… Request-Reply: REPLY_TO for response routing"
364
+ puts "āœ… Instance Override: Runtime addressing changes"
365
+ puts "āœ… Gateway Pattern: Message transformation and routing"
366
+ puts "\nFor more details, see docs/addressing.md"