Pentru a ajuta pe oamenii sarmani, ne pune la dispozitie si clasa EASY-ACCEPTOR,
care extinde ACCEPTOR si ofera mijloace mai facile de a raspunde la cereri.
In timpul procesarii unei cereri vor exista totdeauna variabilele speciale
*REQUEST* si *REPLY*.
ACCEPTOR ia in primire un port TCP si sta acolo sa primeasca cereri, pe care
apoi le proceseaza cu HANDLE-REQUEST, care apeleaza ACCEPTOR-DISPATCH-REQUEST.
Aceasta incearca sa serveasca un fisier cu functia HANDLE-STATIC-FILE sau
raspunde cu HTTP 404: not found.
EASY-ACCEPTOR are o lista de functii, *DISPATCH-TABLE*, in care cauta dispatcher
pentru cererea primita - apeleaza functie dupa functie pana cand una din ele
returneaza non-NIL; daca a gasit asa ceva, returneaza rezultatul apelarii ei
cu FUNCALL, aceasta avand acces in timpul executiei la cererea HTTP prin
variabila *REQUEST*. Daca nu gaseste, atunci apeleaza CALL-NEXT-METHOD, ceea ce
inseamna ca va fi apelata metoda ACCEPTOR-DISPATCH-REQUEST din ACCEPTOR, care,
dupa cum spuneam, incearca sa serveasca un fisier static sau da 404.
Cel putin o functie exista la inceput in *DISPATCH-TABLE* -
DISPATCH-EASY-HANDLERS - a carei rost este sa caute printr-o ALTA lista,
*EASY-HANDLER-ALIST*, un handler de cereri creat cu macro-ul
(cum altfel i-ar putea spune?) DEFINE-EASY-HANDLER care are o gramada de optiuni
si din cauza asta il las in pace.
Deci daca ar fi sa mergem pe varianta cu EASY-ACCEPTOR o cerere ar fi procesata
astfel de catre ACCEPTOR-DISPATCH-REQUEST:
- intai se cauta vreun handler special pentru ea in *DISPATCH-TABLE* (handler-ele
de aici pot fi, de exemplu, create cu CREATE-PREFIX-DISPATCHER, ca mai devreme,
si adaugate manual in lista)
- daca nu se gaseste nimic, este apelata DISPATCH-EASY-HANDLERS care cauta
handler-e definite cu DEFINE-EASY-HANDLER
- daca nici aici nu se gaseste nimic, CALL-NEXT-METHOD (care face parte din
mecanismul de metode din CLOS) o sa apeleze ACCEPTOR-DISPATCH-REQUEST din clasa
mama, care este ACCEPTOR; metoda aceasta va incerca sa serveasca vreun fisier
- daca nu face nici aici nimic, atunci da 404
Ce vreau eu sa fac necesita si servirea unor fisiere statice (poze, favicon.ico,
fisiere Javascript si CSS), pe care le voi lasa in seama lui ACCEPTOR, dar si
raspunderea la mai multe cereri speciale, pe care le voi procesa cu metodele
din EASY-ACCEPTOR.
Hai sa punem pagina de administrare la /admin. In ea vreau sa vedem atributele
si autovehiculele disponibile, sa adaugam altele noi si sa asociem atribute
vehiculelor, impreuna cu valorile aferente.
Pagina asta n-o sa arate fantastic, cel putin nu acum. Ea este alcatuita
dintr-un template si un dispatcher. Ca idee, template-ul l-am facut intai
ca fisier HTML, caruia i-am facut un link simbolic cu nume de template -
cred ca se poate face si in Windows chestia asta, cu mklink. Ulterior
va ramane doar fisierul .TMPL, care va fi adevarat, nu link la vreun alt fisier,
dar momentan ma ajuta sa deschid fisierul HTML in browser sa vad cum arata,
iar eventualele modificari sa apara automat in fisierul TMPL.
In plus, cat timp fac debugging folosesc cereri GET in loc de POST, desi cele
POST ar fi mai nimerite in tipul asta de interactiune. Cererile GET imi arata
parametrii in bara de adresa a browser-ului, si asta ajuta acum, la inceput.
Cand voi schimba in POST va trebui sa inlocuiesc apelurile catre
GET-PARAMETER cu apeluri catre POST-PARAMETER.
Functia o scriu o singura data in fisierul .LISP. Inainte de functie pun
apelul catre PUSHNEW cu care introduc dispatcher-ul in *DISPATCH-TABLE*,
si il evaluez in Slime/Emacs cu C-x C-e. Apoi scriu definitia functiei
si pot sa o refac de oricate ori vreau, evaluand-o tot apasand C-x C-e
dupa paranteza de inchidere.
(pushnew (create-prefix-dispatcher "/admin" 'web-admin) *dispatch-table*) (defun web-admin () (let ((cmd (get-parameter "cmd"))) (cond ((equal cmd "add-vehicle") (let ((name (get-parameter "vehicle"))) (add-vehicle (make-instance 'vehicle :name name)))) ((equal cmd "add-attribute") (let ((name (get-parameter "attribute"))) (add-attribute (make-instance 'attribute :name name)))) (t nil))) (with-output-to-string (s) (html-template:fill-and-print-template (get-template "admin.tmpl") (list :vehicles (mapcar #'(lambda (x) (list :id (id x) :name (name x))) (get-all-vehicles)) :attributes (mapcar #'(lambda (x) (list :id (id x) :name (name x) :att-type (att-type x))) (get-all-attributes))) :stream s) s))
Nu e ceva foarte complicat. Fiind pagina in care adaugam si autovehicule, si
atribute, o sa folosim un parametru CMD care indica tipul de comanda: adaugare
de vehicul sau de atribut. Intai ma uit sa vad ce comanda e ceruta, si o execut:
adaug vehiculul sau atributul al carui nume l-am primit in NAME in colectia
corespunzatoare. Daca nu e nici o comanda, nu fac nimic.
Apoi iarasi procesez un template, de data asta "admin.tmpl" (care e link catre
un fisier "1.html" in faza de dezvoltare/debugging dar o sa ajunga fisier normal
in GIT). Ca date ii transmit o lista de vehicule si una de atribute. Elementele
din fiecare lista sunt perechi de simboluri cuvinte-cheie si valorile sloturilor
corespunzatoare din instantele respective.
(mapcar #'(lambda (x) (list :id (id x) :name (name x))) (get-all-vehicles))Codul de mai sus trece prin toate elementele din lista obtinuta apeland
GET-ALL-VEHICLES si pentru fiecare creeaza o lista cu id si nume.
Datele sunt apoi adaugate in lista de vehicule/atribute.
<html> <head> <style type='text/css'> .admin-container { overflow: auto; padding: 5px; width: 600px; height: 200px; background: #eee } </style> </head> <body> <!-- autovehicule --> Autovehicule:<br> <form action='/admin' method='GET'> <input type='text' id='vehicle' name='vehicle'> <input type='hidden' name='cmd' value='add-vehicle'> <input type='submit' value='Adauga vehicul'> </form> <div class='admin-container'> <table> <!-- TMPL_LOOP vehicles --> <tr><td id='<!-- TMPL_VAR id -->'><!-- TMPL_VAR name --></td></tr> <!-- /TMPL_LOOP --> </table> </div> <br><br> <!-- atribute --> Atribute:<br> <form action='/admin' method='GET'> <input type='text' id='attribute' name='attribute'> <input type='hidden' name='cmd' value='add-attribute'> <input type='submit' value='Adauga atribut'> </form> <div class='admin-container'> <table> <!-- TMPL_LOOP attributes --> <tr><td id='<!-- TMPL_VAR id -->'><!-- TMPL_VAR name -->: <!--TMPL_VAR att-type --></td></tr> <!-- /TMPL_LOOP --> </table> </div> <!-- atribute la vehicule --> <div class='admin-container' style='left: 630px; top: 50px; position: absolute; '> </div> </body> </html>
Fisierul template arata urat. Pe langa asta, contine doua formulare, pentru
adaugarea elementelor. Data viitoare va aparea si un formular pentru arasarea
de atribute vehiculelor, care va functiona la modul: dai clic pe un vehicul si
vezi in partea dreapta toate atributele, cu cate o casuta in dreptul fiecaruia,
in care sa poti sa scrii valoarea respectivului atribut. Cand dai submit,
se trimit toate valorile si se salveaza numai cele ne-nule.
Dupa cum ziceam, e urata pagina de administrare, dar momentan nu ma intereseaza
aspectul, ci functionalitatea.
Niciun comentariu:
Trimiteți un comentariu
S-ar putea sa nu vedeti comentariul aparand imediat, asta inseamna ca el asteapta aprobarea mea. Aceasta e o masura anti-SPAM.