Skip to content

Add documentation about InlineJavascriptRequirement expressionLib #242

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Oct 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/_includes/cwl/expressions/custom-functions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Capitalize each word passed. Will split the text by spaces.
* For instance, given "hello world", it returns "Hello World".
*
* @param {String} message - The input message.
* @return {String} the message with each word with its initial letter capitalized.
*/
function capitalizeWords (message) {
if (message === undefined || message === null || typeof message !== 'string' || message.trim().length === 0) {
return '';
}
return message
.split(' ')
.map(function (token) {
return token.charAt(0).toUpperCase() + token.slice(1);
})
.join(' ');
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
cwlVersion: v1.2
class: CommandLineTool
requirements:
- class: InlineJavascriptRequirement
expressionLib:
- { $include: custom-functions.js }

baseCommand: echo

inputs:
message:
type: string

arguments: [$( capitalizeWords(inputs.message) )]

outputs: []
34 changes: 34 additions & 0 deletions src/_includes/cwl/expressions/hello-world-expressionlib-inline.cwl
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
cwlVersion: v1.2
class: CommandLineTool
requirements:
- class: InlineJavascriptRequirement
expressionLib:
- |
/**
* Capitalize each word passed. Will split the text by spaces.
* For instance, given "hello world", it returns "Hello World".
*
* @param {String} message - The input message.
* @return {String} the message with each word with its initial letter capitalized.
*/
function capitalizeWords (message) {
if (message === undefined || message === null || typeof message !== 'string' || message.trim().length === 0) {
return '';
}
return message
.split(' ')
.map(function (token) {
return token.charAt(0).toUpperCase() + token.slice(1);
})
.join(' ');
}

baseCommand: echo

inputs:
message:
type: string

arguments: [$( capitalizeWords(inputs.message) )]

outputs: []
27 changes: 27 additions & 0 deletions src/_includes/cwl/expressions/hello-world-expressionlib.cwl
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
cwlVersion: v1.2
class: CommandLineTool
requirements:
- class: InlineJavascriptRequirement
expressionLib:
- { $include: custom-functions.js }
- |
/**
* A merely illustrative example function that uses a function
* from the included custom-functions.js file to create a
* Hello World message.
*
* @param {Object} message - CWL document input message
*/
var createHelloWorldMessage = function (message) {
return capitalizeWords(message);
};

baseCommand: echo

inputs:
message:
type: string

arguments: [$( createHelloWorldMessage(inputs.message) )]

outputs: []
91 changes: 88 additions & 3 deletions src/topics/expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ When manipulating file names, extensions, paths etc, consider whether one of the
[built in `File` properties][file-prop] like `basename`, `nameroot`, `nameext`,
etc, could be used instead.
See the [list of best practices](best-practices.md).
```

```{literalinclude} /_includes/cwl/expressions/expression.cwl
:language: cwl
Expand Down Expand Up @@ -112,7 +113,91 @@ only in certain fields. These are:

[file-prop]: https://www.commonwl.org/v1.0/CommandLineTool.html#File

## Using external libraries and inline JavaScript code with `expressionLib`

The requirement `InlineJavascriptRequirement` supports an `expressionLib` attribute
that allows users to load external JavaScript files, or to provide inline JavaScript
code.

Entries added to the `expressionLib` attribute are parsed with the JavaScript engine
of a CWL runner. This can be used to include external files or to create JavaScript
functions that can be called in other parts of the CWL document.

```{note}

The CWL standards (versions 1.0 through 1.2) [states](https://www.commonwl.org/v1.0/CommandLineTool.html#Expressions)
that the only version of JavaScript valid in CWL expressions is
[ECMAScript 5.1](https://262.ecma-international.org/5.1/). This means that any
code that you include or write in your CWL Document must be compliant with
ECMAScript 5.1.
```

For example, we can use `InlineJavascriptRequirement` and write a JavaScript function
inline in `expressionLib`. That function can then be used in other parts of the
CWL document:

```{literalinclude} /_includes/cwl/expressions/hello-world-expressionlib-inline.cwl
:language: cwl
:caption: "`hello-world-expressionlib-inline.cwl`"
:name: "`hello-world-expressionlib-inline.cwl`"
:emphasize-lines: 5, 14, 32
```

Running this CWL workflow will invoke the JavaScript function and result in
the `echo` command printing the input message with capital initial letters:

```{runcmd} cwltool hello-world-expressionlib-inline.cwl --message "hello world"
:caption: "Running `hello-world-expressionlib-inline.cwl`."
:name: running-hell-world-expressionlib-inline-cwl
:working-directory: src/_includes/cwl/expressions/
```

Let's move the `capitalizeWords` function to an external file, `custom-functions.js`, and
import it in our CWL document:

```{literalinclude} /_includes/cwl/expressions/custom-functions.js
:language: javascript
:caption: "`custom-functions.js`"
:name: "`custom-functions.js`"
```

```{literalinclude} /_includes/cwl/expressions/hello-world-expressionlib-external.cwl
:language: cwl
:caption: "`hello-world-expressionlib-external.cwl`"
:name: "`hello-world-expressionlib-external.cwl`"
:emphasize-lines: 5-6, 14
```

The `custom-functions.js` file is included in the CWL document with the `$include: custom-functions.js`
statement. That makes the functions and variables available to be used in other parts of
the CWL document.

```{runcmd} cwltool hello-world-expressionlib-external.cwl --message "hello world"
:caption: "Running `hello-world-expressionlib-external.cwl`."
:name: running-hell-world-expressionlib-external-cwl
:working-directory: src/_includes/cwl/expressions/
```

% TODO
% - (maybe not before other concepts? move this to after inputs/outputs/etc?)
% - External libraries and expressionLib - https://github.com/common-workflow-language/user_guide/issues/126
Finally, note that you can have both inline and external JavaScript code in your
CWL document. In this final example we have added another entry to the `expressionLib`
attribute with the new function `createHelloWorldMessage`, that calls the `capitalizeWords`
function from the external file `custom-functions.js`.

```{literalinclude} /_includes/cwl/expressions/hello-world-expressionlib.cwl
:language: cwl
:caption: "`hello-world-expressionlib.cwl`"
:name: "`hello-world-expressionlib.cwl`"
:emphasize-lines: 5-17, 25
```

```{runcmd} cwltool hello-world-expressionlib.cwl --message "hello world"
:caption: "Running `hello-world-expressionlib.cwl`."
:name: running-hell-world-expressionlib-cwl
:working-directory: src/_includes/cwl/expressions/
```

```{note}
The `$include` statement can be used to include a file from the local disk or from a remote location.
It works with both relative and absolute paths. Read the [text about `$include`](https://www.commonwl.org/v1.0/SchemaSalad.html#Include)
from the CWL specification to learn more about it.
```