5

It seems the syntax for writing arara rules has changed since I last updated my copy of LaTeX.

On my old computer the following rule works as desired:

!config
# open rule for arara
# author: A.Ellett
# requires arara 3.0+
identifier: open
name: PREVIEW
commands:
  - <arara>  @{isTrue ( isFile ("./.design/open.pdf.true"), 
                        "open -a /Applications/Skim.app ".concat(getBasename(file)).concat('".pdf"')
                        ""
                      )}
arguments: []

But on the new computer I get the follow error message:

I have spotted an error in rule "open" located at
"/Users/..../arara/rules/open.yaml". I could
not parse the rule, something bad happened. This part is quite
tricky, since it involves aspects of the underlying data
serialization format. I will do my best to help you in any way I
can. There are more details available on this exception:

DETAILS ---------------------------------------------------------
com.charleskorn.kaml.IncorrectTypeException at commands[0] on
line 8, column 5: Expected an object, but got a scalar value

I have no idea what this means.

I've tried going through the arara manual but I'm not finding anything useful. The only thing I can find is that the mentioning of <arara> is no longer supported. But that leaves me clueless about how to rewrite this.

Any suggestions?

4
  • This has been announced for 2 years: The <arara> shorthand has been removed (cf. the changelog).
    – TeXnician
    Dec 24, 2021 at 4:36
  • Oh and string returns have been removed in v5, you have to wrap them in getCommand.
    – TeXnician
    Dec 24, 2021 at 4:49
  • And concerning rewriting I forgot to mention the announcement post of v6 which explained some changes and necessary actions. As I mentioned, you need to remove the shorthand and use getCommand as documented.
    – TeXnician
    Dec 24, 2021 at 8:13
  • I plan to write an answer tomorrow, if it's okay. :) Dec 24, 2021 at 18:00

1 Answer 1

8

Sadly, I won't be able to provide a crash course on how to write a proper rule because there would be lots of things to cover in a very constrained space. That said, I will try to comment things that have changed throughout versions based on your original rule and then provide some updates. :)

(I took the liberty of removing comments so we can focus on the code itself)

!config

Correct, the first line must contain the object mapping metadata.

identifier: ...
name: ...

Nothing changed here, so we are good to go.

commands:

So far, so good, here we have the potential list of commands.

  - <arara> @{ ...

This won't work anymore, for two reasons:

  1. We moved from simple entries to actual structures (series 4). Each command (or subtask, as it's called in the documentation) is enclosed in a proper block. The only mandatory entry from this block is the actual command, denoted by the command key. So instead of:

    - ...
    

    it should be:

    - command: ...
    
  2. The <arara> shorthand is no more (deprecated in series 5, removed in series 6). Instead, we encourage using the folding style of YAML (the documentation appendix covers this topic). So instead of:

    - command: <arara> @{ ...
    

    it should be:

    command: >
      @{ ...
    

Moving on...

isFile(...)

This method is no more (maybe series 4.0, cannot remember). The reason for it is because we've replaced specific, hardcoded methods by toFile(...) which gives us an actual representation of a file.

"open -a /Applications/Skim.app ..."

The tool does not allow commands in plain strings anymore. One of the reasons is that string manipulation for commands is tricky, unstable and error-prone. From series 4 on, we've introduced the concept of a Command, which allows you to build your commands in a safe way. So instead of:

"open -a /Applications/Skim.app ..."

it should be:

getCommand("open", "-a", "/Applications/Skim.app", ... )

Moving on.

arguments: []

Correct, no arguments.

I will now present my proposal for an updated rule. I tried to follow the idea of the original rule with a couple of modifications. :)

!config
identifier: open
name: Preview

Nothing new here. :)

authors: []

This key is optional, but for the sake of completeness, I like to add it, even if it is empty. It helps me remind of the rule format.

commands:

Nothing here as well.

- name: Open Skim

Although it is optional, I find it useful to add a name to each task. Otherwise, the tool will use an unnamed reference which is not too easy to understand if you run into problems during your work flow. :)

  command: >
    @{

Great, folded style. We are now ready to add our rule logic.

        if (exists(toFile("./.design/open.pdf.true"))) {

Three things are happening here:

  1. .design/open.pdf.true is converted from a plain string to an actual file reference (using toFile(...) method). A file reference can be created regardless of the file existence.

  2. We use a method called exists(...) which, as the name implies, returns a logical value whether that file reference actually exists. Note that this method could be replaced by invoking the exists() method from the File class, so toFile(...).exists() would be semantically equivalent.

  3. We wrap the logical test in a conditional, so we can do certain things when the test holds true, and other things otherwise.

Moving on...

            base = getBasename(reference.getName()) + ".pdf";

To ease reading, we created a local variable named base holding the base name (i.e, the file name without the extension) of the current file. Note that file is no more (version 6, I guess). We've replaced it by a way more powerful representation, which is the canonical, absolute reference to the current file (hence the reference name). For this specific purpose, it suffices to get just the file name, so we called getName() to get it.

            return getCommand("open", "-a", "/Applications/Skim.app", base);
        }

We used a method called getCommand(...) to, as the action name implies, get a Command representation of what to execute. Each element to be provided (command + arguments) has to be explicitly parsed (in opposition to the plain string approach which was guesswork). Then we return the command we just created.

        else {
            return [];
        }
    }

Now we are in the else branch. Nothing should be done if that file does not exist. But nothing is, well, nothing, and we need to tell the tool that we want to run... nothing. :) If you do not return anything, arara will complain. One possible way to return nothing is to provide an empty list [], so the tool understands it's an empty list of commands to execute.

arguments: []

Nothing to do here, and we are done. :)

The complete open.yaml rule, for reference:

!config
identifier: open
name: Preview
authors: []
commands:
- name: Open Skim
  command: >
    @{
        if (exists(toFile("./.design/open.pdf.true"))) {
            base = getBasename(reference.getName()) + ".pdf";
            return getCommand("open", "-a", "/Applications/Skim.app", base);
        }
        else {
            return [];
        }
    }
arguments: []

Hope it helps. :)

3
  • I'm away off the grid mostly and only on grid very briefly this afternoon. I won't have a chance to play with this until the new year. But, I did want to thank you for your very thorough answer. There's more here than I think I even hoped for. I just really didn't want you to think I was ignoring you in the interim. See you in the New Year.
    – A.Ellett
    Dec 26, 2021 at 20:06
  • @A.Ellett no worries! I am very happy to help! Happy New Year! :) Dec 27, 2021 at 8:27
  • Once again thank you. This did the trick. And you gave me enough here to work with to figure out how to fix up my other rules. :-)
    – A.Ellett
    Jan 3, 2022 at 22:54

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .