omniJS – from 'Alert Dialog' to possible button choice value

The very helpful tutorial material at omni-automation.com shows a step-by-step approach to:

  1. Setting a series of Alert object properties, and then
  2. performing ‘actions’ inside the scope of a call-back function.

At my advanced age this feels a little complex, and I might be discouraged by having to do it repeatedly, every time I wanted to enable a user choice.

Not sure why, but I personally happen to find it a bit quicker, more reliable, and less mentally taxing to assemble scripts by snapping them together from simpler building blocks – single function calls which take an argument or two, and return a usable value.

I would like, for example, to be able to write:

const maybeChoice = buttonChoiceMay(
    'Preference',
    'Which do you prefer to build scripts with ?',
    ['Statements', 'Functions returning values']
);

return show(maybeChoice);

defining a dialog like:

which returns a two-part ‘option’ value, with a Boolean true|false to let me know if the user cancelled, and, if they didn’t, then also a String containing the chosen button name.

I like cancellation to return the dictionary value {"nothing":true}, while an option choice returns something like:

{"nothing":false,"just":"Functions returning values"}

This lets me test the boolean before trying to use a choice.

To enable this, whenever I need an omniJS dialog, I paste, from my snippet library, the following function:

// buttonChoiceMay :: String -> String -> [String] -> Maybe String
const buttonChoiceMay = (title, msg, buttons) => {
    const
        options = ['Cancel'].concat(buttons)
        .reverse();
    var result = undefined;
    return (
        // IO ------------------------------------------------------------
        options.reduce((a, k) => (
            a.addOption(k),
            a
        ), new Alert(title, msg))
        .show(n => result = n),
        // Value ---------------------------------------------------------
        result !== buttons.length ? {
            nothing: false,
            just: options[result]
        } : {
            nothing: true
        }
    );
};

and a call might look like this:

(() => {
    'use strict';

    // buttonChoiceMay :: String -> String -> [String] -> Maybe String
    const buttonChoiceMay = (title, msg, buttons) => {
        const
            options = ['Cancel'].concat(buttons)
            .reverse();
        var result = undefined;
        return (
            // IO ------------------------------------------------------------
            options.reduce((a, k) => (
                a.addOption(k),
                a
            ), new Alert(title, msg))
            .show(n => result = n),
            // Value ---------------------------------------------------------
            result !== buttons.length ? {
                nothing: false,
                just: options[result]
            } : {
                nothing: true
            }
        );
    };

    const maybeChoice = buttonChoiceMay(
        'Preference',
        'Which do you prefer to build scripts with ?',
        ['Statements', 'Functions returning values']
    );

    return maybeChoice.nothing ? 'User cancelled ...' : maybeChoice.just;

})();

(To test, copy the whole of the latter script, pasting and entering it into the Automation Console of a recent build of OmniGraffle (or OmniOutliner).