Skip to content

Commit

Permalink
yaml package documentation was amended to describe how to loop over p…
Browse files Browse the repository at this point in the history
…arts of the yaml structure
  • Loading branch information
verhas committed Oct 9, 2024
1 parent bf43659 commit c96d646
Show file tree
Hide file tree
Showing 12 changed files with 374 additions and 34 deletions.
2 changes: 1 addition & 1 deletion README.jrf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# This is a Jamal reference file containing serialized base64 encoded macros
# Created: 2024-10-07 21:54:28 +0200
# Created: 2024-10-08 20:08:24 +0200
# id|openStr|closeStr|verbatim|tailParameter|pure|content|parameters
# TOC
VE9D|eyU=|JX0=|0|0|0|Ci4gPDxJbnN0YWxsYXRpb24+PgouIDw8R1M+PgouIDw8Q29uZmlndXJhdGlvbj4+Ci4gPDxGZWF0dXJlcz4+Ci4gPDxDb250cmlidXRpbmc+PgouIDw8RG9jdW1lbnRhdGlvbj4+Ci4gPDxMaWNlbnNlPj4KLiA8PENoYW5nZWxvZz4+Ci4gPDxSb2FkbWFwPj4KLiA8PFN1cHBvcnQ+PgouIDw8RkFRPj4KLiA8PE1haW50ZW5hbmNlPj4=|
6 changes: 5 additions & 1 deletion jamal-cmd/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,8 @@ See also the option `-from`.

* `-jamalize` will install Jamal in the `.asciidoctor/lib` directory.
It version of the installed Jamal is the same as the one installing it.
It can also install a different version if the option `-version=xxx` is specifying a specific versions.
It can also install a different version if the option `-version=xxx` is specifying a specific versions.




38 changes: 34 additions & 4 deletions jamal-cmd/src/main/java/javax0/jamal/cmd/Jamalizer.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,50 @@ public class Jamalizer {


private static final String version = System.getProperty("jamal.version") == null ? Processor.jamalVersionString() : System.getProperty("jamal.version");
private static final String DOWNLOAD_URL_TEMPLATE = "https://repo.maven.apache.org/maven2/com/javax0/jamal/jamal-asciidoc/%s/jamal-asciidoc-%s-jamal-asciidoc-distribution.zip";

static void jamalize(String version) throws IOException, BadSyntax {
if( version == null ){
if (version == null) {
version = Jamalizer.version;
}
warnAboutSnapshot(version);
createAsccidoctorLibDir();
extratZip(FileTools.getFileBinaryContent(
String.format("https://repo.maven.apache.org/maven2/com/javax0/jamal/jamal-asciidoc/%s/jamal-asciidoc-%s-jamal-asciidoc-distribution.zip", version, version),
extractZip(FileTools.getFileBinaryContent(
String.format(DOWNLOAD_URL_TEMPLATE, version, version),
false,
new javax0.jamal.engine.Processor())
);
}

private static void extratZip(final byte[] zipContent) throws IOException {
private static void warnAboutSnapshot(String version) {
if (version.contains("SNAPSHOT")) {
System.err.println("The version of the Jamalizer is a SNAPSHOT version.\n" +
"This is not recommended for production use.\n" +
"Since the command 'jamalize' downloads Jamal from Maven central using the URL\n\n" +
String.format(DOWNLOAD_URL_TEMPLATE, version, version) +
"\n\nit is possible that the version is not available in the repository yet.\n" +
"Use 'jamal -jamalize version=...' to specify a version that is available in the repository."
);
}
}

/**
* Extracts JAR files from a ZIP archive provided as a byte array.
*
* <p>This method processes the given byte array, which represents the content
* of a ZIP archive. It scans the archive, looking for entries that are JAR files
* (files with a ".jar" extension). For each JAR file found, it writes the content
* to a specified directory on the filesystem.</p>
*
* <p>The output JAR files are extracted to the path specified by the
* concatenation of {@code ASCIIDOCTOR_DIR} and {@code LIB_DIR}, using
* the JAR filename from the ZIP entry.</p>
*
* @param zipContent the byte array containing the ZIP archive content
* @throws IOException if an I/O error occurs while reading the ZIP archive
* or writing the extracted JAR files to the filesystem
*/
private static void extractZip(final byte[] zipContent) throws IOException {
try (final var is = new JarInputStream(new ByteArrayInputStream(zipContent))) {
ZipEntry entry;
while ((entry = is.getNextEntry()) != null) {
Expand Down
2 changes: 1 addition & 1 deletion jamal-snippet/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -3816,7 +3816,7 @@ will result in the output
.output
[source]
----
2024-10-04 10:17:51
2024-10-08 09:13:51
----


Expand Down
Binary file modified jamal-sql/demodb.mv.db
Binary file not shown.
Binary file modified jamal-word/src/test/resources/demoConverted.docx
Binary file not shown.
Binary file modified jamal-word/src/test/resources/includetestConverted.docx
Binary file not shown.
Binary file modified jamal-word/src/test/resources/pictureConverted.docx
Binary file not shown.
Binary file modified jamal-word/src/test/resources/sampleConverted.docx
Binary file not shown.
2 changes: 1 addition & 1 deletion jamal-xls/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ Parameter options (parops) can be defined without parentheses.
The `(` and `)` are optional.)

* `file` (aliases are `in`, `input`, `from`) is the name of the file that contains the XLS workbook.
Technically `file` is also an alias, thus a macro havign the name of `file` will not be considered.
Technically, `file` is also an alias; thus a macro having the name of `file` will not be considered.
This parop must be defined.
* `out` (aliases are `output`, `to`) is the name of the file where the XLS workbook is written.
This parop is optional.
Expand Down
202 changes: 187 additions & 15 deletions jamal-yaml/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ To use this module, you have to add the dependency to your Maven project, as:

Following that, you can use the


. <<define,`yaml:define`>>
. <<ref,`yaml:ref`>>
. <<resolve,`yaml:resolve`>>
Expand All @@ -30,6 +29,8 @@ Following that, you can use the
. <<dump,`yaml:dump`>>
. <<xml,`yaml:xml`>>
. <<output,`yaml:output`>>
. <<looping,Looping over values>>



macros.
Expand Down Expand Up @@ -113,7 +114,7 @@ b: this is c.b

The advantage of using this macro over just writing the Yaml directly to the output is that:

* you can split up the Yaml file into smaller pieces using the <<ref,`yaml:ref`>>, and <<resolve,`yaml:resolve`>> macros,
* you can split up the Yaml file into smaller pieces using the <<ref,`ref`>>, and <<resolve,`resolve`>> macros,

* you can use user-defined macro parameters mixing the yaml content with Jamal macros.

Expand Down Expand Up @@ -260,7 +261,7 @@ The format of the macro is
----


User-defined Yaml macros created using the <<define,`yaml:define`>> or <<import,`yaml:import`>> macros may reference other user-defined Yaml macros.
User-defined Yaml macros created using the <<define,`define`>> or <<import,`import`>> macros may reference other user-defined Yaml macros.
When you invoke the macro `yaml:resolve`, it will replace the references in the Yaml macro content with the content of the Yaml macro it references.
The resolving process is recursive.
If there are any references in the referenced Yaml macro, it will also be resolved.
Expand Down Expand Up @@ -621,7 +622,7 @@ This macro does not test the structure.
It simply tells that the structure went through the resolve process or not.

Usually there is narrow use of this macro.
There is no penalty invoking <<resolve,`yaml:resolve`>> on a structure that was already resolved.
There is no penalty invoking <<resolve,`resolve`>> on a structure that was already resolved.
The macro resolve does not re-run the resolution process for a structure that was already resolved.
Other macros that need resolved structures automatically invoke resolving.

Expand Down Expand Up @@ -650,7 +651,7 @@ For more information about the OGNL language visit the web site https://commons.

When getting a value out of a Yaml user defined macro the macro will automatically be resolved.
The resolution can be cloning or in-place.
To control the resolution process the same options can be used as for the <<resolve,`yaml:resolve`>> macro.
To control the resolution process the same options can be used as for the <<resolve,`resolve`>> macro.

===== Examples

Expand Down Expand Up @@ -682,9 +683,10 @@ deep h
=== vii. `yaml:set`


The macro `yaml:set` can define a yaml user defined macro from an already existing yaml macro.
It is similar to `yaml:define` but this macro does not parse a text and interpret it as yaml formatted text.
Instead, it uses an already defined yaml user defined macro and uses some part of it, and it assignes that to a new user defined macro name.
The macro `yaml:set` can define a user-defined YAML macro from an already existing YAML macro.
It is similar to `yaml:define` but this macro does not parse a text and does not interpret it as YAML formatted text.
Instead, it uses an already defined YAML user-defined macro and uses some part of it.
It assigns the specific part to a new user-defined macro.

The syntax of the macro is

Expand All @@ -698,11 +700,11 @@ The syntax of the macro is

** `yamlResolveClone` (alias `clone`) to clone
** `yamlResolveCopy` (alias `copy`) to copy resolve
** `yamlDataSource` (alias `from`) the name of the user defined macro which is the source of the data
** `yamlDataSource` (alias `from`) the name of the user-defined macro which is the source of the data

If the `from` value is missing then the macro interprets the `OGNL` expression star as the name of the macro.
It should start as `/xxxx.`.
The identifier `xxxx` between the starting `/` and the `.` is used as the name of the macro, from which the data is to be fetched.
If the `from` value is missing then the macro interprets the `OGNL` expression start as the name of the macro.
In this case the first character of the expression has to be `/`.
It essentially will start with `/xxxx.` where `xxxx` is the name of the macro.

The `macroName` is the name of the macro to assign the new object value to.

Expand Down Expand Up @@ -737,6 +739,43 @@ deep h
----


When calling set the new macro will refer to the same object as the original one.
It does not create a copy of the object pointed by the OGNL expression.
This is demonstrated with the followinf example:

.Jamal source
[source]
----
{@yaml:define a=
a: alma
b:
c: 3
d:
- 1
- 2
- q:
h: shallow dust}
{@yaml:set s=/a.b.d[2].q}
{@yaml:add to=a.b.d[2].q key=z
deep water
}
{s}
----

We set the reference to the object `q`.
Then we add a new key-value pair to the object `q` using the macro `a`.
In the output the result is modified in the macro `s` as well.

.output
[source]
----
h: shallow dust
z: deep water
----


It is the behavior even of the `clone` or `copy` options are used.
These options are controlling the resolution process and not the object creation.

[[add]]
=== viii. `yaml:add`
Expand Down Expand Up @@ -781,7 +820,7 @@ When this option is specified it is an error to specify any `key` since in this

==== Examples

===== Adding a value to the top level Map
===== Adding a value to the top-level Map

This example adds a new value to the root of the Yaml structure.

Expand Down Expand Up @@ -1848,13 +1887,146 @@ The format of the macro is:
{@yaml:output yamlMacro}
----

Here the `yamlMacro` is the name of a Yaml macro to be rendered as the final output of the Jamal processing.
Here the `yamlMacro` is the name of a YAML macro to be rendered as the final output of the Jamal processing.
It has to be defined at the end of the processing.
It also means that this macro has to be on the top level in the macro hierarchy.
In other words, it has to be a global macro.

When this macro is used, the output of the Jamal processing will be the Yaml formatted structure of the data held in the macro `yamlMacro`.
If this macro contained references and was not yet resolved, then it will be resolved.
Since this is the last step processing the whole Jamal structure following the entire process, usually there is no need for cloning.
Since this is the last step to processing the whole Jamal structure following the entire process, usually there is no need for cloning.
If for any reason there is need for cloning then the `clone` option may be used on the command.
The command also supports the `copy` option.

[[Looping]]
==== xiii. Looping over values

There is no special macro to support looping over values in a YAML structure.
It can be done using the core `for` macro.

The core `for` macro has two forms.
The conventional is when the macro loops over strings provided in the macro invocation.
In this case the loop the `in` keyword separates the variables from the value list.

The other case is when the looping is over some Java object.
In this case the `from` keyword separates the variable from the macro holding the object.
These macros must be so-called "object holder" macros, and the user-defined YAML macros are such.

In the following example we have a YAML structure defined in the macro `games`.
This structure has two lists, `PCgames` and `ConsoleGames`.
We want to loop over the `PCgames` list.

.Jamal source
[source]
----
{@yaml:define games=
PCgames:
- "Doom"
- "Quake"
- "Unreal"
ConsoleGames:
- "Mario"
- "Zelda"
- "Metroid"
}
{@yaml:set cGames=/games.PCgames}
{@for game from cGames=
- game}
----

The output is the list of the games under the `PCgames` key.

.output
[source]
----
- Doom
- Quake
- Unreal
----


Note that we had to use the macro `yaml:set` to set the `cGames` macro to the `PCgames` list.
That is because the `for` macro can only loop over an object provided by a macro.
Writing

.Jamal source
[source]
----
{@yaml:define games=
PCgames:
- "Doom"
- "Quake"
- "Unreal"
ConsoleGames:
- "Mario"
- "Zelda"
- "Metroid"
}
{@yaml:set cGames=/games.PCgames}
{@try! {#for game from {@yaml:get /games.PCgames}
- game}}
----

will result in the error message:

.output
[source]
----
The user defined macro '[Doom,' does not exist or cannot be used as data source for a 'for' loop.
----


because the `{@yaml:get /games.PCgames}` macro evaluated is a string and not a macro.
As you can see from the error message, the result of the macro is the list of the game names.

You can iterate through lists and maps in the same way.
When you iterate through maps you will get the keys:

.Jamal source
[source]
----
{@yaml:define cGames=
first: "Doom"
second: "Quake"
third: "Unreal"
}
{@for game from cGames=
- game}
----

The output is the list of the games under the `PCgames` key.

.output
[source]
----
- first
- second
- third
----


If you want to go deeper, you can use the loop variable as a key in the map.
For example, you can loop through the list of games by key:

.Jamal source
[source]
----
cGames is still defined from the prior example
{!@for game from cGames=
{@yaml:get (from=cGames) game}}
----

The output is the list of the games under `cGames`

.output
[source]
----
cGames is still defined from the prior example
Doom
Quake
Unreal
----


For a full-fledged example how to document an OpenAPI stack using Jamal and the YAML macros, visit the documentation at https://github.com/serverless-u/AxsessGard/blob/main/README.adoc.jam.
Loading

0 comments on commit c96d646

Please sign in to comment.