20 нояб. 2007 г.

CGI::Application и AJAX

В выходные решил попрактиковаться с CGI::Application, библиотекой JQuery и AJAX, потому что за год погружения в краулеры/парсеры подзабыл, как пишутся CGI-приложения. Получился вот такой словарик онлайн.

Больше всего времени угрохал на то, чтобы разобраться, как передавать клиенту в ответ на асинхронный запрос данные в JSON-формате -- то есть ни HTML ни XML, а JavaScript. Соответственно, content-type в заголовке HTTP-ответа должен быть "text/javascript". Между тем, фреймворк CGI::Application предполагает, что каждый "run-mode" возвращает HTML.
Вот как выглядит решение:
$self->header_type('none');
print "Content-Type: text/javascript; charset=utf-8\n\n";
return objToJson($self->_get_dictionaries() );
  • $self->header_type(none) "сообщает" фреймворку: не заботься о HTTP-заголовках.
  • затем мы выводим свой собственный, нестандартный заголовок
  • метод $self->_get_dictionaries() возвращает структуру данных, которая преобразуется в JSON-формат функцией objToJson из CPAN-библиотеки JSON
На стороне клиента:
function resetDictionaries() {
$.getJSON('/cgi-bin/words.cgi',
{rm: 'update_dicts',
dicttype: document.SearchForm.DictType.value
},
function(data) {
populateCombo('#Dict', data);
});
}
Эта функция вызывается когда пользователь меняет категорию словаря в верхнем выпадающем списке.
  • методом JQuery getJSON мы отправляем на сервер асинхронный запрос
  • ответные данные -- те самые, которые были упакованы методом objToJson -- приходят в анонимную callback-функцию. Ее аргумент data преставляет собой готовый к использованию JavaScript-объект. Функция populateCombo (которая здесь не приводится) динамически меняет содержание выпадающего списка словарей.
Разобраться с JQuery мне, помимо официальной документации, помогла статья на RSDN: JQuery -- Javascript нового поколения.

16 комментариев:

cmdx комментирует...

Забавный словарик (в хорошем смысле).
Сергей, скажите, вы работаете перл-программистом или делаете это все для себя?

Наувул-Наувул комментирует...

Спасибо!
На прошлую работу (поисковый проект) я устраивался как питонист, чтобы разрабатывать краулеры и парсеры. Perl оказался по ряду причин удобнее, поэтому я пересел на него и втянулся. Сейчас ищу работу исключительно как Perl-программист.

cmdx комментирует...

Могу для разминки подкинуть задачу :)

входные данные:
-, не указано, Селэгвож, Привокзальная, 1/А, к.Д, 12/13

<... обработка на Perl ...>

выход:
169258, Коми респ., Удорский р-н, п. Селэгвож, Привокзальная ул., д. 1а корп Д, кв. 12-13

cmdx комментирует...

Или вот задача попроще:
Поставить перед номером дома букву "д.", перед номером квартиры "кв."

ул Ленина, д 57/а, 3
Социалистическая, д,92, кв,60
Петрозаводская, д, 56к3
Строительная, д, 20 к, 3, кв, 200
Ленина ул, д.57 кор.Б, кв.28
Ленина, 9 Г,корп.1, 6
Димитрова, 15 3, 53
наб. 50 Лет ВЛКСМ 6 кв.65
60 лет Октября, 12/1, 414-416
Школьная ул., д.4, ч\д
60 лет Октября, 6\1, 409-410
Молодежная, 8, 41-а
Парковая, 7, 46,47

cmdx комментирует...

Если у тебя есть интересные задачки для Perl-программиста - поделись.

Наувул-Наувул комментирует...

По поводу первой задачки. Дана серия адресов в одном формате, все они относятся к пос.Селэгвож (что в Удорском районе, Коми, 169258). Надо написать код, который будет преобразовывать их в другой формат. Я правильно понял условие?

cmdx комментирует...

Не совсем :)
Есть строки, которые, возможно, являются почтовым адресом.
Нужно отформатировать их в соответствии с "Правилами оформления почтовых отправлений":
1. поставить почтовый индекс.
2. указать республику (край, область...) и район
3. перед номером дома и квартиры поставить "д." и "кв."

Например:
Было:
0, Россия, Коми респ. не указано, г. Выльгорт село, Каликовой ул., 7, 8

Стало:
168220, Коми респ., с. Выльгорт, Домны Каликовой ул., д. 7, кв. 8

Наувул-Наувул комментирует...

А откуда программа узнает, что, напр. Коми респ. относится к России и индекс каждого населенного пункта?

cmdx комментирует...

Например, из КЛАДР:
http://www.gnivc.ru/document.asp?id=80

Других источников я не знаю...

Наувул-Наувул комментирует...

По-моему, это задача на "конечные автоматы". Одним регекспом с ней не справиться. Возможны разные варианты входных данных, следовательно мы имеем дерево решений.

Надо использовать честный парсер, вроде RecDescent.

Наувул-Наувул комментирует...

Получается, что входных данных у нас больше, чем строки с адресами в исходном формате. Чтоб решить задачку надо еще скрапер написать и использовать какие-то базы. ...Нет, это не для разминки :)

cmdx комментирует...

У меня получились regex-пы на два экрана :)
Зато узнал что за зверь такой "Zero width negative lookbehind"

Наувул-Наувул комментирует...

Не завидую парню, которому придется это поддерживать :)

На прошлой неделе я позорно завалил тест в Mail.ru. В двух заданиях предлагалось regexp-ами вытащить серию данных, в первом -- из лога apache, во втором -- с HTML-страницы.

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

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

cmdx комментирует...

Поддерживать придется мне, но я пишу "развернутые" регехпы с комментариями:

my $flat = qr{
(?<![0-9]) # перед кв. нет цифры
(?:
кв
\s*
(?:,)?
\s*
)?
# $1 ($5), номер квартиры, 56
([0-9]+)
(?:
# $2 ($6), диапазон кв, 56-57
(?:-|/|,)([0-9]+)?
|
# $3 ($7), литера, 41"а"
(?:\/|-|\s)?([аб])?
)?
}x;

cmdx комментирует...

Отступы в комментах не сохраняются, увы.

"Разбирать иерархическую структуру регулярными выражениями" -- приходилось несколько раз, и действительно, это не то, чем хотелось бы заниматься :)

Наувул-Наувул комментирует...

Класс! Я тоже так хочу.