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,545 @@
|
|
|
1
|
+
# JSON Serialization
|
|
2
|
+
|
|
3
|
+
BunnyFarm uses JSON as its message serialization format, providing human-readable, language-agnostic message passing that's easy to debug and integrate with other systems.
|
|
4
|
+
|
|
5
|
+
## Why JSON?
|
|
6
|
+
|
|
7
|
+
### Human Readable
|
|
8
|
+
JSON messages are easy to read and debug:
|
|
9
|
+
|
|
10
|
+
```json
|
|
11
|
+
{
|
|
12
|
+
"order_id": 12345,
|
|
13
|
+
"customer": {
|
|
14
|
+
"name": "John Doe",
|
|
15
|
+
"email": "john@example.com"
|
|
16
|
+
},
|
|
17
|
+
"items": [
|
|
18
|
+
{
|
|
19
|
+
"product_id": 1,
|
|
20
|
+
"quantity": 2,
|
|
21
|
+
"price": 29.99
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"total": 59.98,
|
|
25
|
+
"timestamp": "2024-01-15T10:30:00Z"
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Language Agnostic
|
|
30
|
+
Other systems can easily produce and consume messages:
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
# Python producer
|
|
34
|
+
import json
|
|
35
|
+
import pika
|
|
36
|
+
|
|
37
|
+
message = {
|
|
38
|
+
"order_id": 12345,
|
|
39
|
+
"customer": {"name": "John", "email": "john@example.com"},
|
|
40
|
+
"total": 59.98
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
channel.basic_publish(
|
|
44
|
+
exchange='bunny_farm_exchange',
|
|
45
|
+
routing_key='OrderMessage.process',
|
|
46
|
+
body=json.dumps(message)
|
|
47
|
+
)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Tooling Support
|
|
51
|
+
Rich ecosystem of JSON tools for debugging and monitoring.
|
|
52
|
+
|
|
53
|
+
## Serialization Process
|
|
54
|
+
|
|
55
|
+
### Publishing Flow
|
|
56
|
+
|
|
57
|
+
```ruby
|
|
58
|
+
class OrderMessage < BunnyFarm::Message
|
|
59
|
+
fields :order_id, :customer, :items
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# 1. Create message
|
|
63
|
+
message = OrderMessage.new
|
|
64
|
+
message[:order_id] = 12345
|
|
65
|
+
message[:customer] = { name: "John", email: "john@example.com" }
|
|
66
|
+
|
|
67
|
+
# 2. Publish - automatic serialization
|
|
68
|
+
message.publish('process')
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Internal process:**
|
|
72
|
+
1. Message data collected from `@items` hash
|
|
73
|
+
2. Data serialized to JSON string
|
|
74
|
+
3. JSON sent to RabbitMQ as message body
|
|
75
|
+
4. Routing key: `OrderMessage.process`
|
|
76
|
+
|
|
77
|
+
### Consumption Flow
|
|
78
|
+
|
|
79
|
+
```ruby
|
|
80
|
+
# Consumer receives JSON message body and routing key
|
|
81
|
+
# BunnyFarm automatically:
|
|
82
|
+
# 1. Parses routing key → OrderMessage.process
|
|
83
|
+
# 2. Deserializes JSON to Ruby hash
|
|
84
|
+
# 3. Creates OrderMessage instance
|
|
85
|
+
# 4. Populates @items with deserialized data
|
|
86
|
+
# 5. Calls 'process' method
|
|
87
|
+
|
|
88
|
+
class OrderMessage < BunnyFarm::Message
|
|
89
|
+
def process
|
|
90
|
+
puts @items[:order_id] # 12345
|
|
91
|
+
puts @items[:customer][:name] # "John"
|
|
92
|
+
success!
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Data Types and Serialization
|
|
98
|
+
|
|
99
|
+
### Supported Data Types
|
|
100
|
+
|
|
101
|
+
JSON supports these Ruby types natively:
|
|
102
|
+
|
|
103
|
+
```ruby
|
|
104
|
+
message = OrderMessage.new
|
|
105
|
+
|
|
106
|
+
# String
|
|
107
|
+
message[:customer_name] = "John Doe"
|
|
108
|
+
|
|
109
|
+
# Number (Integer/Float)
|
|
110
|
+
message[:order_id] = 12345
|
|
111
|
+
message[:total] = 59.99
|
|
112
|
+
|
|
113
|
+
# Boolean
|
|
114
|
+
message[:is_priority] = true
|
|
115
|
+
message[:requires_signature] = false
|
|
116
|
+
|
|
117
|
+
# Null
|
|
118
|
+
message[:notes] = nil
|
|
119
|
+
|
|
120
|
+
# Array
|
|
121
|
+
message[:items] = [
|
|
122
|
+
{ product_id: 1, quantity: 2 },
|
|
123
|
+
{ product_id: 2, quantity: 1 }
|
|
124
|
+
]
|
|
125
|
+
|
|
126
|
+
# Hash/Object
|
|
127
|
+
message[:customer] = {
|
|
128
|
+
name: "John Doe",
|
|
129
|
+
email: "john@example.com",
|
|
130
|
+
address: {
|
|
131
|
+
street: "123 Main St",
|
|
132
|
+
city: "Boston",
|
|
133
|
+
state: "MA"
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Serialized JSON Output
|
|
139
|
+
|
|
140
|
+
```json
|
|
141
|
+
{
|
|
142
|
+
"customer_name": "John Doe",
|
|
143
|
+
"order_id": 12345,
|
|
144
|
+
"total": 59.99,
|
|
145
|
+
"is_priority": true,
|
|
146
|
+
"requires_signature": false,
|
|
147
|
+
"notes": null,
|
|
148
|
+
"items": [
|
|
149
|
+
{"product_id": 1, "quantity": 2},
|
|
150
|
+
{"product_id": 2, "quantity": 1}
|
|
151
|
+
],
|
|
152
|
+
"customer": {
|
|
153
|
+
"name": "John Doe",
|
|
154
|
+
"email": "john@example.com",
|
|
155
|
+
"address": {
|
|
156
|
+
"street": "123 Main St",
|
|
157
|
+
"city": "Boston",
|
|
158
|
+
"state": "MA"
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Complex Data Handling
|
|
165
|
+
|
|
166
|
+
### Date and Time
|
|
167
|
+
|
|
168
|
+
Dates should be serialized as ISO 8601 strings:
|
|
169
|
+
|
|
170
|
+
```ruby
|
|
171
|
+
class OrderMessage < BunnyFarm::Message
|
|
172
|
+
def set_timestamps
|
|
173
|
+
@items[:created_at] = Time.current.iso8601
|
|
174
|
+
@items[:processed_at] = DateTime.current.iso8601
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def get_created_time
|
|
178
|
+
Time.parse(@items[:created_at])
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# JSON output
|
|
183
|
+
{
|
|
184
|
+
"created_at": "2024-01-15T10:30:00Z",
|
|
185
|
+
"processed_at": "2024-01-15T10:30:00+00:00"
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Binary Data
|
|
190
|
+
|
|
191
|
+
Base64 encode binary data:
|
|
192
|
+
|
|
193
|
+
```ruby
|
|
194
|
+
class DocumentMessage < BunnyFarm::Message
|
|
195
|
+
def set_file_content(file_path)
|
|
196
|
+
content = File.binread(file_path)
|
|
197
|
+
@items[:file_content] = Base64.encode64(content)
|
|
198
|
+
@items[:filename] = File.basename(file_path)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def get_file_content
|
|
202
|
+
Base64.decode64(@items[:file_content])
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Custom Objects
|
|
208
|
+
|
|
209
|
+
Convert complex objects to simple data structures:
|
|
210
|
+
|
|
211
|
+
```ruby
|
|
212
|
+
class Customer
|
|
213
|
+
attr_accessor :id, :name, :email, :created_at
|
|
214
|
+
|
|
215
|
+
def to_hash
|
|
216
|
+
{
|
|
217
|
+
id: @id,
|
|
218
|
+
name: @name,
|
|
219
|
+
email: @email,
|
|
220
|
+
created_at: @created_at.iso8601
|
|
221
|
+
}
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def self.from_hash(data)
|
|
225
|
+
customer = new
|
|
226
|
+
customer.id = data[:id]
|
|
227
|
+
customer.name = data[:name]
|
|
228
|
+
customer.email = data[:email]
|
|
229
|
+
customer.created_at = Time.parse(data[:created_at])
|
|
230
|
+
customer
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
class OrderMessage < BunnyFarm::Message
|
|
235
|
+
def set_customer(customer)
|
|
236
|
+
@items[:customer] = customer.to_hash
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def get_customer
|
|
240
|
+
Customer.from_hash(@items[:customer])
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Performance Considerations
|
|
246
|
+
|
|
247
|
+
### Message Size
|
|
248
|
+
|
|
249
|
+
JSON can become large with complex data:
|
|
250
|
+
|
|
251
|
+
```ruby
|
|
252
|
+
# Monitor message sizes
|
|
253
|
+
class OrderMessage < BunnyFarm::Message
|
|
254
|
+
def publish(action)
|
|
255
|
+
json_size = to_json.bytesize
|
|
256
|
+
logger.warn "Large message: #{json_size} bytes" if json_size > 10.kilobytes
|
|
257
|
+
|
|
258
|
+
super(action)
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Compression
|
|
264
|
+
|
|
265
|
+
For large messages, consider compression:
|
|
266
|
+
|
|
267
|
+
```ruby
|
|
268
|
+
require 'zlib'
|
|
269
|
+
|
|
270
|
+
class LargeDataMessage < BunnyFarm::Message
|
|
271
|
+
def set_compressed_data(data)
|
|
272
|
+
json_data = data.to_json
|
|
273
|
+
compressed = Zlib::Deflate.deflate(json_data)
|
|
274
|
+
@items[:compressed_data] = Base64.encode64(compressed)
|
|
275
|
+
@items[:compression] = 'zlib'
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def get_decompressed_data
|
|
279
|
+
return @items[:data] unless @items[:compression]
|
|
280
|
+
|
|
281
|
+
compressed = Base64.decode64(@items[:compressed_data])
|
|
282
|
+
json_data = Zlib::Inflate.inflate(compressed)
|
|
283
|
+
JSON.parse(json_data)
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Parsing Performance
|
|
289
|
+
|
|
290
|
+
JSON parsing can be optimized:
|
|
291
|
+
|
|
292
|
+
```ruby
|
|
293
|
+
# Use Oj gem for faster JSON parsing
|
|
294
|
+
require 'oj'
|
|
295
|
+
|
|
296
|
+
class FastMessage < BunnyFarm::Message
|
|
297
|
+
def to_json
|
|
298
|
+
Oj.dump(@items, mode: :compat)
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def self.from_json(json_string)
|
|
302
|
+
data = Oj.load(json_string, mode: :compat)
|
|
303
|
+
message = new
|
|
304
|
+
message.instance_variable_set(:@items, data)
|
|
305
|
+
message
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
## Debugging JSON Messages
|
|
311
|
+
|
|
312
|
+
### Pretty Printing
|
|
313
|
+
|
|
314
|
+
Format JSON for easier reading:
|
|
315
|
+
|
|
316
|
+
```ruby
|
|
317
|
+
require 'json'
|
|
318
|
+
|
|
319
|
+
class OrderMessage < BunnyFarm::Message
|
|
320
|
+
def pretty_print
|
|
321
|
+
JSON.pretty_generate(@items)
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
puts message.pretty_print
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
Output:
|
|
329
|
+
```json
|
|
330
|
+
{
|
|
331
|
+
"order_id": 12345,
|
|
332
|
+
"customer": {
|
|
333
|
+
"name": "John Doe",
|
|
334
|
+
"email": "john@example.com"
|
|
335
|
+
},
|
|
336
|
+
"items": [
|
|
337
|
+
{
|
|
338
|
+
"product_id": 1,
|
|
339
|
+
"quantity": 2
|
|
340
|
+
}
|
|
341
|
+
]
|
|
342
|
+
}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### JSON Validation
|
|
346
|
+
|
|
347
|
+
Validate message structure:
|
|
348
|
+
|
|
349
|
+
```ruby
|
|
350
|
+
require 'json-schema'
|
|
351
|
+
|
|
352
|
+
class ValidatedMessage < BunnyFarm::Message
|
|
353
|
+
SCHEMA = {
|
|
354
|
+
type: 'object',
|
|
355
|
+
required: ['order_id', 'customer'],
|
|
356
|
+
properties: {
|
|
357
|
+
order_id: { type: 'integer' },
|
|
358
|
+
customer: {
|
|
359
|
+
type: 'object',
|
|
360
|
+
required: ['name', 'email'],
|
|
361
|
+
properties: {
|
|
362
|
+
name: { type: 'string' },
|
|
363
|
+
email: { type: 'string', format: 'email' }
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}.freeze
|
|
368
|
+
|
|
369
|
+
def validate_schema
|
|
370
|
+
errors = JSON::Validator.fully_validate(SCHEMA, @items)
|
|
371
|
+
failure("Schema validation failed: #{errors.join(', ')}") unless errors.empty?
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
### Message Inspection
|
|
377
|
+
|
|
378
|
+
Inspect messages in RabbitMQ management UI or with tools:
|
|
379
|
+
|
|
380
|
+
```bash
|
|
381
|
+
# View message in RabbitMQ management interface
|
|
382
|
+
# Messages tab → Get Messages → View JSON payload
|
|
383
|
+
|
|
384
|
+
# Use rabbitmqadmin to inspect messages
|
|
385
|
+
rabbitmqadmin get queue=order_queue requeue=true
|
|
386
|
+
|
|
387
|
+
# Use jq to format JSON in command line
|
|
388
|
+
echo '{"order_id":12345,"customer":{"name":"John"}}' | jq '.'
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
## Integration Patterns
|
|
392
|
+
|
|
393
|
+
### Multi-Language Consumers
|
|
394
|
+
|
|
395
|
+
Other languages can consume BunnyFarm messages:
|
|
396
|
+
|
|
397
|
+
**Python Consumer:**
|
|
398
|
+
```python
|
|
399
|
+
import json
|
|
400
|
+
import pika
|
|
401
|
+
|
|
402
|
+
def process_order(channel, method, properties, body):
|
|
403
|
+
# Parse BunnyFarm JSON message
|
|
404
|
+
message_data = json.loads(body)
|
|
405
|
+
order_id = message_data['order_id']
|
|
406
|
+
customer = message_data['customer']
|
|
407
|
+
|
|
408
|
+
# Process order
|
|
409
|
+
print(f"Processing order {order_id} for {customer['name']}")
|
|
410
|
+
|
|
411
|
+
# Acknowledge message
|
|
412
|
+
channel.basic_ack(delivery_tag=method.delivery_tag)
|
|
413
|
+
|
|
414
|
+
# Set up consumer
|
|
415
|
+
connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
|
|
416
|
+
channel = connection.channel()
|
|
417
|
+
channel.basic_consume(queue='order_queue', on_message_callback=process_order)
|
|
418
|
+
channel.start_consuming()
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
**Node.js Consumer:**
|
|
422
|
+
```javascript
|
|
423
|
+
const amqp = require('amqplib');
|
|
424
|
+
|
|
425
|
+
async function processOrders() {
|
|
426
|
+
const connection = await amqp.connect('amqp://localhost');
|
|
427
|
+
const channel = await connection.createChannel();
|
|
428
|
+
|
|
429
|
+
channel.consume('order_queue', (message) => {
|
|
430
|
+
// Parse BunnyFarm JSON message
|
|
431
|
+
const messageData = JSON.parse(message.content.toString());
|
|
432
|
+
const orderId = messageData.order_id;
|
|
433
|
+
const customer = messageData.customer;
|
|
434
|
+
|
|
435
|
+
console.log(`Processing order ${orderId} for ${customer.name}`);
|
|
436
|
+
|
|
437
|
+
// Acknowledge message
|
|
438
|
+
channel.ack(message);
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
### API Gateway Integration
|
|
444
|
+
|
|
445
|
+
Expose message creation via REST API:
|
|
446
|
+
|
|
447
|
+
```ruby
|
|
448
|
+
class OrdersController < ApplicationController
|
|
449
|
+
def create
|
|
450
|
+
# Create message from API payload
|
|
451
|
+
order_message = OrderMessage.new
|
|
452
|
+
order_message[:order_id] = params[:order_id]
|
|
453
|
+
order_message[:customer] = params[:customer]
|
|
454
|
+
order_message[:items] = params[:items]
|
|
455
|
+
|
|
456
|
+
# Publish for processing
|
|
457
|
+
if order_message.publish('process')
|
|
458
|
+
render json: { status: 'accepted', order_id: params[:order_id] }
|
|
459
|
+
else
|
|
460
|
+
render json: { error: 'Failed to process order' }, status: 500
|
|
461
|
+
end
|
|
462
|
+
end
|
|
463
|
+
end
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
## Best Practices
|
|
467
|
+
|
|
468
|
+
### 1. Keep Messages Focused
|
|
469
|
+
|
|
470
|
+
```ruby
|
|
471
|
+
# Good: Focused message structure
|
|
472
|
+
class OrderMessage < BunnyFarm::Message
|
|
473
|
+
fields :order_id, :customer_id, :items, :total, :shipping_address
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
# Avoid: Kitchen sink approach
|
|
477
|
+
class EverythingMessage < BunnyFarm::Message
|
|
478
|
+
fields :order_data, :customer_data, :inventory_data, :shipping_data,
|
|
479
|
+
:payment_data, :analytics_data, :audit_data, :metadata
|
|
480
|
+
end
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
### 2. Use Consistent Naming
|
|
484
|
+
|
|
485
|
+
```ruby
|
|
486
|
+
# Good: Consistent snake_case
|
|
487
|
+
{
|
|
488
|
+
"order_id": 12345,
|
|
489
|
+
"customer_email": "john@example.com",
|
|
490
|
+
"created_at": "2024-01-15T10:30:00Z"
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
# Avoid: Mixed naming conventions
|
|
494
|
+
{
|
|
495
|
+
"orderId": 12345, # camelCase
|
|
496
|
+
"customer_email": "john@example.com", # snake_case
|
|
497
|
+
"CreatedAt": "2024-01-15T10:30:00Z" # PascalCase
|
|
498
|
+
}
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
### 3. Version Your Message Structure
|
|
502
|
+
|
|
503
|
+
```ruby
|
|
504
|
+
class OrderMessage < BunnyFarm::Message
|
|
505
|
+
def initialize
|
|
506
|
+
super
|
|
507
|
+
@items[:_version] = '1.0'
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
def process
|
|
511
|
+
case @items[:_version]
|
|
512
|
+
when '1.0'
|
|
513
|
+
process_v1
|
|
514
|
+
when '2.0'
|
|
515
|
+
process_v2
|
|
516
|
+
else
|
|
517
|
+
failure("Unsupported message version: #{@items[:_version]}")
|
|
518
|
+
end
|
|
519
|
+
end
|
|
520
|
+
end
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
### 4. Handle Missing Fields Gracefully
|
|
524
|
+
|
|
525
|
+
```ruby
|
|
526
|
+
def process
|
|
527
|
+
order_id = @items[:order_id]
|
|
528
|
+
return failure("Order ID required") unless order_id
|
|
529
|
+
|
|
530
|
+
customer_email = @items.dig(:customer, :email)
|
|
531
|
+
return failure("Customer email required") unless customer_email
|
|
532
|
+
|
|
533
|
+
# Process with validated data
|
|
534
|
+
success!
|
|
535
|
+
end
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
## Next Steps
|
|
539
|
+
|
|
540
|
+
With JSON serialization mastered:
|
|
541
|
+
|
|
542
|
+
- **[Configure message formats](../configuration/overview.md)** for your environment
|
|
543
|
+
- **[Design message structures](../message-structure/overview.md)** for optimal serialization
|
|
544
|
+
- **[Handle errors](error-handling.md)** in serialization and deserialization
|
|
545
|
+
- **[Integrate with other systems](../architecture/integration.md)** using JSON
|