Apéndice B: Ecosistema y Herramientas
Apéndice B: Ecosistema y Herramientas
“Un buen programador es aquel que siempre mira a ambos lados antes de cruzar una calle de un solo sentido.” — Doug Linder
El ecosistema de Lua es pequeño pero poderoso. Aunque no tiene tantas librerías como Python o JavaScript, las que existen son de alta calidad y bien mantenidas.
LuaRocks: El Package Manager
LuaRocks es el equivalente de npm (Node.js) o pip (Python) para Lua.
Instalación
# macOS
brew install luarocks
# Ubuntu/Debian
sudo apt-get install luarocks
# Desde source
wget https://luarocks.org/releases/luarocks-3.9.2.tar.gz
tar zxpf luarocks-3.9.2.tar.gz
cd luarocks-3.9.2
./configure && make && sudo make install
Verificar:
luarocks --version
# /usr/local/bin/luarocks 3.9.2
Comandos Básicos
# Buscar paquetes
luarocks search json
# Instalar paquete
luarocks install luasocket
# Instalar versión específica
luarocks install lua-cjson 2.1.0
# Listar paquetes instalados
luarocks list
# Actualizar paquete
luarocks install --server=https://luarocks.org/dev lua-cjson
# Remover paquete
luarocks remove lua-cjson
# Ver información de paquete
luarocks show luasocket
Instalar Localmente (sin sudo)
# Instalar en directorio local
luarocks install --local luasocket
# Configurar path
eval $(luarocks path)
# O agregar a .bashrc/.zshrc:
# eval $(luarocks path --bin)
Crear Tu Propio Rock
1. Estructura del Proyecto
mi-libreria/
├── rockspec/
│ └── mi-libreria-1.0-1.rockspec
├── src/
│ └── mi-libreria.lua
├── spec/
│ └── mi-libreria_spec.lua
└── README.md
2. Rockspec File
-- mi-libreria-1.0-1.rockspec
package = "mi-libreria"
version = "1.0-1"
source = {
url = "git://github.com/usuario/mi-libreria.git",
tag = "v1.0"
}
description = {
summary = "Una librería útil para X",
detailed = [[
Descripción detallada de lo que hace tu librería.
]],
homepage = "https://github.com/usuario/mi-libreria",
license = "MIT"
}
dependencies = {
"lua >= 5.1, < 5.5"
}
build = {
type = "builtin",
modules = {
["mi-libreria"] = "src/mi-libreria.lua"
}
}
3. Publicar
# Crear cuenta en luarocks.org
# Subir tu rock
luarocks upload mi-libreria-1.0-1.rockspec --api-key=YOUR_API_KEY
Testing: Busted
Busted es el framework de testing más popular para Lua, inspirado en RSpec (Ruby).
Instalación
luarocks install busted
Ejemplo Básico
-- spec/calculator_spec.lua
describe("Calculator", function()
local Calculator = require("calculator")
local calc
before_each(function()
calc = Calculator.new()
end)
describe("addition", function()
it("adds two numbers", function()
assert.are.equal(5, calc:add(2, 3))
end)
it("handles negative numbers", function()
assert.are.equal(-1, calc:add(-3, 2))
end)
end)
describe("division", function()
it("divides two numbers", function()
assert.are.equal(2, calc:divide(6, 3))
end)
it("throws error on division by zero", function()
assert.has.errors(function()
calc:divide(5, 0)
end)
end)
end)
end)
Ejecutar Tests
# Todos los tests
busted
# Test específico
busted spec/calculator_spec.lua
# Con verbose
busted -v
# Con coverage (requiere luacov)
busted --coverage
Aserciones Comunes
-- Igualdad
assert.are.equal(expected, actual)
assert.are.same(expected, actual) -- Deep equality para tablas
-- Booleanos
assert.is_true(value)
assert.is_false(value)
assert.is_nil(value)
-- Tipos
assert.is_number(value)
assert.is_string(value)
assert.is_table(value)
assert.is_function(value)
-- Errores
assert.has.errors(function() error("fail") end)
assert.has_no.errors(function() return 42 end)
-- Tablas
assert.are.same({1, 2, 3}, actual)
-- Strings
assert.matches("^hello", "hello world")
Mocking y Stubbing
describe("User Service", function()
local UserService = require("user_service")
local mock_db
before_each(function()
-- Crear mock de database
mock_db = {
query = function() end
}
-- Stub del método query
stub(mock_db, "query").returns({
{id = 1, name = "Alice"},
{id = 2, name = "Bob"}
})
end)
it("fetches users from database", function()
local service = UserService.new(mock_db)
local users = service:get_all_users()
assert.are.equal(2, #users)
assert.stub(mock_db.query).was.called()
end)
after_each(function()
mock_db.query:revert()
end)
end)
Spies
describe("Event System", function()
it("calls callback when event is emitted", function()
local EventEmitter = require("event_emitter")
local emitter = EventEmitter.new()
local callback = spy.new(function() end)
emitter:on("test", callback)
emitter:emit("test")
assert.spy(callback).was.called()
assert.spy(callback).was.called(1)
end)
end)
Linting: Luacheck
Luacheck es un linter y analizador estático para Lua.
Instalación
luarocks install luacheck
Uso Básico
# Analizar archivo
luacheck myfile.lua
# Analizar directorio
luacheck src/
# Con configuración
luacheck . --config .luacheckrc
Configuración (.luacheckrc)
-- .luacheckrc
std = "luajit"
codes = true
ignore = {
"212", -- Unused argument
"213", -- Unused loop variable
}
globals = {
"ngx", -- Para OpenResty
"love" -- Para Love2D
}
max_line_length = 120
Ejemplos de Warnings
-- ❌ Warning: unused variable
local x = 10
-- ❌ Warning: global variable
function test()
y = 20 -- Debería ser: local y = 20
end
-- ❌ Warning: line too long
local very_long_variable_name = "This is a very long string that exceeds the maximum line length configured in luacheck"
-- ✅ OK
local function test()
local y = 20
return y
end
IDEs y Editores
VS Code (Recomendado)
Extensiones esenciales:
-
Lua (sumneko.lua)
- Language server con autocompletado
- Type checking
- Goto definition
- Refactoring
-
Lua Debug (actboy168.lua-debug)
- Debugging integrado
- Breakpoints
- Watch variables
-
vscode-lua (trixnz.vscode-lua)
- Snippets
- Formatting
Configuración (settings.json):
{
"Lua.runtime.version": "LuaJIT",
"Lua.diagnostics.globals": ["ngx", "love"],
"Lua.workspace.library": [
"/usr/local/share/lua/5.1"
],
"Lua.workspace.checkThirdParty": false
}
Neovim
Configuración con nvim-lspconfig:
-- ~/.config/nvim/lua/lsp.lua
local lspconfig = require('lspconfig')
lspconfig.lua_ls.setup {
settings = {
Lua = {
runtime = {
version = 'LuaJIT',
},
diagnostics = {
globals = {'vim', 'ngx'},
},
workspace = {
library = vim.api.nvim_get_runtime_file("", true),
},
},
},
}
IntelliJ IDEA / CLion
Plugin: EmmyLua
- Autocompletado avanzado
- Type annotations
- Debugging
- Refactoring
ZeroBrane Studio
IDE dedicado para Lua con debugging integrado.
# macOS
brew install --cask zerobranestudio
# Ubuntu (desde .deb)
wget https://download.zerobrane.com/ZeroBraneStudioEduPack-1.90-linux.sh
sh ZeroBraneStudioEduPack-1.90-linux.sh
Características:
- ✅ Debugging con breakpoints
- ✅ Watch expressions
- ✅ Remote debugging
- ✅ Integration con Love2D, Moai, Corona
Librerías Populares
Web Frameworks
1. Lapis (OpenResty)
luarocks install lapis
-- app.lua
local lapis = require("lapis")
local app = lapis.Application()
app:get("/", function()
return "Hello World!"
end)
app:get("/users/:id", function(self)
return "User ID: " .. self.params.id
end)
return app
2. Sailor
luarocks install sailor
MVC framework completo.
HTTP Clients
1. LuaSocket
luarocks install luasocket
local http = require("socket.http")
local body, code = http.request("https://api.github.com")
print(code, #body)
2. lua-http (HTTP/2)
luarocks install http
JSON
1. lua-cjson (Más rápido)
luarocks install lua-cjson
local cjson = require("cjson")
local data = {name = "Alice", age = 30}
local json = cjson.encode(data)
print(json) -- {"name":"Alice","age":30}
local decoded = cjson.decode(json)
print(decoded.name) -- Alice
2. dkjson (Pure Lua)
luarocks install dkjson
Database
1. LuaSQL
# MySQL
luarocks install luasql-mysql
# PostgreSQL
luarocks install luasql-postgres
# SQLite
luarocks install luasql-sqlite3
local luasql = require("luasql.sqlite3")
local env = luasql.sqlite3()
local conn = env:connect("test.db")
conn:execute("CREATE TABLE users (id INTEGER, name TEXT)")
conn:execute("INSERT INTO users VALUES (1, 'Alice')")
local cursor = conn:execute("SELECT * FROM users")
local row = cursor:fetch({}, "a")
while row do
print(row.id, row.name)
row = cursor:fetch(row, "a")
end
2. pgmoon (PostgreSQL async para OpenResty)
luarocks install pgmoon
Redis
luarocks install lua-resty-redis
local redis = require("resty.redis")
local red = redis:new()
red:connect("127.0.0.1", 6379)
red:set("key", "value")
local val = red:get("key")
Debugging
Built-in Debug Library
local debug = require("debug")
function foo(x)
print(debug.getinfo(1).name) -- Nombre de función
print(debug.getinfo(1).source) -- Archivo
print(debug.getinfo(1).currentline) -- Línea
-- Ver todas las locales
local i = 1
while true do
local name, value = debug.getlocal(1, i)
if not name then break end
print(name, value)
i = i + 1
end
end
foo(42)
MobDebug (Remote Debugging)
luarocks install mobdebug
-- En tu código
require("mobdebug").start()
-- Ejecutar con ZeroBrane o otro debugger
Formatters
StyLua
Formatter automático para Lua.
# Instalación
cargo install stylua
# Uso
stylua myfile.lua
# Todo el proyecto
stylua .
Configuración (stylua.toml):
column_width = 120
line_endings = "Unix"
indent_type = "Spaces"
indent_width = 4
quote_style = "AutoPreferDouble"
Profiling
ProFi
luarocks install profi
local ProFi = require("ProFi")
ProFi:start()
-- Tu código aquí
for i = 1, 1000000 do
math.sqrt(i)
end
ProFi:stop()
ProFi:writeReport("profile.txt")
Generadores de Documentación
LDoc
luarocks install ldoc
--- Calculator module
-- @module calculator
local Calculator = {}
--- Create a new calculator instance
-- @treturn Calculator new calculator
function Calculator.new()
return setmetatable({}, {__index = Calculator})
end
--- Add two numbers
-- @tparam number a first number
-- @tparam number b second number
-- @treturn number sum of a and b
function Calculator:add(a, b)
return a + b
end
return Calculator
Generar docs:
ldoc .
Resumen
El ecosistema de Lua es:
- Pequeño pero efectivo: Menos ruido, más señal
- LuaRocks: Package manager sólido
- Busted: Testing moderno y expresivo
- Luacheck: Linting esencial
- VS Code + sumneko.lua: Mejor experiencia de desarrollo
Próximo: Apéndice C: Lua en el Mundo Real