[Перевод] Минимальное DB/GUI приложение на PicoLisp

От переводчика: Продолжаем восполнять недостаток информации на русском языке об интереснейшем диалекте Lisp. Предыдущая статья: Разработка веб-приложений в PicoLispДомашняя страничка проекта: http://picolisp.com

Несколько недель назад моя жена попросила небольшое приложение — онлайновую базу данных для адресов и контактных данных членов семьи, родственников, друзей и так далее.Как правило, в PicoLisp база данных содержит объекты различных классов. Для их обработки в GUI должна быть возможность для поиска, создания и удаления объектов, редактирования их свойств.

Типичное PicoLisp-приложение реализует следующие возможности:

Пункт меню для каждого типа объекта или функции Диалоговое окно поиска, которое появляется при выборе пункта меню Поиск по нужным критериям в диалоговом окне Затем щелкните на найденный объект или на кнопку «Новый» для создания нового объекта Появится форма, где вы можете редактировать этот объект Форма имеет кнопку «Удалить», чтобы удалить этот объект Но в случае простой базы данных адресов это излишне. Есть только один класс объектов. К счастью, есть простой способ. Во-первых, нет необходимости для меню. Мы можем перейти непосредственно к адресам. Всё остальное можно обработать одним компонентом GUI, +QueryChart.Полный листинг в конце статьи.

Модель данных Определим класс «Person» (для целей данной статьи в слегка сокращенной форме) как: (class +Prs +Entity) (rel nm (+Sn +IdxFold +String)) # Name (rel adr (+IdxFold +String)) # Address (rel em (+String)) # E-Mail (rel tel (+String)) # Telephone (rel dob (+Date)) # Date of birth При необходимости класс можно легко расширить.Вместо отдельных свойств для улицы, индекса, города и т.д. у нас есть одно свойство в свободном формате для полного адреса. Мы определим два неуникальных индекса, один для имени пользователя и один для адреса. Индекс «name» поддерживает нечеткий поиск (с использованием алгоритма Soundex для похожих имен).

Функции графического интерфейса У нас есть только одна GUI-функция под названием work. Она начинается со стандартных функций app (= установить сессию), action (= обработка событий формы) и html (= генерировать HTML-страницы), что типично для каждого приложения PicoLisp. Необязательная функция использует JavaScript для создания события keep-alive. (de work () (app) (action (html 0 Ttl »@lib.css» NIL ( 2) Затем, в качестве меры элементарной безопасности, она показывает поле для ввода пароля в первой форме, с жестко зашитым паролем «mypass». (ifn *Login (form NIL (gui 'pw '(+PwField) 20, «Password») (gui '(+Button) , «login» '(ifn (= «mypass» (val> (: home pw))) (error, «Permission denied») (on *Login) (url »! work»)))) (Реальное приложение использует полноценную аутентификацию по пользователю/паролю (с помощью библотеки «lib/adm.l»). Мы опустили ее здесь только для краткости)В любом случае она использует глобальную переменную *Login, которая устанавливается нажатием кнопки «login» в случае совпадения пароля. В этом случае отображается вторая, главная форма.

(form NIL ( »--.» «Name» (gui 'nm '(+DbHint +TextField) '(nm +Prs) 20) (searchButton '(init> (: home query))) «Address» (gui 'adr '(+DbHint +TextField) '(adr +Prs) 20) (resetButton '(nm adr query))) Она отображает два поисковых поля, «Имя» и «Адрес», и две кнопки «Поиск» и «Сброс». Поля поиска используют префикс-класс +DbHint для отображения выпадающего списка с подходящими именами, уже имеющимися в БД. Кнопка «Сброс» очищает все поля.Теперь перейдем к «сердцу» GUI этого приложения. Мы используем класс+QueryChart как для поиска записей, так и для их создания и редактирования.

+QueryChart используется во всех диалогах поиска. Он использует Pilog-запрос для поиска по заданному набору критериев, и отображает возможно неограниченное количество результатов, пока есть подходящие элементы, и пользователь продолжает нажимать кнопки прокрутки.

(gui 'query '(+QueryChart) 12 '(goal (quote @Nm (val> (: home nm)) @Adr (val> (: home adr)) (select (@@) ((nm +Prs @Nm) (adr +Prs @Adr)) (tolr @Nm @@ nm) (part @Adr @@ adr)))) Первый аргумент (здесь 12) дает начальное количество совпадений для заполнения таблицы. Второй аргумент — Pilog-запрос, который использует значения поисковых полей «Имя» и «Адрес» для нечеткого и частичного поиска. Смотрите http://software-lab.de/doc/select.html для подробной информации.Затем следуют три стандартных аргумента для класса +Chart

6 '((This) (list (: nm) (: adr) (: em) (: tel) (: dob))) '((L D) (cond (D (mapc '((K V) (put!> D K V)) '(nm adr em tel dob) L) D) ((car L) (new! '(+Prs) 'nm (car L)))))) , а именно: количество столбцов (здесь 6) и функции put и get.Класс +Chart вызывает эти функции всякий раз, когда что-то происходит в GUI. put-функция преобразует логическое содержание строки таблицы (здесь адрес объекта) в физическое отображение имени, адреса, email-а и т.д.:

'((This) (list (: nm) (: adr) (: em) (: tel) (: dob))) Аргумент This для put-функции — объект, и он разворачивается в список значений для строки таблицы.get-функция выполняет обратное действие, транслируя значения в строке в свойства объекта. Она принимает в L список значений из GUI (строки, числа, даты и т.п., введенные пользователем), а в D — адрес объекта в БД.

'((L D) Затем она проверяет, в выражении cond, существует ли объект D.Если да, она сохраняет значения из L в свойствах соответствующего объекта, обновляя таким образом БД по необходимости: (D (mapc '((K V) (put!> D K V)) '(nm adr em tel dob) L) D) Если объект не существует, но первая колонка таблицы содержит имя (которое пользователь только что ввёл), то создается новый объект в БД с этим именем: ((car L) (new! '(+Prs) 'nm (car L)))))) Вот и всё! Это вся логика, необходимая для создания и редактирования записей.+Chart или +QueryChart — внутренний объект, реализующий логику этого графического интерфейса.Теперь нам нужны физические компоненты для взаимодействия с пользователем.Поместим их в таблицу

(

NIL (choTtl «Entries» '+Prs) с соответствующими заголовками (quote (NIL «Name») (NIL «Address») (NIL «E-Mail») (NIL «Telephone») (NIL «Date of birth»)) следом идут 12 строк с полями текста, email и телефона (do 12 ( NIL (gui 1 '(+TextField) 30) (gui 2 '(+TextField) 40) (gui 3 '(+MailField) 20) (gui 4 '(+TelField) 15) (gui 5 '(+DateField) 10) (gui 6 '(+DelRowButton) '(lose!> (curr)) '(text «Delete Entry @1?» (curr 'nm)))))) Обратите внимание на кнопку +DelRowButton в последней колонке.Она может быть использована для удаления записи из БД. Она вызывает диалог с подтверждением, действительно ли пользователь хочет удалить запись. Впрочем, при удалении нескольких строк, она не будет запрашивать пользователя в следующий раз.И в самом конце отображаются четыре стандартные кнопки прокрутки

(scroll 12)))))) Они позволяют построчно и постранично прокручивать содержимое таблицы.dbe4e4e253c64a84a1a33255722db8f3.jpg

Инициализация и запуск По соглашению, PicoLisp-приложение предоставляет две функции, main и go.Функция main должна инициализировать рабочее окружение, а go должна запускать цикл обработки событий GUI. (de main () (locale «UK») (pool «adr.db»))

(de go () (server 8080 »! work»)) locale главным образом нужен для правильной обработки поля +TelField с номерами телефонов. Вы можете предоставить свои настройки локализации в каталоге loc/.Если вы скопируете код ниже в файл «minDbGui.l» или скачаете с http://software-lab.de/minDbGui.l, можете запустить его таким способом:

$ pil minDbGui.l -main -go -wait либо в режиме отладки: $ pil minDbGui.l -main -go + Исходный код программы # 11jan15abu # © Software Lab. Alexander Burger

(allowed () »! work» »@lib.css»)

(load »@lib/http.l» »@lib/xhtml.l» »@lib/form.l»)

(class +Prs +Entity) (rel nm (+Sn +IdxFold +String)) # Name (rel adr (+IdxFold +String)) # Address (rel em (+String)) # E-Mail (rel tel (+String)) # Telephone (rel dob (+Date)) # Date of birth

(de work () (app) (action (html 0 Ttl »@lib.css» NIL ( 2) (ifn *Login (form NIL (gui 'pw '(+PwField) 20, «Password») (gui '(+Button) , «login» '(ifn (= «mypass» (val> (: home pw))) (error, «Permission denied») (on *Login) (url »! work»)))) (form NIL ( »--.» «Name» (gui 'nm '(+DbHint +TextField) '(nm +Prs) 20) (searchButton '(init> (: home query))) «Address» (gui 'adr '(+DbHint +TextField) '(adr +Prs) 20) (resetButton '(nm adr query))) (gui 'query '(+QueryChart) 12 '(goal (quote @Nm (val> (: home nm)) @Adr (val> (: home adr)) (select (@@) ((nm +Prs @Nm) (adr +Prs @Adr)) (tolr @Nm @@ nm) (part @Adr @@ adr)))) 6 '((This) (list (: nm) (: adr) (: em) (: tel) (: dob))) '((L D) (cond (D (mapc '((K V) (put!> D K V)) '(nm adr em tel dob) L) D) ((car L) (new! '(+Prs) 'nm (car L)))))) (

NIL (choTtl «Entries» '+Prs) (quote (NIL «Name») (NIL «Address») (NIL «E-Mail») (NIL «Telephone») (NIL «Date of birth»)) (do 12 ( NIL (gui 1 '(+TextField) 30) (gui 2 '(+TextField) 40) (gui 3 '(+MailField) 20) (gui 4 '(+TelField) 15) (gui 5 '(+DateField) 10) (gui 6 '(+DelRowButton) '(lose!> (curr)) '(text «Delete Entry @1?» (curr 'nm)))))) (scroll 12))))))

(de main () (locale «UK») (pool «adr.db»))

(de go () (server 8080 »! work»))

# vi: et: ts=3: sw=3

© Habrahabr.ru