Proposta
Di seguito elenco alcune problematiche della codebase attuale e come verrebbero risolte nella ristrutturazione a cui sto lavorando da qualche settimana. Ho già riscritto il codice con i nuovi modelli e qualche plugin attivato; nei prossimi giorni posso aggiungere una branch al mio fork di cerbottana o lavorare direttamente in una branch di questo repository se c'è la disponibilità.
Signature dei comandi
Problema
La funzione che implementa un comando ha la seguente signature:
async def foo(conn: Connection, room: Optional[str], user: str, arg: str) -> None:
pass
Per accedere ai singoli parametri di un argomento bisogna talvolta splittare la stringa arg
; la logica si complica se magari si vuole solo il primo argomento come parametro e il resto come stringa a sé (vd. tell
in plugins/misc.py
). Sarebbe utile definire la logica per queste azioni in modo centralizzato e riutilizzarla di volta in volta, piuttosto che riscrivere tutto da capo ogni volta.
Ovviamente si potrebbe far passare un argomento in più alle funzioni che definiscono i comandi ma a lungo andare non conviene. Ad ogni possibile aggiunta verrebbe allungata la signature di ogni funzione in tutto il repo con argomenti potenzialmente inutilizzati. Inoltre parametri interdipendenti (es. arg
e args
) non rimarrebbero sicronizzati in caso di modifica.
Possibile soluzione
Invece di passare una lista di parametri alla funzione, usare un'istanza di una classe che ha come attributi tutti gli argomenti della signature precedente e aggiunge delle proprietà utili generate dinamicamente, tra cui args
.
async def foo(msg: Message) -> None:
...
print(msg.arg)
print(msg.args[3])
print(msg.user)
...
Eccessiva dipendenza da utils
Problema
Semplici operazioni quali controllare se un utente è driver si appoggiano ad un modulo esterno, appesantendo molto la sintassi e aggiugendo una dipendenza che potrebbe essere evitata.
async def foo(conn: Connection, room: Optional[str], user: str, arg: str) -> None:
if utils.is_driver(room, user):
...
Possibile soluzione
Invece di passare semplici stringhe che rappresentano l'utente e la room, passare delle istanze di classi che ne contengono anche le relative proprietà. Tra l'altro una classe del genere già esiste per le room.
async def foo(msg: Message) -> None:
if not msg.author.is_driver: # author: User
return
...
Sovraccarico di responsabilità di Connection
Problema
Attualmente esiste una classe Room
, eppure codice che concettualmente riguarda una room è relegato all'interno di Connection
. Alcuni esempi sono:
try_modchat()
is_private(conn, room)
, che è attualmente in utils.py
ma potrebbe essere spostata in Connection
per rimuovere un parametro superfluo. Non potrebbe però essere inclusa direttamente in Room
.
Possibile soluzione
Questo problema deriva dal fatto che Room
non può interagire col socket, in quanto ciò renderebbe parzialmente inutile l'astrazione di Connection
. Si potrebbe però passare a Room
un'istanza di Connection
(discorso analogo per User
e Message
).
Così facendo, try_modchat()
e is_private()
potrebbero essere definiti direttamente nell'oggetto Room
. Inoltre, invece di mantenere in Connection
i metodi send_message()
, send_pm()
, send_reply()
questi potrebbero ora essere definiti nei vari oggetti Room
, ... come wrapper di un unico comando connection.send()
.
async def foo(msg: Message) -> None:
...
# Invece di conn.send_message(room, message)
await msg.room.send("Messaggio broadcastato in una room")
# Invece di conn.send_pm(user, message)
await msg.author.send("Ciao ti scrivo in PM")
# Invece di conn.send_reply(room, user, message)
await msg.reply("Forse in room, forse in pm!")
Problematiche della nuova struttura
User è contestuale a Room
Attualmente gli utenti sono memorizzati all'interno di Room
in una lista di stringhe. Si potrebbe fare lo stesso con le istanze di User
, ma all'interno di una funzione si rischierebbe di avere più istanze che descrivono lo stesso utente in relazione a room differenti. Un esempio è una funzione decorata con @parametrize_room
.
Ciò potrebbe risultare confusionario sia nella sintassi, sia nell'organizzazione del codice: nuovi metodi di User
quali can_pminfobox_to
non sono contestuali alla room di appartenenza e attributi quali global_rank
e idle
devono essere propagati ad ogni room user.