Filter by tag: Science
  • Как я перестал бояться и полюбил конфиг by Ast

    Как правило, наибольшего успеха добивается тот, кто располагает лучшей информацией
    Бенджамин Дизраэли

    Лишь совсем простые приложения могут обойтись без файла конфигурации. У остальных всегда найдется, что хранить в конфиге: настройки окружения, настройки логики, часто меняющиеся фрагменты текста, etc. Словом, вопроса «что хранить в конфигурации» обычно не возникает: кандидатов достаточно. А вот вопрос «как хранить конфигурацию» возникает часто и еще чаще решается не самыми оптимальными способами. В этой статье я хотел бы рассмотреть варианты хранения конфигурации приложения, их плюсы и минусы.

    Read more
  • «Strangler Application» и SSI by Ast

    When Cindy and I went to Australia, we spent some time in the rain forests on the Queensland coast. One of the natural wonders of this area are the huge strangler vines. They seed in the upper branches of a fig tree and gradually work their way down the tree until they root in the soil. Over many years they grow into fantastic and beautiful shapes, meanwhile strangling and killing the tree that was their host.
    Martin Fowler, «Strangler application»

    strangler vines

    Однажды мне достался большой и запущенный веб-проект, обладающий, наверное, всеми ярко выраженными признаками и проблемами, что вообще свойственны наследуемым системам. Все компоненты системы были написаны таким образом, что дальнейшее их расширение, изменение и поддержка если и были вообще возможны, превращались в настоящий кошмар для разработчиков и тестировщиков. Даже основные модули, такие как ядро и маршрутизация, были тесно завязаны на серверное окружение, миллион магических цифр, динамически назначаемых констант, огромных ветвистых функций и неочевидных хаков. Цикломатическая сложность с тремя нулями не была чем-то удивительным.

    Переписать всю эту логику, сохраняя работоспособность системы во всех граничных случаях не представлялось возможным, как и развитие имеющейся системы. Было принято решение использовать принцип «Strangler Application», постепенно реализуя отдельные компоненты с нуля и заменяя ими старые. Но, к сожалению, цена входа оказалась слишком велика. Не было проблемой переписать серверную логику, формирующую отдельный блок, но чтобы интегрировать получившийся код на страницу, также требовалось бы переписать серверную логику и всех остальных блоков на странице, т.к. при инициализации приложение серьёзно затрудняло возможность аккуратного подключения каких-то сторонних компонентов. Но вместо того чтобы кушать слона целиком, слона следует кушать понемногу :)

    В такой ситуации может оказаться полезной такая технология, как SSI. Она позволяет осуществлять пост-процессинг тела ответа, например, применять условные функции или делать дополнительные запросы. При реализации «Strangler Application» это может пригодиться следующим образом: старое приложение загружается как обычно, но вместо какого-то отдельного блока на странице происходит обращение к новому приложению. Веб-сервер, получив такие инструкции, делает дополнительный запрос по указанному адресу и вместо инструкции SSI размещает полученный ответ. Можно сравнить это с загрузкой блока посредством AJAX-запроса, но только значительно быстрее и совершенно прозрачно для пользователя, т.к. вся магия происходит еще на сервере, до отправки ответа клиенту.

    Такая строка в коде сайта заставит сервер сделать запрос по адресу «/my-new-application-url» и «вклеить» его результат в код сайта:

    <!--# include virtual="/my-new-application-url" -->
    

    В конфиге nginx следует разрешить такие запросы:

    location / {
            ...
            ssi on;
            ...
    }
    

    Вот так всё просто. Минусом может стать производительность такого подхода, но в данном случае старое ядро грузилось 5-8 секунд, а новое около 150 мс, что не делает большой погоды.

    Следует также учесть, что обработка инструкций SSI происходит только в том случае, если nginx получает ответ от апстрима в чистом виде, без сжатия. Если ответ будет сжат, инструкции выполнены не будут.

  • Событийная архитектура веб-приложения by Ast

    Одной из самых плохо расширяемых частей любого веб-приложения является его клиентский код, как правило, написанный на javascript. Во многих проектах он представляет собой джунгли из функций, принимающих коллбеки — и это в лучшем случае. Многие склонны винить в таком положении дел непосредственно сам язык, припоминая его «низкое» происхождение, странное поведение и отсутствие синтаксического сахара. Несомненно, в этом есть своя правда. Но я полагаю, что основная причина такой запутанности заключается в том, что построить для взаимодействия с интерфейсом стройную и расширяемую архитектуру, руководствуясь только принципами императивного программирования — невозможно. И хотя модель реализации событий в браузере сама подводит к идее организации кода декларативно, почему-то немногие на это отваживаются.

    Приведу пример простейшего API корзины интернет-магазина, а для событий используем уже имеющуюся «шину» — элемент body.

    function add(item_id, quantity) {
        $.ajax({
            type: "POST",
            url: "/api/cart/add",
            dataType: 'json',
            data: {item_id: item_id, quantity: quantity}
        }).done(function (data) {
            $('body').trigger('QuantityChanged', [data.item_id, data.quantity]);
            $('body').trigger('PriceChanged', [data.item_id, data.sum]);
        });
    };
    

    Теперь после добавления товара отправляется два события: изменилось количество, изменилась цена. Можно было бы реализовать это и одним событием с большим количеством параметров, но так лучше не делать: если слушателю события интересно только изменение цены, нет нужды передавать ему и все остальные тридцать три параметра. Это повышает шанс ошибки и ведет к неразберихе в коде. События должны быть предельно атомарными, чтобы и слушателей можно было делать небольшими, выполняющими лишь одну роль — но хорошо. Можно также расширить количество событий, добавив, например, своё событие для ошибки, начала запроса и т.д.

    /**
     * Обновить количество товара в корзине
     */
    $('body').on('QuantityChanged', function (e, item_id, quantity) {
        $('[data-item-quantity="' + item_id + '"]').html(quantity);
    });
    
    /**
     * Обновить цену товара в корзине
     */
    $('body').on('PriceChanged', function (e, item_id, sum) {
        $('[data-item-sum="' + item_id + '"]').html(sum);
    });
    

    Никто теперь не мешает нам расширить систему, добавив на какой-то отдельной странице дополнительное действие при добавлении товара в корзину. И это всё без изменения API, без изменения старых событий и без условных операторов.

  • Учим PowerShell разговаривать by Ast

    Во многих фантастических фильмах люди взаимодействуют с компьютерами, используя голосовые команды. Например, заходит человек в комнату, а там темно. «Компьютер, свет 50%!» — говорит он. «Есть свет 50%!» — отзывается приятным женским контральто компьютер и берет под козырек, включая неяркий свет. Кому такого не хотелось? Но если сама мысль такого взаимодействия родилась на свет очень давно, странствуя с тех пор по страницам фантастических книг и кадрам кинолент, то сейчас всё изменилось: доступность под рукой высоких технологий сделала многое возможным. В романе Роберта Хайнлайна «Луна — суровая хозяйка» (1966г.) суперкомпьютер Майк мог говорить свободно, но он занимал огромное количество места и, уж конечно, не был мобильным. Герой моего рассказа не может похвастаться идеальным произношением или возможностью поддерживать диалог, но, для своих габаритов он очень способный. Разумеется, я говорю о нетбуке Asus EeePC 1000.

    Не так давно я установил на него новую операционную систему MS Windows 7 RC и не мог не ознакомиться с тем, что предлагалось в комплекте. Наибольший мой интерес вызвала командная строка PowerShell и среда PowerShell ISE. Где-то на просторах интернета существуют самые разные командлеты для осуществления почти любых задач, в том числе и для воспроизведения речи. Но мне хотелось реализовать все через обычную функцию, чтобы не быть зависимым от установленных командлетов. Их же, в конце концов, может и не быть на определенном компьютере, а профиль скопировать — дело минуты. Кроме того, недавняя история с исчезновением в новой сборке, например, командлета Get-Clipboard для работы с буфером обмена, настораживает относительно чрезмерного использования редких командлетов. А вот классы .NET никуда не денутся, хотя доступ к ним может быть тоже не самым простым: в данном случае приходится запускать консоль в режиме -sta чтобы иметь возможность работать с буфером обмена.

    Префикс названия функции я использую от одного из своих ников, чтобы иметь возможность по автодополнению быстро попасть на самописные функции, введя всего три символа: они больше нигде в командах PowerShell не встречаются.

    Реализация

    function astSpeak([string]$inputString, [int]$speed = -2, 
             [int]$engine = 0, [switch]$file,
             [switch]$list, [switch]$buffer,
             [int]$volume = 85)
    {
      # Создаем объект
      $oVoice = New-Object -com "SAPI.spvoice"
    
      # Если требуется вывести список голосов
      if($list)
      {
        Write-Output "Установленные в системе голоса: "
        $i = 0
        Foreach ($Token in $oVoice.getvoices())
        {
          Write-Host $i - $Token.getdescription()
          $i++
        }    
      }
      # Если требуется проговорить
      else
      {
        # Получаем текст из файла, если задан переключатель
        if($file){ $toSpeechText = Get-Content $inputString}
        # Проговариваем текст из буфера обмена (требует режима sta)
        elseif($buffer){
         $null = [reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
         $toSpeechText = [Windows.Forms.Clipboard]::GetText() }
        # Используем полученную строку, если переключатель не задан
        else{ $toSpeechText = $inputString}
        
        # Воспроизводим
        $oVoice.rate = $speed
        $oVoice.volume = $volume
        $oVoice.voice = $oVoice.getvoices().item($engine)    
        $oVoice.Speak($toSpeechText)
      }
    }
    

    Что умеет функция?

    Можно вывести список установленных в системе голосов. Существует достаточно большое количество самых разных голосов, в том числе умеющих читать русский текст. У меня как основной установлен голос Алёна, который вполне успешно прошел проверку чтением статей из википедии и художественной литературы. В качестве последней использовался короткий ироничный рассказ Гарри Гаррисона «Абсолютное Оружие», взятый в Библиотеке Мошкова. Благодаря голосу сразу удалось найти несколько опечаток в тексте и понять необходимость расстановки буквы «ё», в остальном же, чтение было впечатляющим. Итак, чтобы получить список голосовых движков, нужно вызвать:

    astSpeak -l 
    

    Можно прочитать любой заданный текст, ну это самое простое.

    
    astSpeak «Привет! Меня зовут Алёна, я голос твоего компьютера!» 
    

    Можно прочитать текстовый файл. Для этого следует указать соответствующий ключ и путь к нужному файлу. Например, так:

    astSpeak -f «D:\Библиотека\Редъярд Киплинг\Заповедь.txt»
    

    Да… пожалуй, доверять Алёне стихи больше не будем :)

    Можно прочитать текст, содержащийся в буфере обмена. С реализацией этого существуют некоторые сложности, так как специальный командлет был исключен из пакета поставки PowerShell, а получить доступ к буферу средствами .NET можно только в режиме sta (Single Threading Apartment), а консоль PowerShell запускается в режиме mta по-умолчанию. В режиме mta работает по-умолчанию ISE. Но это не проблема, достаточно в свойствах ярлыка консоли прописать соответствующий ключ:

    Итак, если позволяет режим консоли, выглядеть это будет так:

    astSpeak -b
    

    Самое страшное — если там был URL.

    При вызове любого режима, предполагающего воспроизведение речи (ну, то есть кроме перечисления голосовых движков), можно также задать скорость речи, громкость голоса и номер звукового движка. Чтобы, например, услышать слово «привет» в исполнении Катерины (движок номер 1), громкостью, установленной на 85/100 и со слегка увеличенной скоростью, нужно ввести следующую команду.

    astSpeak -e 1 -v 85 -s 3 «Привет» 
    

    В общем, все действительно просто! Если у вас есть какие-то комментарии, советы или вы знаете как можно все это улучшить, буду очень рад вас услышать.