Visualizzazione post con etichetta it. Mostra tutti i post
Visualizzazione post con etichetta it. Mostra tutti i post

30 agosto 2016

CORS, apache e jQuery, ovvero "il triangolo di ajax"

Cosa sono le chiamate CORS? 

Le chiamate CORS (cross-origin HTTP request) sono da sempre una bestia nera di cui il programmatore medio del web sa poco o niente.
Nella sostanza si classificano come chiamate CORS tutte le chiamate a servizi che risiedono su un dominio differente da quello della pagina che origina la chiamata.

Se dalla splendida homepage del mio sito:
http://miosito.mio/index.html
voglio chiamare un servizio sul server del mio amico all'indirizzo:
http://serverdelmioamico.suo/servizio.php

La chiamata tipica ad un servizio tramite ajax con jQuery è:
  $.ajax("http://serverdelmioamico.suo/servizio.php",
    {data:jsondidati,
     success:function(data){ //qualcosa col risultato}
  });

questa chiamata ricade nella categoria CORS.
Cosa significa? Che il browser la effettua, la chiamata arriva al server che la elabora e invia la risposta, il browser riceve la risposta e....la scarta.
Perché? Perché di default le chiamate CORS sono disabilitate dal browser e dal server, per motivi di sicurezza.


Come si abilitano le chiamate CORS?

Servono 2 interventi, uno sul client e uno sul server. Il client deve dichiarare di fare una CORS, il server deve acconsentire al client dicendo esplicitamente che acconsente alla richiesta.
Spezziamo quindi l'intervento in 2 parti.

Abilitazione sul client (jQuery)

Il client aggiunge un parametro che consente a jQuery la corretta gestione della chiamata, in termini di header inviati e gestione della risposta. La chiamata usata sopra a titolo d'esempio, diventa:

  $.ajax("http://serverdelmioamico.suo/servizio.php",
    {data:jsondidati,
     success:function(data){ //qualcosa col risultato},
     xhrFields: {withCredentials: true }
  });

Perché questo funzioni è opportuno usare almeno la versione 1.5.2 di jQuery, cosa che, di questi tempi, è assai probabile, ma in caso di vecchio codice da estendere potrebbe non essere scontato.

Abilitazione sul server (Apache)

Prima di tutto è indispensabile abilitare il modulo headers di apache (a2enmod headers su debian e ubuntu).
A questo punto la via semplice, se tutto funziona, è aggiungere un header sul server. Assumendo che si tratti di apache 2.4, l'operazione è piuttosto facile. Nel VirtualHost, o nel file ".htaccess" basta aggiungere la riga:

Header set Access-Control-Allow-Headers "*"

I dettagli sono qui (http://httpd.apache.org/docs/current/mod/mod_headers.html)*.
Nella sostanza questa riga imposta all'header della risposta una informazione che dice: non importa chi mi chiama, a me va bene comunque, browser caro, mostra pure la risposta.

Questa operazione apre il servizio a tutti i chiamanti, senza restrizioni. Per fare in modo che le chiamate siano limitate a quelle provenienti da pagine del server "http://miosito.mio/", la riga dovrà essere:
Header set Access-Control-Allow-Headers "http://miosito.mio/"

Giusto per stare sicuri si possono aggiungere queste 2 righe

Header add Access-Control-Allow-Headers "origin, x-requested-with, content-type"
Header add Access-Control-Allow-Methods "PUT, GET, POST, DELETE, OPTIONS"

Che dovrebbero essere autoesplicative, ma nel dubbio dicono che si permette l'accesso se la chiamata arriva con uno degli header e uno dei metodi elencati fra virgolette.

Purtroppo l'intreccio delle regole di chiamata fa si che questa base non sia sufficiente se vogliamo che il chiamante non sia limitato ad una sola origine (cioè se siamo nel caso in cui ci serve "*" invece di un nome server specifico).
La richiesta withCredentials infatti richiede che Access-Control-Allow-Headers abbia come valore il server d'origine e non un wildchar ("*"), ma togliendola il server non manda l'header per abilitare la CORS.
Quindi serve un piccolo sforzo in più lato server, una volta per tutte.
Il server deve leggere il,l'origine del chiamante (comprensivo di protocollo e porta) e usarlo per impostare l'header di risposta.
Questa operazione, apparentemente ardua, si risolve in realtà con uno sforzo piuttosto piccolo (a sapere come), basta tramutare la nostra riga in:

SetEnvIfNoCase Origin "https?://(www\.)?(.*)(:\d+)?$" ACAO=$0
Header set Access-Control-Allow-Origin %{ACAO}e env=ACAO

Questa diavoleria legge l'header Origin, controlla se ha l'aspetto indicato (che poi è qualunque cosa inizzi con http:// o https:// e mette il valore in una variabile (ACAO, visto l'amore degli informatici per gli acronimi).
L'header a questo punto viene impostato con il valore della variabile ACAO, che poi è quello dell'origine.

Giusto per stare sul sicuro si può aggiungere

Header set Vary Origin

agli header restituiti, che serve a gestire alcune situazioni piuttosto particolari, ma visto che male non ne fa, tanto vale averlo.

Il blocco complessivo diventa:

SetEnvIfNoCase Origin "http(s)?://(www\.)?(.*)(:\d+)?$" ACAO=$0
Header set Access-Control-Allow-Origin %{ACAO}e env=ACAO
Header add Access-Control-Allow-Headers "origin, x-requested-with, content-type"
Header add Access-Control-Allow-Methods "PUT, GET, POST, DELETE, OPTIONS"
Header set Vary Origin


Ultima nota: se si vuole aprire ad una serie di server e non a tutti, la riga del SetEnvIfNoCase deve essere modificata di conseguenza. Per fare un breve esempio, se vogliamo che a chiamare possa essere miosito.mio con tutti i suoi domini di terzo livello (es: test.miosito.mio, prova.miosito.mio, blog.miosito.mio, ...) e serverdimiocugino.suo con i suoi domini di terzo livello, la riga diventerà:

SetEnvIfNoCase Origin "http(s)?://(.*\.)?(miosito\.mio|serverdimiocugino\.suo)(:\d+)?$" ACAO=$0

Si tratta (come l'altra, del resto) di una espressione regolare, che si legge più o meno così:
http con o senza "s" seguito da "://", eventualmente qualcosa, ma se c'è deve essere seguita da un ".", (come potrebbe essere "www.", "test." o altro) seguito da miosito.mio oppure serverdimiocugino.suo, seguito eventualmente da un ":" e dei numeri (indicazione della porta).


* Una piccola nota a riguardo per chi giustamente ha scelto di saltare il manuale: "Header set" imposta un header, mentre "Header add" lo aggiunge. Ai nostri fini la differenza è probabilmente nulla, ma i problemi sorgono quando da codice viene già impostato l'header in questione. Nel caso di "add" l'header viene raddoppiato (e alcuni client un po' permalosi non la prendono bene, specie se non hanno lo stesso valore), mentre "set" sovrascrive il valore. Esistono anche "setifempty", che imposta il valore solo se non era già presente e "merge" che prova ad estendere una lista di valori per gli header a valore multiplo.
La realtà è che manipolare gli stessi header a livello di codice di servizio e di configurazione del web server può portare a una serie di situazioni impreviste e richiede un coordinamento che difficilmente si mantiene nel tempo. Sarebbe buona norma quindi decidere a priori quali header siano da impostare a livello di apache e quali a codice, evitando sovrapposizioni, dove non sia strettamente necessario.
Read more

28 luglio 2014

Improprietà di linguaggio: crescita esponenziale

Avendo un background a suo modo solido in campo scientifico/logico/matematico e una carriera, accademica e professionale nell'IT ho ovviamente un vocabolario adeguato alla mia materia. Quando parlo d'altro dico probabilmente una gran quantità di cazzate senza neppure rendermene conto e magari senza saperlo, semplicemente perché non conosco il vero significato di certe parole o locuzioni e le uso "per sentito dire".

La trave nel mio occhio non mi impedisce certo di vedere che pure gli altri vanno in giro con lunghi pali piantati nei loro.

Questa mattina mi è capitato di sentire un giornalista parlare di "aumento esponenziale" quando molto probabilmente il significato era solo "forte aumento", con buona probabilità circa lineare, sebbene molto "ripido".
Passi? Boh, anche no....ma anche sì.
In questo caso la crescita esponenziale è un'iperbole (ah, che bel gioco di parole), nel senso della figura retorica. L'errore c'è ed è grave, perché la comunicazione corretta fallisce, ma si riesce a comunicare un senso di ansia (però ingiustificato).

Un mesetto fa, sentendo parlare di marketing e traffico web avevo già sentito la terribile definizione di "crescita esponenziale" e in quel caso, essendo parte in causa, sono sicuro che si stesse parlando di un aumento lineare, di quelli belli precisi da y= ax, senza neppure il "+b" a disturbare (e un "a" non troppo spinto).

La gente non sa, o non capisce o non si rende conto che, oltre all'errore matematico, sta facendo anche un madornale errore di comunicazione e, ben più grave, un errore di comprensione dei fenomeni che descrive.
Sia che non si conosca la differenza, sia che la si conosca ma non la si riconosca nei dati, la differenza fra un aumento lineare e uno esponenziale è concettuale e generalmente indica dei comportamenti sottostanti totalmente differenti.

Non solo le 3 funzioni sotto, con crescita lineare, cubica ed esponenziale, non crescono allo stesso modo, ma quasi sicuramente sottendono comportamenti totalmente differenti della popolazione (parlando di marketing).




E poi ci si chiede perché le aziende falliscano...





Read more

25 luglio 2014

Improprietà di linguaggio

La gente comune che lavora con l'informatica senza un background sembra non riuscire a entrare nell'ottica che la controparte di front end è back end ma che questa non ha nulla a che spartire col back office, a sua volta dotato (solitamente, soprattutto nel web) di un suo front end e di un suo back end.
C'è un cortocircuito cerebrale fra flussi aziendali e tecnologia che denota che i non tecnici spesso non capiscono un cazzo neppure di flussi di lavoro aziendali (sì, sono tecnocratico, e allora?!? ).

Per la cronaca la controparte del back office è il front office.

L'incomprensione è così generale e profonda che il linguaggio improprio si è imposto e adesso è considerato accettabile dire le cose a cazzo invece che distinguerne gli ambiti.
Ecco che quindi wikipedia si arrende e nella sua ultima voce della differenziazione fra front end e back end scrive:
  • Nella programmazione e sviluppo dei siti web viene definito front end la parte visibile da chiunque e raggiungibile all'indirizzo web del sito e viene definita back end la parte di amministrazione di un sito (modifica contenuti, creazione pagine) accessibile solo da amministratori del sito web. Front end e back end si utilizzano solamente quando il sito web è dinamico.*


Un po' come io mi sono arreso al fatto che in questo paese di analfabetimezzorincoglioniti "l'ho" venga scritto "lò". Prendo atto, capisco, ma non mi piego.**




* è curioso che nella lingua da cui prendiamo questi termini la confusione avvenga solo nel contesto molto specifico dei CMS, e non in qualunque flusso aziendale...la fonte è sempre la medesima voce di wikipedia, nelle 2 traduzioni:
ITA: http://it.wikipedia.org/wiki/Front-end_e_back-end
EN: http://en.wikipedia.org/wiki/Front_and_back_ends

** lò, pur essendo grammaticalmente scorretto, sarebbe di per sé accettabile, trattandosi di una sorta di neologismo che non porta confusione...sarebbe più calzante dire che invece di scrivere "l'ho" diventa "lo" che ha un diverso significato e quindi generando confusione


P.S: per capire di cosa ho parlato se non ne avete idea: http://it.kioskea.net/contents/776-front-office-e-back-office




Read more

3 dicembre 2013

ProFTPd

Dopo secoli torno a parlare di tecnologia e lo faccio nel modo più classico del mondo: sono inciampato in un argomento apparentemente banale, in realtà spinoso. Uscitone vittorioso, ne condivido il percorso, per la gioia di grandi e piccini.

Task: installare un server FTP su una macchina linux e configurarlo per degli accessi da utenti specifici.
Livello di banalità apparente: 10/10
Sbatti reale: 1000

La prima cosa è scegliere quale server installare. La macchina ha su Debian e la scelta si gioca principalmente fra ProFTPd, PureFTPd e vsftpd. Qui un breve confronto fra i tre concorrenti.
Per ragioni che non sto a sviscerare scelgo ProFTPd.

NOTA BENE: questa non è una guida alla configurazione sicura e ottimizzata del serer proFTPd e non intende esserlo, ma è solo una guida veloce a come risolvere dei problemi comuni senza perdere ore su internet e vagare per mille pagine di manuale, dando invece una visione organica e funzionale in pochi minuti.

L'installazione è banale come un semplice

apt-get install proftpd-basic

Un attimo e siamo online...e invece? E invece scegliendo per un server gestito da xined invece che standalone ci si butta in un piccolo delirio che non vale la pena di affrontare: le risorse occupate sono oggettivamente così poche che vale la pena tenerlo acceso e risparmiarsi lo sbatti.
Quindi, nella configurazione ( /etc/proftpd/proftpd.conf ) bisogna assicurarsi di avere la riga:

ServerType                      standalone

E con questo, magia, parte.
Primo accesso con un utente di sistema e tutto funziona. Ma come si arginano gli utenti a non andare in giro a curiosare per il mondo?
La cara vecchia impostazione dei permessi potrebbe pure essere una strada, ma non è praticabile, perchè significa precludere agli utenti locali di girovagare per il sistema. Quindi?
Quindi la risposta è in questa riga nel file di conf:

DefaultRoot ~

Ossia gli utenti si trovano ad avere la loro home ( ~, appunto, per chi non ha dimestichezza) come root, durante il loro accesso ftp. Da lì non si esce. Ci sono degli effetti collaterali, in questo, che sono ben spiegati in questa pagina sul chroot.

Per riavviare il server basta il solito:

/etc/init.d/proftpd restart

Fatto?
No, a meno che non vogliate creare un utente di sistema per ogni utente ftp, cosa che a volte va bene e a volte no.
Qui parte uno sbatti facile, ma lungo e articolato, se non ci avete mai avuto a che fare.

Step 1: abilitare l'autenticazione su un file alternativo

Nel file di conf deve essere aggiunta una riga come questa:

AuthOrder                       mod_auth_unix.c mod_auth_file.c

dove mod_auth_file.c indica che proFTPd può leggere i dati di autenticazione dell'utente in un file ad hoc separato da quello delle password di unix/linux. Qui si può leggere un approfondimento, che parla anche della questione del chroot discussa prima.
L'ordine conta e quindi prima il server usa mod_auth_unix.c (verifica fra gli utenti di sitema), poi in un file esterno.
Ma quale file?

Step 2: indicare il file alternativo di autenticazione

Semplice, si fa con questa riga:

AuthUserFile /etc/proftpd/vusers.conf

si indica dov'è il file in questione. Il path è arbitrario, come il nome file. Meglio scegliere un percorso fuori dal path raggiungibile/visibilie/modificabile dagli utenti. Nel mio caso l'ho messo nella stessa cartella del file principale di configurazione.
Il file deve essere esistente e leggibile, ma può anche essere vuoto.

Step 3: popolare il file per gli utenti virtuali

Un file di utenti vuoto serve a poco. Il modo più facile per popolarlo è usare il comando ftpasswd.
Per usarlo basta scrivere nella shall:

ftpasswd --passwd --file=/etc/proftpd/vusers.conf --name=myuser --uid=15000 --home=/path/to/home --shell=/bin/sh

Il comando chiede la password da assegnare al'utente e ne mette una hash nel file selezionato, alla riga dell'utente. Se l'utente esiste già propone di modificare la password, altrimenti appende una nuova riga per il nuovo utente al file.

Dove --file indica il path al file indicato nel file di cofigurazione di proFTPd, e --uid è un qualunque uid (user ID, per i neofitissimi), anche inventato. Si può specificare anche un group id che, se omesso, è di default uguale all'uid.

La home per l'utente non viene creata, quindi deve essere un path esistente (non necessariamente subito, ma al primo accesso). La shell ha usi speciali che non interessano adesso, ma deve essere una shell esistente, quindi /bin/sh è la scelta di comodo.

Se volete che l'utente entri con lo stesso utente con cui gira apache, quindi possa leggere e scrivere in una cartella da cui legge e scrive una web-app, basta indicare lo uid di quell'utente. Per esempio, su debian/ubuntu l'utente è www-data, nel fail /etc/passwd c'è una riga relativa all'utente www-data ad cui si può prendere lo uid assegnatogli.
Stessa cosa per qualunque altro utente, se volete evitare la configurazione di dettaglio dei permessi.

ATTENZIONE: non mettete un utente virtuale con la home in una cartella in cui può fare danni. Per girare intorno al problema create nella webapp un link alla home dell'utente. In questo modo la webapp accede con percorsi relativi alla cartella ftp, ma non viceversa.


The END!
A questo punto è tutto configurato, e basta un semplice riavvio del server per vedere tutto funzionare. Volendo si può riavviare già alla fine dello step 2, dal momento che il contenuto del file degli utenti è letto dinamicamente al login.

Note di sistema
Mentre tutto va beatamente liscio usando debian/ubuntu, centOS riserva, tanto per cambiare, le sue piccole e fastidiose sorprese.
Prima fra tutte, il pacchetto proftpd NON è disponibile di default con yum.
Per usarlo bisogna seguire questi semplici step (presi in prestito dai tutorial di Digital Ocean, visto che decine di alternative simili che si trovano online non funzionano e si rischia di perdere ore alla ricerca del repository giusto...e non imbarcatevi nel download degli rpm, perchè le dipendenze sono più forti di quelle di un tossico allo stadio terminale), che sono però duri da trovare online:


rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm

e magicamente ci appare il pacchetto...ora si può pacificamente fare:

yum install proftpd

Inutile dire che la struttura dei file di configurazione cambia, ma il senso no. Banalmente proftpd.conf non è più in /etc/proftpd ma direttamente in /etc. Fossero questi i problemi....


Enjoy ;)











Read more

9 luglio 2010

Roba da NERD!

Mi rendo conto che chi, come me, può apprezzare e gioire di cose del genere, sia ormai allo stadio terminale di informatichite acuta.

So quindi che farei una figura migliore a tacere e non pubblicare certe cose, che mi catalogano indiscutibilmente nella categoria dei nerd.

So anche che alcuni dei lettori/autori di questo blog sono ridotti male quanto me, forse peggio (spero per loro di no). Per questo abbandono la decenza e pubblico.

Il gioco è fare un programma che canti la canzone delle 99 bottiglie di birra, il cui testo è visibile qui.

Queste sono delle soluzioni che non stento a definire eccezionali!


Soluzione "bottiglie"

Soluzione "bottiglia"

Grandiosi.
Grazie a tutti i perl monks (e simili) di esistere e rendere questo mondo un po' più bello...
Read more

18 giugno 2010

Acronimi e filosofia

L'informatica si è evoluta in modo strano.
Centinaia di acronimi l'hanno sempre popolata, rendendo la vita difficile ai non addetti ai lavori, ma non rendendola facile neppure a chi ci vive in mezzo, perché ci sono troppi acronimi e alcuni sono ripetuti in contesti diversi con significati diversi. Delirio.


In qualche momento di questo gioco di follia collettiva sono arrivati anche gli acronimi ricorsivi, perché visto che era già una giungla intricata, perché non provare a farla diventare un mega parco giochi per nerd?!?


In un altro momento si è iniziato a fare anche filosofia informatica per acronimi. In questo modo sono nati 2 principi interessanti che mi piace portarmi dietro come compagni di viaggio nella mia carriera, sempre meno brillante (e non certo per causa mia), di programmatore.


Due, dicevo: KISS e DRY. Il primo è una filosofia di vita, più che informatica, e significa Keep It Simple, Stupid.
Riporto da wikipedia: "The KISS principle states that simplicity should be a key goal in design, and that unnecessary complexity should be avoided."
Il problema di questo principio è che è espresso da nerd per nerd, mentre dovrebbe essere un principio generale ed inalienabile della vita di tutti i giorni.


I pipponi sono divertenti, quando sono fatti per il gusto di farli, tutti gli altri scopi andrebbero evitati.


L'altro, DRY, sta per Don't Repeat Yourself. Non ripeterti. Non è che siamo tutti Paganini, il significato è un po' diverso: la verità deve restare coesa, non sparpagliata in più punti, se questi rischiano di andare alla deriva sino a contraddirsi fra di loro. In informatica è un principio fondamentale, troppo spesso dimenticato. Se chi ci lavora lo infrange troppo spesso, non stupisce certo che il nostro governo lo ignori completamente.


Giusto per non lasciare questo parallelismo con la nostra disastrata vita politica abbandonato nel nulla, a proposito di KISS, pare che Antoine de Saint-Exupéry abbia detto (non so in che lingua in originale, quindi bisogna accontentarsi dell'inglese) "It seems that perfection is reached not when there is nothing left to add, but when there is nothing left to take away", che suona più o meno così: la perfezione non si raggiunge quando non c'è più nulla da aggiungere, ma piuttosto quando non c'è altro che si possa togliere.
Ecco, guardando il nostro parlamento, in relazione a chi c'è seduto, lo vedo drammaticamente lontano dalla perfezione...





Read more

27 maggio 2010

Odio tecnologico

Seguendo una serie di articoli che mi convincessero a buttarmi nel mondo Java, mi sono imbattuto in un genio.

Qui potete godervi la sua dissertazione sui framework che rispecchia tutto il mio pensiero per Java, che, per chi come me viene dal mondo Python, non sembra altro che un immenso modo di complicare gli affari semplici....
Read more

18 maggio 2010

Il diverso scorrere del tempo

Sto mettendo mani ad una applicazione abbandonata, sviluppata da altri, di cui non resta praticamente nessuna documentazione. Essendo ancora in produzione, è il caso di aprirla e vedere come funziona e quindi eccomi qua.

È un'applicazione Java, ufficialmente di pochi anni fa. Voci dicono che nella realtà si tratti di un restyling di un ritaglio di una applicazione originariamente risalente al periodo 1999-2000.

Quando ho iniziato l'attività non lo sapevo e mi stupivo di scelte che mi sembravano datate anche per quando ho iniziato a lavorare io (2000) e di approcci che mi pareva fossero concettualmente non aggiornati per quando ero all'università ('95-'01).
Adesso sapendo che l'applicazione è di quel periodo, quindi sviluppata prima e pensata ancor prima, non sono più stupito: tutto torna. Era al passo coi tempi.

Al passo coi tempi significa solo 10 anni fa. Nel mondo non è cambiato molto, in Italia ancora meno...abbiamo anche lo stesso imbarazzante presidente del consiglio, che del resto avevamo anche 5-6 anni prima.

Nel mondo dell'informatica è cambiato tutto. Nel mondo del web si stenta a riconoscere che sia la stessa epoca storica.

All'epoca c'era java 1.2 e non da molto. Probabilmente il prodotto ha iniziato a essere pensato con la 1.1. Era stata rilasciata da poco, o forse addirittura in corsa, la specifica delle Servlet, ma non ancora di JSP. Difficile ricordare un mondo così. Effettivamente leggendo il codice sembra di vedere l'approccio alla CGI in Perl che andava in voga prima dei vari template per il rendering.

C'era già JDBC, per fortuna. Muoveva i primi passi XML come standard di fatto.

Mi sono chiesto a che punto fosse la concorrenza all'epoca. ASP da un lato, PHP dall'altro. ASP era avanti, come mi racconta un collega che all'epoca aveva seguito le vicende web e il tentativo di Sun di non far schiacciare la sua piattaforma da Microsoft. PHP era avanti anni luce, per funzionalità, ma anche per storia; purtroppo è sempre stato fuori dall'ambiente enterprise e all'epoca questo era anche per filosofia del linguaggio stesso. Java, dunque, ma forse Perl sarebbe stata un'alternativa non meno valida. Il campione uscente della programmazione web stava già perdendo visibilmente terreno, ma era pur sempre la scelta "nota".

Non era ancora chiaro infatti, all'epoca, che Java sarebbe diventato il mostro a più teste che è oggi. Muovevano i (primi? ultimi? )passi in quei tempi decine di alternative, spesso proprietarie. Il mondo non aveva ancora scelto di basarsi su pochi standard concorrenti e preferiva ancora soluzioni tecnologiche ad hoc.

Parlando con una amica domenica alla festa di Gaia riflettevo su come un tempo l'unico grande metro di paragone fossero le prestazioni, perché erano limitate. Adesso le prestazioni offerte da workstation neppure troppo d'avanguardia sopravanzano di gran lunga le esigenze di singole applicazioni e le prestazioni sono passate in secondo piano rispetto alla flessibilità.

Prestazioni contro manutenibilità contro flessibilità (e chi pensa che manutenibilità e flessibilità siano parenti ha una idea molto ridotta o del concetto di flessibilità o di quello di manutenibilità).
La filosofia è cambiata molto e il peso delle tre componenti è cambiato radicalmente.

Sembra che abbia fatto un trattato di storia sugli anni '60 o forse anche più. Che abbia raccontato di un mondo che la mia generazione non ha mai visto. Invece sono solo 10 anni, come se il tempo della vita e quello dell'informatica corressero su binari separati.


Read more

8 aprile 2010

Uno sfogo tecnologico

Ultimamente sono in cerca di lavoro. Questa condizione, che non mi era più relamente capitata dal 2005, credo, mi ha aperto gli occhi in modo disarmante su una situazione che già conoscevo, già soffrivo, già esponevo, eppure mi restava, nella sua dimensione globale a livello inconscio.
L'Italia è informaticamente un paese retrogrado.
Non sto parlando della scarsa alfabetizzazione informatica o del fatto che un numero immenso di persone ha competenze ridotte e poche interazioni con l'informatica, lato utente.
Sto parlando dell'altro lato della barricata, il lato degli sviluppatori, delle società che si occupano in modo diretto o indiretto di software e di quelle che hanno immensi reparti IT gestiti dalla solita mandria mista di interni e consulenti.

L'Italia è un paese conservatore. Questo, in apparente contrasto con una buona vocazione artistica e creativa, è un fatto noto e, in generale, innegabile. In alcuni casi essere conservatori paga, in altri no. Come sempre anche le doti, portate a dogmi di vita, finiscono per diventare vizi.

Parecchi anni fa ho intrapreso la strada che mi ha portato a conoscere Python, un linguaggio di programmazione come tanti, uno dei più diffusi al mondo, non nell'olimpo della diffusione, ma distintamente sopra il marasma di tecnologie che bollono nel pentolone di alcuni smanettoni per poi evaporare senza aver lasciato il segno.
Un annetto fa lo mettevo orgogliosamente in cima alle mie competenze parlando con vecchie conoscenze in una rinomata società di consulenza. Mi hanno tutti chiesto cosa fosse.
Un amico lavora oltre confine in Ruby. Decisamente meno diffuso in generale, ma con una forte presenza in ambito web grazie al successo in una epoca storica decisiva del suo framework Ruby On Rails.
Sono potenzialmente i linguaggi del futuro, pur venendo da un passato non poi così vicino attraverso la lunga strada verso la maturità. L'ha capito Google, lo sospetta Microsoft, c'ha scommesso Nokia, se lo chiede l'elite dei guru del mondo Java.

Oggi ho fatto la prova di usare Monster, sito ormai quasi universale, per un piccolo esperimento:
ricerca di lavoro con parola chiave Python.
Italia : 11 risultati
Svezia: 30 risultati
Polonia: 11 risultati
Germania: 89 risultati
UK: 127 risultati

Altra prova, simile, ma con Ruby:
Italia : 10 risultati
Svezia: 18 risultati
Polonia: 1 risultati
Germania: 45 risultati
UK: 56 risultati

Cioè è giusta la percezione che Python è generalmente più diffuso di ruby ma in europa l'Italia pareggia con la Polonia su Python, mentre la sconfigge su Ruby. Il punto è che la Polonia è il classico paese emergente che deve convertire la sua economia, i nostri concorrenti dovrebbero essere germania e UK (non ho considerato la francia, ma credo i dati non si scosterebbero più di tanto). Ci distanziano entrambi di svariate lunghezze. E che dire della Svezia? Fa meno abitanti della lombardia ma ci tiene testa!

Ho provato a fare la ricerca anche con Java, usando J2EE come parola chiave, tanto per vedere cosa ne usciva.
Italia 161
Svezia 43
Polonia 36
Germania 397
Regno unito 196.

Cioè il mercato per gli sviluppatori in Italia offre effettivamente meno spazio che in altri paesi, ma sulle tecnologie comprovate che ogni buon conservatore sceglierebbe in ambiti mission critical, i numeri si avvicinano molto di più ai rapporti di popolazione e a quelli presunti di mercato.

Oggi parlavo di cloud computing. Nel mondo l'argomento spumeggia, in Italia molti addetti ai lavori l'hanno sentito ma storcono il naso, e 6 mesi fa ti guardavano come se stessero vedendo un marziano con le antenne.

Sono scoraggiato. Ho come l'impressione che se potessero, molti dirigenti farebbero usare ancora la macchina da scrivere per scrivere documenti.
Vita dura per chi come me ama sperimentare nuove soluzioni.
Read more

29 gennaio 2010

Me la meno!

Ho recentemente scoperto il sito rosettacode.org, che tratta la soluzione di determinati problemi o design patterns in diversi linguaggi, oltre che come i vari linguaggi approccino problematiche diffuse.
Nella seconda casistica ricadono cose tipo la chiamata di funzione con il passaggio di parametri per nome, di cui avevo parlato tempo fa nel mondo Python.
Nella prima casistica ci sono approcci di linguaggi differenti a problematiche ben note, fra cui per esempio la scomposizione in fattori primi.
Problema matematicamente noto, quindi non particolarmente interessante, che in Python viene risolto facendo uso di alcuni costruiti che non avevo mai usato.
Mi sono quindi imbastito a confrontare il programma fornito, la sua versione ottimizzata e la mia soluzione al problema.
Dopo qualche tentativo andato in fumo a causa di un erroraccio di ottimizzazione da parte mia, sono fottutamente orgoglioso di dire che la mia versione disintegra la soluzione ottimizzata proposta, con un fattore che in svariati casi è vicina al 2 a 1 su numeri oltre le 10 cifre.
Le versioni originali le trovate qui, quelle python esattamente qui, mentre la mia, la potete leggere a seguire:



Per la cronaca ho preso e completato la versione Java proposta e l'ho paragonata alla mia e risulta piuttosto evidente la superiorità della mia soluzione. Il paragone è fra Java 1.5 e Py 2.6 (lo dico a beneficio di chi dovesse confrontare java 1.6 contro Py3 e trovare risultati magari eclatantemente diversi).
Il problema è, ovviamente, di sola mancata ottimizzazione del codice Java. Dico ovviamente perché Java è più performante di py di parecchie lunghezze, se si parla di calcoli puri fortemente iterativi. L'inghippo sta nel fatto che il codice Java è scarsamente ottimizzato, mentre quello Py, e il mio in particolare, è piuttosto ben limato.
Questo per ricollegarci in modo nuovo al discorso sui benchmark di qualche tempo fa: un algoritmo ottimizzato in un linguaggio lento spesso è prestazionalmente migliore di uno scritto alla buona in un linguaggio veloce.
Ovviamente ottimizzando per quanto possibile la versione Java (non ho trovato in 5 minuti di googling come fare la radice quadrata di un BigInteger) siamo andati a pari sino a cifre molto molto grosse, poi dai 16 digits in su....ha vinto lui. Capita.

Ultima nota di colore: guardate questa implementazione nel liguaggio False (mai sentito nominare): [2[\$@$$*@>~][\$@$@$@$@\/*=$[%$." "$@\/\0~]?~[1+1|]?]#%.]d:

Astonishing!
Read more

6 settembre 2009

benchmark

Prima dell'estate ho letto un sacco di benchmark di paragone fra vari linguaggi di programmazione.
Cercavo, spunti, conferme e dati interessanti che mi permettessero di avere una valutazione prestazionale di varie tecnologie, che comunque rimane solo uno dei mille aspetti che determinano una scelta tecnologica rispetto ad altre.

Sono sempre stato un appassionato di classifiche, quasi un feticista delle classifiche. Delle scarpe col tacco o della lingerie no, ma delle classifiche sì, quindi in prima posizione la lingerie e in seconda i tacchi. Quindi i benchmark mi appassionano anche perché hanno sempre questo aspetto di confronto che definisce, spesso, un sacco di classifiche, per dati semplici, poi per aggregazione di dati non omogenei in partenza che vengono pesati...un'orgia di classifiche, insomma. Ma questa è un'altra storia.

Ho trovato un sacco di pane per i miei denti in rete. Ho letto ed elaborato informazioni. Poi sono stato assalito da dubbi. Ho cercato dove possibile di capire la natura delle prove che costituivano i benchmark in questione. I più interessanti si trovano qui: Computer Language Benchmark Game.

Sono tutti o quasi benchmark di problemi dalla soluzione nota risolubili con un algoritmo computazionale più o meno pesante, quasi sempre su interi o numeri in virgola mobile. Qualche eccezione prevede operazioni su stringhe e espressioni regolari triviali.

Mi ha colpito come su decine di prove si verifichino sempre le stesse cose, creando sproporzioni artificiose in favore di alcuni e contro altri: chi risolve bene i problemi di una categoria risolve bene, con minime varianti, i problemi della stessa categoria che presentano solo piccole varianti sul problema originale. Ergo chi vince sul primo vince su tutti, cioè piazza invece che un 1 a 0 un 10 a 0. Poi magari di un'altra categoria si pone un solo problema, in cui il primo linguaggio perde in favore del secondo, ma non esistendo varianti nel benchmark il risultato è 10 a 1 e non un 1 pari come forse sarebbe stato più giusto...

Vabbeh. Poco male. Le classifiche sono associazioni di dati, quando si vuole il dettaglio bisogna scavare.

E scavando appare anche evidente che l'obiettivo sembra quello di porre problemi relativamente facili da rappresentare, ma questo, seppure in senso lato, è un altro modo di categorizzare il mondo. Chi fa della flessibilità o della capacità di modellare situazioni il suo punto di forza perde magari tutti i confronti sui casi ben definiti, ma vince in quelli più difficili.

Per ora tutti discorsi generici, entro nello specifico, prendendo 4 linguaggi di cui ho una conoscenza di base sufficiente per poterne parlare con cognizione di causa: C, C++ (e chi dice C/C++ probabilmente è perché il C++ ha mantenuto la backward compatibility e crede quindi che sia una estensione con le stringhe nella STL del C), Python 2.x e Java 6. Li abbrevierò in C, C++, Py e J6.

Che in problemi strettamente computazionali il C batta Py e J6 è così evidente che serve a poco scriverlo...ma è comunque interessante sapere di quante lunghezze. Sul confronto C vs C++ si dovrebbero spendere troppe parole, fatto sta che i 2 per problemi del genere possono risolvere il problema in modo così simile da risultare quasi indistinguibili.

J6 straccia Py, merito soprattutto dell'ottimo JIT di Java e dell'esclusione di Psyco dai benchmark di Py (che farebbe scendere il rapporto da x30 a circa x1.3, che insomma, ha il suo peso).

I valori medi dei 4 linguaggi per tutti i benchmark su grossi numeri è:
C : 1.01
C++ : 1.00
J6 : 1.68
Py :54.03

Dove 1 è il valore del migliore, il resto in rapporto, credo.

Cosa emerge cercando meglio? che sono quasi tutti dei mega loop con operazioni matematiche semplici, ossia cose che se dici a un compilatore C di sbrogliare i loop, lui bello bello ti cabla la ripetizione delle operazioni nel programma, facendotelo diventare sì un tonno, ma togliendo pure quell'overhead dei jump a fine ciclo. Che se hai un JIT o simili il peso del JIT scompare. Che se sei un interpretato interpreterai 10mila volte 1+1...

Ora vediamo nello specifico che invece, se si prende il test regex-dna, che ha un po' di smanettamento sulle stringhe, si ottiene questa classifica, questa volta in secondi di esecuzione:

C++ : 19sec (ma bara, usando un botto di librerie di terze parti, e anche al soluzione da 26 secondi usa una libreria di espressioni regolari non standard)
Py : 29sec
j6 : 31sec (in server edition, per questo test)
C : 32sec

Questo perché? come può C perdere contro Py?
Diamo una risposta di base un po' strana: l'interprete Python è scritto in C, quindi formalmente un programma Python potrebbe essere considerato come un file di configurazione un programma C incredibilmente complesso. Quindi Py non batte realmente C. Il problema è che bisogna trovare un soluzione che tenga conto anche delle dimensioni e non solo delle prestazioni. Cioè non posso scrivere una applicazione da alcuni mega per uno stupido programma Py da 20 righe circa...
La realtà è che quindi il modulo delle espressioni regolari in Py lavora meglio di quanto possa fare uno bravo che scrive una soluzione specifica al problema senza voler costruire un missile nucleare per uccidere una zanzara.

Ora andiamo avanti con il ragionamento, ma spostiamoci nell'astratto: cosa succederebbe se a dover essere rappresentato fosse un problema abbastanza complesso da spingere un programmatore medio a scrivere una gerarchia di classi?
Il C++ lo affronterebbe benissimo, allo stesso modo J6 e Py, almeno da un punto di vista della rappresentazione della soluzione. In C si comincerebbe a chiedersi se il problema abbia complessità tale da imbarcarsi in una serie di struct di puntatori a funzioni, ossia della cosa che più si avvicina a una classe, senza però tutte le facility del C++, senza costruttori e distruttori, con meccanismi di ereditarietà tutti da inventare....

E se il problema fosse abbastanza complesso da spingere un programmatore C++ o Py a scrivere un programma strutturato con ereditarietà multipla? In J6 te la cavi o tramutando il canonico rapporto is-a con un has-a poi ridefinendo tutto il flusso della soluzione, o duplicando tonnellate di codice o smanettando con pratiche non molto comuni. In C ti metti le mani nei capelli e poi ti rassegni: comincia a costruire un castello immenso che richiede analisi approfondita del caso in questione...oppure abbandoni e lo fai in C++ :)

Però se un problema arriva a quel livello di complessità, sicuramente esistono più soluzioni plausibili per affrontarlo e quindi le soluzioni proposte non sempre sono pienamente paragonabili. Peggio, quelle più preformanti sono probabilmente quelle che prescindono da una buona rappresentazione del problema e vanno dritte al nocciolo, con buona pace della manutenibilità del codice e, peggio, della sua estensibilità.

Purtroppo banchmark su problemi di questo tipo è veramente dura trovarne, quindi ci si rassegna a un po' di speculazioni, che riguardano la bontà di tenersi una tabella di riferimenti ai metodi come fanno C++ e Py contro la ricerca in risalita lungo la gerarchia come fa (o faceva, su questo punto sono rimasto indietro, ma non credo abbiano cambiato) Java.

Poi c'è la questione che Java arriva insieme a librerie per fare di tutto di più, Python un po' meno, C++ mostruosamente meno, C è un disastro. Quindi quello che è un problema banale in un linguaggio in un altro potrebbe essere fonte di non pochi grattacapi, anche se poi, la soluzione specifica al problema potrebbe essere più performante.

Insomma, mi hanno detto in mille modi che una ferrari va più veloce di una persona, ma non mi hanno detto che la persona poteva tagliare per un vicoletto di 15 metri e la ferrari ha dovuto fare un giro di 15 km con 4 semafori per arrivare allo stesso punto.

Resta il fatto che se uno deve fare un loop in cui fa 10000 mila volte una somma e lo deve fare in modo performante, se sceglie un linguaggio interpretato è probabilmente un imbecille...
Read more

7 luglio 2009

Python sugar

Un po' di zucchero per mandare giù le pillole, fa sempre bene (quest'uso della virgola, 'enfatica', fra soggetto e verbo mi viene dalla recente lettura di un libro che ne teorizza il valore nella rappresentazione scritta del linguaggio parlato).

I dizionari sono una invenzione imperdibile e moltissimi linguaggi ad alto livello, usando nomi diversi, ne fanno un uso così intensivo da averli fatti assurgere a tipi di dato built-in (python, php, perl e javascript, tanto per citare quelli che mi vengono in mente al volo fra quelli che conosco).
Meno interessante, perchè più antico, l'uso di array (che ora sono delle liste mascherate, o almeno così pare, dietro la parvenza di array), ma comunque degno di nota.

è però anche necessario trovare un modo per usarli nel modo più efficace possibile.

Oggi vediamo uno degli zuccherini di python a riguardo.
Prendiamo le seguenti dichiarazioni:



2 funzioni, un dizionario e una lista. Beh?
Non vi piacerebbe un sacco poter usare il dizionario o la lista come parametri per le funzioni senza obbligare le funzioni a ricevere come parametro un'illeggibile lista (o un non meno illeggibile dizionario)?

Detto fatto:



Magia. Sti cazzi, magia!
Read more

9 giugno 2009

Console remota embeddable in python

Altro post di informatica pura, che del resto è il mio mondo molto più di quanto non lo sia la politica o la sociologia ;)

A parte le cazzate, il tema odierno è una facility che usai un tempo tramite twisted matrix, una grossa libreria che permette di rendere asincrono praticamente tutto, anche lo svegliarsi rispetto all'alzarsi dal letto, purche lo si faccia in python.

La facility in questione era l'esposizione di una console di python via rete. Sin qui è già una bella magia ma non stratosferica, più interessante il fatto che fosse anche integrata in un programma esistente (ed in esecuzione), dando quindi accesso, per i più svariati scopi, al programma medesimo, come se lo stessimo eseguendo dall'interactive console (IDLE, per intenderci) .

In rete ci sono pochissimi riferimenti a come ottenere il medesimo risultato senza portarsi dietro il tonno che è Twisted Matrix, per il quale non provo oltretutto grande simpatia.

C'è qualcuno che fa cose simili, ma o non le fa in rete ma con widget, o usa trucchi distruttivi sull'ambiente del programma rendendo impossibile l'integrazione in un contesto già esistente, o lo fa in modo così triviale che lo sforzo per renderlo funzionante in un contesto più generico supera il beneficio di avere già una base di partenza.

Mi è costato una giornata di prove assortite, ma alla fine sono arrivato a scrivere un pezzo convincente di codice, che come si vedrà, alla fine, è anche piuttosto ridotto, ma contiene dei barbatrucchi piuttosto importanti.

Come al solito, prima il codice poi i commenti! Eccolo:



I pezzi importanti sono due, il resto è solo un po' di fumo al contorn, ma lo vedremo.

Il pezzo chiave del tutto è il metodo mainloop della classe ipconsole.
Qui viene istanziato un interprete a cui vengono passate le stringhe lette dalla rete, qui si fa la lettura dalla rete e le varie interazioni con l'interprete medesimo.
Tutto sembrerebbe a posto, se non fosse che non c'è modo di redirigere l'output dell'interprete in questione senza gabbarlo. è qui che casca l'asino in quasi tutte le implementazioni che si trovano in giro.
Il modo logicamente più semplice di fregare l'interprete, e anche più pulito, sarebbe stato quello di eseguirlo in un ambiente modificato in cui il riferimento a sys fosse modificato (verso un clone di tutto il modulo sys e non un suo riferimento che non serve a nulla. In tale contesto diventa facile fare quello che si vuole di sys.stdout, sys.stderr ed eventualmente sys.stdin del sys clonato.
Se si riuscisse a ottenere questo clone, passarlo come contesto all'interprete sarebbe semplice, ma...peccato che non ci sia verso in Python di clonare un modulo (oddio, forse qualche modulo stupido sì, ma sys no). Che fare allora?
Si tarocca direttamente sys.stdout!
La soluzione è un po' invasiva e fa si che non sia completamente generica, ma almeno nei casi comuni (quelli che non manipolano sys.stdout e sys.stderr runtime e non si mettono a giocare coi reload di sys) è trasparente. Il trucco consiste nel creare una classe ponte che nasconda le 2 istanze stdout e stderr, incapsulandole.
Questa classe è appunto la mia fakeout.
fakeout manipola il metodo write, quello che, per questioni di interfacce, tutti gli stream writers devono esporre e stdout, che è un outputfile, non può esimersi da mettere a disposizione e viene usato anche da statement come "print".
Nel nuovo write, se il thread corrente non si è registrato, si passa la chiamata allo streamwriter originale (stdout o chi per lui), che è stato incapsulato durante la creazione dell'istanza di fakeout, se invece il thread corrente si è registrato, usando il metodo setout4thread, si usa il writer passato durante la registrazione.
In questo modo thread diversi possono registrarsi, ognuno usando come stdout taroccato un proprio writer.

Questa genialata mi è stata suggerita da una discussione su una newsletter (che mi sono salvato e potete leggere qui, perché è giusto che il merito vada a chi ha le idee) ed è la base della soluzione a tutti i problemi!

Beh, tutti si fa per dire, perché in realtà un problema rimane: la formattazione dell'output.
Il programma funziona correttamente con il telnet di Linux come client, ha dei problemi di formattazione con la raw connection di putty, nel senso che quando va a capo non torna a inizio linea, e non funziona con telnet di putty, che si ostina a mandare spazzatura ad inizio linea nonostante io provi a impostarlo diversamente, risultato netto: non interpreta nulla.
Credo comunque che siano problemi di configurazione del client e se qualcuno ha una idea di come arginarli o, ancora meglio, risolverli...ben venga!

Ah....quasi dimenticavo...per usare il tutto basta copiare il pezzo di codice di sopra in un file qualunque, chiamiamolo tcpconsole.py. A questo punto vi basta fare import tcpconsole dal vostro codice e poi una chiamata alla funzione tcpconsole.createServer passandole come parametro ip e porta su cui ascoltare.

Nulla di più semplice....sempre se il firewall di vista ve lo lascia fare....
Read more

8 maggio 2009

Contenitore in javascript

Come si costuisce una tabella in javascript per contenere dei dati? Quelle canoniche tabelle con una paccata di dati estratti da una query? E come si dà un modo comodo all'utente di navigarla?
Intendo per esempio la possibilità di ordinare i risultati in base a qualche attributo, piuttosto che paginare il risultato per non dover vedere 5000 righe di tabella in un colpo solo?

La soluzione canonica è usare PHP (o qualunque altro linguaggio lato server) per strutturare la tabella, aggiungere un po' di campi nascosti di una forma che vengono popolati da un po' di click in giro per la pagina e usati per inviare una richiesta al server per ottenere un altro ordinamento dei dati, piuttosto che l'accesso ad un altro subset di dati. 

È una soluzione, effettivamente. È una buona soluzione, anzi, che presenta alcuni notevoli vantaggi rispetto a quella che sto per descrivere, ma anche qualche svantaggio. Ma non è l'unica: si possono fare quasi tutte queste operazioni anche lato client, con un po' di sano Javascript, l'aiuto di DHTML e un approccio meno “old-style” al problema.

Prima di esaminare una possibile soluzione alternativa facciamo un attimo il gioco dei pro e dei contro delle due soluzioni, tanto per allenare la mente e confermarci, se mai ce ne fosse bisogno, che non esiste una soluzione sempre migliore, dipende quasi sempre tutto dal contesto.

Soluzione lato Server.

Pro:
- è tradizionale, un sacco di gente lo fa, si può eveitare di spremere il cervello.
- In caso di paginazione si possono inviare via rete pochi dati, relativi alla sola pagina visualizzata
- In caso di ordinamento è facile che il server abbia algoritmi più performanti di quelli di javascript

Contro:
- senza la paginazione, ma con ordinamento, a ogni ordinamento si passano tutti i dati, potenzialmente parecchi
- anche se la paginazione lato client è meno performante come algoritmo, è nulla rispetto alla latenza di una chiamata e risposta al server, quindi il risultato è quasi sempre molto più brillante
- il server diventa un collo di bottiglia. Con o senza paginazione rischia o di dover estrarre in continuazione i dati e inviarli o di doverli tenere in cache per ogni utente, con uno sforzo mostruoso (e una conseguente spesa non indifferente per dimensionare l'hardware...)

Soluzione lato Client:

Pro:
- è un po' più trendy, più web 2.0, come dicono le persone alla moda (soprattutto quelle che non sanno di cosa parlano)
- permette di cambiare impostazioni e interagire in modo molto più rapido
- usa le risorse del client, spesso più libero nel contesto di naviazione web, e occupa il server solo nel caricamento iniziale

Contro:
- i dati vengono trasferiti tutti subito, quindi il primo caricamento potrebbe essere più lento
- si usano le risorse del client, quindi la navigazione con un catorcio risulta poco agile
- le differenze prestazionali fra browser sono notevolissime, quindi una soluzione che appare ottima per prestazioni ed aspetto con opera o firefox potrebbe (beh, dai, il condizionale lo uso anche se il periodo ipotetico è purtroppo della realtà) risultare pessima con IE.


Insomma si sposta il peso di alcune operazioni. Cosa si meglio dipende dallo stile di navigazione degli utenti, ma anche, e soprattutto, dal tipo e dalla mole di dati presentati.

Detto questo presento un piccolo “widget”, se così vogliamo chiamarlo, per ora prototipale, che mi sono realizzato per studio e per comodità di riutilizzo. 
La mia soluzione è sviluppata in 3 classi ed è basata su jQuery, che permette una veloce e buona manipolazione del DHTML senza dover inseguire le differenze fra browser e ha delle API molto comode.
La prima classe che introduco è il contenitore dei dati, che con grande fantasia ho chiamato Container. Vediamolo:





Se non sapete nulla sulle classi in javascript, può essere utile prima approfondire questo argomento (ma per i nostri scopi basta dire, in modo improprio, che una funzione può essere il costruttore di un oggetto e che a quella funzione si possono agganciare dei metodi tramite la definizione di prototipi), altrimenti passiamo subito all'analisi del contenitore.

Quello che un contenitore deve fare è mostrare una serie di righe contenenti i risultati, permettere di ordinare per alcune (potenzialmente tutte, ma dipende dal contesto, e a volte nessuna) colonne, e spezzare il risultato in più pagine quando ci sono troppi dati.

Per costruire un nuovo Container necessitiamo di questi parametri:
htmlcontainerid: id dell'elemento html in cui vogliamo posizionare il contenitore. Tipicamente sarà un div, ma può essere quello che serve che sia.
myid: id del contenitore stesso. Non è in realtà poi così importante ai nostri fini, serve per ritrovarlo con i canonici metodi uasti da altri script nella pagina.
obja: array degli elementi contenuti. I contenuti sono oggetti di una classe, che vedremo più avanti.
colnames: array con i nomi delle colonne da mostrare.


Facciamo ora una veloce carrellata dei metodi utili in questo contenitore:
SetRowNumColumn: Questo metodo serve per impostare il nome della colonna che indica il numero di riga. Se viene impostato ad undefined o a qualunque cosa che in javascript dia False, elimina la colonna con il numero di riga.
SetPager: imposta il paginatore. Non l'abbiamo ancora visto, ma il paginatore è una classe usata dal contenitore per alcune operazioni. Se si passa undefined si elimina il paginatore.
SetSortList: richiede l'elenco delle colonne per cui si può ordinare, come elenco di numeri, senza contare quella opzionale dei numeri di riga. Il parametro depth al momento non è usato (ma deve comunque essere maggiore di 0), mentre i 2 successivi parametri sono le immagini (o quel che si vuole) da usare come frecce sulla colonna per cui si ordina.
DrawAll: non dovrebbe mai essere necessario chimarlo se non dopo l'impostazione del comportamento desiderato, ma è comunque sempre accessibile per forzare un ridisegno del contenitore.
SetColNames: imposta il nome delle colonne. Dopo che sono state impostate il contenitore non si ridisegna automaticamente, quindi il cambio al volo di questi nomi potrebbe essere un caso di necessità di utilizzo della DrawAll.
SetClassName: imposta il nome della classe da usare per il contenitore, ai meri fini di creazione di un foglio di stile consono.

Niente allarmi, è tutto piuttosto semplice, in realtà!
Passiamo dunque a vedere l'oggetto contenuto, eccolo:



Anche se fornisce una implementazione minima, l'ho comunque chiamato IContained, perchè vorrebbe essere, più che altro, una interfaccia.

C'è anche un costruttore super ridotto che permette l'utilizzo vero e proprio di questa classe. Questi i suoi argomenti:
uid: questo è un id (univoco?) dell'elemento, previsto perchè è assai probabile che i vari oggetti esposti abbiano un identificativo con cui riconoscersi, utile speciamente nelle eventuali successive chiamate al server.
va: array di valori, per le singole colonne.


Ecco i suoi pochi metodi utili, li descrivo perchè sono quelli di cui più facilmente si farà override in classi derivate:
DrawAsRow: viene chiamato dal Container per dire all'oggetto di restituire la sua rappresentazione sulla riga. In questa versione l'oggetto suppone di dover scrivere i contenuti dell'argomento va del costruttore in ordine come li ha ricevuti, uno per colonna, senza manipolazioni. 
GetValueByPos: chiamato dal Container in fase di ordinamento, richiede all'elemento di restituire il valore di una certa colonna (argomento). L'implementazione di base restituisce quello che l'oggetto stamperebbe in quella colonna, nulla vieta di modificare il comportamento per restituire un altro valore magari più performante per l'ordinamento o semplicemente diverso.

Come si vede è una classe minimale, con pochi fronzoli, ma abbastanza sostanza da stare in piedi.

Passiamo dunque all'ultima classe, il paginatore, GPager. Eccolo:



Il costruttore vuole questi paramentri:
uid: questa volta è verametne univoco...o così dovrebbe essere. Usato per poterlo ritrovare nella pagina per altri script.
container: riferimento al conenitore, viene usato quando cambia qualcosa nel pager e questo ha effetti sul contenuto da mostrare, per esempio quando si cambia pagina.
itemsperpage: numero di righe per pagina. Più propriamente numero di elementi, visto che potenzialmente potrebbero anche disegnarsi su più righe.
showpagesatedge: quante pagine mostrare vicino ai bordi? Il canonico far sempre vedere la prim e l'ultima, o le prime 3 e le ultime 3...canonici parametri di navigazione.
showpagesatposition: quante pagine mostrare nel pager adiacenti a quella attuale? Quella prima e quella dopo o le 3 prima e le 3 dopo? Altro canonico parametro di navigazione.


Non ci sono metodi utili da chiamare dall'esterno, mentre magari come estenderlo lo vediamo in una puntata successiva. Ci sono invece du parametri direttamente accessibili che posono essere cambiati e sono prevsymbol e nextsymbol che sono i simboli usati per fare previous e next page, altra caratteristica standard di navigazione


Come accennato, anche questa classe può essere specializzata per apparire più bella o per avere un comportamento più evoluto.



Per provare il pager con dei contenuti bisogna salvare le 3 classi in uno o più file (in ho usato un unico file chiato “Icontainer.js”) e scaricare e mettere in un posto accessibile anche una versione non preistorica di jQuery (io ho usato la 1.2.6). Modificate come necessario i link a questi file.

Ed ora ecco la paginetta per provare il comporamento del pager con dei contenuti:



ah, quasi dimenticavo....i link rotti ad immagini che vedete è perchè uso 'uparr.gif' e 'dwarr.gif' come freccia in su e freccia in giù. Scegliete su internet delle frecce che vi piacciano e cambiate i riferimenti come preferite (possono essere anche degli url remoti).

Come ultima note, se vedete che IE ci mette circa 1 minuto a ordinare 10000 elementi, laddove opera ci mette pochi secondi e FF è istanteneo, non scappate...pensate solo che è il caso di cambiare browser...e per stare sul sicuro si può sempre lasciare l'ordinamento al server ;)

Come rendere il tutto più presentabile lo vediamo in una prossima puntata con un po' di css!

Read more