This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 2be41c31ca66 CAMEL-24874: camel-core-languages - the simple init block
operators := and ~:= are in the catalog (#26680)
2be41c31ca66 is described below
commit 2be41c31ca661e5a58968ef05a04bd5c5b3a30b2
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Sep 21 21:12:31 2026 +0200
CAMEL-24874: camel-core-languages - the simple init block operators := and
~:= are in the catalog (#26680)
The init block of a simple expression ($init{ ... }init$) assigns local
variables with := and declares local custom functions with ~:= (usually a
~> chain), but the catalog had no operator entry for either, so nothing
that reads simple.json knew they existed: camel_catalog_doc /
tui_catalog_doc
(operator syntax list and optionsFilter), the generated JS validator, the
MCP server and any agent fetching the catalog. The docs were the only
source, and docPage=advanced is the whole simple-advanced page.
Both operators are declared in SimpleOperatorConstants (kind init) with
their syntax and complete examples, listed in the operators doc table with
a link to the init block sections, and the unfinished sentence and typos
of the "Init Blocks with custom functions" section are fixed.
Signed-off-by: Claus Ibsen <[email protected]>
Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
---
.../apache/camel/catalog/docs/simple-advanced.adoc | 4 +---
.../apache/camel/catalog/docs/simple-operators.adoc | 2 ++
.../org/apache/camel/catalog/languages/simple.json | 4 +++-
.../camel/catalog/simple/camel-simple-validator.js | 4 +++-
.../org/apache/camel/language/simple/simple.json | 4 +++-
.../modules/languages/pages/simple-advanced.adoc | 4 +---
.../modules/languages/pages/simple-operators.adoc | 2 ++
.../language/simple/SimpleOperatorConstants.java | 20 ++++++++++++++++++++
.../dsl/jbang/core/commands/ai/CatalogDocsTest.java | 15 +++++++++++++++
9 files changed, 50 insertions(+), 9 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/simple-advanced.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/simple-advanced.adoc
index 58c5c2057aed..8925f33c254f 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/simple-advanced.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/simple-advanced.adoc
@@ -160,9 +160,7 @@ and then refer to the file such as
`resource:classpath:mymapping.txt` where the
=== Init Blocks with custom functions
-You can also declare custom functions using
-
-Inside the init block, it is a lso possible to define custom functions in the
syntax `$nane ~:= <statement>;` where you can then use simple language to
declare
+Inside the init block, it is also possible to define custom functions in the
syntax `$name ~:= <statement>;` where you can then use simple language to
declare
the structure of the function. Then you can later use these custom functions
in your simple language expressions.
For example to create a function that can cleanup a `String` value:
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/simple-operators.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/simple-operators.adoc
index da22008cd82e..e5354e79f7db 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/simple-operators.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/simple-operators.adoc
@@ -115,6 +115,8 @@ The following other operators can be used:
|`?:` | The elvis operator returns the left-hand side if it has an effective
Boolean value of true, otherwise it returns the right-hand side. This is useful
for providing fallback values when an expression may evaluate to a value with
an effective Boolean value of false (such as `null`, `false`, `0`, empty/blank
string).
|`~>` | The chain operator is used in the situations where multiple nested
functions need to be applied to a value, while making it easy to read. The
value on the left-hand-side is evaluated, and set as the new message body
before evaluating the right-hand-side function. This concept is similar to the
xref:eips:pipeline-eip.adoc[Pipeline EIP].
|`?~>` | The null-safe chain operator, where the chain will not continue when
a function returned `null`.
+|`:=` | Assigns a local variable in the init block at the top of an expression
(`$init{ $minAge := 18; }init$`), see
xref:simple-advanced.adoc#_init_blocks[Init Blocks].
+|`~:=` | Declares a local custom function in the init block, usually as a
chain (`$init{ $clean ~:= ${trim()} ~> ${uppercase()}; }init$`), called as
`${clean()}`, see
xref:simple-advanced.adoc#_init_blocks_with_custom_functions[Init Blocks with
custom functions].
|====
==== Ternary Operator
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/simple.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/simple.json
index 4e7580acfd4e..e2b8a01cf9be 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/simple.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/simple.json
@@ -213,6 +213,8 @@
"? :": { "index": 30, "kind": "operator", "displayName": "Ternary",
"label": "ternary", "required": false, "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "description": "Ternary conditional
operator. Evaluates the predicate and returns trueValue if true, falseValue if
false. Requires spaces around both ? and : tokens.", "operatorKind": "ternary",
"operatorSyntax": "predicate ? trueValue : falseValue", "precedence": 25,
"examples": [ "${header.foo} > [...]
"~>": { "index": 31, "kind": "operator", "displayName": "Chain", "label":
"chain", "required": false, "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "Pipes the result of the
left expression as input body to the right expression. Use $param in the right
expression to reference the piped value explicitly.", "operatorKind": "chain",
"operatorSyntax": "expr ~> expr", "precedence": 5, "examples": [ "${trim()} ~>
${uppercase()}", "${substrin [...]
"?~>": { "index": 32, "kind": "operator", "displayName": "Chain null
safe", "label": "chain", "required": false, "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false, "description":
"Null-safe chain operator. Same as ~> but stops chaining and returns null if
the left expression evaluates to null.", "operatorKind": "chain",
"operatorSyntax": "expr ?~> expr", "precedence": 5, "examples": [
"${header.name} ?~> ${trim()} ?~> ${uppercase()}" ] },
- "?:": { "index": 33, "kind": "operator", "displayName": "Elvis", "label":
"other", "required": false, "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "Elvis operator
(null-coalescing). Returns the left operand if it is not null\/empty, otherwise
returns the right operand as a fallback value.", "operatorKind": "other",
"operatorSyntax": "expr ?: defaultValue", "precedence": 20, "examples": [
"${header.username} ?: 'Guest'", "${body} ?: $ [...]
+ "?:": { "index": 33, "kind": "operator", "displayName": "Elvis", "label":
"other", "required": false, "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "Elvis operator
(null-coalescing). Returns the left operand if it is not null\/empty, otherwise
returns the right operand as a fallback value.", "operatorKind": "other",
"operatorSyntax": "expr ?: defaultValue", "precedence": 20, "examples": [
"${header.username} ?: 'Guest'", "${body} ?: $ [...]
+ ":=": { "index": 34, "kind": "operator", "displayName": "Init variable",
"label": "init", "required": false, "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "Assigns a local variable
in the init block at the top of an expression ($init{ ... }init$), computed
once and used in the expression as ${name}. Each statement ends with a
semicolon and a new line.", "operatorKind": "init", "operatorSyntax": "$name :=
expr;", "precedence": 1, "exam [...]
+ "~:=": { "index": 35, "kind": "operator", "displayName": "Init function",
"label": "init", "required": false, "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "Declares a local custom
function in the init block at the top of an expression ($init{ ... }init$),
usually as a chain of functions on the input, called as ${name()} (the message
body as input), ${name(exp)} (an explicit input) or from another function as
${function(name)}. Each s [...]
}
}
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/simple/camel-simple-validator.js
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/simple/camel-simple-validator.js
index fedf540459e7..091e88d267c2 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/simple/camel-simple-validator.js
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/simple/camel-simple-validator.js
@@ -192,7 +192,9 @@ const OPERATORS = {
'? :': { kind: 'ternary', description: 'Ternary conditional operator.
Evaluates the predicate and returns trueValue if true, falseValue if false.
Requires spaces around both ? and : tokens.' },
'~>': { kind: 'chain', description: 'Pipes the result of the left expression
as input body to the right expression. Use $param in the right expression to
reference the piped value explicitly.' },
'?~>': { kind: 'chain', description: 'Null-safe chain operator. Same as ~>
but stops chaining and returns null if the left expression evaluates to null.'
},
- '?:': { kind: 'other', description: 'Elvis operator (null-coalescing).
Returns the left operand if it is not null/empty, otherwise returns the right
operand as a fallback value.' }
+ '?:': { kind: 'other', description: 'Elvis operator (null-coalescing).
Returns the left operand if it is not null/empty, otherwise returns the right
operand as a fallback value.' },
+ ':=': { kind: 'init', description: 'Assigns a local variable in the init
block at the top of an expression ($init{ ... }init$), computed once and used
in the expression as ${name}. Each statement ends with a semicolon and a new
line.' },
+ '~:=': { kind: 'init', description: 'Declares a local custom function in the
init block at the top of an expression ($init{ ... }init$), usually as a chain
of functions on the input, called as ${name()} (the message body as input),
${name(exp)} (an explicit input) or from another function as ${function(name)}.
Each statement ends with a semicolon and a new line.' }
};
diff --git
a/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/simple/simple.json
b/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/simple/simple.json
index 4e7580acfd4e..e2b8a01cf9be 100644
---
a/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/simple/simple.json
+++
b/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/simple/simple.json
@@ -213,6 +213,8 @@
"? :": { "index": 30, "kind": "operator", "displayName": "Ternary",
"label": "ternary", "required": false, "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "description": "Ternary conditional
operator. Evaluates the predicate and returns trueValue if true, falseValue if
false. Requires spaces around both ? and : tokens.", "operatorKind": "ternary",
"operatorSyntax": "predicate ? trueValue : falseValue", "precedence": 25,
"examples": [ "${header.foo} > [...]
"~>": { "index": 31, "kind": "operator", "displayName": "Chain", "label":
"chain", "required": false, "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "Pipes the result of the
left expression as input body to the right expression. Use $param in the right
expression to reference the piped value explicitly.", "operatorKind": "chain",
"operatorSyntax": "expr ~> expr", "precedence": 5, "examples": [ "${trim()} ~>
${uppercase()}", "${substrin [...]
"?~>": { "index": 32, "kind": "operator", "displayName": "Chain null
safe", "label": "chain", "required": false, "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false, "description":
"Null-safe chain operator. Same as ~> but stops chaining and returns null if
the left expression evaluates to null.", "operatorKind": "chain",
"operatorSyntax": "expr ?~> expr", "precedence": 5, "examples": [
"${header.name} ?~> ${trim()} ?~> ${uppercase()}" ] },
- "?:": { "index": 33, "kind": "operator", "displayName": "Elvis", "label":
"other", "required": false, "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "Elvis operator
(null-coalescing). Returns the left operand if it is not null\/empty, otherwise
returns the right operand as a fallback value.", "operatorKind": "other",
"operatorSyntax": "expr ?: defaultValue", "precedence": 20, "examples": [
"${header.username} ?: 'Guest'", "${body} ?: $ [...]
+ "?:": { "index": 33, "kind": "operator", "displayName": "Elvis", "label":
"other", "required": false, "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "Elvis operator
(null-coalescing). Returns the left operand if it is not null\/empty, otherwise
returns the right operand as a fallback value.", "operatorKind": "other",
"operatorSyntax": "expr ?: defaultValue", "precedence": 20, "examples": [
"${header.username} ?: 'Guest'", "${body} ?: $ [...]
+ ":=": { "index": 34, "kind": "operator", "displayName": "Init variable",
"label": "init", "required": false, "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "Assigns a local variable
in the init block at the top of an expression ($init{ ... }init$), computed
once and used in the expression as ${name}. Each statement ends with a
semicolon and a new line.", "operatorKind": "init", "operatorSyntax": "$name :=
expr;", "precedence": 1, "exam [...]
+ "~:=": { "index": 35, "kind": "operator", "displayName": "Init function",
"label": "init", "required": false, "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "Declares a local custom
function in the init block at the top of an expression ($init{ ... }init$),
usually as a chain of functions on the input, called as ${name()} (the message
body as input), ${name(exp)} (an explicit input) or from another function as
${function(name)}. Each s [...]
}
}
diff --git
a/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-advanced.adoc
b/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-advanced.adoc
index 58c5c2057aed..8925f33c254f 100644
---
a/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-advanced.adoc
+++
b/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-advanced.adoc
@@ -160,9 +160,7 @@ and then refer to the file such as
`resource:classpath:mymapping.txt` where the
=== Init Blocks with custom functions
-You can also declare custom functions using
-
-Inside the init block, it is a lso possible to define custom functions in the
syntax `$nane ~:= <statement>;` where you can then use simple language to
declare
+Inside the init block, it is also possible to define custom functions in the
syntax `$name ~:= <statement>;` where you can then use simple language to
declare
the structure of the function. Then you can later use these custom functions
in your simple language expressions.
For example to create a function that can cleanup a `String` value:
diff --git
a/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-operators.adoc
b/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-operators.adoc
index da22008cd82e..e5354e79f7db 100644
---
a/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-operators.adoc
+++
b/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-operators.adoc
@@ -115,6 +115,8 @@ The following other operators can be used:
|`?:` | The elvis operator returns the left-hand side if it has an effective
Boolean value of true, otherwise it returns the right-hand side. This is useful
for providing fallback values when an expression may evaluate to a value with
an effective Boolean value of false (such as `null`, `false`, `0`, empty/blank
string).
|`~>` | The chain operator is used in the situations where multiple nested
functions need to be applied to a value, while making it easy to read. The
value on the left-hand-side is evaluated, and set as the new message body
before evaluating the right-hand-side function. This concept is similar to the
xref:eips:pipeline-eip.adoc[Pipeline EIP].
|`?~>` | The null-safe chain operator, where the chain will not continue when
a function returned `null`.
+|`:=` | Assigns a local variable in the init block at the top of an expression
(`$init{ $minAge := 18; }init$`), see
xref:simple-advanced.adoc#_init_blocks[Init Blocks].
+|`~:=` | Declares a local custom function in the init block, usually as a
chain (`$init{ $clean ~:= ${trim()} ~> ${uppercase()}; }init$`), called as
`${clean()}`, see
xref:simple-advanced.adoc#_init_blocks_with_custom_functions[Init Blocks with
custom functions].
|====
==== Ternary Operator
diff --git
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleOperatorConstants.java
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleOperatorConstants.java
index 658878ea5778..6b1c8318dbf7 100644
---
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleOperatorConstants.java
+++
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleOperatorConstants.java
@@ -239,6 +239,26 @@ public final class SimpleOperatorConstants {
annotations = { "kind=other", "syntax=expr ?: defaultValue",
"precedence=20" })
public static final String ELVIS = "?:";
+ // --- Init block operators (only inside $init{ ... }init$ at the start of
an expression) ---
+
+ @Metadata(description = "Assigns a local variable in the init block at the
top of an expression ($init{ ... }init$),"
+ + " computed once and used in the expression as
${name}. Each statement ends with a semicolon and a new line.",
+ label = "init",
+ examples = { "$init{\n $minAge := 18;\n $greeting := 'Hello
${body}';\n}init$\n${greeting} (${minAge})" },
+ annotations = { "kind=init", "syntax=$name := expr;",
"precedence=1" })
+ public static final String INIT_VARIABLE = ":=";
+
+ @Metadata(description = "Declares a local custom function in the init
block at the top of an expression ($init{ ... }init$),"
+ + " usually as a chain of functions on the input,
called as ${name()} (the message body as input),"
+ + " ${name(exp)} (an explicit input) or from
another function as ${function(name)}."
+ + " Each statement ends with a semicolon and a new
line.",
+ label = "init",
+ examples = {
+ "$init{\n $cleanName ~:= ${trim()} ~>
${normalizeWhitespace()} ~> ${uppercase()};\n}init$\nCustomer:
${cleanName(${header.customerName})}",
+ "$init{\n $clean ~:= ${trim()} ~>
${normalizeWhitespace()};\n $count ~:= ${clean()} ~> ${split(' ')} ~>
${size()};\n}init$\nYou said: ${clean()} in ${count()} words" },
+ annotations = { "kind=init", "syntax=$name ~:= expr ~> expr;",
"precedence=1" })
+ public static final String INIT_FUNCTION = "~:=";
+
private SimpleOperatorConstants() {
}
}
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocsTest.java
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocsTest.java
index 97cbf8277fbe..a715af758295 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocsTest.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocsTest.java
@@ -177,6 +177,21 @@ class CatalogDocsTest {
assertTrue(binary.getInteger("matchedOperators") > 5);
JsonObject eq = (JsonObject)
binary.getCollection("operators").iterator().next();
assertEquals("LHS == RHS", eq.getString("syntax"));
+
+ // CAMEL-24874: the init block operators are in the catalog, so a
model asking about a local function or the
+ // init block gets the syntax and a complete example instead of the
whole advanced page
+ JsonObject init = catalogDoc(Map.of("name", "simple", "kind",
"language", "optionsFilter", "init"));
+ assertEquals("init", init.getString("operatorKind"));
+ assertEquals(2, init.getInteger("matchedOperators"));
+ JsonObject function = init.getCollection("operators").stream()
+ .map(JsonObject.class::cast)
+ .filter(op -> "~:=".equals(op.getString("name")))
+ .findFirst().orElseThrow();
+ assertEquals("$name ~:= expr ~> expr;", function.getString("syntax"));
+ String example = (String)
function.getCollection("examples").iterator().next();
+ assertTrue(example.startsWith("$init{\n") &&
example.contains("}init$\n"), example);
+ JsonObject summary = catalogDoc(Map.of("name", "simple", "kind",
"language"));
+ assertTrue(summary.getCollection("operatorSyntax").contains("$name :=
expr;"));
}
@Test