Let's Do It Romania - 24 Septembrie 2011



   

Aplicaţii web cu Zope

   
   

În Wikipedia, Zope este succint definit ca fiind un server obiectual de aplicaţii web, open source, scris în limbajul de programare Python. Evident, o astfel de definiţie este potrivită pentru o enciclopedie, însă pentru a face lumină asupra subiectului e nevoie de o abordare ceva mai aprofundată.

Mircea Sârbu


În Wikipedia, Zope este succint definit ca fiind un server obiectual de aplicaţii web, open source, scris în limbajul de programare Python. Evident, o astfel de definiţie este potrivită pentru o enciclopedie, însă pentru a face lumină asupra subiectului e nevoie de o abordare ceva mai aprofundată.

Pornind de la suprafaţă, primul termen care s-ar cere detaliat e cel de "aplicaţie web". În principiu, o aplicaţie web ar putea fi aproximată ca un sit web ale cărui pagini sunt generate dinamic. Din punctul de vedere al utilizatorului, acest lucru este în mare măsură transparent, deoarece către client sunt trimise pagini HTML obişnuite. Totuşi, faptul că URL-urile nu indică fişiere cu extensia "html" este un indiciu că paginile sunt generate dinamic. De asemenea, un grad ridicat de interactivitate bazat pe formulare web este un semn sigur că avem de-a face cu o aplicaţie şi nu cu un sit static.

Din această perspectivă, Zope poate fi asemănat cu Php. Diferenţa notabilă este însă că Php este un limbaj de programare utilizat pentru realizarea de aplicaţii web, în vreme ce Zope este un o aplicaţie complexă, care tratează în mod coerent o întreagă paletă de probleme specifice aplicaţiilor web, începând de la autentificarea şi autorizarea utilizatorilor până la sincronizarea serverelor şi echilibrarea încărcării. Deoarece o bună parte din funcţionalitate este furnizată nativ de Zope, se poate spune că este totodată un framework.

Fiind o aplicaţie care se plasează între partea de prezentare (în general browserul web) şi diverse alte resurse software din partea server, Zope este un server de aplicaţii, fiind din acest punct de vedere asemănător cu Macromedia ColdFusion şi similar ca funcţionalitate cu IBM WebSphere sau alte servere bazate pe J2EE. Poate fi considerat şi un middleware software care gestionează logica aplicaţiei, furnizând altor aplicaţii serviciile pe care le implementează. În paranteză fie spus, Zope poate să comunice nu doar cu browsere web, ci cu orice programe capabile să utilizeze HTTP sau protocoale bazate pe acesta, precum SOAP sau XML-RPC.

Caracterul obiectual se referă atât la modul în care este construită aplicaţia, cât şi la modul în care este utilizată. De fapt, Zope se bazează pe o bază de date obiectuală numită ZODB (Zope Object Database) care stochează diferitele obiecte Python ce formează aplicaţia web. Obiectele Zope (ca orice obiecte de altfel) constau dintr-o structură de date împreună cu o serie de metode specifice clasei de obiecte respective.

În fine, ar mai trebui spus că:

  • Zope are înglobat şi un server HTTP, care în mod implicit foloseşte portul 8080. Are însă şi posibilitatea să folosească un server extern, cum ar fi Apache sau IIE, rezervându-şi astfel doar sarcina de a genera paginile HTML. De asemenea, cuprinde un server FTP.

  • Zope poate fi aproape integral administrat printr-o interfaţă web, numită ZMI (Zope Management Interface). Pentru editarea unor anumite tipuri de obiecte se poate folosi un editor de text extern, care rulează pe computerul local şi comunică prin FTP cu serverul Zope.

  • Zope are o arhitectură modulară, bazate pe componente care se numesc "produse" (products). Aceste componente pot fi dezvoltate de oricine pe baza modulelor şi interfeţelor Python specifice, pot reprezenta simple obiecte care pot fi stocate în ZODB sau chiar aplicaţii complexe şi uşor integrate în orice instalare Zope.

Puţină istorie

Totul a început în 1996, când Jim Fulton de la firma Digital Creations a trebuit să ţină un curs despre CGI. Cum nu prea ştia mare lucru despre subiect, a studiat în avion documentaţia existentă şi a constatat că metoda tradiţională de a publica pagini web dinamice prin intermediul CGI este greoaie şi ineficientă. Povestea spune că în cele câteva ore cât a durat zborul de întoarcere, Fulton şi-a ocupat timpul schiţând o metodă mai elegantă de a dezvolta aplicaţii web.

Din aceste schiţe s-au conturat trei produse software care ar trebui să conlucreze pentru a facilita crearea unor situri web dinamice complexe: un program care să furnizeze funcţii de publicare (Bobo), unul care să permită crearea de machete de pagini (Document Template) şi un sistem de stocare obiectual (BoboPOS). Deoarece programele urmau să fie complexe, dinamice şi orientate pe obiecte, pentru implementare a fost ales limbajul Python. Cele trei programe au fost apoi reunite într-un produs complex, numit Principia. În ciuda calităţilor şi caracterului inovator al produsului, acesta nu s-a făcut remarcat pe piaţă iar cei de la Digital Creations au apelat la un investitor care să-i ajute să-şi promoveze produsul.

În noiembrie 1998 a apărut şi investitorul -- pe nume Hadar Pedhazur -- care le-a sugerat celor de la Digital Creation o cale diferită de a-şi promova şi dezvolta produsul: să-l publice în regim open source. Ideea s-a dovedit cât se poate de productivă, deoarece sub noul nume, Zope (Z Object Publishing Environment), produsul a adunat foarte repede o comunitate entuziastă de programatori care au contribuit masiv la îmbunătăţirea produsului şi au creat sute de componente pentru cele mai variate domenii, astfel încât în scurt timp Zope a căpătat o nesperată popularitate.

Digital Creations şi-a schimbat numele în Zope Corporation şi a devenit o companie specializată pe servicii şi integrarea unor sisteme complexe bazate pe Zope, printre clienţii săi numărându-se mari organizaţii din cele mai diverse domenii: comandamentul NATO, US Navy, Bank of America, Duke University, NASA, Viacom Television etc. În paralel, Zope Corporation supervizează în continuare dezvoltarea Zope.

Cum funcţionează

Sarcina fundamentală pe care o îndeplineşte Zope este să "publice" obiecte pe care le stochează în baza sa de date ZODB. Deoarece această structură de stocare este riguros ierarhică -- fiecare obiect este plasat într-un obiect de tip container -- mecanismul de publicare realizează la fiecare apel o mapare a căii de acces la un obiect din baza de date într-un URL.

Spre exemplu, dacă vom considera un obiect de tip document numit DocEx plasat într-un obiect de tip folder (care este un container) numit Nod2, la rândul său conţinut într-un folder numit Nod1, atunci documentul în chestiune poate fi apelat printr-un URL de genul:

http://localhost:8080/Nod1/Nod2/DocEx?nume=Vasile

Am considerat că serverul Zope este pe computerul local (dar ar putea fi oriunde în internet) şi funcţionează pe portul 8080. Maparea este cât se poate de simplă, cu condiţia să ţinem seama că totul porneşte de la un folder rădăcină, care este baza unei instalări Zope şi care în cazul nostru poate fi accesată prin URL-ul:

http://localhost:8080/

Dacă documentul numit DocEx (majusculele contează) conţine doar cod HTML, atunci acesta este trimis browserului apelant, iar acesta îl afişează ca pe oricare altă pagină web. În felul acesta putem să realizăm foarte simplu situri web similare cu cele statice, cu diferenţa că documentele nu sunt stocate în structura de fişiere a serverului ci într-o bază de date. Deşi Zope oferă o serie întreagă de avantaje chiar şi în acest caz trivial de aplicaţie web (de pildă mecanismele de control al accesului, accesul prin interfaţă web etc.), puterea produsului se manifestă plenar abia atunci când este vorba de a genera dinamic conţinutul paginii.

Până acum am făcut abstracţie de stringul de interogare (query string), însă intenţia este evidentă: documentele sunt obiecte active, care au posibilitatea să-şi "compună" conţinutul şi să interacţioneze cu alte obiecte Zope. În exemplu de mai sus, documentul DocEx va dispune de variabila nume cu valoarea "Vasile", pe care o poate plasa în conţinut (ca şi în Php) astfel încât utilizatorului i se va trimite o pagină web personalizată.

O observaţie finală referitoare la modul de funcţionare ar fi că acesta poate fi exprimat în mod obiectual. Publicarea unui obiect Zope înseamnă de fapt trimiterea unui mesaj HTTP (eventual cu parametri) obiectului, al cărui răspuns este trimis apoi clientului.

Obiecte Zope de bază

Zope dispune de o serie destul de extinsă de clase de obiecte, fiecare dispunând de un API public şi riguros documentat. Este greu (dacă nu imposibil) de distins între obiectele "native" şi cele dezvoltate de terţi proiectanţi. Numeroase componente (products) se concretizează în obiecte care, odată instalate, pot fi folosite exact ca cele furnizate în distribuţia standard. Totuşi, câteva pot fi considerate "de bază" datorită faptului că sunt cel mai frecvent utilizate iar Zope n-ar putea funcţiona fără ele.

Folder

Este principalul container folosit în Zope. Practic orice obiect Zope se află într-un folder (la limită în folderul rădăcină). Ca orice obiect Zope, este identificat printr-un proprietate id şi dispune de proprietatea title (care este cea utilizată pentru vizualizare, dacă este definită). În plus, utilizatorii pot adăuga (prin ZMI sau programatic) orice proprietăţi doresc. Proprietăţile folderului sunt implicit accesibile obiectelor conţinute şi în mod explicit celorlalte.

Clasa Folder este sub-clasă a clasei ObjectManager, de la care moşteneşte cea mai mare parte a funcţionalităţii, care constă în principal din metode care asigură accesul la obiectele conţinute. De pildă, metoda objectItems returnează o listă de perechi (id, object) corespunzătoare obiectelor conţinute (eventual restrânsă la cele de anumite tipuri, precizate prin argumentul type).

De notat că apelul direct al unui obiect de tip folder este implicit redirectat către un obiect numit index_html din folderul respectiv.

DTMLDocument şi DTMLMethod

Ambele sunt obiecte al căror rol principal este să genereze conţinut text (HTML, XML, text simplu etc.) care este apoi trimis apelantului. Apearea unui astfel de document determină interpretarea codului DTML cuprins în conţinut (dacă există) şi returnarea conţinutului astfel obţinut.

DTML (Document Template Markup Language) este un limbaj de marcare care cuprinde o serie de construcţii exprimate prin tag-uri de forma:


Aceste construcţii -- pe care, printr-un abuz de limbaj, le voi numi instrucţiuni -- pot fi intercalate oriunde în conţinutul textual al obiectului şi furnizează funcţionalităţi tipice unui limbaj de programare: teste, iteraţii, apeluri de procedură etc. Un exemplu simplu poate furniza o sugestie:


Este vorba de o iteraţie prin lista obiectelor conţinute în folderul container (obţinute apelând metoda objectValues). Vor fi prezentate doar cele de tipul "DTML Method", pe baza testării atributului meta_type. Metoda title_or_id furnizează titlul obiectului curent, sau id-ul în cazul în care titlul lipseşte. E de remarcat că dtml-var nu are tag de închidere, expresiile urmează sintaxa Python iar interpretorul ştie să aleagă între atribute şi metode.

Diferenţa dintre documentele şi metodele DTML este oarecum subtilă. Documentele pot avea atribute definite de proiectant în vreme ce metodele nu. În schimb, metodele au acces la API-ul folderului care le conţine, în schimb documentele nu -- deci fragmentul de cod furnizat ca exemplu nu va funcţiona decât într-o metodă DTML, nu şi într-un DTMLDocument.

Zope Page Template (ZPT)

Machete Zope Page Templates sunt o alternativă la obiectele de prezentare bazate pe DTML şi reprezintă la momentul actual modalitatea recomandată. Ca şi metodele şi documentele DTML, documentele ZPT produc conţinut HTML (sau XML) cu diferenţa că folosesc un alt limbaj pentru generarea dinamică a paginilor: TAL (Template Attribute Language). Diferenţa majoră este că TAL se exprimă integral ca atribute dintr-un spaţiu de nume XML şi se pot aplica documentelor HTML (sau XML), cu avantajul că acestea rămân valide. Aceasta înseamnă că ele pot fi schimbate liber între un graficieni (sau web designeri) şi programatori, primi ocupându-se de aspect (folosind editoare HTML, care vor ignora atributele TAL) iar ultimii de chestiunile procedurale.

Pentru a simţi "aroma" machetelor ZTP, iată echivalentul fragmentului DTML prezentat anterior exprimat în TAL:




Este de remarcat că deoarece "instrucţiunile" TAL sunt atribute HTML, a fost nevoie să forţăm puţin nota şi să introducem elemente structurale (în cazul acesta span) la care să putem ataşa atributele. Dacă prezentarea ar fi fost tabelară, am fi putut recurge la elementele tr şi td. Ar mai fi de remarcat că un acelaşi element HTML poate avea mai multe atribute TAL şi că textul de afişat (în cazul acesta "Titlu sau id") este doar un placeholder care va fi înlocuit cu valoarea actuală ce trebuie afişată. Este important însă pentru designer, deoarece are astfel posibilitatea să vizualizeze aspectul paginii populată cu date. Există în plus posibilitatea de a plasa şi "elemente fantomă", care vor fi vizibile în faza de design dar nu vor fi generate în cazul unui apel în Zope (servesc doar pentru ghidarea vizuală a graficianului).

Ar mai fi de notat că TAL este folosit în conjuncţie cu METAL (Macro Expansion TAL) care este utilizat pentru a include blocuri de cod numite macro-uri, care pot fi particularizate în funcţie de context prin intermediul unor aşa-numite slot-uri. În plus, sintaxa expresiilor folosite de atributele TAL şi METAL este standardizată prin TALES (TAL Expression Syntax).

DTML şi TAL sunt alternative care însă nu se exclud reciproc. DTML are o mai mare putere procedurală şi, în plus, permite unele procesări care nu sunt posibile în ZTP (de pildă generarea de mesaje e-mail).

Fişiere

Fişierele Zope (File) sunt simple depozitare de conţinut textual sau binar. Spre deosebire de fişierele obişnuite, acestea sunt stocate în baza de date obiectuală (ZODB) şi, desigur, sunt şi ele obiecte, dotate cu atribute şi metode. Pentru cele care cuprind text există posibilitatea de a fi editate prin ZMI. Tipul conţinutului este desemnat prin metoda standard MIME -- de pildă text/plain, text/xml, application/pdf etc. -- şi poate fi obţinut prin metoda getContentType.

Evident, conţinutul fişierelor -- fie importate, fie generate în Zope -- poate fi inclus în documentele generate. Ele reprezintă metode tipice de a stoca, de exemplu, declaraţii CSS, programe JavaScript, applet-uri Java, conţinut Flash etc.

Este de notat că deşi fişierele pot conţine imagini, Zope pune la dispoziţie şi un obiect specializat în această privinţă: Image. Explicaţia constă în faptul că acestea beneficiază implicit de proprietăţile corespunzătoare dimensiunilor, ceea ce facilitează controlul afişării.

Scripturi Python

Scripturile Python sunt exact ceea ce par: obiecte Zope care cuprind cod Python şi care se interpretează. Desigur, şi ele dispun de câteva proprietăţi specifice, cum ar fi lista de argumente. Deşi limbajul DTML poate rezolva în mare parte necesităţile procedurale ale unei aplicaţii, scripturile Python reprezintă metoda recomandată pentru implementarea logicii aplicaţiei, lăsând în seama machetelor ZTP şi a documentelor sau metodelor DTML implementarea "logicii prezentării".

Este de remarcat că scripturile Python nu beneficiază -- din motive de securitate -- de întreaga forţă a limbajului Python. Astfel, scripturile pot importa doar un set limitat de module, nu pot accesa fişiere şi nu dispun de anumite funcţii native. De pildă, funcţia type lipseşte, fiind însă disponibilă o funcţie alternativă same_type care poate determina tipul unui obiect prin comparaţie. De asemenea, funcţia range nu va putea genera numere sau secvenţe foarte mari.

În schimb, scripturile Python beneficiază de un mecanism numit bindings, care permit acestora să acţioneze în contextul unei aplicaţii Zope. De pildă, obiectul context este cel care furnizează unui script Python informaţii legate de localizarea sa în ierarhia obiectelor Zope. Iată, spre exemplu, cum ar arăta scriptul Python echivalent cu exemplul dat în DTML:

for o in context.objectValues():
  if o.meta_type == 'DTML Method':
    print o.title_or_id()
return printed

E de observat utilizarea obiectului context pentru a aplica metoda objectValues folderului care conţine scriptul precum şi utilizarea instrucţiunii print. Deşi scripturile Python sunt menite în principal să returneze rezultate, ele pot să şi "tipărească", caz în care obiectul printed reţine întreg textul emis de instrucţiunile print.

De notat că deşi Python este "alegerea naturală" pentru Zope (fiind limbajul în care Zope este implementat) există şi posibilitatea ca, prin intermediul unei componente, să se folosească scripturi Perl.

Metode externe

Metodele externe sunt în mare măsură similare scripturilor Python. Şi ele servesc la implementarea logicii aplicaţiei şi sunt exprimate în limbajul Python. Diferenţele notabile sunt următoarele:

  • Textul sursă Python nu este memorat în baza de date ZODB, ci într-un fişier extern, plasat în subdirectorul Extensions al instalării Zope de pe server. Obiectul memorat în ZODB nu este decât o referinţă la fişierul respectiv şi la funcţia corespunzătoare. Aceasta înseamnă că mai multe metode externe îşi pot avea codul în acelaşi fişier, deoarece obiectul External Method corespunde unei funcţii Python. În plus, metodele externe nu pot fi editate prin interfaţa web ZMI.

  • Metodele externe nu au limitările scripturilor Python: pot importa orice module, pot accesa fişiere etc. După măsurătorile mele, sunt adesea cam cu un ordin de mărime mai rapide decât scripturile Zope echivalente.

  • Metodele externe nu au acces la mecanismul de binding. Cu toate acestea, dacă funcţia este definită ca metodă (cu primul argument self), ea va fi considerată metodă a containerului (adică a folderului în care a fost creat obiectul External Method) şi va avea astfel acces la folder şi la obiectele din acesta.

Request şi response

Clasele Request şi Response conţin fiecare câte un singur obiect -- REQUEST şi respectiv RESPONSE -- corespunzătoare dialogului tipic cerere/răspuns pe baza căruia funcţionează web-ul. Dacă RESPONSE este folosit mai ales pentru redirectări şi manevrarea cookie-urilor, REQUEST este omniprezent într-o aplicaţie Zope non-trivială. REQUEST poartă alături de informaţiile de context (cele din specificaţiile CGI) o serie de date extrem de importante, cum ar fi datele culese din formularele web, care ajung astfel la dispoziţia metodelor care le procesează.

AuthenticatedUser

Zope dispune de un mecanism complex de control al accesului utilizatorilor la diverse resurse şi obiecte. Trebuie mai întâi notat că întreg acest mecanism este decuplat de serviciile de securitate ale sistemului de operare gazdă: cu excepţia administratorului unei instalări Zope, ceilalţi utilizatori nu au nevoie de conturi pe server. Desigur, pentru aplicaţiile publice dezvoltate în Zope, este cel mai adesea permis accesul utilizatorilor anonimi, însă aceştia nu vor avea decât drepturile minimale necesare pentru a utiliza serviciile furnizate. Pentru orice altfel de activitate, întră în joc mecanismele Zope de control al accesului, care încep cu autentificarea utilizatorilor şi continuă cu autorizarea lor pe bază de roluri şi permisiuni.

Fiecare folder din Zope poate avea asociat un folder de utilizatori. Autentificare se face pe baza conturilor din acest folder iar un utilizator, odată ce a fost autentificat pe baza numelui şi a parolei, primeşte unul sau mai multe roluri, pe baza cărora este autorizat să acceadă la anumite obiecte sau funcţii. Odată cu autentificarea, este generat un obiect din clasa AuthenticatedUser, care dispune de metode prin care se pot afla rolurile şi permisiunile de care dispune utilizatorul respectiv. Acestea sunt folosite atât de sistemul de administrare din Zope, dar pot fi folosite şi de proiectanţii de aplicaţii.

Este de notat granulaţia extrem de fină a mecanismului de securitate din Zope. Practic, fiecare obiect din Zope îşi are propriul său sistem de securitate, în care fiecare drept este specificat pentru fiecare rol în parte. Acest mecanism permite delegarea responsabilităţii pe diverse clase de activităţi în cadrul unui sit Zope.

Baze de date

Baza de date obiectuală ZODB poate cuprinde o mare varietate de obiecte, care pot satisface în mare cam orice fel de cerinţe ale unei aplicaţii. Cu toate acestea, pentru aplicaţiile care utilizează volume mari de date complex structurate, utilizarea bazelor de date relaţionale devine obligatorie. Din fericire, Zope dispune de un instrumentar bogat şi eficient, care permite o excelentă integrare a bazelor de date relaţionale în aplicaţiile Zope. Există câteva obiecte şi componente care fac acest lucru posibil.

Adaptoare de baze de date

Adaptoarele (database adapters) sunt componente care asigură accesul aplicaţiei Zope la baze de date relaţionale. Distribuţia standard nu cuprinde decât un singur adaptor, destinat unui mini-sistem relaţional numit Gadfly, implementat chiar în Zope (rolul acestuia este pur demonstrativ). Din fericire sunt disponibile numeroase adaptoare open source pentru toate sistemele relaţionale majore – de pildă, pentru PostgreSQL există PoPy si Psycopg -- plus adaptoare ODBC (acesta din urmă este comercial, dar ieftin). Vestea proastă este că instalarea acestora nu este întotdeauna trivială şi veţi avea nevoie de ajutorul administratorului de sistem (mai ales pe sisteme de tip unix).

Odată instalat, un adaptor va corespund unui obiect numit conexiune (specific SGBD-ului). Este importat de notat că a aplicaţie poate folosi mai multe conexiuni simultan, la diverse baze de date (chiar gestionate de SGBD-uri diferite). O menţiune merită sistemul tranzacţional din Zope, care controlează şi bazele de date. Practic, tot ce se întâmplă între două pagini succesive afişate este o tranzacţie, aşa că orice eroare (indiferent dacă apare în Zope sau baza de date) va declanşa un rollback general.

Metode SQL

Metodele SQL reprezintă un alt tip de obiecte menite să implementeze logica aplicaţiei, care au însă şi calitatea de a izola "logica stocării". Într-un fel, seamănă cu scripturile Python, prin faptul că au o listă de argumente -- dar, atenţie, acestea sunt tipizate -- şi pot fi apelate explicit, ca orice funcţie. Pe de altă parte, seamănă cu metodele DTML, prin faptul că utilizează acest limbaj. Spre deosebire însă de acestea, o metodă SQL nu trimite rezultatul evaluării către browserul apelant, ci către baza de date corespunzătoare conexiunii de care este legată. Deci, rezultatul evaluării trebuie să fie o frază SQL validă.

Din fericire, DTML a fost dotat cu o serie de construcţii care ajută enorm la această sarcină. De pildă, argumentele pot fi referite prin variabile de tipul sqlvar, ceea ce asigură tratarea lor corectă din perspectivă SQL (de pildă, închiderea între apostroafe a argumentelor de tip text etc.). În plus, există construcţia sqltest care asigură construcţia automată a frazei where, cu toate subtilităţile de rigoare (de pildă, în cazul în care un argument este o listă, se foloseşte operatorul IN, corespunzător incluziunii). În fine, posibilitatea de grupare prin sqlgroup este extrem de utilă în conjuncţie cu operatorii logici, astfel încât dacă -- de exemplu -- una dintre condiţiile conectate cu and sau or lipseşte, condiţia va fi omisă împreună cu operatorul aferent.

Un exemplu simplu poate fi sugestiv. Să considerăm o tabelă Angajati cu coloanele cod, nume, prenume şi salar. O metodă SQL (s-o numim SQLTest) va avea lista de argumente:

angs:int:list
sal:float

Corpul metodei poate arăta aşa:


În felul acesta vom selecta angajaţii desemnaţi printr-o listă de coduri angs care au salariul mai mare decât o valoare sal. Dacă angs va cuprinde codurile 1 şi 2 iar sal este 250, se va genera fraza SQL:

SELECT * FROM Angajat
where
(cod in (1, 2)
 and salar > 250.0
)

Dacă angs va cuprinde doar codul 1, fraza va fi:

SELECT * FROM Angajat
where
(cod = 1
 and salar > 250.0
)

Dacă angs nu va cuprinde nici un cod, fraza va fi:

SELECT * FROM Angajat
where
salar > 250.0

Deşi simplist, exemplul este concludent în privinţa puterii metodelor SQL. O variantă echivalentă în -- de pildă -- Php ar însemna câteva zeci de linii de cod. Complicat şi deschis erorilor.

Pluggable brains

Înainte de altceva, trebuie spus că rezultatul unei selecţii SQL este un obiect complex, dotat cu metode puternice care pot extrage numărul de linii returnate, definiţiile coloanelor, rezultatul ca listă de tuple sau ca listă de dicţionare etc. Însă magia de-abia acum începe.

În Zope, putem asocia unei metode SQL (de selecţie) definiţia unei clase Python. Astfel, fiecare linie a rezultatului poate fi tratată ca un obiect, deci ca o instanţă a clasei definite. Desigur, clasa poate avea metode specializate, care pot apela scripturi Python, metode externe sau chiar alte metode SQL, constituindu-se astfel într-un "creier" înglobat într-un rezultat (de unde şi numele pe care-l poartă în Zope).

În exemplul precedent, putem ataşa metodei SQL o clasă, pe care o putem numi Angajat:


Este evident: metoda numeIntreg va formata numele complet al angajatului (pentru simplitate, n-am mai procesat stringurile prin capitalizare). Un exemplu trivial de utilizare într-un script Python:

a = [1, 3, 4]
for r in context.SQLTest(angs=a, sal=250):
  print r.numeIntreg(), r.salar
return printed

Ar mai trebui spus doar că aceste clase asociate sunt într-un fel asemănătoare metodelor externe, deoarece codul lor este stocat în fişiere pe disc şi nu sunt supuse limitărilor aplicate scripturilor Python.

Moştenirea şi achiziţia

În lumea programării obiectuale (sau orientată pe obiecte), moştenirea este calea firească prin care obiectele unei clase sunt dotate cu un comportament implicit pe care-l primesc de la "părinţi" (clase de un nivel superior din perspectiva generalităţii), comportament pe care pot să-l specializeze prin adăugarea unor noi metode sau prin suprascrierea celor moştenite. Procedeul se numeşte "subclasare" şi întregul edificiu Zope se bazează pe această tehnică. Spre deosebire de cele mai multe limbaje obiectuale, Python este un limbaj cu moştenire multiplă -- adică o clasă poate moşteni comportament de la mai mulţi ancestori. De exemplu, clasa Folder moşteneşte de la clasa ObjectManager comportamentul de container de obiecte (de pildă capacitatea de a cunoaşte obiectele pe care le conţine), de la clasa OnjectManagerItem comportamentul tipic al oricărui obiect ce poate fi administrat prin web (aproape toate) iar de la clasa PropertyManager abilităţile privind gestionarea proprietăţilor.

Desigur, moştenirea este extrem de importantă pentru proiectarea componentelor, fie from scratch (în Python) fie pe baza aşa-numitelor ZClasses (un procedeu mai simplu, prin ZMI, dar mai limitat). Însă pentru proiectanţii de aplicaţii Zope mult mai important este un alt procedeu prin care obiectele îşi specializează comportamentul: achiziţia (aquisition).

Intuitiv, achiziţia este extrem de simplă: obiectele "achiziţionează" comportament de la obiectele care le conţin (adică de la container -- de regulă folder). Cum şi containerele sunt cuprinse în alte containere, rezultă că achiziţia funcţionează pe ramuri ale ierarhiei de obiecte din ZODB. De exemplu, dacă un obiect (să zicem o metodă DTML) face referire la un obiect care nu există în folderul său, atunci obiectul referit va fi căutat în folderul părinte. Şi aşa mai departe, până la rădăcină.

Ca de obicei, un exemplu simplu se impune. De obicei, construcţia unei pagini HTML printr-un document sau printr-o metodă DTML urmează următorul tipar:


Adică se plasează la început şi la sfârşit nişte obiecte (de fapt metode DTML) care la modul minimal arată aşa:

Standard_html_header:


Standard_html_footer:


Aceste metode pot fi plasate în folderul rădăcină, ceea ce înseamnă că orice pagină astfel construită le va achiziţiona. Dacă pe o anumită ramură dorim să particularizăm prezentarea (de pildă să punem o altă culoare de fundal), vom plasa în folderul corespunzător un alt standard_html_header, astfel încât pentru obiectele conţinute în folderul respectiv (şi în sub-arborele corespunzător), achiziţia se va opri la noul obiect.

Farmecul achiziţiei constă în specializare: chiar dacă headerul este achiziţionat, atributul title_or_id este cel propriu. Aceasta ne sugerează o altă metodă de a schimba fondul paginii în funcţie de context: putem crea o proprietate color în folderele care ne interesează, pe care o includem în tag-ul body:


Achiziţia funcţionează şi aici. Pentru obiectele care nu au proprietatea color aceasta va fi achiziţionată. La fel se întâmplă şi cu utilizatorii şi drepturile acestora şi cu orice altceva. De fapt, achiziţia este un mecanism bazat pe containerizare şi implementat prin spaţiile de nume.

Ce-a mai rămas...

Oricât de întinsă ar fi această prezentare, ea nu poate decât să puncteze câteva dintre aspectele acestui produs extrem de complex. Nu am vorbit nimic cataloage, despre aplicaţiile de administrare a conţinutului bazate pe Zope (CMF, Plone etc.) şi doar am amintit despre comunitatea Zope, care a creat sute de componente extrem de utile, de la cele mai simple (cu valoare mai mult didactică) şi până la blog-uri sau portaluri comerciale la cheie...

Admit că acest articol are şi o notă propagandistică şi că urmăreşte să vă stârnească interesul pentru un produs open source exemplar.

Resurse

www.zope.org -- situl oficial al comunităţii Zope. Aici se pot găsi sursele Zope, distribuţiile oficiale, documentaţie (începând cu "biblia" The Zope Book), sute de componente (products) realizate de comunitate precum şi referinţe la alte situri despre Zope.

www.zope.com -- situl oficial al Zope Corporation. Pe lângă prezentarea produselor comerciale al firmei, se pot găsi numeroase studii de caz privind diferite organizaţii care au implementat soluţii Zope.

Plone.org -- situl oficial al comunităţii Plone, un sistem de administrare a conţinutului complet, open source, bazat pe Zope.

www.cps-project.org -- situl proiectului CPS (Collaborative Portal Server), în sistem open source de colaborare dezvoltat de firma franceză Nuxeo împreună cu o comunitate de voluntari. Produsul a fost prezentat în NetReport nr. 9.


 

(Publicat în NET Report 11 - octombrie 2005)

 

Copyright © 2005 Agora Media

Creative Commons License
This work is licensed under a Creative Commons License.