вторник, 3 июля 2012 г.

Как добавить поддержку Markdown в Kate, KWrite и встроенный редактор Krusader (F4)

Лично я для работы с PHP проектами уже давно пользуюсь в качестве IDE исключительно Krusader'ом и его встроенным редактором. И встроенным поиском. И встроенным архиватором. Ну, в общем, понятно. Встроенный в Krusader редактор является всего лишь Kate, который запихнули в контейнер. И этот редактор меня устраивает практически во всём, кроме одной вещи: там нет встроенной поддержки Markdown.

Однако, как выяснилось гуглением по англоязычной Сети, это быстро решается.

понедельник, 2 июля 2012 г.

Использование Qt для реализации GUI в Common Lisp

У меня нет никаких предрассудков насчёт Qt и на десктопе у меня KDE, так что для первых шагов в GUI я воспользовался проектом под названием CommonQt. Это CFFI-биндинги к libqt и libsmoke.

Здесь я запишу, как настроить окружение для того, чтобы писать Qt-приложения. Ниже скриншот конечного результата. ;)

четверг, 14 июня 2012 г.

Простое и сложное в Common Lisp

Сегодня развлекался с моим любимым Common Lisp и обнаружил интересную деталь.

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

(average 1 2 3 4)
5/2

...ну хорошо, хорошо:

(coerce (average 1 2 3 4) 'float)
2.5

Заметьте то, что я не хочу передавать в average список, я хочу кортеж чисел. Интересная деталь вот в чём: самая простая (в смысле, лаконичная) реализация, которая пришла мне в голову, это такая:

(defun average (&rest args)
  (/ (reduce #'+ args) (list-length args)))

Дело в том, что определение среднего арифметического — это задачка для средней школы, и мы вполне можем её дать в качестве задания по программированию на второй паре (на первой мы изучим весь синтаксис лиспа вместе с макросами defun и defvar). Но на самом деле мы не можем её дать, потому что у нас есть вот эта форма в решении: (reduce #'+ args), и, чтобы студенты смогли ей бегло пользоваться, нужно, чтобы они знали как минимум:

  • о разделении пространства имён переменных и пространства имён функций;
  • как передавать в функцию имя другой функции;
  • о назначении и работе reduce, что для не знакомого с функциональными методами, вообще говоря, стопроцентная магия (особенно если их уже обработали алголоподобными языками);
  • о том, что параметры, переданные через &rest, оборачиваются в список.

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

Конечно, мы можем соорудить нечто работающее из let и dotimes, но тогда зачем нам Common Lisp вообще? :)

четверг, 7 июня 2012 г.

Паттерн «Веб-функция»

Допустим, вам нужно написать endpoint для аякс-запроса. Или обычный обработчик обычного POST-запроса от веб-формы. Тогда основные действия, которые обязательно должны быть в процессе обработки, будут такие:

  • Фильтрация входных данных (это НЕ валидация бизнес-правилами, только лишь такие действия, как укорачивание, очистка от окружающих пробелов, исключение непечатных символов, приведение к нижнему регистру и т. д.). Или мы можем даже сыграть роль адаптера и переименовать некоторые параметры запроса в вид, ожидаемый обработчиком.
  • Обработка запроса. Здесь мы собственно делаем то, зачем существуем. Мы ожидаем уже более-менее очищенный от мусора массив входных параметров, возможно, среди которых есть ошибочные (например, числовое значение вне допустимого диапазона, или передан массив вместо скаляра).
  • Форматирование результата для отклика клиенту. Например, мы можем возвращать JSON, или генерировать HTML страницу по шаблону. Или генерировать изображения. Или отдавать файлы с диска.

Если записать в функциональном стиле на PHP, то можно получить следующий шаблон хэндлера:


<?php

echo format(process(clean($_REQUEST)));

/**
 * Здесь очистка суперглобального массива $_REQUEST и генерация массива $request
 * 
 * @param array $request Данные запроса из массива $_POST или $_GET (или $_REQUEST).
 * @return array Очищенные данные из запроса, которые ожидает функция process.
 */
function clean($request)
{
  // TODO
  return $request;
}

/**
 * Здесь выполнение действий согласно запросу $request
 * 
 * @param array $request Параметры запроса, очищенные функцией clean.
 * @return array $result Результат работы со всей информацией, необходимой для форматирования ответа клиенту.
 */
function process($request)
{
  // TODO
  return $request;
}

/**
 * Здесь форматирование результата работы для выдачи клиенту.
 * 
 * @param array $result Результат работы, как он сгенерирован процессором. Нам не дозволяется использовать какие-либо другие данные.
 * @return string Форматированный ответ, готовый к отправке клиенту через echo.
 */
function format($result)
{
  // TODO
  return json_encode($result);
}
?>

Если process сталкивается с ошибкой, он пишет об этом в результат своей работы (например, в поле 'error') и сразу возвращает результат в format.

Если format должен генерировать большой HTML документ, то ничего страшного, он может и делать echo внутри себя, вместо того, чтобы возвращать строку. Тогда вызов всей цепочки будет без echo в начале, конечно же.

Как запускать программы на Common Lisp как консольные скрипты

Допустим, у вас есть классная маленькая программка на Common Lisp под названием filter. Она занимает всего один файл исходного кода под названием filter.lisp примерно в 200 строк длиной. В этом файле вот такой заголовок:


(defpackage :localhost.me.filter
  (:use :common-lisp)
  (:export :run))
(in-package :localhost.me.filter)

И дальше собственно сама программа. Допустим также, что вы пользуетесь своей программой часто. Загвоздка в том, что для того, чтобы её запустить, вам необходимо сделать слишком много действий:


$ cd $PROGRAMDIR
$ sbcl
CL-USER> (load "filter.lisp")
CL-USER> (:localhost.me.filter:run)

Рассмотрим два решения, одно простое, но не всегда поможет, другое посложнее и поможет уже в большем числе случаев.

Решение первое

SBCL умеет запускать программы на CL в режиме скриптов командной строки, для этого используется специальный флаг --script. Более того, благодаря этому можно написать лисп-программу, добавить к ней типичный юниксовый шебанг, вызывающий SBCL с этим флагом, и она будет работать как любой другой шелл-скрипт (за исключением того, что рантайм SBCL весит 50MB бгг). Так что напишем этот скрипт:


#!/usr/bin/sbcl --script
(load "filter.lisp")
(:localhost.me.filter:run)

Теперь положим этот скрипт в ту же директорию, что и filter.lisp, в файл с именем run и можно будет запускать нашу программу так:


$ ./run

Что и требовалось.

Решение второе

Однако, у флага --script есть одна особенность: SBCL не загружает никакие пользовательские скрипты инициализации при старте, так что скрипт будет выполняться в 100% дефолтном рантайме. Это плохо, если, допустим, вы используете Quicklisp и у вас в filter.lisp есть такой вызов:


...
(ql:quickload :CL-FAD)
(use-package :CL-FAD)
...

Ну или вызов любой другой библиотеки, неважно. Если запустить такой скрипт первым способом, то SBCL будет грязно ругаться по поводу того, что он не знает, что такое ql:quickload.

Для решения этой проблемы мы смухлюем, подменив бинарник SBCL таким бинарником, у которого все нужные библиотеки уже загружены. В SBCL есть функция save-lisp-and-die, которая выгружает текущее состояние рантайма в файл с указанным именем. Дальше этот файл можно использовать как обычный бинарник SBCL, в том числе, и для вызовов с использованием --script. Поэтому если запустить SBCL и сразу выгрузить его в файл, то полученный бинарник будет содержать всё, что SBCL подключил при загрузке, основываясь на пользовательских конфигах.


(save-lisp-and-die "/абсолютный/путь/до/файла")

Теперь в скрипте run, который написан по первому варианту, заменяем путь до системного SBCL путём до выгруженного бинарника, и скрипт начнёт работать как должен.

Как писать юнит-тесты к программе на Free Pascal при помощи FPTest

В свете работы над моими старыми программами из КГУ понадобилось покрывать код юнит-тестами. Как выяснилось после гуглопоиска, для Free Pascal, которым я компилирую свою переработку, существует проект под названием FPTest, представляющий собой каркас для написания юнит-тестов на этом замечательном языке.

Документации по этому проекту довольно немного, и официальная вики, как и README, сильно Lazarus-ориентированы. Поэтому расскажу здесь, как подключить FPTest к существующему консольному проекту и собрать тесты, написанные с его помощью.

среда, 23 мая 2012 г.

PHP: архивы PHAR не работают с включённым suhosin patch

Сегодня ставил behat и ужаснулся. phar-архивы не запускаются в командной строке вообще никак. Даже не появляется сообщения об ошибке, просто молча ничего не выполняется.

После некоторого продолжительного поиска в Сети был найден ответ на ServerFault: выполнение phar-архивов блокирует suhosin patch.

Для решения проблемы нужно раскомментировать следующую строку в /etc/php5/cli/conf.d/suhosin.ini:


;suhosin.executor.include.whitelist =

И заменить её на


suhosin.executor.include.whitelist = "phar"

Одноразовое решение выглядит так:


$ php -d suhosin.executor.include.whitelist="phar" MYPHAR.phar