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,163 @@
1
+ # Actions DSL
2
+
3
+ The Actions DSL defines the operations that your message can perform. Each action becomes a routable method that can be called when the message is consumed.
4
+
5
+ ## Basic Actions
6
+
7
+ ```ruby
8
+ class OrderMessage < BunnyFarm::Message
9
+ actions :validate, :process, :ship, :cancel
10
+
11
+ def validate
12
+ # Validation logic
13
+ success!
14
+ end
15
+
16
+ def process
17
+ # Processing logic
18
+ success!
19
+ end
20
+
21
+ def ship
22
+ # Shipping logic
23
+ success!
24
+ end
25
+
26
+ def cancel
27
+ # Cancellation logic
28
+ success!
29
+ end
30
+ end
31
+ ```
32
+
33
+ ## Action Method Implementation
34
+
35
+ Each action must be implemented as a method:
36
+
37
+ ```ruby
38
+ class PaymentMessage < BunnyFarm::Message
39
+ actions :authorize, :capture, :refund
40
+
41
+ def authorize
42
+ result = payment_gateway.authorize(@items[:amount], @items[:card_token])
43
+
44
+ if result.success?
45
+ @items[:authorization_id] = result.authorization_id
46
+ success!
47
+ else
48
+ failure("Authorization failed: #{result.error_message}")
49
+ end
50
+
51
+ successful?
52
+ end
53
+
54
+ def capture
55
+ return failure("No authorization ID") unless @items[:authorization_id]
56
+
57
+ result = payment_gateway.capture(@items[:authorization_id])
58
+
59
+ if result.success?
60
+ @items[:transaction_id] = result.transaction_id
61
+ success!
62
+ else
63
+ failure("Capture failed: #{result.error_message}")
64
+ end
65
+
66
+ successful?
67
+ end
68
+ end
69
+ ```
70
+
71
+ ## Action Routing
72
+
73
+ Actions create routing keys automatically:
74
+
75
+ ```ruby
76
+ # Publishing creates routing keys
77
+ message = PaymentMessage.new
78
+ message.publish('authorize') # Routing key: PaymentMessage.authorize
79
+ message.publish('capture') # Routing key: PaymentMessage.capture
80
+ message.publish('refund') # Routing key: PaymentMessage.refund
81
+ ```
82
+
83
+ ## Action Patterns
84
+
85
+ ### CRUD Operations
86
+ ```ruby
87
+ class UserMessage < BunnyFarm::Message
88
+ actions :create, :read, :update, :delete
89
+ end
90
+ ```
91
+
92
+ ### Workflow Actions
93
+ ```ruby
94
+ class OrderWorkflow < BunnyFarm::Message
95
+ actions :start, :validate, :process_payment, :fulfill, :ship, :complete
96
+ end
97
+ ```
98
+
99
+ ### State Machine Actions
100
+ ```ruby
101
+ class DocumentMessage < BunnyFarm::Message
102
+ actions :draft, :review, :approve, :publish, :archive
103
+ end
104
+ ```
105
+
106
+ ## Action Validation
107
+
108
+ ```ruby
109
+ class ValidatedActions < BunnyFarm::Message
110
+ actions :process
111
+
112
+ def process
113
+ # Validate before processing
114
+ return unless validate_preconditions
115
+
116
+ # Perform work
117
+ do_processing
118
+
119
+ # Validate after processing
120
+ return unless validate_postconditions
121
+
122
+ success!
123
+ end
124
+
125
+ private
126
+
127
+ def validate_preconditions
128
+ failure("Missing required data") unless required_data_present?
129
+ successful?
130
+ end
131
+
132
+ def validate_postconditions
133
+ failure("Processing incomplete") unless processing_complete?
134
+ successful?
135
+ end
136
+ end
137
+ ```
138
+
139
+ ## Best Practices
140
+
141
+ ### 1. Use Descriptive Action Names
142
+ ```ruby
143
+ # Good: Clear, descriptive names
144
+ actions :validate_order, :process_payment, :ship_order, :send_confirmation
145
+
146
+ # Avoid: Vague or generic names
147
+ actions :do_stuff, :handle, :process
148
+ ```
149
+
150
+ ### 2. Single Responsibility
151
+ ```ruby
152
+ # Good: Each action has one purpose
153
+ actions :validate_customer, :validate_inventory, :validate_payment
154
+
155
+ # Avoid: Actions that do too much
156
+ actions :validate_everything
157
+ ```
158
+
159
+ ### 3. Logical Flow
160
+ ```ruby
161
+ # Good: Actions follow logical order
162
+ actions :create_draft, :add_content, :review, :approve, :publish
163
+ ```
@@ -0,0 +1,146 @@
1
+ # Fields DSL
2
+
3
+ The Fields DSL is BunnyFarm's declarative syntax for defining the data structure of your messages. It provides a clean, readable way to specify expected fields and their organization.
4
+
5
+ ![Fields DSL Structure](../assets/fields_dsl_structure.svg)
6
+
7
+ ## Basic Field Declaration
8
+
9
+ ```ruby
10
+ class UserMessage < BunnyFarm::Message
11
+ # Simple fields
12
+ fields :user_id, :email, :name, :created_at
13
+ end
14
+ ```
15
+
16
+ ## Nested Objects
17
+
18
+ ```ruby
19
+ class CustomerMessage < BunnyFarm::Message
20
+ fields :customer_id, :email,
21
+ { address: [:street, :city, :state, :zip] },
22
+ { preferences: [:newsletter, :promotions, :sms] }
23
+ end
24
+ ```
25
+
26
+ ## Complex Structures
27
+
28
+ ```ruby
29
+ class OrderMessage < BunnyFarm::Message
30
+ fields :order_id, :total, :tax, :shipping,
31
+ { customer: [:name, :email, :phone] },
32
+ { billing_address: [:street, :city, :state, :zip, :country] },
33
+ { shipping_address: [:street, :city, :state, :zip, :country] },
34
+ { items: [:product_id, :name, :quantity, :unit_price, :total_price] },
35
+ { payment: [:method, :transaction_id, :status] }
36
+ end
37
+ ```
38
+
39
+ ## Data Access
40
+
41
+ ```ruby
42
+ message = OrderMessage.new
43
+
44
+ # Simple fields
45
+ message[:order_id] = 12345
46
+ message[:total] = 99.99
47
+
48
+ # Nested objects
49
+ message[:customer] = {
50
+ name: "John Doe",
51
+ email: "john@example.com",
52
+ phone: "+1-555-123-4567"
53
+ }
54
+
55
+ # Arrays of objects
56
+ message[:items] = [
57
+ {
58
+ product_id: 1,
59
+ name: "Widget",
60
+ quantity: 2,
61
+ unit_price: 29.99,
62
+ total_price: 59.98
63
+ },
64
+ {
65
+ product_id: 2,
66
+ name: "Gadget",
67
+ quantity: 1,
68
+ unit_price: 39.99,
69
+ total_price: 39.99
70
+ }
71
+ ]
72
+
73
+ # Access nested data
74
+ puts message[:customer][:name] # "John Doe"
75
+ puts message[:items][0][:product_id] # 1
76
+ puts message[:billing_address][:city] # Access nested fields
77
+ ```
78
+
79
+ ## Field Validation
80
+
81
+ ```ruby
82
+ class ValidatedMessage < BunnyFarm::Message
83
+ fields :user_id, :email, :age
84
+
85
+ def validate
86
+ failure("User ID required") unless @items[:user_id]
87
+ failure("Invalid email") unless valid_email?(@items[:email])
88
+ failure("Age must be positive") unless @items[:age]&.positive?
89
+
90
+ success! if errors.empty?
91
+ end
92
+
93
+ private
94
+
95
+ def valid_email?(email)
96
+ email =~ /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
97
+ end
98
+ end
99
+ ```
100
+
101
+ ## Dynamic Field Access
102
+
103
+ ```ruby
104
+ message = OrderMessage.new
105
+
106
+ # Get all field keys
107
+ field_keys = message.keys
108
+ # => [:order_id, :total, :customer, :items, ...]
109
+
110
+ # Check if field exists
111
+ has_customer = message.keys.include?(:customer)
112
+
113
+ # Iterate over fields
114
+ message.keys.each do |field|
115
+ value = message[field]
116
+ puts "#{field}: #{value}"
117
+ end
118
+ ```
119
+
120
+ ## Best Practices
121
+
122
+ ### 1. Logical Grouping
123
+ ```ruby
124
+ # Good: Related fields grouped together
125
+ fields :order_id, :status, :created_at,
126
+ { customer: [:id, :name, :email] },
127
+ { items: [:id, :name, :price] }
128
+ ```
129
+
130
+ ### 2. Consistent Naming
131
+ ```ruby
132
+ # Good: Consistent snake_case
133
+ fields :user_id, :created_at, :email_address
134
+
135
+ # Avoid: Mixed conventions
136
+ fields :userId, :created_at, :EmailAddress
137
+ ```
138
+
139
+ ### 3. Meaningful Structure
140
+ ```ruby
141
+ # Good: Clear hierarchy
142
+ fields { address: [:street, :city, :state, :zip] }
143
+
144
+ # Avoid: Flat structure
145
+ fields :address_street, :address_city, :address_state, :address_zip
146
+ ```
@@ -0,0 +1,115 @@
1
+ # Instance Methods
2
+
3
+ BunnyFarm message instances provide a rich set of methods for data access, state management, and message operations.
4
+
5
+ ## Data Access Methods
6
+
7
+ ### Hash-like Access
8
+ ```ruby
9
+ message = OrderMessage.new
10
+
11
+ # Get values
12
+ order_id = message[:order_id]
13
+ customer_name = message[:customer][:name]
14
+
15
+ # Set values
16
+ message[:order_id] = 12345
17
+ message[:customer] = { name: "John", email: "john@example.com" }
18
+ ```
19
+
20
+ ### Field Introspection
21
+ ```ruby
22
+ # Get all field keys
23
+ keys = message.keys # => [:order_id, :customer, :items]
24
+
25
+ # Check if field exists
26
+ has_customer = message.respond_to?(:[]) && message[:customer]
27
+
28
+ # Get field count
29
+ field_count = message.keys.length
30
+ ```
31
+
32
+ ## State Management
33
+
34
+ ### Success/Failure Tracking
35
+ ```ruby
36
+ def process_order
37
+ validate_data
38
+ return unless successful?
39
+
40
+ charge_payment
41
+ return unless successful?
42
+
43
+ update_inventory
44
+ success! # Mark as successful
45
+ end
46
+
47
+ # Check state
48
+ if message.successful?
49
+ puts "Order processed successfully"
50
+ else
51
+ puts "Order failed: #{message.errors.join(', ')}"
52
+ end
53
+ ```
54
+
55
+ ### Error Management
56
+ ```ruby
57
+ def validate
58
+ failure("Order ID required") unless @items[:order_id]
59
+ failure("Invalid email") unless valid_email?(@items[:email])
60
+
61
+ # Check error state
62
+ if failed?
63
+ puts "Validation errors: #{errors.join(', ')}"
64
+ else
65
+ success!
66
+ end
67
+ end
68
+ ```
69
+
70
+ ## Message Operations
71
+
72
+ ### Publishing
73
+ ```ruby
74
+ # Publish message
75
+ message.publish('process') # Routing key: MessageClass.process
76
+
77
+ # Check if publish succeeded
78
+ if message.successful?
79
+ puts "Message published successfully"
80
+ else
81
+ puts "Publish failed: #{message.errors.join(', ')}"
82
+ end
83
+ ```
84
+
85
+ ### Serialization
86
+ ```ruby
87
+ # Convert to JSON
88
+ json_string = message.to_json
89
+ puts json_string
90
+ # => {"order_id":12345,"customer":{"name":"John"}}
91
+
92
+ # Get raw payload (if received message)
93
+ original_json = message.payload
94
+ ```
95
+
96
+ ## Utility Methods
97
+
98
+ ### Inspection and Debugging
99
+ ```ruby
100
+ # Inspect message contents
101
+ puts message.inspect
102
+
103
+ # Pretty print for debugging
104
+ puts JSON.pretty_generate(message.to_hash)
105
+ ```
106
+
107
+ ### Cloning and Copying
108
+ ```ruby
109
+ # Create copy with same data
110
+ new_message = message.class.new
111
+ message.keys.each { |key| new_message[key] = message[key] }
112
+
113
+ # Publish copy with different action
114
+ new_message.publish('different_action')
115
+ ```
@@ -0,0 +1,211 @@
1
+ # Message Structure Overview
2
+
3
+ Understanding how BunnyFarm messages are structured is key to using the library effectively. This section covers the anatomy of a BunnyFarm message and how the DSL components work together.
4
+
5
+ <img src="../assets/message_structure.svg" alt="Message Structure" width="100%">
6
+
7
+ ## Message Class Anatomy
8
+
9
+ A BunnyFarm message class consists of several key components:
10
+
11
+ ```ruby
12
+ class OrderMessage < BunnyFarm::Message
13
+ # 1. Fields DSL - Define data structure
14
+ fields :order_id, :total,
15
+ { customer: [:name, :email] },
16
+ { items: [:id, :qty] }
17
+
18
+ # 2. Actions DSL - Define available operations
19
+ actions :validate, :process_payment, :ship, :cancel
20
+
21
+ # 3. Action Methods - Business logic
22
+ def validate
23
+ # Validation logic here
24
+ success!
25
+ successful?
26
+ end
27
+
28
+ def process_payment
29
+ # Payment processing logic
30
+ success!
31
+ successful?
32
+ end
33
+
34
+ # 4. Helper Methods (private)
35
+ private
36
+
37
+ def validate_customer
38
+ # Helper logic
39
+ end
40
+ end
41
+ ```
42
+
43
+ ## DSL Components
44
+
45
+ ### Fields DSL
46
+ The `fields` DSL defines the expected data structure for your messages:
47
+
48
+ - **Simple fields**: Basic data types like strings, numbers, booleans
49
+ - **Nested objects**: Complex data structures with sub-fields
50
+ - **Arrays**: Lists of items or objects
51
+
52
+ ### Actions DSL
53
+ The `actions` DSL defines the operations your message can perform:
54
+
55
+ - Each action becomes a routable method
56
+ - Actions map to routing keys: `MessageClass.action`
57
+ - Must implement corresponding methods
58
+
59
+ ## Instance Structure
60
+
61
+ When a message is instantiated, it contains several important instance variables:
62
+
63
+ ### @items
64
+ Validated and structured data extracted from the raw JSON based on your fields definition.
65
+
66
+ ```ruby
67
+ message[:customer][:name] # Access via hash-like interface
68
+ ```
69
+
70
+ ### @elements
71
+ Raw JSON data as received from the message broker.
72
+
73
+ ```ruby
74
+ message.elements # Access raw data
75
+ ```
76
+
77
+ ### @payload
78
+ The original JSON string as received from RabbitMQ.
79
+
80
+ ```ruby
81
+ message.payload # Original JSON string
82
+ ```
83
+
84
+ ### @errors
85
+ Array of error messages accumulated during processing.
86
+
87
+ ```ruby
88
+ message.errors # => ["Validation failed", "Payment declined"]
89
+ ```
90
+
91
+ ## Data Flow
92
+
93
+ The message goes through several stages:
94
+
95
+ 1. **JSON Input** - Raw message data from RabbitMQ
96
+ 2. **Parse & Validate** - Extract fields using DSL definition
97
+ 3. **Route Action** - Call method based on routing key
98
+ 4. **Execute Logic** - Run business logic in action method
99
+ 5. **ACK/NACK** - Acknowledge to RabbitMQ based on success/failure
100
+
101
+ ## Access Methods
102
+
103
+ BunnyFarm provides several ways to access and manipulate message data:
104
+
105
+ ### Hash-like Access
106
+ ```ruby
107
+ msg[:field] # Get value
108
+ msg[:field] = val # Set value
109
+ ```
110
+
111
+ ### JSON Serialization
112
+ ```ruby
113
+ msg.to_json # Convert to JSON string
114
+ ```
115
+
116
+ ### Field Inspection
117
+ ```ruby
118
+ msg.keys # Get all available fields
119
+ ```
120
+
121
+ ## State Management
122
+
123
+ Messages track their processing state throughout the lifecycle:
124
+
125
+ ### Success States
126
+ ```ruby
127
+ success! # Mark as successful
128
+ successful? # Check if successful
129
+ ```
130
+
131
+ ### Failure States
132
+ ```ruby
133
+ failure(msg) # Mark as failed with reason
134
+ failed? # Check if failed
135
+ ```
136
+
137
+ ### Publishing
138
+ ```ruby
139
+ msg.publish(action) # Send message with routing key
140
+ ```
141
+
142
+ ## Best Practices
143
+
144
+ ### 1. Design Clear Field Structures
145
+ ```ruby
146
+ # Good: Clear, hierarchical structure
147
+ fields :order_id, :amount,
148
+ { customer: [:name, :email, :phone] },
149
+ { billing_address: [:street, :city, :state, :zip] }
150
+
151
+ # Avoid: Flat, unclear structure
152
+ fields :order_id, :customer_name, :customer_email,
153
+ :billing_street, :billing_city # ... too flat
154
+ ```
155
+
156
+ ### 2. Use Meaningful Action Names
157
+ ```ruby
158
+ # Good: Descriptive action names
159
+ actions :validate_order, :process_payment, :ship_order, :send_confirmation
160
+
161
+ # Avoid: Generic action names
162
+ actions :process, :handle, :do_work
163
+ ```
164
+
165
+ ### 3. Implement Proper Error Handling
166
+ ```ruby
167
+ def process_payment
168
+ return failure("Missing payment info") unless payment_present?
169
+ return failure("Invalid amount") unless valid_amount?
170
+
171
+ charge_result = payment_gateway.charge(@items[:amount])
172
+
173
+ if charge_result.success?
174
+ success!
175
+ else
176
+ failure("Payment failed: #{charge_result.error}")
177
+ end
178
+
179
+ successful?
180
+ end
181
+ ```
182
+
183
+ ### 4. Keep Methods Focused
184
+ Each action method should have a single, clear responsibility:
185
+
186
+ ```ruby
187
+ def validate_order
188
+ validate_customer_info
189
+ validate_items
190
+ validate_shipping_address
191
+
192
+ success! if errors.empty?
193
+ successful?
194
+ end
195
+
196
+ private
197
+
198
+ def validate_customer_info
199
+ failure("Customer name required") if @items[:customer][:name].blank?
200
+ failure("Customer email required") if @items[:customer][:email].blank?
201
+ end
202
+ ```
203
+
204
+ ## Next Steps
205
+
206
+ Now that you understand message structure, explore:
207
+
208
+ - **[Fields DSL](fields-dsl.md)** - Detailed guide to defining data structures
209
+ - **[Actions DSL](actions-dsl.md)** - Complete actions DSL reference
210
+ - **[Instance Methods](instance-methods.md)** - All available instance methods
211
+ - **[Configuration](../configuration/overview.md)** - Message configuration options