Generador de código a partir de plantillas

Vamos a ver cómo funciona chicote un generador de código basado en plantillas. Hace tiempo tuve que hacer un trabajo muy repetitivo de migración de código de una tecnología a otra, al final el 90% del trabajo era repetitivo. No tan simple como copiar y pegar, pero nada que requiriera demasiado cerebro. Para esos casos cree chicote. Chicote es el nombre de un famoso chef español. Por eso el uso de términos relativos a la cocina.

Vamos a ver de que consta este sistema.

Motor de plantillas

Lo primero que necesita un generador basado en plantillas es un motor de plantillas que genere texto a partir de una plantilla.

Para este caso opte por mustache.js que es simple, ligero y muy fácil de aprender. Además permite personalizar varios aspectos que necesitaba.

Una de sus principales limitaciones es que no se le puede pasar «parámetros» a las etiquetas, para resolverlo tuve que modificar el código añadiendo esa posibilidad.

Operadores de texto

Otra cosa necesaria para generar código amigable para los seres humanos es poder operar sobre el texto. Es habitual que el mismo nombre se escriba en capital, mayúsculas, minúsculas, separado por guiones,… Los programadores somos unos maniáticos de esto y definimos documentos de estilo solo para especificar este tipo de cosas.

Para esto elegí dos librerías voca.js y pluralize.js y transforme sus operaciones en etiquetas que se puedan usar desde la plantilla.

Además son útiles funciones que actúen a nivel de línea para ordenarlas, limpiar espacios, eliminar líneas duplicadas o tabularlas.

Generador de datos falsos

Los datos falsos con cierta estructura son útiles para generar test, plantillas o demos. En este caso recurrí a la librería faker.js

Algunas ayudas más

Finalmente añadí alguna ayuda más con función habituales en programación como la fecha, la hora, un timestamp, números aleatorios o un contador

Directorios y ficheros

Otra necesidad de los generadores de código programador es poder generar directorios y nombres de archivos con algún tipo de plantilla. Es muy habitual usar la estructura de directorios para organizar el código.

El cocinero (Chicote)

Chicote es un generador agnóstico de código basado en plantillas. Es agnóstico puesto que sirve para cualquier lenguaje que use ficheros de texto. Necesita NodeJS para funcionar. No necesitas instalar nada, sólo descargar el código y ejecutar cooking.js

node cooking.js

La salida se genera en el directorio output

La receta (recipes.json)

Necesitas crear un archivo recipes.json, que contiene una o mas recetas, cada receta contiene todos los ingredientes y pasos para generar código. El único apartado obligatorio en cada receta es steps ,un array con donde se indican que pasos (steps) tiene que ejecutar para realizar la receta. Un step es el nombre de un archivo JSON en el directorio steps.

Ejemplo de fichero recipes.json con solo una receta:

[
{	
    "name": "Test",
	"author": "Bob",
	"fields": [
		{"name": "id", "type": "int"},
		{"name": "firstname", "type": "String"},
		{"name": "lastname", "type": "String"},
		{"name": "birthday", "type": "date"}
	],
	"names": ["HelloWorld", "helloWorld", "hello-world"],
	"steps": ["example"]
}
]

Pasos de la receta (step)

Un step es un archivo JSON en el directorio «steps» donde se preparan los ingredientes. Un paso tiene tres partes: variables (vars), directorios (directories), plantillas (templates)

  • vars son variables creadas a partir de los ingredientes. Actúan de forma similar a los ingredientes.
  • directories indica los directorios de salida.
  • plantillas archivos de texto en el directorio de plantillas. Se compone de un array de dos cadenas de texto, la primera indica la ruta de la plantilla en el directorio templates, la segunda la ruta del fichero generado en el directorio output

Se pueden usar los tags que más adelante veremos para calcular lo valores de estos apartados.

Ejemplo de step:

{
    "vars":[
        ["filename1", "exampleA-{{timestamp}}.code"],
        ["filename2", "exampleB-{{timestamp}}.code"],
        ["className", "{{#capital}}{{name}}{{/capital}}"],
        ["varName", "{{#decapital}}{{name}}{{/decapital}}"],
        ["moduleName", "{{#dash}}{{name}}Module{{/dash}}"]
    ],	
    "directories":[
        "example/example1",
        "example/example2",
        "examples/example1/example2"
    ],
    "templates":[
        ["example/example1.text", "text.txt"],
        ["example/example1.text", "example/example1/{{filename1}}"],
        ["example/example2.text", "example/example2/{{filename2}}"],
        ["example/example1.text", "examples/example1/{{filename1}}"],
        ["example/example2.text", "examples/example1/example2/{{filename2}}"]
    ]
}

El cocinado (templates)

Las plantillas son archivos de texto en el directorio «templates» que utilizan la sintaxis de mustache pero con algunas «etiquetas especiales».

{{timestamp}}  
{{date}} - fecha en formato yyyy-mm-dd
{{time}} - hora en formato hh:mm:ss
{{year}} - año
{{month}} - mes
{{day}} - dia
{{hour}} - hora
{{minute}} - minuto
{{second}} - segundo
{{GUID}}   

{{#plural}}text{{/plural}}  
{{#camel}}text{{/camel}}  
{{#capital}}text{{/capital}}   
{{#decapital}}text{{/decapital}}  
{{#dash}}text{{/dash}}  
{{#snake}}text{{/snake}}  
{{#swap}}text{{/swap}}  
{{#title}}text{{/title}}  
{{#lower}}text{{/lower}}  
{{#upper}}text{{/upper}}  
{{#slug}}text{{/slug}}  
{{#reverse}}text{{/reverse}}  
{{#stripTags}}text{{/stripTags}}  
{{#escHtml}}text{{/escHtml}}  
{{#unHtml}}text{{/unHtml}}  
{{escRegExp}}text{{/escRegExp}}  
{{#trim}}text{{/trim}}  
{{#latin}}text{{/latin}}  

{{#count}}text{{/count}} - reemplaza texto por número de caracteres    
{{#countWords}}text{{/countWords}} - reemplaza el texto por el número de palabras  

{{#delSpaces}}text{{/delSpaces}} - suprimir todos los espacios  
{{#delDuplicateSpaces}}text{{/delDuplicateSpaces}}  - eliminar los espacios duplicados   
{{#delLast|C}}text{{/delLast|C}} - suprimir la última coincidencia de caracteres C  
{{#delFirst|C}}text{{/delFirst|C}} - suprimir la primera coincidencia de caracteres C  
{{#delEnd|N}}text{{/delEnd|N}} - suprimir N caracteres del final  
{{#delStart|N}}text{{/delStart|N}} - del N caracteres desde el inicio  
  
{{#repeat|N}}text{{/repeat2|N}} - repetir el texto N veces  
  
{{#sortAscL}}text{{/sortAscL}} - ordenar las líneas de forma ascendente   
{{#sortDescL}}text{{/sortDescL}} - líneas de ordenación descendente  
{{#naturalSortAscL}}text{{/naturalSortAscL}} - líneas de ordenación natural ascendente  
{{#naturalSortDescL}}text{{/naturalSortAscL}} - líneas de ordenación natural descendente  
{{#shuffleL}}text{{/shuffleL}} - barajar líneas  
{{#trimL}}text{{/trimL}} - recortar líneas  
{{#joinL}}text{{/joinL}} - unir líneas  
{{#removeDuplicateL}}text{{/removeDuplicateL}} - eliminar líneas duplicadas  
{{#spaceL|N}}text{{/spaceL|N}} - Añadir N espacios al principio de cada línea   
{{#tabL|N}}text{{/tabL|N}} - Añadir N tabulaciones al principio de cada línea  
{{#addStartL|C}}text{{/addStartL|C}} - Añadir el carácter C al principio de cada línea   
{{#addEndL|C}}text{{/addEndL|C}} - Añadir el carácter C al final de cada línea  

{{#log}}text{{/log}} - escribir texto en la consola   
{{#eval}}text{{/eval}} - evalúa el texto como código JS  
{{#R}}texto{{/R}} - renderizar texto  
  
{{#C=|N}}{{/C=|N}} - poner el contador en N  
{{#C+|N}}{{/C+|N}} - aumentar el contador en N  
{{#C-|N}}{{/C-|N}} - reducir el contador en N  
{{#C}}{/C}} - imprimir el contador  

{{#K}}texto{{/K}}  - cargar datos de la base de conocimiento (cookbook.json)  
  
{{!text}} - Comentario  

{{#faker}}data{{\faker}} - genera un dato aleatorio usando faker.js

{{=AA BB=}} - Cambia los caracteres para indicar que es un tag de {{ }} to AA BB

Ejemplo de uso de alguno de los tags:

helloWorld
plural: helloWorlds
camel: helloWorld
capital: HelloWorld
decapital: helloWorld
dash: hello-world
snake: hello_world
swap: HELLOwORLD
title: HelloWorld
lower: helloworld
upper: HELLOWORLD
escHtml: helloWorld
slug: hello-world
count: 10
countWords: 2

hello-world
plural: hello-worlds
camel: helloWorld
capital: Hello-world
decapital: hello-world
dash: hello-world
snake: hello_world
swap: HELLO-WORLD
title: Hello-World
lower: hello-world
upper: HELLO-WORLD
escHtml: hello-world
slug: hello-world
count: 11
countWords: 2

El código de Chicote incluye un ejemplo de plantillas.

El libro de cocina (cookbook.json)

Actúa como base de conocimiento para Chicote. Funciona como un sistema de clave valor.

Para leer los datos de la base de conocimiento se usan los tags {{#K}} y {{/K}}. Si tenemos {{#K}}texto{{/K}} texto será reemplazado por el valor que se encuentre en la base de conocimiento con la clave texto

Un ejemplo de base de conocimiento

{	
	"intDefaultValue": " = 0",
	"dateDefaultValue": " = new Date()",
	"stringDefaultValue": " = ''"
}

Ahora veamos un ejemplo de su uso en una plantilla:

var v{{#K}}{{#lower}}{{type}}{{/lower}}DefaultValue{{/K}};

En el ejemplo si type es date dará como resultado:

var v{{#K}}dateDefaultValue{{/K}};

Buscando esa clave en la base de conocimientos:

var v = new Date();

La foto del plato

Como pequeño resumen se puede ver este diagrama que trata de simplificar el funcionamiento de Chicote:

chicote

Bon appetit