Acceso a ficheros y URLs

En MetaCard (y en Revolution) se puede utilizar la típica secuencia de otros lenguajes para acceder a los contenidos de un fichero: open, read/write y close.

on openCard
  put "./texto/introduccion.txt" into filename
  open file filename for read
  read from file filename until eof
  put it into field "explicacion"
  close file filename
end openCard
                  

Si hubiese utilizado

  put ". \texto\introduccion.txt" into filename
hubiese roto las posibilidades de portabilidad, deje que sea la herramienta quien decida el separador a utilizar. Sólo hay un sistema que utiliza esas barras, a pesar de que tenga muchas versiones (o revisiones o actualizaciones o como lo quiera llamar), así que ¿para qué quedarse con las limitaciones?.

Pero, puesto que para MetaCard un fichero es un tipo más de contenedor, podemos aplicar las mismas operaciones y facilidades que, por ejemplo, a un campo de texto. Lo comentamos ya en la sección de nombre Variables en Capítulo 3. Esto es, sobre todo, para recordar que ese tipo de operaciones se puede hacer con un sólo put:

on openCard
  put URL ("file:texto/introduccion.txt") into field "explicacion"
end openCard
                  

Esta orden, ya tan utilizada, ahora con el parámetro URL extiende su alcance a cualquier localización donde sus protocolos le permitan llegar: file, binfile, resfile y http. Claro que si está cargando o descargando unos cuantos "megas", le sugiero que explore otras posibilidades (prepare o load, consulte la pila de descarga de ejemplos de MetaCard en Tools|tools.metacard.com) o, al menos, distraiga al usuario con algo (urlStatus puede ayudar en la web, en local es cosa suya) en pantalla. Si precisa más control sobre lo que envía a la Web deberá hacer uso de la orden post que permite especificar las cabeceras (httpHeaders) del protocolo HTTP.

También habrá podido observar el lector que he omitido los dos primeros caracteres de la ruta del fichero: no es más importante, sólo es que por pereza no los suelo poner. Lo importante es que las dos secuencias han utilizado rutas relativas y eso permite la portabilidad de la aplicación a cualquier otra localización (directorio o unidad de disco, si esto es un concepto que utiliza su sistema) física.

Nota: A la hora de indicar la ruta a un recurso en el sistema de ficheros sólo hay un sistema operativo que no utiliza el carácter "/". ¿Sabes, estimado lector, a cual me refiero? Bueno, tampoco es más importante. En la web también es el separador estándar. Podemos utilizar siempre este elemento como separador en las rutas de los ficheros y dejar que MetaCard o Revolution se ocupen de traducirlo, en caso necesario.

Importante: Déjeme que insista un poco más en esta idea, pero con algo más entretenido: hay que evitar las rutas absolutas si se quiere, se necesita, ser independiente. Un ejemplo de este planteamiento puede ser la pila voluntaris. Esta es una "ruleta" que improvisé un día antes de ir a clase. El ordenador de la clase no comparte ni el sistema operativo ni la disposición de ficheros que aquel en que se crea la aplicación. Para animar un poco la participación de mis alumnos; yo tenía entoces la posibilidad de obtener sus fotos, dirección de correo, nombre completo y grupo de la asignatura en que estaban matriculados. Pues eso, que se me ocurrió hacer algo como lo de la Figura 5-11.

Figura 5-11. Ejemplo de interfaz en funcionamiento.

Al pulsar el botón "Tria un voluntari" deben verse pasar los datos de un número aleatorio de alumnos. Cualquier pulsación debe borrar los datos para que no esten visibles durante mucho tiempo, por si alguien no quiere hacerse propaganda..

#
# La tarjeta
#
global varGrups, varNoms, varUsuaris, varFotos

on openCard
  set the filename of image "foto" to "fotos/0.gif"
  
  put URL "file:voluntari/grupsEUI.txt" into varGrups
  put URL "file:voluntari/nomsEUI.txt" into varNoms
  put URL "file:voluntari/usuarisEUI.txt" into varUsuaris
  put URL "file:voluntari/llistaFotosEUI.txt" into varFotos
  
  put the number of lines of varFotos
  
  put empty into fld "nomAlumne"
  put empty into fld "grupAlumne"
end openCard


on mouseUp
  send "openCard" to me
end mouseUp
                       

Bueno, igual tiene curiosidad por ver cómo era el código, pues nada aquí lo tiene: un bucle con un número de vueltas aleatorio, desde una posición aleatoria, que se incrementa de forma circular y con una espera lo sufientemente pequeña para que se reconozca uno a sí mismo y de sensación de velocidad.


#
# Botón "Tria un voluntari"
#
global varGrups, varNoms, varUsuaris, varFotos

on mouseUp
  repeat with i = random(the number of lines of varFotos) to \
      (the number of lines of varFotos + random(the number of lines of varFotos) )
  
    put ((i mod the number of lines of varFotos) +1) into j
    set the filename of image "foto" to "voluntari/fotos/" & line j of varFotos
    put the line j of varNoms && "  (" & line j of varUsuaris & ")"  into fld "nomAlumne"
    put the line j of varGrups into fld "grupAlumne"
    wait 10 milliseconds
end repeat


if the text of fld  "grupAlumne" is "TEL"
then
  put fld "nomAlumne" & " pot descansar. Atre!"
  send "mouseUp" to me
end if
end mouseUp
                       

Puede ver al final del código un caso de excepción, esto es debido a que hay alumnos que pertenecen a un grupo presencial y otros a uno de teleenseñanza, estos están exhentos de acudir a las clases y ... Bueno, pues que no lo pude resistir y lo utilicé para hacer un pequeño truco: mi nombre y fotos estaban incluídos en la versión que me llevé al aula y había una manera de hacer que saliese yo, pero entonces se hacia cierta la comprobación y volvía a "hacer girar la ruleta". Sólo lo hice una vez, ¡a ver qué está pensando!

Nota: Ahora que lo he contado le toca a Vd., modifique el código para que se pueda realizar el "truco" mencionado. Lo importante es que haya tiempo de que se den cuenta y que el proceso se reinicie. Si lo que pretendemos, como era el caso, que los "asistentes" reaccionaran y se diesen cuenta que aquello estaba preparado y era sólo una manera de promover la discusión sobre cómo implementarlo.

Si lo necesita también puede manipular el sistema de archivos como lo haría a través de su sistema con: create directory/alias y delete file/directory (tanto sobre ficheros como directorios). Sin olvidar las cajas de diálogo estándares para escoger ficheros y directorios: answer file / answer folder y ask file.

Es posible acceder a los ficheros y directorios del disco para listarlos (directories, files, directory) o para examinar sus permisos (en número y nombres diferentes en función del operativo sobre el que trabajamos). Vamos a realizar un breve ejemplo de ... como la que se muestra en la Figura 5-12 en la que se muestran unos botones desabilitados en el menú (por que nos los vamos a utilizar, pero que se vea lo que crea el Menu Builder) y en la tarjeta, por que hemos delegado en ellos ciertas tareas que aparecen el menú que despliega la opción Piles.

Figura 5-12. Creando una pila para cargar cualquier pila disponible en un directorio.

La pieza central es el botón "Recarregar piles" que carga el contenido del menú Piles a partir de examinar los ficheros del directorio que se le indica. Por eso es necesario establecer el directorio de trabajo al indicado en el campo de texto "directoriOnBuscarLesPiles" con la función directory. Hecho esto, obtiene todos los nombres de los ficheros en el directorio actual (el que acaba de establecer), para ello utiliza la función files. De esa lista sólo estamos interesados en los que son nombres de ficheros de pilas de MetaCard (usualmente con la extensión "mc") así que se seleccionan sólo los que tienen la extensión correspondiente y se ordenan de manera alfabética para facilitar su localización en el menú. Este se construye con unas secciones fijas (las listadas en la variable botons), un separador y la lista de nombres de pilas.

#
#Botón "Recarregar piles"
#

# Rellegir el directori i actualitzar la llista de piles disponibles
#

#Definit en la pila:
global botons
local vNomsDeFitxers, directoriActual

on mouseUp
  put the directory into directoriActual  
  set itemDelimiter to "/"
  set the directory to fld "directoriOnBuscarLesPiles"

  put the files into vNomsDeFitxers
  filter vNomsDeFitxers with "*.mc"
  sort vNomsDeFitxers

  if vNomsDeFitxers is not empty
  then put item 1 of botons & return &\
      item 2 of botons & return &\
      item 3 of botons & return &\
      "-" & return &\
      vNomsDeFitxers into btn Piles of group "menuGroup"
  else answer warning "No n'he topetat cap *.mc" titled "[" & the short name of me & "]"

  set the directory to directoriActual
end mouseUp
                       

Este está asistido, principalmente por la opción de menú Piles y la propia pila. El primero se encarga de lanzar a ejecutar la opción escogida: una de las fijas o una de las pilas. Estas se pueden abrir en formas diferentes, dependiendo de la opción escogida por los botones de selección del grupo "Modo". La pila, por su parte, está dedicada a inicializaciones relativas a asignar un valor incial al campo de texto que contiene el directorio donde se buscan las pilas (en este caso, junto a donde está instalado MetaCard) y un par de toques para el interfaz.

#
# Botón "Piles"
#
global botons

on menuPick which
  set itemDelimiter to "/"
  if which is among the items of botons
  then send "mouseUp" to btn which
  else \
      switch (the hilitedButtonName of group "Modo")
      case "Paleta"
               palette which
               break;
      case "Execució sense mensatges"
               lock messages
                open which
               unlock messages
      default  #Edició
               topLevel which
  end switch
end menuPick

#
# La pila
#
# Compartida en btn "Piles" i btn "Recarregar piles"
global botons

on preOpenCard
  put "Recarregar piles/Ficar-me en modo edicio/Canviar directori on buscar les piles" \
         into botons
 
  set itemDelimiter to "/"
  put (char 2 to -1of item 1 to -2 of (word 2 of the long name of stack "HOME")) &\
         "/piles" into fld "directoriOnBuscarLesPiles"
  
  set the  navigationArrows to false 
  focus on btn (the hilitedButtonName of group "Modo") of group "Modo"
end preOpenCard
                       

La propiedad navigation, puesta a falso, evita que al utilizar las teclas del cursor se pase a otras ventanas de MetaCard; lo cual puede se interesante, sobre todo, mientras se desarrolla. Y con focus se envía el control hacia el grupo de selección del modo de abrir una pila.

Otros detalles menores completan esta pila: la opción de cerrar, la de cambiar el directorio donde se buscan las pilas, poner la propia pila en modo edició (para permitir los cambios en ella sin tener que ir a abrirla desde MetaCard) y una pequña "'Message Box'":

#
# Botón "File"
#
on menuPick which
  switch which
    #  case "Open..."
    #    break
  case "Quit"
    answer "Ane'm a tancar!" & return & \
        "Vols gravar canvits en esta pila?"\
        with "No vull eixir encara, torne'm!" or "No grabar" or "Aceptar"\
        titled "[" & the short name of me & "]"
    if it is "Aceptar" then save this stack
    if it is not "No vull eixir encara, torne'm!" then close this stack
    break
  end switch
end menuPick

#
# Botón "Canviar directori on buscar les piles"
#
on mouseUp
  #titled "[" & the short name of me & "]"
  answer folder "Tria el directori d'on buscar les piles" with fld "directoriOnBuscarLesPiles"
  if it is not empty then put it into fld "directoriOnBuscarLesPiles"
end mouseUp

#
#Botón "Ficar-me en modo edició"
#
on mouseUp
  topLevel stack (the name of this stack)
end mouseUp

#
# Campo de texto "ordre"
#
on returnInField
  do the text of me
end returnInField