Search

Dark theme | Light theme

October 30, 2017

Awesome Asciidoctor: Use Diagram Block Macro To Include Diagrams

With the Asciidoctor diagram extension we can include diagrams that are written in plain text. For example PlantUML or Ditaa diagrams. The extension offers a block processor where we include the diagram definitions in our Asciidoctor document. But there is also a block macro processor. With the block macro processor we can refer to an external file. The file is processed and the resulting image is in our output document.

In the following example we see how to use the block macro for a Ditaa diagram:

= Diagram Block Macro

There is another way to use the diagram
extension instead of via a block definition. We
can use a block macro to refer to an external
file that has the diagram definition.

The name of the diagram extension is the
block macro name. For example for a Ditaa diagram
we use `ditaa::` and for PlantUML
we use `plantuml::`. This is followed
by a filename of the file that contains the
diagram source.

We can define attributes just like with the
block definition. The first positional attribute
define the filename. Or we use the attribute
name `target` to set the output filename. The second
positional attribute is the file format. Instead
we can use the attribute `format`.

In the next example we use a block macro
to include a Ditaa diagram definition:

// The first positional attribute is the
// file target name (or we use attribute target),
// the second positional attribute is the
// file format (or use attribute format).
// Other attributes can also be defined.
ditaa::sample.ditaa[ditaa-diagram, png, round-corners="true"]

Written with Asciidoctor {asciidoctor-version}.

The contents of sample.ditaa:

+-------------+
| Asciidoctor |-------+
|   diagram   |       |
+-------------+       | PNG out
    ^                 |
    | ditaa in        |
    |                 v
+--------+   +--------+----+    /---------------\
|        |---+ Asciidoctor +--->|               |
|  Text  |   +-------------+    |   Beautiful   |
|Document|   |   !magic!   |    |    Output     |
|     {d}|   |             |    |               |
+---+----+   +-------------+    \---------------/
    :                                   ^
    |          Lots of work             |
    +-----------------------------------+

When we create a HTML document from this Asciidoctor markup we get the following output:

Written with Asciidoctor 1.5.6.1.

October 27, 2017

Groovy Goodness: Download Grab Dependencies In IntelliJ IDEA

In our Groovy scripts we can use the @Grab annotation. With this annotation we define dependencies for our script and they will be automatically downloaded and added to the class path when we run our script. When we use IntelliJ IDEA we can use a nice intention that is part of the IntelliJ IDEA Groovy support to download the dependencies from our IDE editor. IDEA downloads the dependencies and add it to the project module dependencies. This is useful, because this will also adds code completion for the classes in the dependency to our editor.

Let's see this with a little example. We have the following Groovy script in our Groovy project in IntelliJ IDEA:

We place the cursor on the @Grab annotation and enable the intention popup using Alt+Enter (check Keymap Reference for the key combination). We select the option from the popup:

IntelliJ IDEA downloads the dependency. When we look at our Project Structure and at the Module Dependencies we see the downloaded dependency:

In our example we added the Groovy script to a Groovy project in IntelliJ IDEA. When the project is a Gradle or Maven project we must make sure Ivy is part of the compile class path, because otherwise IntelliJ IDEA cannot download the dependencies using this intention. For example we could add to a Gradle build file the following dependency compileOnly 'org.apache.ivy:ivy:2.4.0'.

Written with Groovy 2.4.12 and IntelliJ IDEA 2017.2.5

October 23, 2017

PlantUML Pleasantness: Using The Built-in Icons

PlantUML has a built-in icon set we can use in our diagram definitions. The icon set is open source icon set OpenIconic. We refer to an icon using the syntax <&iconName>.We can use an icon everywhere where we can use text in our diagrams.

In the following example we use different icons in different places:

@startuml

skinparam DefaultFontSize 24
skinparam DefaultTextAlignment center

title <&caret-right> Using icons in PlantUML <&caret-left>

package "Charts <&menu>" as charts {

    file bar [
        Bar

        <size:120><&bar-chart></size>
    ]

    file pie [
        Pie

        <size:42><&pie-chart></size>
    ]

}   

@enduml

When we generate a diagram we see the following output:

To get a list of available icons we can use the statement listopeniconic. This will create a diagram with all the icons and their names we can use:

@startuml
listopeniconic
@enduml

The following image shows all icons that we can use:

Written with PlantUML 1.2017.18.

October 19, 2017

PlantUML Pleasantness: Creating Our Own Sprites

PlantUML supports sprites to be used in diagrams. A sprite is text encoded monochrome graphic we can reference using the syntax <$spriteName>. The sprite is defined using hexadecimal values. We can define a set of hexadecimal values to create our sprite, but we can also generate the correct values from for example a PNG image.

We start with a simple example where we create a small triangle using hexadecimal values:

@startuml
title Triangle sprite

skinparam defaultTextAlignment center

sprite $triangle {
    00000000000
    00000F00000
    0000FBF0000
    0000FBF0000
    000F999F000
    000F999F000
    00F66666F00
    00F66666F00
    0F3333333F0
    0F3333333F0
    0FFFFFFFFF0
    00000000000
}

rectangle Sample [
    Triangle
    ' Reference sprite with name triangle
    <$triangle>
]

@enduml

Let's generate a PNG image from this PlantUML definition and we get the following result:

If we have already an image we want to turn into a sprite we can use the command-line option -encodesprite when we run PlantUML. As option we must specify the gray level for our sprite. We can choose for level 4, 8 or 16. If we place the letter z after the number the sprite definition is compressed. PlantUML will output to the console the sprite definition that we can use in our PlantUML definition file. We get the best result if the original image is grayscale.

We start with the following image:

Next we run PlantUML to encode the image to a sprite definition:

$ plantuml -encodesprite 16 JDrivenIcon.png
sprite $JDrivenIcon [64x64/16] {
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0010000000000000000000000000000000000000000000000000000000000000
0016510000000000000000000000000000000000000000000000000000000000
25007FA520000000000000000000000000000000000000000000000000000000
0C4004FFFB620000000000000000000000000000000000000000000000000000
06F3002CFFFFC731000000000000000000000000000000000000000000000000
03FF30019FCEFFFF841000000000000000000000000000000000000000000000
01FFF30006FEEEDFFFF942000000000000000000000000000000000000000000
00AFEF20004FFDFFEDFFFF952000000000000000000000000000000000000000
007FDFF20002FFDFFFFEDEFFFA62000000000000000000000000000000000000
004FDEFF100019FDFFFFFFEDEFFFC73100000000000000000000000000000000
002FEFEFE100006FEEFFFFFFFEDEFFFF84100000000000000000000000000000
000BFEFEFD100004FFDFFFFFFFFFEEDFFFF94200000000000000000000000000
0007FEFFDFB100003FFDFFFFFFFFFFFFEDFFFF95200000000000000000000000
0004FDFFFDFA000001AFDFFFFFFFFFFFFFFEDEFFFB6300000000000000000000
0002FEFFFFDF90000007FEEFFFFFFFFFFFFFFFEDEFFFC7310000000000000000
0000BFEFFFFDF90000005FFEFFFFFFFFFFFFFFFFFEDEFFFF8410000000000000
00007FEFFFFFDF80000003FFDFFFFFFFFFFFFFFFFFFFEEDFFFF9410000000000
00004FDFFFFFFDF70000001BFDFFFFFFFFFFFFFFFFFFFFFFEDFFFF9520000000
00002FEFFFFFFFDF700000007FEFFFFFFFFFFFFFFFFFFFFEEEDDCDFFFB631000
00000BFFFFFFFFFDF600000005FFEFFFFEEEEDDDDDEEFFFFFFFFFFFFFFFFA520
000007FEFFFFFFFFDF600000003FFDFFFFFFFFFFFFFFFDBA8766543321100000
000004FDFFFFFFFFFDF500000002BFFECA987654332211000000000000000131
000002FEFFFFFFFFFFDF5000000002110000000000000000000000001357CF40
000000CFEFFFFFFFFFFEF4000000000000000000000000000001247AFFFFC100
0000008FEFFFFFFFFFFFEF4000000000000000000000001247AFFFFFFCF80000
0000004FDFFFFFFFFFFFFEF30000000000000000002469EFFFFFEDEDEF600000
0000002FEFFFFFFFFFFFFEFF10000000000001368CFFFFFEDEEFFFDFF4000000
0000000DFEFFFFFFFFFFFEFB000000001358BFFFFFEDEEFFFFFFFDFD20000000
00000008FEFFFFFFFFFFFDF60000007BFFFFFFEDEFFFFFFFFFFFDF9100000000
00000005FDFFFFFFFFFFFDF3000003FFFDDEEFFFFFFFFFFFFFEEF60000000000
00000002FEFFFFFFFFFFFFE1000009FDFFFFFFFFFFFFFFFFFDFF400000000000
00000001EFFFFFFFFFFFEF8000002FEFFFFFFFFFFFFFFFFFDFF3000000000000
000000008FEFFFFFFFFFDF4000007FEFFFFFFFFFFFFFFFFDFA10000000000000
000000005FDFFFFFFFFFEF200001FFFFFFFFFFFFFFFFFEEF7000000000000000
000000002FEFFFFFFFFEFA000005FDFFFFFFFFFFFFFFEFF50000000000000000
000000001EFFFFFFFFFDF600001CFFFFFFFFFFFFFFFDFF300000000000000000
0000000008FEFFFFFFFEF300004FDFFFFFFFFFFFFFDFB1000000000000000000
0000000005FDFFFFFFFFE10000AFEFFFFFFFFFFFFDF800000000000000000000
0000000003FEFFFFFFEF800003FDFFFFFFFFFFFEFF5000000000000000000000
0000000001EFFFFFFFDF400008FEFFFFFFFFFFDFF30000000000000000000000
00000000009FEFFFFFEF20002FEFFFFFFFFFFDFC200000000000000000000000
00000000005FDFFFFEFA00006FDFFFFFFFFFDF91000000000000000000000000
00000000003FEFFFFDF60001EFFFFFFFFFEEF600000000000000000000000000
00000000001FFFFFFDF30004FDFFFFFFFDFF4000000000000000000000000000
000000000009FEFFFFE1000BFEFFFFFFDFF20000000000000000000000000000
000000000006FDFFEF80003FDFFFFFFDFA100000000000000000000000000000
000000000003FDFFDF40009FEFFFFEEF70000000000000000000000000000000
000000000001FFFFEF1002FEFFFFEFF400000000000000000000000000000000
000000000000AFEEFA0007FDFFFDFF3000000000000000000000000000000000
0000000000006FDEF6001FFFFFDFB10000000000000000000000000000000000
0000000000003FDEF3005FDFFEF8000000000000000000000000000000000000
0000000000001FEFD100CFFEFF50000000000000000000000000000000000000
0000000000000AFF8004FEDFF300000000000000000000000000000000000000
00000000000006FF4009FDFC2000000000000000000000000000000000000000
00000000000003FF203FDF910000000000000000000000000000000000000000
00000000000001FA008FF6000000000000000000000000000000000000000000
00000000000000C602FF40000000000000000000000000000000000000000000
000000000000007307F200000000000000000000000000000000000000000000
0000000000000031191000000000000000000000000000000000000000000000
0000000000000000200000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
}

$

The following PlantUML definition contains the result of encoding the image as sprite for grey levels 4, 8, 16, 4z, 8z and 16z:

@startuml
nsprite $JDrivenIcon4 [64x64/4] {
0000000000000000000000000000000000000000000000000000000000000000
071GKDB732100000000000000000000000000000000000000000000000000000
0G_F300muz___VFFB73210000000000000000000000000000000000000000000
00b___F300Gmy-________lVFB73310000000000000000000000000000000000
000v_____F2000Gqy_______________lVFB7321000000000000000000000000
000G-______lB20000Wqz____________________lVFF7332100000000000000
0000K_________l7100000muz__________________________lVFB732100000
00000b___________V7100000Gmy-____---zzzzyyyyyyuuuqqqqqmmmmmmWG00
000000-_____________V7100000000000000000000000001123377BFFVUymG0
000000G________________E0000000000122337BFFVVl__________yqG00000
0000000a_______________G000002Vl____________________zqW000000000
00000000v_____________a000007___________________zum0000000000000
000000000-___________a00001V________________-ymG0000000000000000
000000000G__________v00002l______________yqW00000000000000000000
0000000000a________v00007____________zum000000000000000000000000
00000000000v______-0000R_________-ymG000000000000000000000000000
000000000000-____-G001l_______yqG0000000000000000000000000000000
000000000000G____G007_____zuW00000000000000000000000000000000000
0000000000000a__a00R__-ym000000000000000000000000000000000000000
00000000000000za01lyqG000000000000000000000000000000000000000000
00000000000000000W0000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
}

sprite $JDrivenIcon8 [64x64/8] {
0000000000000000000000000000000000000000000000000000000000000000
0003200000000000000000000000000000000000000000000000000000000000
8M20OwlNF5310000000000000000000000000000000000000000000000000000
0P_F108my_-_tVF7420000000000000000000000000000000000000000000000
00z__F100Ow__-t__-_dNF421000000000000000000000000000000000000000
00Q_s__F0008uyt-____t__-_lVF631000000000000000000000000000000000
008z_____60000Ow__-_______t___-_dN742100000000000000000000000000
000Q_-__t-l500008uzt-____________t__-_dNF53100000000000000000000
0008z_____t-d400000Ow__________________t__-_tVF74200000000000000
0000Q_-_____t-d3000008uzt-____________________t__-_dN74210000000
00008z________t-V3000000Ow___________-----________tttt___lVF5210
00000Q_-________t-V20000008vzt__-zyyxxwwvvvvumeeWOOOGG8880000010
000008-___________t_N200000008000000000000000000000012357FNVsuG0
000000Y_-_____________N100000000000000000012347FNVl__-_-_txW0000
0000008-_______________z00000000012457FVdt_-____t____-t-vG000000
0000000Y_-___________s_P000001Vl_--____t____________txW000000000
00000008______________y000001d_t________________-t_vG00000000000
00000000Y_-_________t_H00000V__________________txe00000000000000
000000008__________-_h00000M_t_____________-__vG0000000000000000
000000000Y_-_________80000L_t____________-tye0000000000000000000
0000000008________-_Y0000C_t__________-__vG000000000000000000000
0000000000Y_-______z8000B_-_________-tym800000000000000000000000
00000000008______s_P0002_-_______-__wO00000000000000000000000000
00000000000Z_-____y0001l-______-tzu80000000000000000000000000000
000000000008_t__t_G001d________wO0000000000000000000000000000000
000000000000h_-__h000V_t__-tzu8000000000000000000000000000000000
0000000000008_t_-800M_t___wW000000000000000000000000000000000000
0000000000000h__Y00K_-t-v800000000000000000000000000000000000000
00000000000008_z80C_txW00000000000000000000000000000000000000000
00000000000000pP0B_vG0000000000000000000000000000000000000000000
00000000000000801W0000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
}

sprite $JDrivenIcon16 [64x64/16] {
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0010000000000000000000000000000000000000000000000000000000000000
0016510000000000000000000000000000000000000000000000000000000000
25007FA520000000000000000000000000000000000000000000000000000000
0C4004FFFB620000000000000000000000000000000000000000000000000000
06F3002CFFFFC731000000000000000000000000000000000000000000000000
03FF30019FCEFFFF841000000000000000000000000000000000000000000000
01FFF30006FEEEDFFFF942000000000000000000000000000000000000000000
00AFEF20004FFDFFEDFFFF952000000000000000000000000000000000000000
007FDFF20002FFDFFFFEDEFFFA62000000000000000000000000000000000000
004FDEFF100019FDFFFFFFEDEFFFC73100000000000000000000000000000000
002FEFEFE100006FEEFFFFFFFEDEFFFF84100000000000000000000000000000
000BFEFEFD100004FFDFFFFFFFFFEEDFFFF94200000000000000000000000000
0007FEFFDFB100003FFDFFFFFFFFFFFFEDFFFF95200000000000000000000000
0004FDFFFDFA000001AFDFFFFFFFFFFFFFFEDEFFFB6300000000000000000000
0002FEFFFFDF90000007FEEFFFFFFFFFFFFFFFEDEFFFC7310000000000000000
0000BFEFFFFDF90000005FFEFFFFFFFFFFFFFFFFFEDEFFFF8410000000000000
00007FEFFFFFDF80000003FFDFFFFFFFFFFFFFFFFFFFEEDFFFF9410000000000
00004FDFFFFFFDF70000001BFDFFFFFFFFFFFFFFFFFFFFFFEDFFFF9520000000
00002FEFFFFFFFDF700000007FEFFFFFFFFFFFFFFFFFFFFEEEDDCDFFFB631000
00000BFFFFFFFFFDF600000005FFEFFFFEEEEDDDDDEEFFFFFFFFFFFFFFFFA520
000007FEFFFFFFFFDF600000003FFDFFFFFFFFFFFFFFFDBA8766543321100000
000004FDFFFFFFFFFDF500000002BFFECA987654332211000000000000000131
000002FEFFFFFFFFFFDF5000000002110000000000000000000000001357CF40
000000CFEFFFFFFFFFFEF4000000000000000000000000000001247AFFFFC100
0000008FEFFFFFFFFFFFEF4000000000000000000000001247AFFFFFFCF80000
0000004FDFFFFFFFFFFFFEF30000000000000000002469EFFFFFEDEDEF600000
0000002FEFFFFFFFFFFFFEFF10000000000001368CFFFFFEDEEFFFDFF4000000
0000000DFEFFFFFFFFFFFEFB000000001358BFFFFFEDEEFFFFFFFDFD20000000
00000008FEFFFFFFFFFFFDF60000007BFFFFFFEDEFFFFFFFFFFFDF9100000000
00000005FDFFFFFFFFFFFDF3000003FFFDDEEFFFFFFFFFFFFFEEF60000000000
00000002FEFFFFFFFFFFFFE1000009FDFFFFFFFFFFFFFFFFFDFF400000000000
00000001EFFFFFFFFFFFEF8000002FEFFFFFFFFFFFFFFFFFDFF3000000000000
000000008FEFFFFFFFFFDF4000007FEFFFFFFFFFFFFFFFFDFA10000000000000
000000005FDFFFFFFFFFEF200001FFFFFFFFFFFFFFFFFEEF7000000000000000
000000002FEFFFFFFFFEFA000005FDFFFFFFFFFFFFFFEFF50000000000000000
000000001EFFFFFFFFFDF600001CFFFFFFFFFFFFFFFDFF300000000000000000
0000000008FEFFFFFFFEF300004FDFFFFFFFFFFFFFDFB1000000000000000000
0000000005FDFFFFFFFFE10000AFEFFFFFFFFFFFFDF800000000000000000000
0000000003FEFFFFFFEF800003FDFFFFFFFFFFFEFF5000000000000000000000
0000000001EFFFFFFFDF400008FEFFFFFFFFFFDFF30000000000000000000000
00000000009FEFFFFFEF20002FEFFFFFFFFFFDFC200000000000000000000000
00000000005FDFFFFEFA00006FDFFFFFFFFFDF91000000000000000000000000
00000000003FEFFFFDF60001EFFFFFFFFFEEF600000000000000000000000000
00000000001FFFFFFDF30004FDFFFFFFFDFF4000000000000000000000000000
000000000009FEFFFFE1000BFEFFFFFFDFF20000000000000000000000000000
000000000006FDFFEF80003FDFFFFFFDFA100000000000000000000000000000
000000000003FDFFDF40009FEFFFFEEF70000000000000000000000000000000
000000000001FFFFEF1002FEFFFFEFF400000000000000000000000000000000
000000000000AFEEFA0007FDFFFDFF3000000000000000000000000000000000
0000000000006FDEF6001FFFFFDFB10000000000000000000000000000000000
0000000000003FDEF3005FDFFEF8000000000000000000000000000000000000
0000000000001FEFD100CFFEFF50000000000000000000000000000000000000
0000000000000AFF8004FEDFF300000000000000000000000000000000000000
00000000000006FF4009FDFC2000000000000000000000000000000000000000
00000000000003FF203FDF910000000000000000000000000000000000000000
00000000000001FA008FF6000000000000000000000000000000000000000000
00000000000000C602FF40000000000000000000000000000000000000000000
000000000000007307F200000000000000000000000000000000000000000000
0000000000000031191000000000000000000000000000000000000000000000
0000000000000000200000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
}

sprite $JDrivenIcon4z [64x64/4z] {
vPVHbi8m24IPuV__sJLD3JKa1VhaMOwFpWN6O8Z8pmVmIFttKTOHsW1QHa0RfKK9eGt0Wr0eGmy0L6i8Je0Sekbac91577d5cd06eWQS0EXt1ChW5d2Fw1hA
R488SUQak2RS8_GAWAu2MmC20ByCS_HaOy8IWRb2QWRXj0ZLE88LW470DTbJa7Z7sW0Be954HY0tDi3uPdBk08wHoGtkAJM7nJFxHZhbq1i0k9-5wOUoKkym
haReIiTgc6Lx0Zxu-1oxUiR_1jpwVFd-FyHaGAXrpu1rUaH6WClq28qGLkdlRv7jNHIuHTQNuLvkowLRFYIgXvS-Ueswoq10FWpWx1xZUq0llxn7pCjGTAsZ
qJFL-hn795kt3U2IFhCBZd9BydvVMVUogtJFc6zzD30jP7bzUPbdIuf2wny3q6qiFcPw-PIYdYrz-JNrLZwG7u-vPwz9oh-A5m00
}
sprite $JDrivenIcon8z [64x64/8z] {
rPTHiiCW24L5GFQ_uvUg94emnVJh-T5fft831oIOqhzVc7ymXiCOXJEy5IY7G1OHndSIA8STb4D1oXi9a8z08gaIGjimf0bWzQ19RBd1NI0tysq9_9X0YwJ5
K3_3F7FTNxygX2uHumdDeljohJ1FRjkxCwSBOPwTFAiteq148dVkhExC0bzvWkwYT8CW4Pvi1BB8IiBbYM84RX0C31T0_HlJ2i83JnZ-nBI6qFSSo_1aHo0R
4yUVi-kWE08EXC9CY9Wp0BW0ge3Y0QucHDt2IHvO09D0WiV6YTTEusZ-rdEHhoFJ3cvGG4s7Oh40wWBNbCuGZ-teLe0kOujKnYekraMRAHuUIpoQzkSR1ydK
Q7TEWvt3cqBt09GUFzJQknSUhdg02P-SKrJyxCoRQ45LjDVV5ZaLY9uvFpN1EVtUwSNd9ZXcBplHvwVNgCbU2OP-1m3UuvUXtm6OfhGgcXK0Qh_t3dKosQDu
w1O0t5jIWU0SeDd3wRXHV0xesGiLZGUWz66eF9-N1m1JyYBana6gPG-kDo9lJ995iqUReOy0fkHjJQEisKET9FVcOTIslXtw003Ev57QNVsTLlP3Fm6Gpf1l
RXDOdqgnyxAwIq3mlBW0kDOElZHF1Jy-lB_F7JCPokkhN1ruAFsq8FslzGS0
}

sprite $JDrivenIcon16z [64x64/16z] {
rPVPTeKW34MP11dp_tzRvacqUEz3L-bBL-ivafq40q9_lV2tUdvyOK4EX0IeWtngO1X2300q_yo20_MxCDu1ZA3l8w4G3B04uuB7oLvQO8W6VX_EEHii97iL
YG87GU0XU7csUC5J15bud4I3O14YKRiy6OJdGzGUGZGe5diyYT-__y4eGu3UOeUdZWOMemgXMEpn55xjLHhdT3O3sEB9eiA2IlbKeq69H7Fw020QoBeV1okB
EvvwCZW0PeD7dY8_Rk6iDMtXkYfFl08G3KGkQhqqkE59wdQhGTtJPGlEMcCJJzm0G3NWQ8JWeiIl2zHswdGlQmOB25QhKt1-C4e9nWi0qU0eOVbjEQEabqJ5
84cOSWYa3xOPeAkYanx202k7O6_Wwfzlf8G95S-xwduEjEyDgjIBIx7C0AB1ejO9uzALKlJ5o9SLaFM0ns3vQOhKHKRJ5kqKWEuudRgN9iHswkSHG2q2eOVc
gV-MSrwE0O3DyTFm-rnuRWnz3I3voqKNNK9FOOxXdolJ8b-UGpq0cz-mw45_ogrBiWUGlaVnopPtR-tl1m0kdwJ7vI25rXeF07BsiDaCVGQGgtUgwVg5k5dT
RbFsr4ZFzlsod2FSa3qwlFufz0b0pjuvdYbFeQDSRAxBtdGacT-7WGmWPuyFHwB4EuEKQzdhqhdibvj1glPUIzwwNnu0nEpf4dwUZVOcILkoHziNKMtFWr6K
ibUsSjylTm1SE3bpyjnsw7MIJDaJUQBvlMXc035NUATVrW1YxnqDvCjHEes1PgTVrW32IraSKkXxkGSGWCkzVbdV9KWi0_dPZGg7x9smro_BonGFjm3sgHm9
wclduwkS1u2n_Eu-Ij3_MZy0
}

rectangle JDrivenIcon4 [
    Gray level 4

    <$JDrivenIcon4>
]

rectangle JDrivenIcon8 [
    Gray level 8

    <$JDrivenIcon8>
]

rectangle JDrivenIcon16 [
    Gray level 16

    <$JDrivenIcon16>
]

rectangle JDrivenIcon4z [
    Gray level 4
    compressed

    <$JDrivenIcon4z>
]

rectangle JDrivenIcon8z [
    Gray level 8
    compressed

    <$JDrivenIcon8z>
]

rectangle JDrivenIcon16z [
    Gray level 16
    compressed

    <$JDrivenIcon16z>
]
@enduml

The PNG image of the previous PlantUML definition looks like this:

Written with PlantUML 1.2017.16.

October 18, 2017

PlantUML Pleasantness: Layout Elements With Hidden Lines

In a previous post we learned how to use a together block to keep elements together. We can also layout elements in a different way: using hidden lines. We define our elements and by using the keyword [hidden] in our line definition the elements are layout as if there was a line, but we don't see it. This gives us great flexibility on how we layout our elements.

In the following sample we first have a PlantUML definition where we rely on the default layout:

@startuml

package "Core Components" {
    [Backend Client] as BackendClient
    [File Reader] as FileReader
    [Content Transform] as ContentTransform
    [Logging]
}

BackendClient ~~> ContentTransform : uses
FileReader ~~> ContentTransform: uses

@enduml

This definition renders the following diagram:

We want the Logging component at the bottom right corner of the Core Components. Using a visible line we would connect the BackendClient to the Logging component. This would place the Logging component beneath the BackendClient, just like we want. So we add a connecting line and we add the [hidden] attribute on our line definition. The Logging component is placed where we want and we don't see the line supporting this:

@startuml

package "Core Components" {
    [Backend Client] as BackendClient
    [File Reader] as FileReader
    [Content Transform] as ContentTransform
    [Logging]
}

BackendClient ~~> ContentTransform : uses
FileReader ~~> ContentTransform: uses

' Layout Logging under BackendClient
BackendClient -[hidden]- Logging

@enduml

When we regenerate the diagram we get the result we want:

Written with PlantUML 1.2017.16.

October 17, 2017

PlantUML Pleasantness: Use Gradients In Diagrams

We have a lot of ways to customize our PlantUML diagrams. We can change the colors and we can even set gradients as color. A gradient has two colors and a direction. The direction of the gradient is set by the separator between the two colors. We can use the following separators to set the gradient direction:

  • /: direction top left to bottom right
  • \: direction bottom left to top right
  • |: direction left to right
  • -: direction top to bottom

In the following example PlantUML diagram definition we apply different gradient directions and different colors:

@startuml

title Gradient

skinparam defaultTextAlignment center
skinparam RectangleFontSize 20

skinparam TitleFontStyle bold
skinparam TitleFontColor #e723e7

' Define two colors for a gradient
' and use - for left to right.
skinparam BackgroundColor  #e2e2e2-#afafaf

' Define two colors for a gradient
' and use / for top left to bottom right.
skinparam RectangleBackgroundColor #ffd200/#8cfcff

rectangle A [
    From top left
    to bottom right
    <&fullscreen-enter>
]

' Use \ for bottom left to top right
rectangle B #ffd200\8cfcff [
    From bottom left
    to top right
    <&resize-both>
]

' Use | for left to right
rectangle C #ffd200|8cfcff [
    From left
    to right
    <&resize-width>
]

' Use | for top to bottom
rectangle D #ffd200-8cfcff [
    From top
    to bottom
    <&resize-height>
]

@enduml

When we convert this PlantUML syntax to a PNG image we get the following result:

Written with PlantUML 1.2017.16.

Groovy Goodness: Make Sure Closeable Objects Are Closed Using withCloseable Method

If a class implements the Closeable interface Groovy adds the withCloseable method to the class. The withCloseable method has a closure as argument. The code in the closure is executed and then the implementation of the close method of the Closeable interface is invoked. The Closeable object is passed as argument to the closure, so we can refer to it inside the closure.

In the following example we have two objects that implement the Closeable interface. By using withCloseable we know for sure the close method is invoked after all the code in the closure is executed:

@Grab(group='org.apache.httpcomponents', module='httpclient', version='4.5.3')
import org.apache.http.impl.client.HttpClientBuilder
import org.apache.http.client.methods.HttpGet

// HttpClientBuilder.create().build() returns a CloseableHttpClient
// that implements the Closeable interface. Therefore we can use
// the withCloseable method to make sure the client is closed
// after the closure code is executed. 
HttpClientBuilder.create().build().withCloseable { client ->
    final request = new HttpGet('http://www.mrhaki.com')

    // The execute method returns a CloseableHttpResponse object
    // that implements the Closeable interface. We can use
    // withCloseable method to make sure the response is closed
    // after the closure code is executed.
    client.execute(request).withCloseable { response ->
        assert response.statusLine.statusCode == 200
    }
}

Written with Groovy 2.4.12.

October 16, 2017

Awesome Asciidoctor: Use Only Block As List Item

When we define a list in Asciidoctor we usually have a list item that is a paragraph. But if we want to have a block as list item we need to add an extra {blank} element to make sure the block is parsed correctly as list item. Because a list item starts with a . or a * at the beginning of a line and a block also is defined on the beginning of the line, we must add the extra {blank} element. Together with the list item continuation (+) we can have a list with blocks.

In the following example we define a numbered list with three listing blocks:

= Simple steps

We need to type the following commands to generated a HTML version
of our Asciidoctor source document:

. {blank}
+
----
$ asciidoctor sample.adoc
$
----
. {blank}
+
----
$ ls
sample.adoc      sample.html
$
----
. {blank}
+
----
$ open sample.html
$
----

When we generate a HTML version we get the following result:

Written with Asciidoctor 1.5.6.1.

October 13, 2017

PlantUML Pleasantness: Align Multi-line Label Text

PlantUML has some features that come from the underlying software to create diagrams. Graphviz is used by PlantUML and we can use Graphviz features in our PlantUML diagrams. For example we can align multi-line text of labels to be either center (default), left or right aligned using a Graphviz feature. When we want to text to be center aligned we simply use the new-line character \n. If we want to have our text left aligned we use \l instead of \n. And to right align the text we use \r.

In the following example we have three labels that are center, left and right aligned:

@startuml

actor "Application User" as User

[Mail server] as Mail <<Mail>> #ffcc66

package "Sample Application" {
    [Controller] <<Spring REST controllers>>
    [Service] <<Spring service>>
}

' Label text is centered
User --> Controller : Types in\ndata to\nsend a message.

' Label text is left aligned
Controller --> Service : User data to\lcreate a\lnew message.

' Label text is right aligned
Service --> Mail : Mail message\robject containing\rall user information.

@enduml

When we run PlantUML with this diagram syntax we get the following diagram:

Written with PlantUML 1.2017.16.

October 12, 2017

Awesome Asciidoctor: Prevent Transformation of URL to Hyperlink

Normally if we type an URL in Asciidoctor that starts with a scheme Asciidoctor knows about, the URL is turned into a hyperlink. The following schemes are recognized by Asciidoctor:

  • http
  • https
  • ftp
  • irc
  • mailto
  • email@email.com

If we want to keep our URL as text and not a link we must prepend our URL with a backslash (\). This way Asciidoctor will not transform the URL to a hyperlink in our output.

In the following example we have URL that is transformed to a link, followed by the same URL but with a backslash (\) before it, that is not transformed:

== URL not as a link

The URL http://www.asciidoctor.org  should
be turned into a hyperlink.

But now the URL \http://www.asciidoctor.org should
just be text and not a hyperlink.

If we transform our document to HTML with Asciidoctor we get the following result:

Written with Asciidoctor 1.5.6.1.

October 5, 2017

Awesome Asciidoctor: Using Tab Separated Data In A Table

In a previous post we learned how to use data in CSV and DSV format. Recently we can also include tab separated values (TSV) in a Asciidoctor table. We must set the table attribute format to the value tsv. The data can be inside the document, but also defined in an external file which we add with the include macro.

In the following example markup we have a table with inline tab separated values. A second table includes an external file with tab delimited values:

= Tables

Using the `format` attribute value `tsv` we can
use tab-delimited data for table data.

== TSV table

[format=tsv, options="header"]
|===
Writing tools Awesomeness
Asciidoctor Oh yeah!
MS Word  No!
|===

== Table with external data

// We have an external file with 
// tab-delimited values.

[%header,format=tsv]
|===
include::tools.tsv[]
|===

When we convert our Asciidoctor markup to HTML we get the following result:

Written with Asciidoctor 1.5.6.1.

October 4, 2017

Awesome Asciidoctor: Grouping Floating Images

With Asciidoctor markup we can position images in our document. We can even float images, so text can next to an image, inside only below or about the image. We can also define multiple images to float, so the images are displayed on the same line next to each other. Any text that follows these images is also displayed next to the images. If we want only to have floating images, but the text starting under the images we can place the images inside an open block and assign the block the role float-group.

In the next example we first define three images that all have roles to float left. In the second part we group these images using the role float-group, so the text will not be displayed next to the images, but under the images:

:imagesdir: images/

= Grouping floats

// Images in open block to indicate 
// they belong together.
--
image::groovy.png[float="left"]
image::gradle.png[float="left"]
// Define float role, instead of attribute.
[.left]
image::grails.png[]
--

The images are all on one line,
but the text is next to the images.

If we want the images to be on one line,
but the text underneath them, we must use
a _float-group_ role, like in the
following example.

[.float-group]
--
image::groovy.png[float="left"]
image::gradle.png[float="left"]
// Define float role, instead of attribute.
[.left]
image::grails.png[]
--

The images above are all grouped together
to appear on one line.

And the text is not next to the images, but
underneath the images, like we wanted.

When we create a HTML document from the Asciidoctor markup we get the following result:

Written with Asciidoctor 1.5.6.1.

October 3, 2017

Awesome Asciidoctor: Using Paragraphs in Lists With List Item Continuation

When we write a list in Asciidoctor we can simply create a list item by starting the line with a dot (.). To create a another list item we simply start a new line with a dot (.). But what if we want to add a list item with multiple paragraphs, or text and a source code block element. We can use the list item continuation (+) to indicate to Asciidoctor we want to keep these together for a single list item.

In the following example we have a list in Asciidoctor markup. The second list item has multiple paragraphs , the third item has an extra admonition block and the fourth item contains a source code block:

:icons: font

== List continuation

When we have a list item that has for example multiple paragraphs,
we can use Asciidoctor's list continuation feature. We place a
`+` symbol between the paragraphs to indicate the paragraphs
belong to a single list item.

=== Sample list

. A very simple first item
. This item consists of two paragraphs.
+
By adding the `+` symbol we indicate this
paragraph also belongs to the second list item.
. We can even add for example an admonition.
+
TIP: Did you know Asciidoctor is awesome?
. A small code example:
+
[source,groovy]
----
println 'Groovy rocks!'
----
. Let's end with a simple list item.

Let's generate this Asciidoctor markup to HTML and we see the following result:

We see how the extra paragraph, admonition and source code are part of a single list item.

Written with Asciidoctor 1.5.6.1.

October 2, 2017

Spocklight: Reuse Variables In Data Providers

Writing a parameterized specification in Spock is easy. We need to add the where: block and use data providers to specify different values. For each set of values from the data providers our specifications is run, so we can test for example very effectively multiple input arguments for a method and the expected outcome. A data provider can be anything that implements the Iterable interface. Spock also adds support for a data table. In the data table we define columns for each variable and in the rows values for each variable. Since Spock 1.1 we can reuse the value of the variables inside the data provider or data table. The value of the variable can only be reused in variables that are defined after the variable we want to reuse is defined.

In the following example we have two feature methods, one uses a data provider and one a data table. The variable sentence is defined after the variable search, so we can use the search variable value in the definition of the sentence variable.

package mrhaki

import spock.lang.Specification
import spock.lang.Unroll

class CountOccurrencesSpec extends Specification {

    @Unroll('#sentence should have #count occurrences of #search (using data table)')
    void 'count occurrences of text using data table'() {
        expect:
        sentence.count(search) == count

        where:
        search  | sentence                                                  || count
        'ABC'   | "A simple $search"                                        || 1
        'Spock' | "Don't confuse $search framework, with the other $search" || 2
    }

    @Unroll('#sentence should have #count occurrences of #search (using data provider)')
    void 'count occurrences of text using data provider'() {
        expect:
        sentence.count(search) == count

        where:
        search << ['ABC', 'Spock']
        sentence << ["A simple $search", "Don't confuse $search framework, with the other $search"]
        count << [1, 2]
    }
}

When we run the specification the feature methods will pass and in we see in the @Unroll descriptions that the sentence variable uses the value of search:

Written with Spock 1.1-groovy-2.4.