Programación literaria colaborativa y multilingüe. Usa pads (como HedgeDoc) para programar en cualquier lenguaje con Markdown.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Perro Tuerto 6bc94e046f Se puede usar STDOUT como entrada; arreglo de bug que causaba error cuando no hay bloques ejecutables; implementación de macros discrecionales 2 months ago
exe Cambios conceptuales y refactorizaciones varias ad hoc 1 year ago
lib Se puede usar STDOUT como entrada; arreglo de bug que causaba error cuando no hay bloques ejecutables; implementación de macros discrecionales 2 months ago
raw Se puede usar STDOUT como entrada; arreglo de bug que causaba error cuando no hay bloques ejecutables; implementación de macros discrecionales 2 months ago
.gitignore Ahora es posible implementar pads dentro de pads 1 year ago
CHANGELOG.md Se puede usar STDOUT como entrada; arreglo de bug que causaba error cuando no hay bloques ejecutables; implementación de macros discrecionales 2 months ago
MANUAL.md Se puede usar STDOUT como entrada; arreglo de bug que causaba error cuando no hay bloques ejecutables; implementación de macros discrecionales 2 months ago
README.md Se puede usar STDOUT como entrada; arreglo de bug que causaba error cuando no hay bloques ejecutables; implementación de macros discrecionales 2 months ago
ruweb.gemspec Se puede usar STDOUT como entrada; arreglo de bug que causaba error cuando no hay bloques ejecutables; implementación de macros discrecionales 2 months ago

README.md

title tags
RuWEB: un preprocesador de programación literaria ruweb, programación literaria, dep:mkdir

:::info Para la lectura de este documento se recomienda su vista compartida. :::

RuWEB: un preprocesador de programación literaria

RuWEB es un preprocesador de programación literaria multilingüe y colaborativo en tiempo cuasireal a partir de pads con sintaxis Markdown. Es decir, RuWEB permite ejecutar textos en Markdown en cuyo proceso de escritura puede usarse una plataforma de pads como Etherpad, HackMD, CodiMD, HedgeDoc o CryptPad. RuWEB es el «estado del arte» de la unidad de trabajo Programando LIBREros en su búsqueda por publicar, desde una sola fuente y con software libre o de código abierto (FOSS, por sus siglas en inglés), una multiplicidad de formatos de manera automatizada. Esta punta de lanza de dicha unidad es una respuesta a las limitaciones técnicas que se nos empezaron a ser patentes con el pasar de los años.

Este texto es la fuente que comprende el programa RuWEB, la documentación de su funcionamiento, la historia de su gestación y la teoría sobre su implementación. Como prueba de concepto, al ejecutarse este pad con RuWEB el resultado es una versión más reciente del mismo programa. Desde su versión 1620598149 ---tiempo UNIX que convertido a formato estándar fue el 9 de mayo de 2021 a las 22:09:09 horas en GMT -05:00--- RuWEB de manera satisfactoria se construye literariamente a sí mismo. Si tu interés es solo aprender a usar RuWEB, visita este otro pad con la fuente de su manual.

Sobre el nombre, los requisitos, la instalación y la actualización

«RuWEB» es un nombre en el que el prefijo «Ru» hace mención al lenguaje de programación con el que está escrito: Ruby. Este es un lenguaje interpretado, reflexivo, de propósito general, de alto nivel y orientado a objetos cuya «filosofía» en su diseño es «para la productividad y la diversión del desarrollador, siguiendo los principios de una buena interfaz de usuario». «WEB» es en honor al primer preprocesador de programación literaria hecho por Donald Knuth, el cual fue presentado en el artículo «Literate Programming», publicado en The Computer Journal en 1984.

Para la distribución de RuWEB, este programa se empaqueta como una «gema». En Ruby una gema es «un formato estándar y autocontenido» que incluye el código, su documentación y sus metadatos. La gema de RuWEB se encuentra alojada en la plataforma RubyGems.org.

Para la instalación, uso o empaquetamiento de RuWEB además es necesario un intérprete de comandos estandarizado (terminal). Con ello se obtienen los dos requisitos para RuWEB:

  1. Ruby en al menos versión 2.5.1
  2. Un intérprete de comandos sh.

Con los requisitos satisfechos es posible instalar RuWEB si en tu terminal ejecutas:

gem install ruweb

Una vez instalado, RuWEB puede ejecutar pads si en la terminal escribes con esta sintaxis:

ruweb URI

El identificador de recursos uniforme (URI, por su acrónimo en inglés) puede ser la dirección local a un documento Markdown o la URL a un pad. Por ejemplo, para producir una nueva versión de RuWEB solo es necesario indicarle la URL de este pad en versión descargable:

ruweb https://pad.programando.li/ruweb/download

Para actualizar puedes optar por empaquetar una nueva versión como en el ejemplo anterior. También es posible actualizar según la versión disponible en RubyGems.org. La diferencia reside en que en el primer caso se actualiza al estado más reciente mientras que en el segundo es conforme a su estado disponible en esa plataforma. Tú eliges cómo actualizar RuWEB, aunque por estabilidad la recomendación es actualizar a su versión en RubyGems.org. Para ello, ejecuta lo siguiente en tu terminal:

gem update ruweb

¡Con esto ya tienes lo necesario para usar RuWEB! En lo siguiente se habla sobre su funcionamiento, historia y teoría. Si quieres una explicación sintética sobre el uso de RuWEB, visita este otro pad con la fuente de su manual.

Del desafío de la publicación...

RuWEB es desarrollado y mantenido por Programando LIBREros, una unidad de trabajo dedicada a la gestión de proyectos editoriales con FOSS, al desarrollo de software libre para la edición y la publicación, además de la difusión de licencias copyfarleft para la producción cultural. Esto hace que RuWEB sea de uso libre, acceso abierto y disponibilidad gratuita según los términos de su Licencia Editorial Abierta y Libre (LEAL).

Las necesidades de Programando LIBREros requieren de un control técnico en pos de una calidad editorial además de una resiliencia tecnológica que permita la reproducción de proyectos editoriales y sus publicaciones. Con una sola fuente el mantenimiento técnico se simplifica, con la automatización es posible la publicación multiformato sin que cada uno represente un nuevo ciclo de producción y con el uso de FOSS y sus formatos abiertos no acontece una pérdida de datos ni se está constreñido a la obsolescencia de algún programa. El flujo puede esquematizarse de la siguiente manera:

digraph pub {
  subgraph cluster0 {
    label="Desafío de la publicación"
    pub[label="Documento"]
    out1[label="Soporte 1"]
    out2[label="Soporte 2"]
    out3[label="Soporte 3"]
    pub -> {out1 out2 out3}
  }
}

Un ejemplo detallado de este flujo sería:

digraph single_source_publishing {
  android[label="App de\nAndroid" shape=box]
  cordova[label="Cordova" shape=hexagon]
  ebook[label="Libros\nelectrónicos" shape=box]
  html[label="Red de\nHTML"]
  ios[label="App de\niOS" shape=box]
  kindlegen[label="KindleGen" shape=hexagon]
  lualatex[label="LuaLaTeX" shape=hexagon]
  pandoc[label="Pandoc" shape=hexagon]
  pecas[label="Pecas" shape=hexagon]
  print[label="Libro\nimpreso" shape=box]
  repo[label="Repositorio\nacadémico" shape=box]
  site[label="Sitio\nweb" shape=box]
  MD -> pandoc -> {TeX HTML XML}
  TeX -> lualatex -> PDF -> print
  HTML -> {pecas cordova}
  cordova -> {APK IPA}
  APK -> android
  IPA -> ios
  pecas -> html -> site
  pecas -> EPUB -> kindlegen -> MOBI -> ebook
  EPUB -> ebook
  XML -> repo
}

En este ejemplo de diversos formatos de entrada (en óvalos), soportes de salida (en rectángulos) y tecnologías involucradas (en hexágonos), un archivo Markdown (MD) es la fuente para un proceso de publicación automatizada con FOSS que genera en un principio otros tres archivos gracias a Pandoc: $\TeX$, HTML y XML. El archivo $\TeX$ permite tener un PDF con gran cuidado tipográfico perfilado para su impresión gracias a LuaLaTeX. El archivo XML es útil para su uso en repositorios académicos alojados en plataformas como OJS o proyectos interinstitucionales como SciELO. El archivo HTML permite, por medio de Pecas, la elaboración de distintos formatos de libros electrónicos y la publicación de un sitio web con las publicaciones e incluso su hibridación mediante tecnologías como Cordova para la producción de aplicaciones para móviles. Los usuarios dedicados a la edición solo trabajan el archivo MD, el resto del flujo es programado por algún usuario o un proveedor para que una máquina lo reproduzca de manera automatizada. Aunque en la actualidad los flujos de publicación desde una sola fuente son posibles para cualquier proyecto editorial, las adaptaciones a las diversas tradiciones de diseño o a interfaces de usuario más amigables tienen aún varios elementos por afinar. Si deseas saber más sobre esta manera de publicar, consulta las entradas escritas por uno de los miembros de Programando LIBREros que fueron publicadas en el blog de Mariana Eguaras.

El preprocesador RuWEB es una respuesta a las limitaciones técnicas que en Programando LIBREros empezaron a ser patentes con el pasar de los años. La publicación desde una sola fuente por lo general se lleva a cabo a través de algún sistema de publicación. En este contexto un sistema permite pasar de unos formatos de entrada (inputs) a otros de salida (outputs), por ejemplo, de MD o DOCX a PDF, EPUB, HTML o MOBI. En los sistemas de publicación hay flujos de trabajo presupuestos que, según el caso, pueden ser configurables para adaptarse a las necesidades de cada proyecto editorial. Los sistemas de publicación con licencias de FOSS por lo general utilizan tecnologías debajo del capó como controladores de versiones ---por ejemplo, Git o Fossil---, conversores de documentos de propósito general ---como Pandoc---, compositores tipográficos ---como la familia $\TeX$--- y lenguajes de marcado ---como Markdown, ReStructuredText, AsciiDoc, HTML, JATS XML, SciELO XML, DocBook o sintaxis $\TeX$---. Algunos de los sistemas de publicación más utilizados son Jekyll, Hugo, Pelican u Org-mode. Antes de RuWEB, Programando LIBREros desarrolló la primera implementación de Pecas como sistema de publicación.

A pesar de la gran diversidad y versatilidad de los sistemas de publicación, por diferentes motivos se tiene la constante necesidad de adaptación para la producción de publicaciones según los estándares profesionales de la edición en español u otras lenguas distintas al inglés o alemán. Esta carencia es poco perceptible cuando el sistema se utiliza para la autopublicación o la generación de páginas web estáticas. Para la «edición profesional» y sus diversas tradiciones estos sistemas requieren de modificaciones que con el transcurso del tiempo la labor de mantenimiento se vuelve más compleja y la flexibilidad para la solución de problemas comienza a ser menos frecuente. A esto se suma el desfase que empieza a existir entre la modificación del sistema y la documentación de sus cambios, por lo que con los años se pierde capacidad en la reproducción de proyectos.

Un ejemplo de estas dificultades fue la primera implementación de Pecas que, debido a las constantes necesidades técnicas, terminó por no satisfacerlas de manera plena. Otro ejemplo fue orb-weaver, un sistema de publicación inconcluso hecho por Programando LIBREros. No se terminó de desarrollar porque en su camino se hizo manifiesto que el flujo editorial como sistema cerrado de publicación o se diseña para casos de uso específicos o se destina a padecer la misma suerte que Pecas. La emergente necesidad de adaptación, que en un contexto técnico es la demanda de reescritura de programas en lugar de su mera reutilización a partir de opciones de configuración, es como surgió la necesidad de facilitar la reescritura para el reuso de programas.

Ante estas dificultades es posible recurrir a las «infraestructuras de bolsillo», una propuesta de la comunidad de Grafoscopio: tecnologías cuyo diseño es simple, autocontenido, funcional sin necesidad de conexión a internet y aplicable a un amplio rango de hardware y de software, sea desde modestas computadoras personales hasta servidores transoceánicos con distintos sistemas operativos. Gracias a esta propuesta, Programando LIBREros empezó a tener conocimiento de personas pioneras en la computación como Dan Ingalls, Alan Kay y Donald Knuth.

En los ecosistemas abiertos de publicación, Knuth es una figura que sobresale por haber escrito el sistema de composición tipográfica $\TeX$. Este sistema se desarrolló entre 1978 y 1985. De manera contraria al destino habitual de un software longevo, aún hoy en día $\TeX$ es el programa que se emplea para la producción de publicaciones con gran cuidado editorial. Aunque su comparación con maquetadores como InDesign o Scribus tiene que hacerse con cautela, ya que esta familia de programas parte de un paradigma gráfico para la diagramación de textos (desktop publishing en inglés) mientras que $\TeX$ emplea un paradigma de programación y estructuración de datos, en las comunidades FOSS hay un consenso sobre su gran control técnico y el consecuente beneficio en el cuidado editorial que ningún maquetador ha logrado hasta la fecha. Por ello es relevante que, durante una entrevista llevada a cabo en 2008, Knuth dijera:

Yet to me, literate programming is certainly the most important thing that came out of the $\TeX$ project.1

Al estar Knuth en medio del desafío de la publicación, en Programando LIBREros hizo eco que el productor de uno de los sistemas de composición tipográfica más potentes para la producción de publicaciones hablase de la «programación literaria» como el resultado más importante de semejante empresa. ¿Cómo es que, de un sistema de publicación, Knuth dio el salto a un paradigma de programación?

En el camino a la programación literaria

En una búsqueda posterior se empezaron a encontrar elementos sobre cómo fue posible el salto de un sistema de publicación a un paradigma de programación. Por ejemplo, en un panfleto con un extracto fechado en 1994, Knuth nos comenta sobre el problema de programar y documentar:

[A]n experienced system programmer, who wants to provide the best possible documentation of his or her software products, needs two things simultaneously: a language like TeX for formatting, and a language like C for programming. Neither type of language can provide the best documentation by itself; but when both are appropriately combined, we obtain a system that is much more useful than either language separately.2

En la búsqueda knuthiana de una «mejor» documentación se empieza a ser patente el carácter sinergético de la combinación de lenguajes de marcado con lenguajes de programación. Las características adicionales y posibles por esta conjunción llevaron a Knuth a replantearse la pregunta de qué es programar, como es perceptible en una entrada de blog en 1992, donde nos dice:

[F]irst, I thought programming was primarily analogous to musical composition---to the creation of intricate patterns, which are meant to be performed. But lately I have come to realize that a far better analogy is available: Programming is best regarded as the process of creating works of literature, which are meant to be read.3

Esta nueva «analogía» es la que tiempo antes permitió a Knuth proponer la programación literaria, como es perceptible en el paper inaugural de 1984, donde nos pide que...

Let us change our traditional attitude to the construction of programs: Instead of imagining that our main task is to instruct a computer what to do, let us concentrate rather on explaining to human beings what we want a computer to do.4

Desde sus inicios, la programación literaria fue ligada a una exposición para la inteligibilidad de un homínido. Por último, en Computer Programming as an Art, su discurso de 1974 al recibir el premio Turing, Knuth da pauta para entender la motivación que lo llevaría a la programación literaria:

[N]one of our existing languages is really ideal for dealing with program structure and data structure, nor it is clear what an ideal language should be. Therefore I look foward to many careful experiments in language design during the next few years.5

Para Knuth los lenguajes de programación y de marcado le parecen que tienen camino por recorrer, así que la programación literaria puede comprenderse como un paso experimental para el diseño de lenguajes computacionales.

La programación literaria

La programación literaria es un «paradigma» en el cual (1a) la escritura de programas es acorde a los criterios discursivos de un homínido desde (2a) una misma fuente para el programa y su documentación. Knuth fue el primer proponente de este «paradigma» que distingue de la programación «estructurada», «habitual» o «tradicional» en donde (1b) la escritura es conforme al orden de ejecución de una máquina o a partir de (2b) fuentes diferenciadas para el código y su documentación.

La asociación de la programación literaria como «paradigma» no fue realizada por Knuth, además de que no existe consenso sobre la pertinencia de este término para clasificar los lenguajes de programación. Por ello, aquí se habla de «paradigma» a un modo de programar que se satisface a sí mismo y, cuando no es el caso, se le denomina «estilo» de programación. Este marco ha de tomarse con cautela ya que no está exento de ambigüedades.

Por ejemplo, para la instalación de WEB se requieren varios archivos, entre ellos un programa «estructurado» con código fuente en Pascal. Por otro lado, RuWEB al inicio requirió de la programación «estructurada» para su implementación y, a pesar de que hoy en día se realiza de manera literaria, su empaquetamiento como gema de Ruby hace que RuWEB regrese a un paradigma estructurado para su distribución, instalación y uso. Debido al diseño actual de los lenguajes computacionales, no existe un lenguaje literario de programación, por lo que es necesaria la instalación de preprocesadores literarios, como WEB o RuWEB, que de una u otra forma recurren a paradigmas estructurados de programación para la efectiva ejecución del programa. Sin embargo, esta aparente limitación permite interoperabilidad ya que sin dificultades es posible trasladarse de un paradigma «estructurado» a uno «literario» o visceversa. Entonces, si la satisfacción a sí mismo se entiende como una exigencia en su uso cotidiano, la programación literaria puede considerarse un paradigma. Pero si esta autosatisfacción se entiende como una demanda en torno a sus orígenes, la programación literaria requiere de un «fermento primario» de programación estructurada para su implementación.

Otra ambigüedad en torno a la programación literaria es su constante confusión con código coposamente comentado desde una sola fuente (2a) que un generador extrae para la producción de documentación. No obstante, en el código documentado la exposición queda delimitada al orden de ejecución de una máquina (1b), mientras que en la programación literaria la exposición es según los criterios discursivos de un homínido (1a). Por ello, lenguajes de marcado como POD o herramientas como Javadoc no satisfacen las dos condiciones necesarias (1a y 2a) para la programación literaria. De manera similar, los cuadernos computacionales como Jupyter Notebook o entornos de desarrollo integrado como RStudio no pueden considerarse programación literaria en su acepción knuthiana. En Jupyter Notebook o en RStudio hay una intercalación entre «prosa» y «código», por lo que la exposición sigue las pautas de su orden de ejecución (1b) pese a su intrincada combinación; es decir, su escritura corresponde a la programación estructurada de los lenguajes empleados, por lo que pueden considerarse estilos de programación y, de manera más específica, estilos de código documentado.

Estas tecnologías y la programación literaria tienen en común la escritura de programas y su documentación desde una misma fuente (2a), pero difieren en sus posibilidades expositivas (1a y 1b). La exposición literaria es acorde a criterios discursivos en lugar de conformarse a un orden de ejecución. Además, la programación literaria permite la expresión con tanta brevedad o verbosidad se desee. Es decir, en la programación literaria la exposición y su verbosidad son cuestiones en relación con quien escribe. Estas posibilidades en la escritura no son replicables en la programación estructurada, ya que una escritura distinta al orden de ejecución conllevaría a errores sintácticos. Por ello, esta satisfacción de criterios discursivos permiten hablar de la programación literaria como un paradigma de programación.

¿Cómo es posible la escritura de programas y su documentación sin estar constreñidos al orden de ejecución (1b) sino conforme al discurso de quien escribe (1a)? Las posibilidades en la escritura de la programación literaria son gracias a los macros.

Un macro en este contexto permite encapsular fragmentos de código en su declaración para su reuso en cualquier parte del programa durante su llamado, incluyendo su anidamiento en otros macros. El llamado a un macro permite utilizar el código encapsulado a partir de una palabra o un número clave, lo que cabe denominarse como «nombre del macro». La declaración de un macro, que también puede designarse como «contenido del macro», se puede llevar a cabo antes o después de su llamado. La posibilidad de declaración de macros en cualquier ubicación del texto es la característica que permite la exposición de un programa y su documentación acorde a los criterios discursivos de un homínido. Esta facultad, que por lo general ha pasado desapercibida hasta el punto donde la programación literaria se confunde con el código documentado, habilita diversos modos de exposición, incluso aquellos que no requieren seguir el orden de ejecución.

De esta manera es como la programación literaria puede comprenderse como un paradigma interoperativo. Por un lado, esta manera de programar satisface pautas expositivas que ningún otro paradigma sacia. Por el otro, un programa escrito de manera literaria puede almacenarse como programa «estructurado», aunque con la pérdida del discurso y la división de la fuente entre el código del programa y el texto de la documentación. La programación literaria, puede decirse, no es código documentado sino texto ejecutable.

El preprocesador WEB

El primer preprocesador para la programación literaria que implementó Knuth es WEB. El sistema WEB emplea dos lenguajes para la escritura de la fuente: Pascal como lenguaje de programación y $\TeX$ como lenguaje de marcado (2a). En la actualidad Knuth emplea CWEB, un preprocesador que utiliza C en lugar de Pascal. Dado a que WEB, CWEB y otros preprocesadores literarios como noweb recurren a dos diferentes rutinas, weave y tangle, y a que el objetivo de este apartado es explicar en qué consisten estos procesos, en lugar de describir las particularidades de cada preprocesador, se ha optado por delinearlos a partir de la primera implementación knuthiana descrita en su paper inaugural de 1984. Entonces, el flujo de WEB es el siguiente:

digraph web {
  Fuente -> WEB
  WEB -> TeX[dir=backward xlabel="weave   "]
  WEB -> Pascal[dir=forward xlabel="    tangle"]
  TeX -> Documento
  Pascal -> Programa
}

En WEB la fuente es bilingüe. La fuente tiene una estructura a partir de secciones numeradas que se componen de tres subsecciones: comentarios, definiciones y programa. Los comentarios son la documentación que describe el funcionamiento del programa. Las definiciones son breves declaraciones de macros «simples» o «paramétricos» que pueden contener hasta un argumento. El programa es el código a ejecutar y el lugar donde se puede llamar cualquier tipo de macro. Las tres subsecciones son opcionales, aunque su orden no puede ser alterado. El programa de cada sección es un macro que puede llamarse en otras secciones a partir de su «nombre». Este consiste en una descripción informal que no requiere ser uniforme más el número de sección. Con esto se obtienen posibilidades en la exposición que no necesariamente siguen un orden de ejecución (1a).

El sistema WEB se compone de dos procesos independientes para la obtención del programa y su documentación a partir de la fuente. Uno de los procesos Knuth lo llama «weaving the web» («tejiendo la red»), con el cual se produce un documento de composición tipográfica en formato $\TeX$. Mediante el procesador de $\TeX$ este documento es convertido a un archivo binario listo para su impresión. Hoy en día existen procesadores de $\TeX$ que permiten la producción de PDF, HTML, EPUB u otras salidas a partir del documento en formato $\TeX$. El preprocesador WEB en su rutina weave no tiene el objetivo de convertir el formato $\TeX$, sino que lo teje a partir de la fuente. Quien usa WEB tiene que darse la tarea posterior e independiente de producir el formato deseado a partir de la documentación tejida.

El otro proceso independiente a weave es el que Knuth denominó «tangling the web» («enmarañando la red»), con el cual se produce el código Pascal a ejecutar por una máquina. Mientras que la rutina weave extrae el texto en $\TeX$ para su tejido, la rutina tangle extrae el código en Pascal para minificarlo y guardarlo en un archivo listo para su compilación. El «enmarañamiento» debe su denominación a la minificación del código que se lleva a cabo durante este proceso ---y que resta legibilidad al código por suponer que no es para el «consumo humano» dentro de una propuesta de programación cuyo enfoque consiste «en explicarle a los seres humanos qué queremos que la computadora lleve a cabo»---. Esta rutina no tiene el objetivo de compilar el archivo en formato Pascal. Quien usa WEB tiene que realizar el quehacer posterior e independiente de compilar el código enmarañado para su ejecución.

Con las rutinas weave y tangle de WEB se hace posible la programación literaria cuyas prioridades son la inteligibilidad, simplicidad, escalabilidad y portabilidad. Una de las críticas constantes a la programación literaria ha sido que no pone énfasis en la eficiencia. No obstante, desde su discurso recepcional del premio Turing en 1974, Knuth ya criticaba la necedad de convertir a la eficiencia en el único criterio para determinar la calidad de un programa. Knuth no está en contra de la eficiencia en la programación, solo antepone la inteligibilidad o simplicidad si esta se enfoca en menudencias. En cuanto a la escalabilidad y portabilidad, WEB pretende ser un sistema apto para la escritura de programas de cualquier tamaño y desde cualquier sistema operativo.

Para Knuth, gracias a WEB es posible que las personas dedicadas a la programación puedan considerarse ensayistas. Al tener mayores posibilidades en la escritura, quien programa de manera literaria es un autor que con cautela se preocupa por la exposición y el estilo de su texto. Knuth admite que en un primer momento WEB parece ser solo apto para las personas que se dedican a las ciencias de la computación. Sin embargo, este no descarta la ampliación de su uso debido a que con WEB se escribe de modo más legible y acorde al «flujo de la consciencia».

Para terminar lo que Knuth llama la «filosofía de WEB», esta primera implementación para la programación literaria tiene al menos tres cuestiones a mejorar. Primero, la unificación de las rutinas weave y tangle, así como de los pasos posteriores para la composición de la documentación o la compilación del programa. Aunque Knuth no lo menciona, esta característica a mejorar también podría aplicarse al proceso de implementación e instalación de WEB ya que no puede implementarse a sí mismo, además de requerir varios archivos para su instalación. Segundo, una modularización que permita compilar diferentes partes del programa de manera independiente. Otra manera de entender la modularidad podría residir en la posibilidad de escribir programas en una red de textos si su complejidad o reutilización de partes así lo requieren. Tercero, la renderización al vuelo de $\TeX$ en lugar de esperar a su conversión en un formato legible en una pantalla. En la actualidad es posible visualizar documentos $\TeX$ en plataformas colaborativas como Overleaf, aunque están pensadas para trabajar sobre los macros que componen $\LaTeX$ y no para el quehacer requerido por WEB. Además, vale la pena tomar en cuenta otros lenguajes de marcado, como Markdown, que simplifican la renderización al vuelo.

El preprocesador RuWEB

RuWEB es un preprocesador influido por la «filosofía» del sistema WEB, así como de las necesidades cotidianas de Programando LIBREros. El sistema RuWEB pretende emplear cualquier lenguaje para la escritura de la fuente (2a). Por simplicidad y unitariedad este preprocesador ejecuta la fuente sin pasos intermedios. Sin embargo, de manera opcional cuenta con dos parámetros, --save-source y --save-code, que descargan la fuente para su lectura o depuración. A continuación se explican las particularidades de este sistema a partir de sus semejanzas y diferencias respecto a WEB. Entonces, el flujo de RuWEB es el siguiente:

digraph web {
  texto1[label="Texto\nlegible"]
  texto2[label="Texto\ndepurable"]
  Fuente -> RuWEB
  RuWEB -> MD[dir=backward xlabel="--save-source   "]
  RuWEB -> TXT[dir=forward xlabel="    --save-code"]
  MD -> texto1
  TXT -> texto2
}

En RuWEB la fuente es multilingüe. La fuente carece de estructura predeterminada, por lo que es a elección de quien escribe. La distinción entre el texto solo para lectura y el ejecutable reside en el empleo de sintaxis Markdown para la demarcación de código. El código se incorpora a la ejecución conforme al orden de lectura al menos que se declare como macro para su posterior llamado en otro bloque de código. No hay distinción entre tipos de macros ni contienen argumentos, porque estos pueden funcionar como argumentos en el llamado a otros pads durante el cosido de la fuente. Los bloques de código son apilados según el lenguaje que se utilice para resaltar su sintaxis o acorde al comando personalizado que se estipule para su ejecución. Con esto se obtienen posibilidades en la exposición que no necesariamente siguen un orden de ejecución (1a).

Además, la fuente puede ser un recurso local o remoto sin nombre de extensión requerida. Para recursos locales se recomienda emplear nombres de extensión comunes para archivos Markdown. Para recursos remotos es posible utilizar plataformas de pads como Etherpad, HackMD, CodiMD, HedgeDoc o CryptPad. Los pads permiten la escritura colaborativa en línea, lo que posibilita la programación literaria colaborativa en tiempo cuasireal. Los pads pueden utilizarse para escribir y publicar blogs, notas, traducciones, artículos, presentaciones, narrativa, poesía, música, video, arte gráfico, videojuegos, programas o cualquier otra salida producible mediante escritura. Programando LIBReros cuenta con una instancia de HedgeDoc gracias a su soporte para la renderización de imágenes, tablas, diapositivas, videos, fórmulas matemáticas, diagramas, líneas de tiempo o notación musical, además de la extensión de la sintaxis Markdown y la conservación del historial de escritura.

Por la capacidad de renderización al vuelo de los pads, se extienden las posibilidades en la escritura e inteligibilidad de la programación literaria. Esta amplitud también permite prescindir de la rutina weave, ya que no es necesaria para la producción de un documento legible. No obstante, si el pad no satisface ciertos criterios editoriales, en el mismo pueden programarse o implementarse de manera literaria los procesos para la producción de las salidas según los estándares requeridos. Estas operaciones permiten solventar las carencias de Markdown respecto a $\TeX$, además de tener la ventaja de ser un documento de fácil lectura, escritura, procesamiento y ejecución: una fuente ejecutable en lugar de una fuente para obtener un programa y su documentación. En seguimiento a la «filosofía» de WEB, en RuWEB se simplifican y unifican los pasos hasta el punto de ejecutar al vuelo el texto del pad. Esto conlleva a la omisión de la rutina tangle, ya que no se requiere un proceso adicional para la minificación y el guardado del código en un archivo para su posterior ejecución.

Una de las dos rutinas que RuWEB realiza de manera automática se llama «cosiendo la fuente» («sewing the source»). Este proceso permite incorporar los elementos modulares que componen la fuente ejecutable. En RuWEB la modularización se lleva a cabo gracias a una red de pads. El llamado a otros pads es mediante enlaces que indican la información a coser en un solo texto para ejecutarlo al vuelo. Esto implica una diferencia entre la fuente y el código. En lugar de hablar de «código fuente», en RuWEB la fuente es la información contenida en uno o más pads relacionados; la información comprende el «código», que es el texto ejecutable, y el texto no ejecutable, que puede ser la documentación, teoría, historia, etcétera. Con finalidades de portabilidad, conservación o lectura, RuWEB permite el parámetro opcional --save-source para guardar en un solo archivo Markdown el telar resultante de esta rutina.

La segunda rutina que RuWEB urde automáticamente es la denominada «cosiendo el código» («sewing the code»). Este proceso permite obtener un orden de ejecución a partir del criterio narrativo de la fuente cosida. Este proceso extrae los bloques de código de la fuente conforme a su orden de lectura, reemplaza los nombres de macros por sus contenidos, apila el código según su lenguaje o comando personalizado y procede a su ejecución. Por la posibilidad de errores de software o debido al riesgo de ejecutar código arbitrario desde un recurso remoto, RuWEB permite el parámetro opcional --save-code que en vez de correr el código lo almacena en un archivo TXT para su depuración o audición. Este documento no está configurado para su ejecución. En su lugar, este texto permite revisar el código para detectar bugs o infracciones de seguridad.

Con las rutinas sewing the source y sewing the code de RuWEB se hace posible la programación literaria cuyas prioridades adicionales a la «filosofía» de WEB son la reproducibilidad científica y la interacción con «cajas transparentes». En tiempos donde los recursos de cómputo son relevantes para la producción de conocimiento, los medios tradicionales para compartir sus hallazgos han hecho patente sus limitaciones. Por un lado, la divulgación de los descubrimientos está en flujos que destacan más por su necedad en mimetizar técnicas análogas de publicación que en aprovechar la comunicación posible por la infraestructura de internet. Por el otro, en culturas logocéntricas los textos como vehículos del conocimiento han dejado de ser suficientes si se entienden como mera gráfica; es decir, como símbolos legibles por homínidos. Los textos literarios ejecutables modifican estos vehículos al incorporar los recursos de cómputo empleados para la producción de conocimiento, por lo que se hace innecesaria la división entre los procesos y sus resultados. Además, si esta información es accesible desde internet, la reproducción de experimentos podría ser tan sencilla como ejecutar un pad con un preprocesador literario. Las redes de pads en este sentido son soportes para la gestación del conocimiento.

En un mundo en crisis, el ocultamiento de la infraestructura y los componentes que permiten la producción de conocimiento en cajas negras parece ser una apuesta poco pertinente. El encubrimiento del qué y el cómo de los sistemas limita su escrutinio y dificulta su hackeo sin una justificación allende a la custodia del conocimiento como propiedad privada. Ante las incertidumbres económica, política y del medio ambiente, tal vez es momento de repensar si esta centralización del conocimiento y la ofuscación de su operatibilidad son adecuadas para los desafíos y las responsabilidades que en la actualidad y en el porvenir tendremos como especie. Las cajas transparentes son una metáfora que nos permite concebir la delimitación de los sistemas sin necesidad de su ocultación u olvido. Al poder examinar todos los componentes de un sistema, con menores dificultades se lleva a cabo su modificación, manutención y comprensión. Los textos ejecutables de la programación literaria permiten visibilizar y dar inteligibilidad homínida a los componentes que usualmente se meten en bolsas mágicas y que de manera habitual se les llama «programa», «documentación» y «teoría», o bien, «código» y «prosa».

La implementación de RuWEB está en curso, así que además de sus tareas pendientes tiene al menos cuatro cuestiones a mejorar. Primero, por el momento los macros multilínea solo soportan lenguajes que no son sensibles a los espacios. En lenguajes como Python los espacios son elementos estructurales sintácticamente relevantes para su ejecución. RuWEB puede ser utilizado en ese tipo de lenguajes pero limitados a macros de una sola línea. En otros lenguajes, como Ruby o Bash, no han sido detectadas limitaciones en este sentido. Segundo, una posibilidad que simplificaría la constante ejecución de RuWEB al momento de escribir un texto ejecutable sería un entorno de live coding. Tercero, la división entre el texto marcado para la edición y el renderizado para la lectura de varias plataformas de pads parece ser una herencia de cierta dicotomía que también podría unificarse. Cuarto, más experimentos son necesarios para verificar qué tan afín es RuWEB para la reproducibilidad del conocimiento así como para probar su capacidad para narrativas crossmedia y transmedia. Unos textos ejecutables para la producción de media desbodarían al paradigma literario, ya que esta manera de programar desde sus inicios se ha entendido como escritura de programas y su documentación, cuando también podría comprenderse como una escritura de programas y medios. Para conocer casos de uso bajo estas intenciones, visita el manual de RuWEB.

La implementación

Como prueba de concepto en torno a la programación literaria como paradigma, a continuación se implementa literariamente el preprocesador RuWEB. Esta puesta en funcionamiento podría ser expuesta de cualquier manera. Sin embargo, la siguiente narración está pensada para personas que nunca han programado o recién están explorando estos senderos. La siguiente exposición es análoga a lo que en la programación estructurada sería el código fuente y la documentación de un programa. Si jamás has leído un programa, se recomienda la lectura de todos los enlaces referenciados para una mejor comprensión.

Si quieres llevar a cabo una lectura comparativa entre este texto y el código que al final ejecuta RuWEB, usa el parámetro --save-code. Con este parámetro se produce un texto depurable y estructurado de este documento si en tu terminal ejecutas:

ruweb --save-code https://pad.programando.li/ruweb/download

==TODO==

El evento

event = '\u005F'

La marca de tiempo

timestamp = Time.now.to_i.to_s

Las especificaciones

La especificación de los medatados de la gema:

# frozen_string_literal: true

lib = File.expand_path('lib', __dir__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)

Gem::Specification.new do |spec|
  spec.name          = 'ruweb'
  spec.version       = 1653809677.0
  spec.authors       = ['Programando LIBREros', 'Nika Zhenya']
  spec.email         = ['hi@programando.li']
  spec.summary       = 'Execute Markdown pads as literate programming.'
  spec.description   = 'Multilingual and collaborative literate programming in quasi-real time from pads.'
  spec.homepage      = 'https://pad.programando.li/ruweb'
  spec.license       = 'LEAL'
  spec.required_ruby_version = Gem::Requirement.new('>= 2.5.1')

  raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.' unless spec.respond_to?(:metadata)

  spec.metadata['homepage_uri'] = spec.homepage
  spec.metadata['allowed_push_host'] = 'https://rubygems.org/'
  spec.metadata['source_code_uri'] = 'https://gitlab.com/programando-libreros/herramientas/ruweb'
  spec.metadata['changelog_uri'] = 'https://pad.programando.li/ruweb#Lista-de-cambios'

  spec.files = ["README.md", "MANUAL.md", "raw/MANUAL.md", "raw/README.md", "CHANGELOG.md", "lib/ruweb/help.rb", "ruweb.gemspec", "lib/ruweb.rb", "lib/ruweb/sew.rb", "lib/ruweb/read.rb", "lib/ruweb/sewsource.rb", "lib/ruweb/sewcode.rb", "lib/ruweb/sewmacros.rb", "lib/ruweb/save.rb", "lib/ruweb/execute.rb", "exe/ruweb"]

  spec.bindir        = 'exe'
  spec.executables   = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
  spec.require_paths = ['lib']
end

La máquina de coser

# frozen_string_literal: true

require 'ruweb/sew'

module RuWEB
end

La guía

Guía en español:

🕷 ruweb: hola: mostrando ayuda

ruweb [OPCIÓN] [URI]

Opciones:
  --save-raw     Guarda el texto de la fuente sin modificar.
  --save-source  Guarda el texto de la fuente de toda la red de pads.
  --save-code    Guarda el texto del código en lugar de ejecutarlo.

Lee mi manual en 'https://pad.programando.li/ruweb:manual'.

Ejecutando ejemplo con mi manual:

Guía en inglés:

🕷 ruweb: hi: displaying help

ruweb [OPTION] [URI]

Options:
  --save-raw     Saves source text unchanged.
  --save-source  Saves source text of the entire pad network.
  --save-code    Saves code text instead of execute it.

Read my manual on 'https://pad.programando.li/ruweb:manual'.

Executing example with my manual:

Impresión de la guía:

# frozen_string_literal: true

module RuWEB
  # Displays help
  module Help
    extend self
    
    def init
      lang = ENV['LANG'] ? ENV['LANG'][0..1] : 'en'
      help = Help.method_defined?("help_in_#{lang}") ? "help_in_#{lang}" : 'help_in_en'
      puts send(help)
    end

    def help_in_es
      %Q{🕷 ruweb: hola: mostrando ayuda

ruweb [OPCIÓN] [URI]

Opciones:
  --save-raw     Guarda el texto de la fuente sin modificar.
  --save-source  Guarda el texto de la fuente de toda la red de pads.
  --save-code    Guarda el texto del código en lugar de ejecutarlo.

Lee mi manual en 'https://pad.programando.li/ruweb:manual'.

Ejecutando ejemplo con mi manual:}
    end

    def help_in_en
      %Q{🕷 ruweb: hi: displaying help

ruweb [OPTION] [URI]

Options:
  --save-raw     Saves source text unchanged.
  --save-source  Saves source text of the entire pad network.
  --save-code    Saves code text instead of execute it.

Read my manual on 'https://pad.programando.li/ruweb:manual'.

Executing example with my manual:}
    end
  end
end

El flujo de trabajo

# frozen_string_literal: true

require 'ruweb/help'
require 'ruweb/read'
require 'ruweb/sewsource'
require 'ruweb/sewcode'
require 'ruweb/execute'
require 'ruweb/save'

module RuWEB
  # Preprocesses the URI
  module Sew
    extend self

    def init(*opts)
      if STDIN.tty?
        @opts    = opts.first == nil ? ['--help'] : opts
        @man_uri = 'https://pad.programando.li/ruweb:manual/download'
        @uri     = obtain_uri
        RuWEB::Help.init if help?
      else
        @opts = opts
        @uri  = STDIN.read
      end
      run
    end
    
    private
    
    def help?
      [
        @opts.include?('--help'),
        @opts.include?('-h'),
        @opts.empty?,
        @uri == @man_uri
      ].include?(true)
    end
    
    def run
      raw    = STDIN.tty? ? RuWEB::Read.init(@uri) : @uri
      source = RuWEB::SewSource.init(raw)
      code   = RuWEB::SewCode.init(source)
      if @opts.any? { |opt| /^--save/ =~ opt }
        RuWEB::Save.init('raw.md',    raw)    if @opts.include?('--save-raw')
        RuWEB::Save.init('source.md', source) if @opts.include?('--save-source')
        RuWEB::Save.init('code.txt',  code)   if @opts.include?('--save-code')
      else
        RuWEB::Execute.init(code) unless @opts.include?('--save-code')
      end
    end
    
    def obtain_uri
      @opts.last[0..1] == '--' ? @man_uri : @opts.last
    end
  end
end

La preparación

# frozen_string_literal: true

require 'open-uri'

module RuWEB
  # Reads the Markdown
  module Read
    extend self
    
    def init(uri)
      File.exist?(uri) ? File.read(uri).strip : URI.parse(uri).open.read.strip
      rescue StandardError
        puts  "🕷 ruweb: [ERROR] couldn't read '#{uri}': verify its availability or run 'ruweb' for help"
        abort
    end
  end 
end

El cosido de la fuente

# frozen_string_literal: true

module RuWEB
  # Gets source
  module SewSource
    extend self
    
    def init(raw)
      @source = sew(remacro(raw))
    end

    private

    def sew(markdown)
      regex = /\u005F\[(.+?)\]\((.+?)\)/
      markdown = markdown.gsub(regex) do |match|
        name  = match.gsub(regex, '\1').strip
        uri   = match.gsub(regex, '\2').strip
        puts  "🕷 ruweb: other pad detected: sewing '#{name}' from '#{uri}'"
        pad   = remove_metadata(RuWEB::Read.init(uri))
        generate_source_block_from(match[1..-1], pad)
      end
      markdown.scan(regex).any? ? sew(markdown) : markdown
    end
    
    def remacro(raw)
      tag  = /\S*=>\s+/
      stop = /[\.,:;!?]/
      code_tag = "\u0060" * 3
      raw.gsub(/\s+#{tag}.+?#{stop} */,) do | protomacro |
        macro = protomacro.strip.gsub(tag, '').gsub(stop, '').gsub(/\s+/, '_')
        lang  = protomacro.split('=>').first.strip
        lang  = lang.empty? ? 'ruby' : lang
        "\n\n#{code_tag}#{lang}\n_#{macro}\n#{code_tag}\n\n"
      end
    end

    def generate_source_block_from(link, pad)
      msg = "\n\n:::info\n▶ [#{Time.now}] #{link}\n:::\n\n"
      "#{msg}#{pad}#{msg.gsub('▶', '⏹')}"
    end

    def remove_metadata(markdown)
      markdown.sub(/^---\n+(.|\n)+?---(\n+|$)/, '')
    end
  end
end

El cosido del código

# frozen_string_literal: true

require 'ruweb/sewmacros'

module RuWEB
  # Gets code
  module SewCode
    extend self
    
    def init(source, return_cmds: true, return_macros: false, return_both: false)
      @return_macros = return_macros
      @return_cmds   = return_cmds
      @return_both   = return_both
      @code          = sew(extract_blocks_from(source))
    end

    private

    def extract_blocks_from(markdown)
      b = '\u0060' * 3
      markdown.scan(/#{b}((.|\n)+?)#{b}/).map(&:first)
    end

    def sew(blocks)
      unless blocks.empty?
        blocks.map!.with_index do |block, i|
          lines = block.split("\n")
          if lines.any?
            meta  = lines[0]
            data  = lines[1..-1].join("\n")
            tokenize(meta, data, i)
          end
        end
        tipify(blocks)
      end
    end

    def tokenize(meta, data, index)
      case meta.split(/\s+/).length
      when 0 then nil
      when 1 then { index: index, cmd: meta[/^\w+/], data: data }
      else obtain_custom_or_macro_from(meta, data, index) end
    end

    def obtain_custom_or_macro_from(meta, data, index)
      obj    = { index: index }
      custom = meta.gsub(/^\S+\s+/, '')
      macro  = custom =~ /^\u005F/ ? custom : nil
      macro ? obj[:macro] = macro : obj[:custom] = custom
      obj.merge({ data: data })
    end

    def tipify(blocks)
      blocks.compact!
      cmds   = arrange(blocks)
      custom = rearrange(blocks)
      macros = preserve('macro', blocks).reverse
      cmds   = RuWEB::SewMacros.init(macros, cmds: custom.concat(cmds))
      resolve(macros, cmds.sort_by { |e| e[:index] })
    end

    def arrange(cmds)
      arranged = preserve('cmd', cmds).map! { |e| { cmd: e[:cmd], data: '' } }.uniq
      pick(cmds, arranged)
    end

    def pick(cmds, arranged)
      arranged.map! do |e1|
        picked = cmds.select { |e2| e1[:cmd] == e2[:cmd] }
        index  = picked.map { |e| e[:index] }.min
        data   = picked.map { |e| e[:data] }.join("\n")
        { index: index, cmd: e1[:cmd], data: data }
      end
    end

    def rearrange(custom)
      custom = preserve('custom', custom)
      custom.reject! { |e| e[:custom] == 'x' }
      custom.map { |e| { index: e[:index], cmd: e[:custom], data: e[:data] } }
    end

    def preserve(key, array)
      array.select { |e| e[key.to_sym] }
    end
        
    def resolve(macros, cmds)
      case
      when @return_both
        cmds.concat(macros)
      when @return_macros
        macros
      when @return_cmds
        cmds
      end    
    end
  end
end

El cosido de los macros

# frozen_string_literal: true

module RuWEB
  # Gets macros
  module SewMacros
    extend self
    
    def init(macros, cmds: [], in_macro: '', loops: 100)
      @macros = macros
      @pass   = true
      @els    = obtain_els(cmds, in_macro)
      @loops  = loops
      sew
      inspect
      resolve
    end
    
    private
    
    def obtain_els(cmds, macro)
      cmd = [{index: 0, cmd: '', data: "\u005F#{macro}"}]
      cmds.empty? ? cmd : cmds
    end
    
    def sew
      @macros.map do |m|
        @els.map! do |e|
          e.each do |k, v|
            e[k] = e[k].gsub(/\b#{m[:macro]}\b/, m[:data]) if k == :data
          end
        end
      end
    end
    
    def inspect
      @macros.map do |e|
        @pass = false if @els.join.scan(e[:macro]).any?
      end
    end
    
    def resolve
      if @pass
        @els[0][:cmd].empty? ? @els[0][:data] : @els
      else
        inspect_loops
        init(@macros, cmds: @els, loops: @loops - 1)
      end
    end
    
    def inspect_loops
      if @loops <= 0
        puts '🕷 ruweb: [ERROR] too much recursion: check for missing macros or typos in their declarations or calls'
        abort
      end
    end
  end
end

La ejecución

# frozen_string_literal: true

require 'English'

module RuWEB
  # Executes the code
  module Execute
    extend self
    
    def init(code)
      return unless code
      index = 1
      code.each do |e|
        cmd = merge(e[:cmd], e[:data])
        system(cmd)
        next unless $CHILD_STATUS.exitstatus.positive?

        file = "ruweb_log#{index}"
        puts "🕷 ruweb: something went wrong on top: saving #{file}"
        File.write(file, e[:data])
        index += 1
      end
    end

    private

    def merge(cmd, data)
      cmd =~ /%/ ? cmd.gsub('%', data) : "#{cmd} <<'EOS'\n#{data}\nEOS"
    end
  end
end

El guardado

# frozen_string_literal: true

module RuWEB
  # Saves the content
  module Save
    extend self
    
    def init(file, content)
      content = File.extname(file) == '.md' ? content : convert_data_of(content)
      puts "🕷 ruweb: parameter detected: saving '#{file}' in '#{Dir.pwd}'"
      File.write(file, content)
    end

    private

    def convert_data_of(content)
      content.map { |e| "$ #{e[:cmd]}\n#{numerate(e[:data])}" }.join("\n\n")
    end

    def numerate(data)
      data = data.split("\n")
      data.map!.with_index { |e, i| "#{format(i, data.length)}│ #{e}" }
      data.join("\n")
    end

    def format(num, total_num)
      num  += 1
      chars = total_num.to_s.length - num.to_s.length
      space = ' ' * chars
      "#{space}#{num}"
    end
  end
end

El programa

#!/usr/bin/env ruby
# frozen_string_literal: true

require 'ruweb'
RuWEB::Sew.init(*ARGV)

La gema

Satisfación de requisitos:

require 'ruweb'
puts "🕷 ruweb: [0/3] it seems you want to update me: building gem"

Obtención del texto de este pad:

raw_md = RuWEB::Read.init('https://pad.programando.li/ruweb/download')

Obtención del texto del manual:

raw_manual = RuWEB::Read.init('https://pad.programando.li/ruweb:manual/download')

Obtención del texto cosido de este pad:

md = RuWEB::SewSource.init(raw_md)

Obtención del texto cosido del manual:

manual = RuWEB::SewSource.init(raw_manual)

Obtención de macros de este pad:

macros = RuWEB::SewCode.init(md, return_macros: true)

Llamado a pad con comando emulado

:::info [2022-05-29 02:34:38 -0500] mkdir :::

Generador de directorios mkdir

Requisitos

Ruby > 2.5.1

Opciones

  • rebuild: reconstruye el árbol de directorios (elimina directorios si ya existen)

Opciones por defecto:

rebuild: false

Módulo

module CLI
  extend self
  require 'fileutils'
  require 'pathname'
  require 'yaml'
  
  def mkdir(*dirs, config: YAML.load(%Q{_mkdir_config}))
    mkdir_rm(dirs) if config['rebuild']
    dirs.each do |dir|
      Pathname.new(dir).descend do |component|
        Dir.mkdir(component) unless File.exist?(component)
      end
    end
  rescue
    puts "something went wrong with 'mkdir'"
  end
  
  private

  def mkdir_rm(dirs)
    dirs.each do |dir|
      root = File.split(dir).first
      FileUtils.rm_rf(root) if File.exist?(root)
    end
  end
  
end

:::info [2022-05-29 02:34:38 -0500] mkdir :::

.

Generación de árbol de directorios:

CLI.mkdir('ruweb/lib/ruweb', 'ruweb/exe', 'ruweb/raw')

Relación de archivos:

files = [
  { name: 'README.md', path: 'ruweb/', data: md },
  { name: 'MANUAL.md', path: 'ruweb/', data: manual },
  { name: 'MANUAL.md', path: 'ruweb/raw/', data: raw_manual },
  { name: 'README.md', path: 'ruweb/raw/', data: raw_md },
  { name: 'CHANGELOG.md', path: 'ruweb/' },
  { name: 'help.rb', path: 'ruweb/lib/ruweb/' },
  { name: 'ruweb.gemspec', path: 'ruweb/' },
  { name: 'ruweb.rb', path: 'ruweb/lib/' },
  { name: 'sew.rb', path: 'ruweb/lib/ruweb/' },
  { name: 'read.rb', path: 'ruweb/lib/ruweb/' },
  { name: 'sewsource.rb', path: 'ruweb/lib/ruweb/' },
  { name: 'sewcode.rb', path: 'ruweb/lib/ruweb/' },
  { name: 'sewmacros.rb', path: 'ruweb/lib/ruweb/' },
  { name: 'save.rb', path: 'ruweb/lib/ruweb/' },
  { name: 'execute.rb', path: 'ruweb/lib/ruweb/' },
  { name: 'ruweb', path: 'ruweb/exe/', permission: 0755 }
]

Escritura de archivos:

files.each do |file|
  path = "#{file[:path]}#{file[:name]}"
  permission = file[:permission] || 0644
  data = file[:data] || macros.select { |e| e[:macro][1..-1] == file[:name] }.first[:data]
  data = data.gsub('\u005F', event)
  data = data.gsub('1653809677', timestamp)
  data = data.gsub('🕷 ruweb: hola: mostrando ayuda

ruweb [OPCIÓN] [URI]

Opciones:
  --save-raw     Guarda el texto de la fuente sin modificar.
  --save-source  Guarda el texto de la fuente de toda la red de pads.
  --save-code    Guarda el texto del código en lugar de ejecutarlo.

Lee mi manual en 'https://pad.programando.li/ruweb:manual'.

Ejecutando ejemplo con mi manual:', %Q{_help_es})
  data = data.gsub('🕷 ruweb: hi: displaying help

ruweb [OPTION] [URI]

Options:
  --save-raw     Saves source text unchanged.
  --save-source  Saves source text of the entire pad network.
  --save-code    Saves code text instead of execute it.

Read my manual on 'https://pad.programando.li/ruweb:manual'.

Executing example with my manual:', %Q{_help_en})
  data = data.gsub('["README.md", "MANUAL.md", "raw/MANUAL.md", "raw/README.md", "CHANGELOG.md", "lib/ruweb/help.rb", "ruweb.gemspec", "lib/ruweb.rb", "lib/ruweb/sew.rb", "lib/ruweb/read.rb", "lib/ruweb/sewsource.rb", "lib/ruweb/sewcode.rb", "lib/ruweb/sewmacros.rb", "lib/ruweb/save.rb", "lib/ruweb/execute.rb", "exe/ruweb"]', files.map { |e| "#{e[:path].gsub(/^.+?\//, '')}#{e[:name]}" }.to_s)
  File.write(path, data)
  File.chmod(permission, path)
end

Formateo, empaquetamiento e instalación de la gema:

echo "🕷 ruweb: [1/3] packing new version of me"
cd ruweb
ls ruweb-*.gem 1> /dev/null 2>&1 && rm ruweb-*.gem
gem build ruweb.gemspec
echo "🕷 ruweb: [2/3] installing me"
gem install ruweb-*.gem
gem cleanup ruweb
echo "🕷 ruweb: [3/3] what's up!: it is me RELOADED ⚡"
echo "🕷 ruweb:       the directory 'ruweb' was left for your inspection: you can delete it without problems"

...al desafío de la escritura

digraph ed {
  subgraph cluster1 {
    label="Desafío de la escritura"
    ed[label="Fuente"]
    program[label="Programa"]
    media[label="Otras\nmedias"]
    subgraph cluster0 {
      label="Desafío de la publicación"
      pub[label="Medio\ntextual"]
      out1[label="Soporte 1"]
      out2[label="Soporte 2"]
      out3[label="Soporte 3"]
      pub -> {out1 out2 out3}
    }
    ed -> {pub program media}
    program -> {pub media}
  }
}

La programación literaria es solo un punto de partida.

Los macros son una posibilidad pero también limitación de la programación literaria.

¿lenguaje literario de programación? No sería como es actualmente la programación literaria.

Debido a que son necesarios los «bloques de código» para poder escribir en una sola fuente diversos tipos de textos, la división entre «código» y «escritura» aún persiste. Sin embargo, tómese esta implementación como un punto de partida que comienza a matizar esta escisión. El punto de llegada es un modo de escritura ideal donde los diversos bloques de texto son una cuestión de estilo al gusto de quien escribe o según las expectativas de las tradiciones que se son parte, en lugar de ser una necesidad técnica para poder hablar de una sola fuente. El horizonte aquí advertido supera con creces la capacidad de un homínido, por lo que se sugiere enfocar la reflexión en el fenómeno manifestado antes de dirigir la atención en la confección de ideas que en este texto ha pretendido intelegirlo. Nuestra comprensión sobre estas máquinas en las que leemos tan solo ha comenzado y con altas probabilidades es una labor que no se agotará durante nuestra época.

Lista de cambios

# 1653809677

* STDOUT readability.
* Bug fix that causes execution error when there are no executables blocks.
* Implementation of discretional macros.

# 1649993986.0

* No pad execution if there is any save option.

#  1628636073.0

* `Sew` now requieres at least one argument.
* Module `SewCode` now can return only macros, cmds or both.
* New module `SewMacros` for methods that were in `SewCode`.
* Renamed parameters from `--download`, `--knit` and `--unknit` to `--save-raw`, `--save-source` and `--save-code`.
* Conceptual corrections where “preprocess”, “knit” and “unknit” are now “sew”, “sew source” and “sew code”.
* Change from class variables to instance variables.
* Change from classes to modules.
* Formatting on help display.

# 1625795090

* Bug fix that causes non-stop loops in some macros substitutions.

# 1624474085

* Macros can be use as parameters of other pads after its call.
* New class `Read` for getting local or remote texts.
* New --download argument for downloading raw text.

# 1623376710

* Macros doesn't have inline comments between parentheses.
* Macros declarations are now with underscore.
* Other pads declarations are now with underscore + link in Markdown.
* Version number and latest change synced to the same timestamp.

# 1620938655

* Lincense now is [Open and Free Publishing License (LEAL)](https://programando.li/bres/).

# 1620894786

* Conceptual correction where “weave” is now “knit” and “unweave” is now “unknit”.

# 1620891507

* Typo fix from “weaved” to “woven”.

# 1620871648

* Bug fixes that didn't allow local execution.

# 1620701079

* MANUAL splitted from README.

# 1620609469

* Conceptual correction where “tangle” is now “unweave”.

# 1620598149

* Since this version, RuWEB successfully build itself from [its pad](https://pad.programando.li/ruweb).

Agradecimientos

Este horizonte no hubiera sido posible sin las siguientes personas o entidades:

  • Mi madre, Altagracia Anguiano Cárdenas: ante mi actual desempleo de manera incondicional me has brindado el apoyo material necesario para poder tener tiempo de escribir este trabajo.
  • Mi pareja, Deirdre Donahoe: gracias por dejarme ser parte de tu vida, por tu sostén económico, por ayudarme a traducir este trabajo al inglés y por enseñarme sobre las arañas.
  • Programando LIBREros, con especial énfasis a Melisa Bayardo: en nuestra reflexión hemos descubierto que hay más mundo que el delimitado en cada una de las disciplinas que cruzamos como la filosofía, las artes y las ciencias.
  • Grupo Miau, con especial énfasis a Juana y Hacklib: en nuestras conversaciones poco a poco estamos encontrando maneras en que la teoría y la praxis no queden escindas y en donde el compromiso social y los cuidados afectivos no son una dicotomía entre «lo público» y «lo privado».
  • Grafoscopio, con especial énfasis a Offray: en una serie de gratas coincidencias me has mostrado caminos que nadie más me había presentado y que me han permitido atreverme a pensar sin importar equivocarme y sin importar los «campos disciplinares».
  • Colima Hacklab, con especial énfasis a Brenda y Lexo: gracias por enseñarme el capital político del software y la cultura libres para un «pensar libre» o, como también podría denominarse, un pensar radical.
  • Rancho Electrónico, tantas personalidades como Hacklib, Alma, Cacu, Babel, Aradnix, Argumento, Occidere, Petrohs, Gato Viejo y muchas más personas que de continuar jamás acabaría de mencionarlas: gracias por darme una muestra concreta de la construcción de otros mundos posibles.
  • Otras entidades como Orbilibro, la Academia Mexicana de la Lengua (AML), Partido Interdimensional Pirata (PIP), LIDSoL, CuatroLibertades, Ediciones Imago Mundi y Primero de Mayo, con espacial énfasis y de manera respectiva a Julián, Edgar y Pedro de la AML, Fauno y Librenauta del PIP, además de Gunnar, Félix, Alberto Moyano y Mimex: me han convencido de que otros mundos no solo son posibles, sino que no se requiere del ejercicio de la violencia para llegar a ellos, solo necesitamos ser más personas que en el día a día vemos que las utopías son posibles en semejante mundo tan distópico y tal vez en camino hacia la guerra.
  • Más activistas, académicos y otras personas en el barco de la locura, como Laura, Mayra, Nepotina, Lobaluna, Aimé, Mendiola, Pau, Gabi, Alberto López, Ara, Categulario, David, Daniel y Salvador: son un ejemplo para mí de lucha y de crítica rigurosas pese a que varias personas dicen que eso ya ha muerto, por ustedes estoy convencido de que nuestra especie no tiene la suerte echada.

Licencia

Este texto está bajo Licencia Editorial Abierta y Libre (LEAL). Con LEAL eres libre de usar, copiar, reeditar, modificar, distribuir o comercializar bajo las siguientes condiciones:

  • Los productos derivados o modificados han de heredar algún tipo de LEAL.
  • Los archivos editables y finales habrán de ser de acceso público.
  • El contenido no puede implicar difamación, explotación o vigilancia.

Tareas pendientes

  • Arreglar error de ejecución cuando no hay bloques de código ejecutables.
  • Posibilidad de ejecutar código en línea.
  • Posibilidad de declaración de macros en código en línea.
  • Posibilidad de uso de macros para reciclar código; similar a los macros de Rust.
  • Sustitución de macros en comandos.
  • Sistema de caché para sincronizar pads locales con pads remotos.
  • Sistema de monitoreo de pads locales para autoejecución (quizá combinar ambos sistemas con un módulo Watch).
  • Usar módulo Puts de Pecas para impresión en consola (implica colocar en otro pad y refactorizar).
  • Releer para comprobar que nuevas metáforas y parámetros ya no empaten con «knit» y «unkit» (también aplica para el manual).
  • Permitir alterar el índice de los bloques de código mediante la especificación de su número de línea.
  • Resolver conflicto entre reescritura de macros, se tiene que detectar el \w+ entero y no solo el \w+ que corresponde al macro; de lo contrario el macro _download sobreescribe al _init_download, por ejemplo.

  1. No obstante, para mí, la programación literaria es sin duda la cosa más importante que salió del proyecto $\TeX$.

  2. Una persona con experiencia en la programación de sistemas, alguien que quiere proveer la mejor documentación posible para su programa de cómputo, necesita dos cosas de manera simultánea: un lenguaje como $\TeX$ para el formato y un lenguaje como C para la programación. Ningún tipo de lenguaje por sí mismo puede proveer la mejor documentación; pero cuando ambos son combinados de manera apropiada, obtenemos un sistema que es más útil que cualquiera de los lenguajes por separado.

  3. Primero pensé que la programación era en principio análogo a la composición musical; la creación de patrones intrincados que están pensados para ser interpretados. Pero más tarde me di cuenta de una mejor analogía disponible: la programación es más comprensible como el proceso de creación de obras literarias que están hechas para ser leídas.

  4. Cambiemos nuestra actitud tradicional acerca de la construcción de programas: en vez de imaginar que nuestra tarea principal es la de instruirle a la computadora qué hacer, en su lugar concentrémonos en explicarle a los seres humanos qué queremos que la computadora lleve a cabo.

  5. Ninguno de nuestros lenguajes existentes son realmente ideales para lidiar con las estructuras de programas y datos, ni tampoco es claro cómo debería ser un lenguaje ideal. Por lo tanto, con cautela en los pŕoximos años espero llevar a cabo varios experimentos sobre el diseño de lenguajes.