Как нам обустроить FAST_ENTIRE_SCRIPT

В предыдущей статье речь шла о том, что такое UndoModes и какую великую пользу можно извлечь от использования правильного UndoMode. Теперь разберем, как правильно использовать потенциально небезопасный UndoModes.FAST_ENTIRE_SCRIPT максимально безопасно и относительно удобно.

Сразу же следует признать, что никакого волшебного способа обмануть поведение FAST_ENTIRE_SCRIPT нет и быть не может. По крайней мере на уровне скриптов это явно и очевидно невозможно. И, главное, не нужно. Достаточно знать об особенностях FAST_ENTIRE_SRIPT и использовать их в своих целях.

Основная проблема, напомню, в том, что при возникновении ошибки в блоке try…catch, запущенном через FAST_ENTIRE_SCRIPT пропадают все изменения, сделанные до возникновения ошибки, что категорически затрудняет отлов ошибок и может привести к странному поведению скрипта.

Соответственно, решение тут строго одно: после возникновения любой ошибки в блоке try…catch скрипт должен прекратить выполнение и позволить отловить ошибку.

Как делать не надо

Самым простым на первый взгляд решением было вернуть ошибку как результат выполнения app.doScript()

function something () {
    try {
        app = 42;
    } catch (error) {
        return error;
    };
}
app.doScript(something, ScriptLanguage.JAVASCRIPT, [], UndoModes.FAST_ENTIRE_SCRIPT, 'Test');

Бесполезность этого пути стала очевидной при первом же тестировании. Дело в том, что возврат функцией ошибки интерпретируется как ошибка… в том месте, где эта функция была вызвана. То есть, ошибку даст вызов app.doScript(). Даже если ее в свою очередь отловить в try…catch, то получим не ту ошибку, которую вернула вызываемая функция, а совсем другую.

function something () {
    try {
        app = 42;
    } catch (error) {
        return error;
    };
}
function main () {
    try {
        app.doScript(something, ScriptLanguage.JAVASCRIPT, [], UndoModes.FAST_ENTIRE_SCRIPT, 'Test');
    } catch (error) {
        $.writeln(error.line)
    }
}
main();

В результате выполнения этот код сообщит, что ошибка возникает в строке 10, а не в строке 3.

Как делать не совсем правильно

Второй заход на цель был связан с моим личным умением не всегда правильным образом  полагаться на силу объектно ориентированного программирования. Было принято решение в стиле: если нельзя передать объект Error напрямую, то я создам свой объект UndoError с лото и библиотекаршами данными и методами! После вызова app.doScript() достаточно будет проверить тип результата и, буде возвращен пользовательский объект подходящего типа, получим сообщение об ошибке.

var UndoError = function UndoError (error) {
    this.line = error.line;
    return this;
}

function something () {
    try {
        app = 42;
    } catch (error) {
        return new UndoError(error);
    };
}
function main () {
    var res = app.doScript(something, ScriptLanguage.JAVASCRIPT, [], UndoModes.FAST_ENTIRE_SCRIPT, 'Test');
    if (res.constructor.name == 'UndoError') {
        $.writeln(res.line);
    }
}

main();

Запускаем скрипт и в результате получаем… ничего. Дело в том, что возвращаемые app.doScript() пользовательские объекты превращаются в тыкву обычные объекты типа Object. Как и почему это происходит, объясню в отдельной статье.

Как правильно

Сама идея возвращать пользовательский объект в нашем случае вполне применима. Вопрос только в том, как правильно обустроить отлов ошибки. После недолгого размышления получилось следующее вполне пригодное для практического использования решение.

var UndoError = function UndoError (error) {
    this.generator = {name: this.constructor.name};
    this.number = error.number;
    this.message = error.message;
    this.fileName = error.fileName;
    this.line = error.line;
    this.toString = function () {
        return 'Error ' + this.number + ': "' + this.message + ' " in:\nFile: ' + this.fileName + '\nLine: ' + this.line;
    }
    return this;
}

function something () {
    try {
        app = 42;
    } catch (error) {
        return new UndoError(error).toSource();
    };
}

function main () {
    var res = app.doScript(something, ScriptLanguage.JAVASCRIPT, [], UndoModes.FAST_ENTIRE_SCRIPT, 'Test');
    var uError = eval(res);
    if (uError.hasOwnProperty('generator') && uError.generator.name == 'UndoError') {
        $.writeln(uError.toString());
    }
}

main();

Как это работает. В пользовательском объекте UndoError определяется свойство generator – объект со свойством name, где хранится значение constructor.name. Определив, есть ли у результата выполнения app.doScript() свойство generator и проверив generator.name, можно информировать пользователя об ошибке.

Обращаю внимание на два важных момента.

Первое и главное. Возвращать результат именно так:

new UndoError(error).toSource();

Почему – объясню в следующей статье отдельно.

Второе и полезное.  Зачем нужна поле с информацией о имени файла fileName в нашем UndoError. Это на случай вызова через app.doScript() функций, находящихся в отдельных файлах, подключенных через директиву #include.

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход /  Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход /  Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход /  Изменить )

Connecting to %s