GUBUS COMPOSE
COMPOSE creates formatted messages from object data using template patterns and special operations. Modern implementation with direct field mapping, special placeholders, and SchemaContext integration.
Use COMPOSE to create formatted messages with dynamic field values and aggregations. Supports special placeholders like {$counter}, {$summator:field}, and {$context_variable:name} for powerful message construction.
Overview
COMPOSE creates formatted messages from object data. Combines static text (header/footer) with dynamic field values using template patterns and special operations.
When to use:
- Build notification messages for Telegram/Viber bots
- Create summary reports with aggregations (counts, sums, enumerations)
- Format confirmation messages for users
- Construct email bodies with dynamic content and context variables
Key characteristics:
- Direct field reference with
{field_name}placeholders - Special operations with
{$operation:field}syntax (counters, enumerators, summators) - SchemaContext integration for cross-transform variable access
- 3 destination types: main_message, obj_field, context_variable
- Conditional composition based on field values
- Empty data handling with custom fallback messages
How It Works (Modern Implementation)
[Preprocessing]
→ [Apply special placeholders to header/footer/pattern]
→ [Resolve {$counter}, {$enumerator}, {$summator}, etc.]
→ [Resolve {$context_variable:var_name} from SchemaContext]
[For each object]
→ [Check condition if specified]
→ [Replace {field_name} directly from object properties]
→ [No need for message_construct_fields list]
→ [Concatenate with newlines]
[Finalization]
→ [Prepend header with \n\n]
→ [Append footer with \n]
→ [Write to destination]
Key difference from legacy: Modern approach uses direct field mapping from object properties rather than requiring explicit field lists. Special placeholders are processed before object iteration.
Rule Properties (Modern)
| PROPERTY | DESCRIPTION |
|---|---|
| reference | Required. Example: “orderConfirmation” |
| message_construct_pattern | Required. Template with {field_name} and {$operation:field}. Example: “Order {order_id} total: {total}“ |
| destination_type | Required. “set_main_message”, “join_main_message_empty_line”, “join_main_message_new_line”, “obj_field”, or “context_variable” |
| header | Static text prepended. Supports special placeholders. Example: ”📋 ORDER SUMMARY ({$counter} items)“ |
| footer | Static text appended. Supports special placeholders. Example: “Total: {$summator:price}“ |
| if_empty_message | Message when no objects match condition |
| field_name_if_target_in_obj | Required if destination_type=“obj_field”. Field name to write to |
| condition | Condition type. Example: “equals” |
| condition_field | Field to check for condition |
| condition_value | Value to match for condition |
Key changes from legacy:
message_construct_fieldsis NO LONGER REQUIRED - fields are extracted directly from pattern- Special placeholders work in header, footer, AND pattern
- Must pass SchemaContext (sc) for context variable resolution
Special Placeholders (NEW in Modern)
Special placeholders use the syntax {$operation:field} and are resolved BEFORE object iteration. They provide aggregation and cross-transform capabilities.
| PLACEHOLDER | DESCRIPTION | EXAMPLE |
|---|---|---|
{$counter} | Total count of objects | {$counter} → “5” (for 5 objects) |
{$enumerator:field} | Comma-separated values of field | {$enumerator:item_no} → “A1, A2, A3” |
{$enumerator_newline:field} | Newline-separated values | {$enumerator_newline:name} → “Widget\nGadget\nTool” |
{$unique_enumerator:field} | Unique comma-separated values | {$unique_enumerator:category} → “Electronics, Tools” |
{$summator:field} | Sum of numeric field | {$summator:quantity} → “150” (sum of all quantities) |
{$context_variable:var_name} | Value from SchemaContext | {$context_variable:user_id} → retrieves sc.contextVariables[‘user_id’] |
Where they work: header, footer, message_construct_pattern
Use cases:
- Headers showing totals:
"Processing {$counter} orders" - Footers with sums:
"Total amount: ${$summator:price}" - Listing all values:
"Items: {$enumerator:item_name}" - Cross-transform data:
"User {$context_variable:username} completed {$counter} tasks"
Destination Types
set_main_message
REPLACES SchemaContext.mainMessage entirely (overwrites existing content).
{
"destination_type": "set_main_message",
"message_construct_pattern": "System initialized with {$counter} records"
}
join_main_message_new_line / join_main_message_empty_line
APPENDS to SchemaContext.mainMessage with newline or empty line separator.
{
"destination_type": "join_main_message_new_line",
"header": "Orders ({$counter}):",
"message_construct_pattern": "- {order_id}: {customer_name}"
}
obj_field
Writes message to specified field in each object.
{
"destination_type": "obj_field",
"field_name_if_target_in_obj": "notification",
"message_construct_pattern": "Item {name}: ${price}"
}
Each object gets new field with formatted message.
context_variable
Stores message in SchemaContext variable for use by other transforms.
Common Use Cases (Modern Implementation)
Use Case 1: Order Confirmation with Aggregations
{
"destination_type": "join_main_message_new_line",
"header": "✅ ORDER CONFIRMED ({$counter} items)",
"message_construct_pattern": "- {item_name} x{quantity}",
"footer": "Total: ${$summator:total_price}\nThank you!"
}
Input:
[
{item_name: "Widget", quantity: 2, total_price: 50},
{item_name: "Gadget", quantity: 1, total_price: 100}
]
Output (mainMessage):
✅ ORDER CONFIRMED (2 items)
- Widget x2
- Gadget x1
Total: $150
Thank you!
No message_construct_fields needed! Fields extracted directly from pattern.
Use Case 2: Summary Report with Special Placeholders
{
"destination_type": "set_main_message",
"header": "📦 SHIPMENT REPORT",
"message_construct_pattern": "Tracking: {tracking_no}\nDestination: {city}",
"footer": "Total packages: {$counter}\nCities: {$unique_enumerator:city}"
}
Input:
[
{tracking_no: "TRK001", city: "New York"},
{tracking_no: "TRK002", city: "Los Angeles"},
{tracking_no: "TRK003", city: "New York"}
]
Output:
📦 SHIPMENT REPORT
Tracking: TRK001
Destination: New York
Tracking: TRK002
Destination: Los Angeles
Tracking: TRK003
Destination: New York
Total packages: 3
Cities: New York, Los Angeles
Use Case 3: Context Variable Integration
{
"destination_type": "join_main_message_empty_line",
"header": "Processing for user: {$context_variable:username}",
"message_construct_pattern": "Task {task_id}: {task_name}",
"footer": "Completed {$counter} tasks"
}
Prerequisites: SchemaContext must have contextVariables['username'] set by previous transform.
Output: Uses username from context and counts current objects.
Use Case 4: Static Message (No Object Iteration)
{
"destination_type": "set_main_message",
"message_construct_pattern": "System initialized. Found {$counter} records from {$context_variable:source_file}"
}
Use case: When destination_type includes “set_main_message”, the pattern can be used without iterating objects. Special placeholders still work.
Use Case 5: Newline-Separated Enumeration
{
"destination_type": "set_main_message",
"header": "Pending Items:",
"message_construct_pattern": "{$enumerator_newline:item_no}"
}
Input: [{item_no: "A1"}, {item_no: "A2"}, {item_no: "A3"}]
Output:
Pending Items:
A1
A2
A3
Use Case 6: Per-Object Field with Aggregations
{
"destination_type": "obj_field",
"field_name_if_target_in_obj": "summary",
"message_construct_pattern": "{name} (${price}) - Part of {$counter} total items"
}
Input: {name: "Laptop", price: 999}
Output: Object gets summary: "Laptop ($999) - Part of 5 total items" (if 5 objects total)
Best Practices (Modern)
Do’s ✅
- Use special placeholders for aggregations instead of manual counting
- Reference fields directly in pattern:
{field_name} - Use
{$context_variable:name}for cross-transform data sharing - Set
if_empty_messagewhen using conditions - Use newlines (\n) in pattern for multi-line formatting
- Pass SchemaContext when using context variables
- Use
{$unique_enumerator:field}to avoid duplicates in lists
Don’ts ❌
- Don’t use
message_construct_fields- it’s deprecated in modern implementation - Don’t forget curly braces:
{field_name}notfield_name - Don’t use special placeholder syntax for regular fields:
{field}not{$field} - Don’t use COMPOSE for data transformation - use TRANSFORM flow
- Don’t try to iterate objects when using
set_main_messagewith only special placeholders
Troubleshooting (Modern)
Special placeholders not working:
- Ensure syntax is exact:
{$counter}not{counter} - For field operations:
{$enumerator:field_name}not{$enumerator field_name} - Verify SchemaContext passed when using
{$context_variable:name} - Check field exists in objects for field-based operations
Regular placeholders not replaced:
- Verify field exists in objects
- Check spelling in pattern (case-sensitive)
- Ensure curly braces:
{field}not{{field}} - Don’t mix with
message_construct_fields- remove that property
Empty message:
- Add
if_empty_messagefor user feedback - Check condition logic
- Verify objects array is not empty
- For
set_main_messagewithout objects, ensure using only special placeholders
Destination not working:
- For obj_field: must specify
field_name_if_target_in_obj - For main_message variations: use correct type (
set_main_message,join_main_message_new_line,join_main_message_empty_line) - Check
destination_typespelling (case-sensitive)
Context variables undefined:
- Ensure SchemaContext parameter passed to function
- Verify variable set in previous transform
- Check variable name spelling:
{$context_variable:var_name}
Technical Implementation Details
The modern composeReadMessageFromStraightObjsModern function uses:
1. replaceSpecialPlaceholders() - Preprocesses header, footer, and pattern
- Regex:
/\{\$([^{}]+)\}/gmatches{$operation:field} - Splits on
:to get operation and field name - Executes operations (counter, enumerator, summator, etc.)
- Returns early-resolved strings before object iteration
2. DataOperatorUtil.replaceFieldsWithValues() - Direct field mapping
- Replaced legacy
replaceArrayValues()which required field arrays - Pattern
{field_name}directly replaced withobj[field_name] - No need for
message_construct_fieldsproperty - Cleaner, more maintainable code
3. SchemaContext Integration
- Required parameter for context variable access
sc.contextVariables[fieldName]provides cross-transform state- Enables complex multi-step workflows with shared data
Key architectural improvement: Separation of concerns - special placeholders resolved once (aggregations), then per-object field replacement happens independently.
Migration from Legacy to Modern
Legacy approach:
{
"message_construct_pattern": "Order {0} total: {1}",
"message_construct_fields": "order_id,total"
}
Modern approach:
{
"message_construct_pattern": "Order {order_id} total: {total}"
}
Steps to migrate:
- Remove
message_construct_fieldsproperty - Replace numeric placeholders
{0},{1}with field names{field_name} - Add special placeholders for aggregations in header/footer
- Ensure SchemaContext passed to function
- Update destination_type if using “main_message” (change to specific type)
Benefits:
- More readable patterns (self-documenting)
- Less configuration (no field lists)
- Fewer errors (typos caught immediately)
- More powerful (special placeholders, context variables)
Summary
COMPOSE (Modern) creates formatted messages from object data:
- Direct field reference with
{field_name}syntax - Special operations:
{$counter},{$enumerator:field},{$summator:field},{$unique_enumerator:field},{$enumerator_newline:field} - SchemaContext integration via
{$context_variable:name} - Header/footer with full placeholder support
- Multiple destination types for different use cases
- Conditional composition
- Empty data handling
When to use: Bot notifications, email composition, report generation, user messages with aggregations, cross-transform data sharing
When NOT to use: Data transformations (TRANSFORM), calculations (TRANSFORM), filtering (FILTER), complex data restructuring
Modern advantages: Simpler configuration, more readable patterns, powerful aggregations, context sharing, maintainable code
Next Steps
👉 GUBUS SCHEMA → - Learn how schemas orchestrate workflows
👉 GUBUS MULTIPANEL → - Learn about panel-based message construction
👉 ← Back to Getting Started - Return to the main documentation