Pequeñas ayudas para la programación en Smalltalk (Express)
Este texto es la junta de varios post por separado que tenía en borrador y que decidí postearlos todos juntos. Creo que así es más sencillo de ubicar y poder agregar otros luego. Son algunas cosas que observé o hice cada vez que tuve algún tiempo con Smalltalk. Acá van 😛
Escondiendo el Transcript en Smalltalk Express
El Transcript que utilizamos para trabajar es un workspace. Este objeto viene por defecto «vivo» en el entorno de Smalltalk Express. Se lo puede cerrar enviándole el mensaje close al mismo (a través de la variable global Transcript).
El método «esconderTranscript» podría ser implementado en la ventana principal de la aplicación, para poder esconder el Transcript una vez que no lo necesitemos.
1 2 3 | esconderTranscript Transcript close. Transcript := nil. |
Lo que si, una vez cerrado, ¿cómo volvemos a abrirlo? ¿a quién le enviamos un mensaje? MDITranscript es la clase responsable de la inicialización del mismo. O sea, deberíamos tener alguna manera siempre de poder hacer un «do it» sobre alguna ventana para poder volver a mostrar el Transcript.
El método «mostrarTranscript» podría ser implementado en la ventana principal de la aplicación, para poder esconder el Transcript una vez que no lo necesitemos.
1 2 3 | mostrarTranscript (Transcript = nil) ifFalse: [Transcript close.]. Transcript := MDITranscript initializeTranscript. |
Serializando Objetos en Smalltalk
Con la clase ObjectFiler podemos guardar una representación binaria de nuestro objeto en un archivo en el disco. De esta manera, si nuestro objeto tiene referencias a otros objetos (haya ciclos o no), estos serán serializados también. Esto nos permitiría poder guardar los datos de nuestra aplicación en un archivo en vez de usar la imagen de Smalltalk. Por ejemplo el método guardar a continuación, está serializando el objeto referenciado por la variable Instancia.
1 2 | guardar ObjectFiler dump: Instancia. |
Si queremos recuperarlo desde el disco, bastará con tener implementado algún método que utilice el ObjetFiler de la siguiente manera:
1 2 | cargar Instancia := ObjectFiler load. |
Aclaración: Para poder reconstruir el objeto es necesario que las clases que definen su estructura se encuentren cargadas en el entorno.
Cambiando la forma en que el Inspector nos ve en Smalltalk
Cuando hacemos un inspect de un objeto generalmente si tiene referencias a otros objetos vemos que la representación textual de estas referencias dice «anObjeto», «aSystem», «aTalCosa». Esto es además de un poco molesto, incómodo si se desea hacer un debug rápido y saber de que objeto se está hablando. Para esto, podemos reeimplementar en nuestra clase el método «printOnStream», que es el que se ejecuta cuando el objeto recibe el mensaje con el mismo nombre desde el Inspector. ¿Cómo lo reimplementamos? Le podemos agregar a esa salida algún valor clave que identifique a nuestro objeto, o lo que necesitemos ver en un inspect típico conservando quizá el a y an object agregados.
1 2 3 4 5 6 7 8 9 10 | printOn: aStream "Agregaremos el DNI de la persona y delegaremos el resto a la implementación de la clase Object" | aString | "aquí le pedimos el DNI y lo guardamos en aString. Además le concateno una barra para separarlo luego de la salida por defecto de la clase Object " aString := (self getTitulo) , ' / '. aStream nextPutAll: aString. super printOn: aStream. |
La implementación original en la clase Object:
1 2 3 4 5 6 7 8 9 10 11 12 | printOn: aStream "Append the ASCII representation of the receiver to aStream. This is the default implementation which prints 'a' ('an') followed by the receiver class name." | aString | aString := self class name. "debido a las reglas del inglés, si comienza con vocal se antepone 'an' y sino 'a'. Ej anObject (en castellano sería unObjeto)" (aString at: 1) isVowel ifTrue: [aStream nextPutAll: 'an '] ifFalse: [aStream nextPutAll: 'a ']. aStream nextPutAll: aString |
Exportando/Importando clases de Smalltalk
Para exportar desde el Transcript una clase de Smalltalk, podemos usar la clase ClassReader. El método forClass, instancia un ClassReader para una determinada clase. Esto es, si tenemos una clase Persona, obtendremos la instancia con
1 | ClassReader forClass: Persona |
A este objeto le podemos enviar el mensaje fileOutOn: file, donde file es una instancia de File.
Para esto, en un TP que implementamos hace un tiempo, creamos una clase ABackUp, para exportar de forma sencilla todas las clases de nuestro sistema en un solo archivo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | Object subclass: #ABackUp instanceVariableNames: '' classVariableNames: 'LasClases ' poolDictionaries: '' ! !ABackUp class methods ! backup | reader file | ABackUp inicializar. file := File newFile: 'export\backup.cls'. LasClases do: [:c | c fileOutOn: file. file nextChunkPut: String new. (ClassReader forClass: c class) fileOutOn: file. (ClassReader forClass: c) fileOutOn: file ]. file close.! inicializar LasClases := OrderedCollection new. " Carga de las clases a backupear " LasClases add: Articulo; add: ArticuloDB; add: ArticuloEstado. LasClases := (LasClases,ArticuloEstado allSubclasses). LasClases add: ComunidadCientificaDB; add: Congreso; add: CongresoDB; add: PanelArbitros; add: Persona; add: Recibo; add: Reporte; add: Simposio; add: SistemaCongreso; add: Principal; add: AsignarCalificacionJuez; add: AsignarJuez; add: CrearCongreso; add: NuevoSimposio; add: RanckingArticulos; add: RegistrarAutor; add: RegistrarJuez; add: RegistrarPersona; add: SubirArticulo; add: Rol. LasClases := (LasClases,Rol allSubclasses). LasClases add: ABackUp.! new ^super new inicializar.! ! !ABackUp methods ! ! |
Cada vez que creábamos una clase nueva la agregábamos en la implementación del método «inicializar». Se podría haber realizado de otra forma, algo más automático o dinámico, como ser las clases utilizadas, pero esta fue la solución más rápida.
Luego desde el Transcript, haciendo un «do it» sobre «ABackUp backup.», obtenemos una serialización de las mismas en un archivo de texto.
Esto es todo por ahora. Si alguno tiene algo que quiera aportar y quiera que agregue en este mini artículo, coméntelo o me lo manda por email que lo agrego. Nos vemos en el próximo. 🙂