Proyecto práctico: Aplicación de consola con persistencia JSON
Antes: Concurrencia y canales. Después: Proyecto web completo. Referencia técnica: Línea de comandos (CLI), Estructura de proyectos.
¿Qué vas a construir aquí?
Una aplicación de consola (CLI) es un programa que se ejecuta directamente en la terminal sin interfaz gráfica. Es el tipo de software utilizado en automatizaciones, procesamiento de datos por lotes (batch processing), scripts de mantenimiento de servidores y utilidades para desarrolladores.
En este tutorial guiado construiremos un gestor de compras e inventario:
1. Crear la estructura oficial del proyecto con joss new console.
2. Estructurar una lista de artículos con cantidades y precios decimales exactos.
3. Guardar los datos en un archivo físico en el disco (compras.json) en formato JSON estructurado.
4. Leer el archivo del disco, validar su integridad sintáctica (json_verify) y reconstruir las estructuras de datos en memoria (json_decode).
5. Procesar y totalizar los importes con precisión decimal financiera usando funciones modulares.
6. Compilar el proyecto a un ejecutable nativo independiente con joss build.
1. Preparar el entorno de trabajo con joss new console
Abre tu terminal y genera un proyecto de consola oficial con el CLI de Joss:
joss new console proyecto-compras
cd proyecto-compras
Joss habrá creado la estructura base del proyecto (incluyendo main.joss, env.joss, joss.yaml y la carpeta app/). Abre main.joss en tu editor para escribir la lógica de nuestra aplicación.
2. El código completo del programa
Reemplaza el contenido de main.joss por el siguiente programa completo:
public func totalizar(array $compras): decimal {
decimal $total = 0m
foreach ($compras as $compra) {
$total = $total + decimal($compra["precio"]) * intval($compra["cantidad"])
}
return $total
}
var $compras = [
{"nombre": "Cuaderno", "precio": "12.50", "cantidad": 2},
{"nombre": "Lapiz", "precio": "3.50", "cantidad": 5}
]
var $guardado = file_put_contents("compras.json", json_encode($compras))
$guardado ? {} : { throw "No se pudo guardar compras.json" }
var $texto = file_get_contents("compras.json")
$texto == null ? { throw "No se pudo leer compras.json" } : {}
json_verify($texto) ? {} : { throw "El archivo no contiene JSON valido" }
var $leidas = json_decode($texto)
print("Articulos: " . count($leidas))
print("Total: " . totalizar($leidas))
print("Archivo guardado")
3. Explicación paso a paso de la arquitectura
Analicemos cómo interactúan los distintos subsistemas del lenguaje en este programa:
1. La función de cálculo (totalizar)
public func totalizar(array $compras): decimal {
decimal $total = 0m
foreach ($compras as $compra) {
$total = $total + decimal($compra["precio"]) * intval($compra["cantidad"])
}
return $total
}
- Recibe un
arrayde compras y promete retornar un valor de tipodecimal. - Inicializa un acumulador exacto:
decimal $total = 0m. - Recorre cada elemento con
foreach ($compras as $compra). - Extrae
"precio"y"cantidad"usando claves de mapa. Observa cómo convertimos explícitamente:decimal($compra["precio"]): Convierte el texto numérico a decimal de coma fija.intval($compra["cantidad"]): Convierte la cantidad a entero de 64 bits.
- Multiplica ambos valores y los suma a
$total.
2. Estructura de datos en memoria
var $compras = [
{"nombre": "Cuaderno", "precio": "12.50", "cantidad": 2},
{"nombre": "Lapiz", "precio": "3.50", "cantidad": 5}
]
- Definimos un array cuyos elementos son mapas asociativos (
{"clave": valor}). - Guardar los precios como texto (
"12.50") dentro del JSON es una buena práctica contable: evita que los decodificadores JSON estándar introduzcan imprecisiones binarias al leer números flotantes.
3. Persistencia en disco con JSON
var $guardado = file_put_contents("compras.json", json_encode($compras))
$guardado ? {} : { throw "No se pudo guardar compras.json" }
json_encode($compras): Transforma la estructura en memoria de Joss a un texto estándar JSON.file_put_contents("compras.json", ...): Escribe ese texto en el archivo físico en el disco duro. Devuelvetruesi tuvo éxito ofalsesi falló por permisos o falta de espacio.- La expresión ternaria actúa como salvaguarda: si
$guardadoes falso, lanza un error conthrow.
4. Lectura y validación de seguridad
var $texto = file_get_contents("compras.json")
$texto == null ? { throw "No se pudo leer compras.json" } : {}
json_verify($texto) ? {} : { throw "El archivo no contiene JSON valido" }
file_get_contents(...): Recupera los bytes del archivo en una cadena de texto. Si el archivo no existe, devuelvenull.json_verify($texto): Función nativa de Joss que comprueba si una cadena cumple con la especificación JSON válida sin llegar a parsear todo el árbol a memoria. Si el archivo está corrupto o fue editado incorrectamente por un usuario, lo detecta de inmediato.json_decode($texto): Reconstruye los datos JSON en arrays y maps nativos de Joss listos para ser procesados.
4. Análisis, Ejecución y Compilación Nativa
Primero, ejecutemos el análisis estático para garantizar que no haya inconsistencias:
joss analyze main.joss
Si todo es correcto, ejecuta el programa con el intérprete:
joss run main.joss
Salida producida en consola:
Articulos: 2
Total: 42.5
Archivo guardado
Si revisas tu carpeta con ls (o dir en Windows) o el explorador de archivos, verás que se ha creado el archivo físico compras.json:
[{"cantidad":2,"nombre":"Cuaderno","precio":"12.50"},{"cantidad":5,"nombre":"Lapiz","precio":"3.50"}]
Compilar a un binario nativo independiente (joss build)
Cuando quieras distribuir tu herramienta de consola a otros equipos sin que necesiten tener instalado Joss ni entregar tu código fuente, compílala con:
joss build main.joss
Joss generará un ejecutable nativo dentro de la carpeta build/ (build/main.exe en Windows o ./build/main en Linux/macOS) listo para ejecutarse directamente.
5. Salida visual con colores: El módulo nativo Console
Para que tu aplicación de consola ofrezca una experiencia visual atractiva y profesional, puedes colorear y destacar los mensajes en la terminal mediante la clase nativa Console:
print(Console::green("✓ Archivo guardado con éxito"))
print(Console::yellow("⚠ Advertencia: El stock es bajo"))
print(Console::red("✗ Error al procesar datos"))
print(Console::bold("Total a pagar: S/ 42.50"))
Métodos disponibles de Console:
| Método | Propósito y Color | Uso típico |
|---|---|---|
Console::green($t) |
Verde | Operaciones exitosas, confirmaciones (✓ OK). |
Console::red($t) |
Rojo | Errores, fallos de validación, excepciones. |
Console::yellow($t) |
Amarillo | Advertencias, avisos que requieren atención. |
Console::blue($t) / Console::cyan($t) |
Azul / Cian | Títulos, enlaces, información descriptiva. |
Console::bold($t) |
Negrita | Totales numéricos, nombres destacados. |
Console::clear() |
Limpiar pantalla | Reinicia la terminal antes de mostrar un menú. |
6. Ejercicios prácticos
Nivel 1 — Prueba rápida
Agrega un tercer artículo al array $compras (por ejemplo, {"nombre": "Mochila", "precio": "35.00", "cantidad": 1}), vuelve a ejecutar joss run main.joss y verifica que el total se actualice a 77.5 y que compras.json refleje los 3 artículos.
Nivel 2 — Ejercicio guiado
- Crea una función
public func articulosCaros(array $compras, decimal $umbral): arrayque recorra$comprasconforeachy retorne un nuevo array solo con los productos cuyodecimal($compra["precio"])supere$umbral. - Imprime cuántos artículos superan
10.00musandocount(articulosCaros($leidas, 10.00m)). - Colorea el mensaje final
"Archivo guardado"usandoConsole::green("Archivo guardado").
Nivel 3 — Reto
Modifica el programa para que antes de guardar el archivo le pida al usuario un artículo adicional por teclado:
1. Declara string $nuevoNombre = "", string $nuevoPrecio = "" y int $nuevaCantidad = 0.
2. Pide cada dato usando cin >> $nuevoNombre, cin >> $nuevoPrecio y cin >> $nuevaCantidad.
3. Agrégalo a $compras con $compras[] = {"nombre": $nuevoNombre, "precio": $nuevoPrecio, "cantidad": $nuevaCantidad}, guarda el JSON y compila tu aplicación con joss build main.joss.
Siguiente paso
Ahora que has dominado la persistencia de datos en disco y el desarrollo de utilidades de consola, es hora de explorar el área más fuerte de Joss: el desarrollo de aplicaciones web de alto rendimiento con rutas, controladores, bases de datos y vistas HTML.
Continúa con: Construir una aplicación web completa con el stack nativo.