set_on_create
Timestampiamocelo bene in testa: se vuoi usae il set_on_create su un timestamp ricorda di aggiungere:
__PACKAGE__->load_components("TimeStamp");
ovviamente dopo aver installato DBIx::Class:Timestamp!
Today's Document
The Bowery Presents
almost home
noise dept.
cherry valley forever
Mike Driver
taylor price

Jar Jar Binks Fan Club
let's talk about Bridgerton tea, my ask is open
YOU ARE THE REASON
d e v o n

if i look back, i am lost

Kiana Khansmith

PR's Tumblrdome
No title available

No title available
TMBGareOK. The Official They Might Be Giants tumblr

No title available

oozey mess

Product Placement

seen from Philippines

seen from Kenya
seen from France
seen from Germany

seen from Malaysia

seen from Uzbekistan

seen from New Zealand

seen from Mexico

seen from France

seen from United States
seen from Russia

seen from India
seen from Bahrain
seen from Paraguay
seen from Pakistan

seen from Thailand

seen from Malaysia

seen from Pakistan
seen from United States

seen from Yemen
@cym0n-blog
set_on_create
Timestampiamocelo bene in testa: se vuoi usae il set_on_create su un timestamp ricorda di aggiungere:
__PACKAGE__->load_components("TimeStamp");
ovviamente dopo aver installato DBIx::Class:Timestamp!
Pausa di riflessione
Per un po' non ho scritto perché la revisione della prima storia ha avuto bisogno di più di una riflessione.
Anche introducendo il sospetto è stato praticamente necessario riscriverla tutta e riorganizzare in generale il lavoro per realizzarla, per evitare di perderne nuovamente il controllo.
Ora è quasi finita, mi manca di scrivere il finale e poi fare qualche giro di test.
Intanto, un paio di azioni di maquillage: ora il numero di click necessari sono visibili mentre si gioca e ho fatto una paginetta esplicativa del gioco, raggiungibili tramite un grezzissimo menu.
La regola del sospetto
Ho implementato la logica del sospetto. Ora esiste un nuovo comando nel metalinguaggio Djakarta che alza il sospetto nei confronti di una persona. Questo, dall'altra parte, rende certe azioni meno pericolose dal punto di vista dell'indicatore della tensione.
Ora, per poter far tornare il sistema in pista, manca un pochino di frontend e la riscrittura delle storie secondo questo nuovo parametro.
Riflessioni sul gameplay
Anche grazie al fatto di aver un po' mostrato Djakarta in giro, sono sorte interessanti riflessioni sul gameplay che, evidentemente, ha bisogno di un po' di tuning...
La vittoria in Djakarta si ottiene passando alcuni oggetti in un certo stato. Ovviamente la catena che porta a questi passaggi è nascosta in mezzo a svariate altre opzioni che confondono le acque. Primo errore della storia di test: la catena minima è data da tre semplici passaggi: rivelazione dell'esistenza di due elementi e azione per farli cambiare di stato.
Ma questa catena non è logica, significa sparare a caso nel mucchio oppure ripetere il gioco dopo aver acquisito informazioni in una partita precedente. Dovrebbe essere in qualche modo "punita".
Ma qui interviene il secondo errore: i livelli di tensione sono sballati, se si esce dalla catena illogica non è possibile vincere.
Secondo me quello che bisogna introdurre è un indicatore che permetta di stabilire se un'azione è sensata o insensata, tipicamente basato sulle informazioni a disposizione. Se voglio uccidere una persona è insensato farlo così, perché ce l'ho davanti, sensato se so che è un terrorista. L'idea è che il primo caso ha un tributo di tensione più alto del secondo (che magari non lo ha proprio).
Quindi, concludendo, la "catena illogica" deve essere punita con dei livelli di tensione tali che fanno arrivare il sistema irrimediabilmente a game over.
Questo, in qualche modo, riequilibra le cose perché punisce la catena illogica e diminuisce i rischi di gameover dei percorsi più lunghi (e, si spera, logici).
Questo meccanismo si potrebbe gestire con gli stati, ma ricordiamoci che il sistema è piuttosto limitato da questo punto di vista e gli stati hanno più senso come modifiche effettive delle persone. Quindi credo che dovrò migliorare proprio il linguaggio di scripting e introdurre quanto detto parallelamente a quanto già esiste.
Redirezione non è risposta
Risolto l'antipatico problema di ieri.
La questione era che, in caso di vittoria, la chiamata click chiede che venga fatto un redirect sulla pagina di vittoria. Il redirect veniva verificato da una apposita response_redirect_location_is, peccato che questo tipo di test NON fa scattare anche la chiamata che sottostà al redirect. Peccato che, nel mio caso, fosse la pagina win quella a far scattare tutti i meccanismi di effettiva concretizzazione della vittoria.
Forzando, dopo aver verificato la redirect, un test response_status_is sulla chiamata diretta win a quel punto il sistema ha chiuso il giro e mi ha dato la situazione esatta.
Due cose rimangono aperte: la prima, una scelta architetturale che mi lascia perplesso, ovvero il fatto che win e gameover scattano in due momenti diversi. Forse è più giusto quello che fa gameover, ovvero prima elabora la sconfitta e poi va a proporre la pagina di finale.
La seconda, il fatto che il test ha bisogno di un po' di refactoring. I due scenari di vittoria e sconfitta andrebbero accorpati in un solo file, con parametri da linea di comando.
Ma, rispetto al resto, sono inezie. La verità è che posso già pensare di riportare su master tutto il lavoro e anche metterlo su openshift.
Il test definitivo
Mi sono messo d'impegno e alla fine ho realizzato il sistema di test definitivo. Ora vengono considerate all'incirca tutte le navigazioni interessanti. Ho addirittura fatto in modo che il sistema non attinga alle storie reali, ma a una directory ad hoc, dove ho ridisegnato una missione in modo che sia raggiungibile in tempi rapidi la vittoria o la sconfitta, così da arrivare a considerare anche quegli stati.
Ho anche risolto un fastidioso bug sul supporto dovuto alla sbagliata configurazione della colonna del DB, il che non è un male.
Ora mi rimane solo un test fastidiosissimo che fallisce alla vittoria e in cui non riesco proprio a raccapezzarmi, anche considerato che, facendo la navigazione, funziona tutto correttamente...
Migrazione completata
Sono riuscito a portare a termine il non banale compito di migrare tutta la logica di game sotto i Result.
Devo confessare che l'applicazione delle foreign key era propedeuito soprattutto a questo. La classe Element::Game, infatti, aveva un mucchio di spinotti che simulavano le relazioni tra le varie tabelle. Migrare tutto sotto Result::Game e portarmeli dietro sarebbe stato poco utile e assolutamente delirante.
Completate le foreign e attivate quindi le relationship anche sotto DBIx::Class ho potuto invece lavorare con un po' più cognizione di causa, trasformando un sacco di chiamate rimbalzanti tra una classe e l'altra in un'unica chiamata coerente.
In proposito della migrazione verso le foreign ho incontrato solo un piccolo ostacolo dovuto all'omonimia tra una delle relazioni generate e uno dei campi della mia tabella. C'è modo di uscirne senza modificare il nome della colonna? Forse si. Sicuramente si può semplicemente dare alla relazione un nome diverso da quello che sceglie da sola. Farlo, però significa pestare i piedi al dbicdump e la cosa non è che mi esalti mai, quindi alla fine ho creduto prendere la decisione corretta rinominando semplicemente la colonna.
Per il resto si è trattato di un grosso esercizio di copia incolla da una parte all'altra. Non credo che, a oggi, il codice sia perfetto, anzi, probabilmente nell'ansia di far funzionare tutto ho adottato alcune soluzioni proprio sporche, ma adesso ho un nuovo punto di partenza molto più solido da cui eseguire tutti i prossimi ragionamenti.
Questo lavoro fa emergere il problema che ci sono stati dell'applicativo che il mio sistema automatico non raggiunge e che servirebbero in quanto sono tra i più noiosi. Penso che ora mi dedicherò a questo, anche perché alcune cose proprio non le ho viste effettivamente funzionare e il dubbio rimane.
dbicdump success
Piccolo aggiornamento: ho lanciato lo script di dump delle tabelle. Per farlo andare innanzitutto ho dovuto farlo puntare a MySQL (prima tirava giù i dati dal SQLite) e poi ho dovuto eliminare il parametro quote_char che evidentemente veniva buono solo per SQLite.
A parte questi piccoli accorgimenti tutto il codice è stato rigenerato senza distruggere niente (il che mi conforta anche riguardo la pulizia che ho mantenuto nello sviluppo lato modello finora). Ovviamente ho già verificato che le nuove relazioni sono correttamente considerate dal perl.
Questo era il punto che mi metteva maggior ansia, sto scrivendo, in verità, senza sapere se ora è tutto rotto o se comunque il sito è comunque operativo, ma già vedere che tutto è stato correttamente recepito è un buon punto a favore dell'operazione.
Adesso abbiamo un mucchio di possibilità in più date da DBIx::Class, discendenti direttamente dall'uso delle relazioni... vediamo di usarle.
Passa lo straniero
Il refactoring che più mi spaventa è quello di installare tutte le relazioni tra le varie tabelle del sistema e poi usarle nel gioco.
E' un refactoring importante perché, oltre al tema dell'efficienza, può aiutarmi a capire meglio come ragiona DBIx::Class.
La cosa che più mi rende nervoso riguardo la cosa è che io sono proprio un ignorante nello sviluppare i DB, anzi, a dirla tutta, i DB mi stanno proprio antipatici. E' stata quindi già una piccola impresa capire come fare ad appiccicare le foreign alle varie tabelle.
Scopro oggi, da bravo dilettante, che si può attaccare una foreign key solo a una colonna che sia indicizzata e solo a tabelle il cui engine sia InnoDB.
Poiché le tabelle sono state create con engine di default (quindi MyISAM) la prima cosa da fare è stato produrre uno script che alterasse tutti gli engine. Una volta fatto ciò mi sono messo a censire tutti i collegamenti e ad aggiungerli. Riporto qui uno script d'esempio:
CREATE INDEX player ON GAMES (USER_ID); ALTER TABLE `GAMES` ADD FOREIGN KEY ( `USER_ID` ) REFERENCES `djakarta`.`USERS` (`ID` );
E' una cosa da cui non si torna indietro. Se passo a un'architettura del genere mi scordo di poter usare un SQLite di supporto. Mi sono quindi messo a riorganizzare le cartelle dei miei script e ho prodotto (ok, vergogna su di me, usando phpmyadmin) uno script di dump con tutta la struttura a oggi, che diventa anche un comodo sistema per ripristinare il sistema attuale o installarlo su un nuovo ambiente.
Accanto a questo dump, ora, ci sono anche tutti gli script per le modifiche, fortunatamente indipendenti tra loro e già installati sul mio ambiente locale.
La palla passa a DBIx::Class.
Refactoring con classe
Fino a oggi avevo una classe ..Element::User che era una classe vera e propria, che controllava l'oggetto user, è una classe Result::User che era, naturalmente la classe Result generata dallo schema del DB.
Andando avanti a sviluppare mi è risultato sempre più ovvio come la classe Element::User non fosse altro che un proxy verso il modello. Ho deciso quindi di fare del refactoring affinché tale classe cessi di esistere e tutto sia gestito dal Result.
Visto che comunque non è che User abbia molta logica è stato tutto estremamente lineare. La maggior parte dei metodi (quelli brutalmente proxy) sono stati semplicemente eliminati mentre gli altri copincollati con poche modifiche nel diverso punto.
L'unica logica un po' pesante su cui ho dovuto lavorare è stata quella contenuta nel new della classe Element::User che si portava dietro, ovviamente diversi ragionamenti. Tutta quella roba l'ho spostata in un metodo dei controllers, anche lì copiandola in modo abbastanza lineare e con pochi problemi.
Nel tentativo, infine, di testare la nuova implementazione con i test automatici, mi sono scontrato per l'ennesima volta col problema della configurazione dell'ambiente Dancer di riferimento. Ho cercato, cioè, di fare in modo che nel momento in cui viene eseguito un test automatico questo carichi un suo specifico yml di configurazione. Qualsiasi esempio trovato su internet ha fallito nel farmi fare una cosa del genere. Non mi rimane che lanciare il test con:
perl t/000X_test.t --environment=t
Ora voglio, come cosa "facile" estendere il test perché il sistema arrivi alla vittoria e e poi come cosa "molto difficile" fare la stessa cosa di Element::User con Element::Game che però ha sicuramente molto più codice interessante...
I più meritevoli
Ho implementato il sistema per cui le missioni vengono proposte in base al proprio punteggio accumulato e in base alla soglia minima necessaria per accedervi. In questo modo posso "nascondere" la missione nuova a chiunque non abbia completato la vecchia.
E' stata un'implementazione decisamente banale, ma se non altro mi ha permesso di rilasciare in produzione anche la nuova vicenda, che tanto rimane, con questo meccanismo, praticamente inaccessibile.
Ho scoperto a mie spese che il mio flusso di rilascio scriptato NON cancella i file perché, semplicemente, il comando git add non aggiunge le cancellazioni per i commit.
Per fare pulizia dovrò, periodicamente, aggiungere i file cancellati a commit ad hoc e mandarli online.
Pensierino di Natale: sarebbe un'idea intelligente fare refactoring di User e Game rendendole estensioni delle classi DBIX::Class che rappresentano il DB? Questo potrebbe essere il modo più pulito per accordare il codice, altrimenti devo continuare a creare funzioni proxy tra gli accessi al DB e gli accessi alla classe...
Who watch the watchers
Quando Djakarta é nato, molto prima di divenire un sito internet, ero terrorizzato da come mantenere il controllo sulla storia e stabilire sempre se era fattibile e giocabile.
Per questa ragione ho fabbricato un sistema di test che percorre l'albero delle possibilità fornendo poi dei dati analizzabili.
Avendo scritto la seconda avventura di Djakarta ho deciso di rispolverare quel meccanismo, adattarlo al sistema attuale e riutilizzarlo.
Non solo l'attività è stata abbastanza indolore (tutto il sito è progettato perché il "cuore" rimanga autonomo e utilizzabile in altri contesti come appunto questo sistema di test), ma soprattutto il test è risultato efficacie, visto che ha trovato una grossa anomalia nel sistema, che ho subito sanato.
Quest'ultimo giro di controllo mi fa dire che l'avventura può dirsi pronta per l'online. Ora posso tornare a dedicarmi alle feature, in particolare far si che sia selezionabile solo con l'opportuno livello di punteggio (ovvero solo sopo aver concluso la prima).
Test interni implementati
Bene, i tre test che avevo segnato come "interni" nel post precedente li ho implementati. Non sono molto soddisfatto di me, perché speravo di usare meccaniche già presenti e semplicemente eseguirle in automatico, invece mi sono trovato a spulciare a manina gli YAML.
Ho anche notato che Test::More non gestisce granché bene i return di array. Volevo far sì che la funzione di test creata in StoryManager creasse un array con le flag dei vari problemi, ma non mi è stato possibile. Così ho spezzato la funzionalità in tre diversi test.
Ho fatto la run sia sulla vecchia storia che su quella nuova. Sulla nuova ha pignolato (più del necessario) su alcuni status non gestiti, ma che io uso come condizione terminale. Lo ho accontentato con qualche entry fake, ma vorrei capire se fare altrimenti.
Sulla vecchia storia invece ha trovato l'ennesimo bug e questa è una cosa buona, perché significa che a qualcosa, tutto sommato, serve.
Mi sono anche preso la briga di blacklistare la nuova storia affinché gli script non la rilascino su openshift. Per ora voglio rimanga una cosa mia. Tanto si tratta solo di intervenire sul file di START...
Appunto: un altro test di coerenza interna da implementare. Che tutti i token nei messaggi siano nella forma corretta, cioè QUATTRO cifre punto name.
Raccontami una storia
Mi sono imbarcato nell'ambizioso progetto di scrivere una seconda avventura per il gioco, molto più contorta della prima.
A riutilizzarlo devo dire di essere pienamente soddisfatto del mio linguaggio di scripting che, pur avendo dei limiti, è abbastanza flessibile per imbastire le vicende.
Dal punto di vista tecnico ora si pone il tema di testare in modo un po' esaustivo la nuova vicenda. La sintassi YAML la verifico col lo script di test che già avevo creato, però é importante anche verificare altri aspetti.
Il più semplice sistema che voglio implementare fa alcune semplici verifiche di coerenza interna ovvero:
Per ogni stato definito nelle azioni esiste un comando TAG che permetta all'elemento di entrarci?
Per ogni TELL esiste un message?
Poi c'è la coerenza un po' più estesa, cioé:
Le DO richiamate fanno riferimento ad azioni m2m reali?
A suo modo si tratta di una vera e propria procedura di "compilazione". Penso che in un giorno molto, molto buio mi metterò seriamente a scrivere la documentazione del metalinguaggio.
Vedo la fine
Gestire i finali di partita è una delle cose più noiose esistenti, se non si è attrezzati, perché significa arrivare in fondo alla partita per ogni test, in un modo o nell'altro.
E anche quando "nell'altro" sono quattro query di DB e comunque una palla se vanno ripetute sei volte.
In ogni caso il gioco finisce in due casi: hai mandato la tensione troppo in alto o hai vinto. Ora tutti e due non solo danno la corretta schermata di risultato, ma aggiornano anche lo stato dell'utente e gli permettono di riprovare la missione (se fallita) o selezionarne una nuova (se ha vinto). Ovviamente di missioni nuove non ne esistono, ma questo è un altro punto.
E' stato un lavoro sacrosanto anche perché sul raggiungimento della vittoria c'era un bug grosso così che non permetteva di vincere mai...
Musica nelle orecchie: http://www.youtube.com/watch?v=AYiSs-slezM
History is not repeating
Bene, avendo risolto i problemi del server ora posso finalmente tornare a dedicarmi agli aggiornamenti.
Finalmente ho realizzato la funzione secondo cui, se si è compiuta un'azione, questa non compare più nei menu. E' una bella miglioria del gameplay, perché fa in modo che, anche se brancoli nel buio e non sai cosa fare, la semplice diminuzione delle opzioni disponibili rende possibile arrivare a una soluzione.
Non è stata un'implementazione banale come si può pensare, ma mi è venuta in aiuto la tabella di tracciamento delle azioni di gioco, che ho un po' arricchito, non ché un paio di raffinamenti nella logica di passaggio dei dati relativi alle azioni.
Questa è stata anche la prima implementazione architetturalmente interessante per cui ho usato i test automatici implementati. Ovviamente non mi danno la certezza che sia tutto ok, ma almeno rappresentano uno screening di massima su quello che potrei avere rotto.
Gripped
Per alcuni giorni il mio progetto non ha funzionato.
Un brutto 503, nessuna risposta nemmeno da phpmyadmin, persino nessun log. Tutta l'impressione che il casino sia "a monte" degli apparati sotto il mio controllo.
Sono rimasto due-tre giorni ad analizzare la cosa, ho provato a usare i (pochi) strumenti che openshift mette a disposizione per la diagnostica e ho cercato supporto via IRC e Twitter (ignorato).
Alla fine la situazione è stata risolta nel più brutale dei modi. Push fake verso il server, forzatura conseguente del riavvio dell'ambiente e tutto è tornato in carreggiata...
Però non sono tranquillo...