Originariamente pubblicato in data 01/07/2000
A
C
C
O
M
A
Z
Z
I
AppleScript - parte II
Corso di AppleScript, il linguaggio di sistema di Mac OS. Seconda puntata
AppleScript non è difficile da padroneggiare per chi conosca già almeno un linguaggio di programmazione, anche se le sue peculiarità possono disorientare il neofita ma questo vale per qualsiasi linguaggio, sia di programmazione che umano.Per avvicinare AppleScript, introduciamo innanzitutto listruzione di assegnamento. Grazie ad essa possiamo memorizzare un valore in una variabile:
set variabile to valore
oppure
copy valore to variabile
Per esempio, scrivendo set dataOdierna to 16 gennaio 1994 assegnamo alla variabile chiamata dataOdierna la stringa di caratteri indicata.
Le variabili non vanno dichiarate preventivamente, e il linguaggio è polimorfico (cioè la stessa variabile può contenere ora una stringa, ora un intero, ora un altro tipo ancora di dato).
AppleScript non possiede nessuna istruzione per fare input o output (è stato creato per controllare le applicazioni esistenti, e si suppone che il programmatore usi queste ultime per comunicare). Possiamo però usare display dialog per far apparire una finestra di dialogo che fornisce un messaggio allutente. Formalmente, si tratta di una estensione al linguaggio (un po come gli XCMD di HyperCard), ma adempie bene al suo compito.
La sintassi è
display dialog Un messaggio ¬
default answer Risposta predefinita ¬
buttons {Pulsante1, Pulsante2 } ¬
default button numeroPulsanteDefault
Anche conoscendo queste sole istruzioni possiamo scrivere la nostra prima applicazione AppleScript, che mostra a video la data e lora corrente. Eccone il testo:
set adesso to current date
set orarioDaMostrare to adesso as string
display dialog orarioDaMostrare
Per comprendere il programma bisogna sapere che current date è una variabile predefinita di AppleScript che contiene data e ora corrente, e che la clausola as string forza la conversione di una variabile di tipo data in una variabile di tipo stringa.

Una pendola per computer
AppleScript fornisce, naturalmente, i due costrutti base della programmazione: il ciclo e la selezione (cioè il mezzo per ripetere un blocco di istruzioni più volte e il mezzo per eseguire una istruzione solo se una certa condizione è verificata.La sintassi della selezione è semplice:
if soldiInBanca > 0 -- se
display dialog Tutto bene
else -- altrimenti
displayDialog Siamo in rosso!
end if -- fine se
Il ciclo è un po più complesso da realizzare, perché ve ne sono diverse varianti. La forma base è:
repeat
-- istruzione da ripetere
end repeat
Armati di questi due strumenti possiamo procedere alla scrittura di una versione più complessa del programma orologio:
set adesso to current date
repeat
if adesso ¤ (current date) then
set adesso to current date
set ora to adesso as string
display dialog ora
end if
end repeat
Questa seconda versione del programma mostra la finestra di dialogo ogni qual volta lora cambia. Disgraziatamente, ciò avviene una volta al secondo! Ma possiamo ottenete un programma che ci dice che ore sono una volta al minuto (o allora) semplicemente trascurando le cifre meno significative dellorario. Dato che un orario viene rappresentato come giorno mese anno hh:mm:ss, dove h sono le ore, m i minuti, e s i secondi, ci basta imporre ad AppleScript di ignorare le ultime due lettere della frase (ovvero le ultime due lettere dellultima parola.
Ecco dunque la versione definitiva del programma orologio, che trasforma un calcolatore da cinque milioni in una pendola da salotto:
set adesso to current date
set oraLunga to adesso as string
set ora1 to characters 1 thru 5 of last word of oraLunga
set ora2 to ""
repeat
set adesso to current date
set oraLunga to adesso as string
set ora1 to characters 1 thru 5 of last word of oraLunga
if ora1 ¤ ora2 then
set ora2 to ora1
display dialog oraLunga
end if
end repeat
Così come mostrato, il programma mostra lorologio ogni minuto, ma basta scrivere characters 1 thru 4 per vedere il dialogo ogni dieci minuti, oppure 1 thru 2 per vederlo una volta ogni ora. Potremmo facilmente aggiungere un bel bong, o un tic-tac, dato che una delle estensioni ad AppleScript fornite insieme con il software di base permette di far emettere un suono a Mac...
Un progetto ambizioso
Ora che abbiamo introdotto le basi di AppleScript possiamo utilizzare il linguaggio per realizzare qualcosa di più significativo. Non è stato facile immaginare qualcosa di sufficientemente complesso per dimostrare le potenzialità del linguaggio, e tuttavia abbastanza breve da trovare spazio sulle pagine della rivista. Ma ecco lidea.In una ditta lavorano tre tecnici e un commesso; ciascuno di loro è dotato di un Mac. Il commesso riceve delle ordinazioni: prende un appunto per ciascuna di esse, scrivendo di cosa si tratta e assegnando una priorità. La priorità è un numero compreso tra uno (massima urgenza) e dieci (non cè alcuna fretta).
I tecnici sbrigano le ordinazioni: quando uno di loro si libera riceve in affidamento la commessa più urgente e, a parità di urgenza, quella che aspetta da più tempo.
Svilupperemo ora tre semplici applicazioni AppleScript che risolvono questo problema. La prima (chiamata Insert) risiederà sul Mac del commesso, e servirà a introdurre una ordinazione nel sistema. La seconda (Checker) risiederà su ciascun Mac dei tecnici. La terza (Server) funge da coordinatore delle attività, e può risiedere su un Mac qualsiasi.
In altri termini, stiamo sviluppando un applicazione client-server, capace di gestire un qualsiasi numero di clienti (non è, infatti, limitata a tre tecnici), e che gira in rete: il tutto con poche righe di codice di un linguaggio di programmazione non specializzato, e senza una base di dati che lavori per noi. Non è poco.
La struttura dati
Per realizzare il nostro obiettivo, utilizzeremo una lista semplice di dati. La lista memorizzerà tutte le commesse in attesa di esecuzione, e sarà memorizzata dallapplicazione Server nella variabile listaEventi. Ogni volta che il commesso aggiunge una ordinazione, alla lista verranno aggiunte due voci: un promemoria che descrive lordinazione (stringa di caratteri) e un indice numerico di priorità (un intero). Quando un tecnico si dece pronto a un nuovo lavoro, Server rintraccia allinterno della lista lordinazione a maggior priorità, la passa al tecnico e la rimuove dallelenco dei lavori da fare.La listaEventi sarà memorizzata come property di Server. Una property è una variabile dalle caratteristiche sofisticate, utilizzabile in AppleScript per la programmazione orientata alloggetto. Nel nostro codice ci limitiamo a sfruttarne una caratteristica interessante: la permanenza; i dati inserti in una variabile property sono infatti insensibili alla terminazione dellapplicazione. Se il programma Server viene chiuso, o il Mac sul quale gira viene spento per mancanza di corrente, la listaEventi non viene cancellata.

Le applicazioni
Líapplicazione CheckerVediamo per prima lapplicazione Checker, la più semplice delle tre. Si tratta dellapplicazione che gira sui Mac dei tecnici. Quando un tecnico esegue questo programma esso non fa altro che mettersi in connessione con il Server e chiedergli quale sia il lavoro più urgente. Ricevuto un messaggio di risposta, Checker lo mostra a video.
Per realizzare la comunicazione tra applicazioni useremo la parola chiave AppleScript tell. Per mettere in contatto tra loro lapplicazione AppleScript che stiamo scrivendo e un altra applicazione qualsiasi (sviluppata in AppleScript come Server o tradizionale come Excel) infatti basta scrivere:
tell application Nome applicazione
-- elenco di ordini per lapplicazione
end tell
Oppure:
tell application Nome to -- un ordine
Checker manda a Server il messaggio controllaEvento. Questo messaggio scatena una procedura allinterno di Server, che vedremo tra poco. Il risultato restituito da Server viene automaticamente messo nella variabile result di Checker. A Checker, dunque, non resta che eseguire un display dialog result.
Il programma AppleScript Checker (vedere Il codice di Checker) va scritto con lo Script editor di AppleScript, e salvato come applicazione.
Il codice di Checker
tell application "Server" to controllaEvento()
if result = "" then
display dialog "Non ci sono compiti in lista" ¬
buttons { "Annulla" } default button 1
else
display dialog result buttons { "OK" } default button 1
end if
Questo codice, così comè, suppone che lapplicazione Server si trovi sullo stesso Mac sul quale gira essa stessa. Questo per permettere a ogni lettore di usarla, anche se non ha a disposizione una rete di Macintosh. Per far funzionare Checker in rete basta sostituire la prima riga con:
tell application "Server" of machine nome Mac of zone Nome zona AppleTalk to controllaEvento()
Su una rete composta da una sola zona non è necessaria la clausola of zone.
Lapplicazione Insert
Insert è lapplicazione del commesso. Deve chiedere al commesso una descrizione, un numero di priorità, e poi passarli a Server.
Insert fa uso di una variante del ciclo repeat, che è stato introdotto in precedenza:
repeat while unaCondizione
-- blocco di istruzioni
end repeat
In questa variante le istrizioni del blocco vengono ripetute se e sino a quando la condizione espressa resta vera.
Nel realizzare Insert ho voluto dimostrare una delle caratteristiche più avanzate di AppleScript: il controllo delle eccezioni. Si tratta di una possibilità che solitamente mettono a disposizione solo i linguaggi più sofisticati, come C++ o Ada.
Allinterno di Insert si vede solo un uso banalizzato di questa caratteristica: lho utilizzata per intrappolare il caso in cui il numero della priorità venga scritto scorrettamente dal commesso. Lo stesso effetto era ottenibile in molti altri modi più tradizionali.
La sintassi da utilizzare per il controllo delle eccezioni è
try
-- blocco di codice
on error
-- codice che viene eseguito se avviene un errore nel blocco
end try
Lo spazio dedicato a questo intero articolo basterebbe appena ad illustrare un meccanismo tanto importante, eclettico e significativo come il controllo delle eccezioni: quindi, a malincuore, passiamo oltre.
Il codice di Insert
set risultato to display dialog "Promemoria?" default answer "Descrizione" ¬
buttons {"OK", "Annulla"} default button 1
if button returned of risultato = "OK" then
set suoNome to text returned of risultato
set bisognaChiederePriorita to true
repeat while bisognaChiederePriorita
try
set risultato to display dialog "Urgenza? (1...10, 1 è maggiore)" ¬
default answer "10" ¬
buttons {"OK", "Annulla"} default button 1
if button returned of risultato = "OK" then
set suaPriorita to (text returned of risultato as integer)
set bisognaChiederePriorita to false
end if
on error
display dialog "Qui mi serve un numero intero, capo!" ¬
buttons "Rifo " default button 1
end try
end repeat
--aggiungi l'evento in lista
tell application "Server" to aggiungereEvento(suoNome, suaPriorita)
end if
Lapplicazione Server
Allinterno del codice di Server, lapplicazione che esegue veramente il lavoro necessario, troviamo tre blocchi: il primo, di una sola riga, dichiara listaEventi come una proprietà di Server (e quindi unità permanente di memoria), e la inizializza a lista vuota.
Il secondo blocco è il codice controllaEvento(), che viene invocato quando Checker richiede la restituzione della commessa più urgente. Al suo interno si trova unaltra variante del ciclo repeat: per ripetere un blocco di istruzioni un numero prefissato di volte si scrive:
repeat with variabile from valoreIniziale to valoreFinale by intervallo
-- blocco istruzioni
end repeat
Questo ciclo è tipicamente chiamato ciclo for nella maggior parte dei linguaggi di programmazione. Una variabile viene inizializzata a valoreIniziale; a ogni iterazione del ciclo viene incrementata di intervallo unità; il ciclo termina quando la variabile supera il valoreFinale.
Dentro controllaEvento() vediamo anche il metodo da utilizzare per accedere agli oggetti dentro listaEventi: length of listaEventi sarà il numero di oggetti contenuti dalla lista, mentre item k of listaEventi sarà il k-esimo oggetto della lista.
Il blocco più massiccio allinterno di controllaEvento() è quello che toglie levento appena selezionato dalla lista. Concettualmente, questa azione potrebbe venire realizzata semplicemente scrivendo:
set listaEventi to (items 1 thru (eventoScelto - 2) of listaEventi) & (items (eventoScelto + 1) thru numEventi of listaEventi)
Disgraziatamente, se il codice è quello appena visto AppleScript si blocca quando viene selezionato il primo o lultimo evento. Nel caso del primo evento, per esempio, la riga di codice si concretizza nella selezione degli elementi da 1 a zero e da 2 a numEventi. Se AppleScript risolvesse listruzione supponendo (correttamente) che linsieme da 1 a zero è un insieme vuoto, non ci sarebbe bisogno di altro codice. Disgraziatamente, AppleScript considera gli elementi da 1 a zero un errore: quindi è necessario esprimere come casi speciali la selezione della prima commissione, dellultima commissione, e dellunica commissione in lista.
Il terzo blocco di codice di Server è il codice usato per aggiungereEvento(). Queste poche righe di codice si limitano ad aggiungere in coda alla lista i dati passati da Insert.
Il codice di Server
property listaEventi : {} -- permanente!
on controllaEvento()
-- qui il problema è che se ho inserito il record
-- {"Esempio", 1} AppleScript mette in lista
-- due campi: "Esempio" e "1".
set eventoScelto to 0
set prioritaScelta to 11
set numEventi to length of listaEventi
-- caso speciale: la lista è vuota
if numEventi = 0 then return ""
-- controlla tutti gli eventi della lista
repeat with i from 2 to numEventi by 2
-- se ne trovi uno che ha priorità maggiore dell'attuale selezionato
if prioritaScelta > (item i of listaEventi) then
-- ... allora selezionalo
set prioritaScelta to item i of listaEventi
set eventoScelto to i
end if
end repeat
-- Prendi nota di quale evento va restituito
set risultato to item (eventoScelto - 1) of listaEventi
-- ... e poi toglilo dalla lista
if numEventi = 2 then
set listaEventi to {}
else if eventoScelto = 2 then
set listaEventi to items 3 thru numEventi of listaEventi
else if eventoScelto = numEventi then
set listaEventi to items 1 thru (numEventi - 2) of listaEventi
else
set listaEventi to (items 1 thru (eventoScelto - 2) of listaEventi) ¬
& (items (eventoScelto + 1) thru numEventi of listaEventi)
end if
-- restituisci il risultato
return risultato
end controllaEvento -- controllaEvento
to aggiungereEvento(nomeEvento, prioritaEvento)
set prioritaNumerica to prioritaEvento as integer --accertiamoci sia un numero
copy {nome:nomeEvento, priorita:prioritaNumerica} to nuovoEvento
set listaEventi to listaEventi & nuovoEvento -- aggiungi in coda
return "OK!"
end aggiungereEvento
Conclusione
AppleScript è un linguaggio semplice da apprendere ma molto evoluto e ricco di costrutti sofisticati. In questo articolo ho preferito mostrarne le potenzialità realizzando una applicazione relativamente sofisticata piuttosto che limitarmi ad elencare qualche parola chiave: in quel caso nessuno avrebbe potuto capire quale flessibilità e potenza si nasconde in AppleScript.