Histórico
Eu ia contar aqui a história de como surgiu o LuaWsgi, mas decidi não fazer isso.
Basta dizer que envolve intriga, maus entendidos e até uma certa dose de má fé.
Mas também gostaria de dizer que desenvolvi o primeiro protótipo funcional em apenas um domingo de diversão.
Outra coisa que é importante dizer é que LuaWsgi nasceu como uma terapia e resolvi publicá-lo para ser meu trabalho de conclusão de curso.
O que é WSGI?
WSGI é uma proposta formal de melhoria para Python, PEP, que sugere uma nova abordagem para o desenvolvimento web mais simpática para programadores.
A forma original como páginas web foram concebidas é muito interessante para não-programadores, pois o HTTP era apenas uma extensão do FTP voltada para a visualização.
Usando HTML, uma linguagem de marcação, era possível criar páginas atraentes com pouco ou nenhum conhecimento de programação.
Porém «marcação» não permite muitos recursos usados na programação. Para suprir essa necessidade, foram criados o CGI as páginas dinâmicas, que são geradas por um programa.
Depois criaram os servlets.
Até aqui, no entanto, o formato continuava desconfortável para programadores – apesar de «arrastadores de componentes» (component drag-n-droppers) se sentirem verdadeiros programadores assim e de alguns servlets serem bastante amigáveis.
Então Phillip J. Eby sugeriu uma API mais interessante para Python: WSGI.
E WSGI é ainda mais! A proposta foi tão bem feita que pode ser implementada em quase qualquer linguagem de programação, quase como um protocolo!
Para saber mais leia a PEP 333.
O que é Lua?
Lua é uma linguagem de programação livre projetada e implementada em 1993 por uma equipe no Lablua da PUC-Rio.
Atualmente se encontra na versão 5.1.2.
É uma linguagem estruturada, orientada a tabelas e com suporte a orientação a objetos.
Sua tipagem é dinâmica, como Python. Não é tão forte quanto a tipagem de Python, mas possui sete tipos bem definidos e de conversão simplificada quando possível.
Para saber mais, leia o livro do Roberto Ierusalimschy.
O que é LuaWsgi?
É uma implementação de WSGI para linguagem de programação Lua da forma mais precisa possível.
Dependências
Para instalar LuaWsgi você precisa instalar antes:
- Lua;
- LuaSocket;
- Copas; e
- lua-unistd
Mas há um jeito mais fácil…
Você pode simplesmente instalar o Projeto Kepler, que já traz todos os módulos que vamos precisar.
Até agora – o momento em que escrevo – não há uma versão final do Kepler 1.1. Se quando você for baixar houver, baixe-a. Se não, baixe o último snapshot.
Obs.: para instalar Kepler 1.1, você precisa ter instalado Expat, ZZipLib, algum SGBD e GNU Readline (você provavelmente já possui algum ou todas as dependências, mas é bom verificar).
Não siga as instruções de instalação de Kepler. Faça o seguinte:
Desempacote o tarball (
tar xzvf kepler-*.tar.gz
). Isso irá gerar um diretório kepler-1.1/
. Acesse-o.Então execute o
./configure
com as seguintes opções:bash$ ./configure --prefix=/usr \
--sysconfdir=/etc \
--lua-suffix='' \
--kepler-web=/var/xavante \
--with-optional=lualogging,luasql,luaexpat,luazip,md5 \
--enable-lua
O que estamos fazendo é dizendo para o configurador onde o instalador deve colocar os arquivos, que módulos queremos usar (todos) e para instalar Lua também.
Você também pode usar a opção
--with-luasql-driver=
para especificar qual SGBD você está usando. Para obter informações tente:bash$ ./configure --help
Se tudo correr bem, o resto é instalação padrão:
bash$ make
bash$ sudo make install
Se não houver problemas, você já terá todos os módulos que precisará instalados – e mais alguns úteis. Se quiser testar o servidor web Xavante, ele também estará instalado – mas aí já é outro assunto.
Opa! Falta ainda instalar o lua-unistd!
Você tem duas escolhas: 1baixar o fonte e compilá-lo ou 2baixar o binário pré-compilado.
Acredito que o binário seja suficiente, mas se você tiver problemas, para compilar o fonte o comando é:
bash$ gcc -c -fPIC -O3 -Wall unistd.c
bash$ ld -shared -lm -lcrypt unistd.o -o libunistd.so.1.0.1
Mova a biblioteca para
/usr/lib/lua/5.1/
e crie neste mesmo diretório uma ligação (link) simbólica chamada unistd.so
:bash# ln -s libunistd.so.1.0.1 unistd.so
Configurando o sistema
Apesar de Kepler não precisar de variáveis de ambiente, LuaWsgi precisa. Crie as seguintes variáveis:
export LUA_PATH=?.lua;?/?.lua;/usr/share/lua/5.1/?.lua;/usr/share/lua/5.1/?/?.lua
Se estiver difícil de visualizar no navegador, trata-se da seguinte sequência, trocando as mudanças de linha por ponto-e-vírgula:
?.lua
?/?.lua
/usr/share/lua/5.1/?.lua
/usr/share/lua/5.1/?/?.lua
A outra variável é:
export LUA_CPATH=?.so;l?.so;/usr/lib/lua/5.1/?.so;/usr/lib/lua/5.1/?/?.so
Idem acima:
?.so
l?.so
/usr/lib/lua/5.1/?.so
/usr/lib/lua/5.1/?/?.so
Aconselho colocar esses comandos em
/etc/profile
Agora, para testar, execute o seguinte:
bash$ lua
Lua 5.1.2 Copyright (C) 1994-2007 Lua.org, PUC-Rio
> require "unistd"
Se nenhum erro ocorrer, você já está com o ambiente configurado! ;)
Finalmente: instalando LuaWsgi
Essa parte é complicada… =D hehehehehe
Baixe o pacote mais recente da página de arquivos (a versão mais recente no momento em que escrevo é a 7.06.1).
Desempacote o tarball em
/usr/share/lua/5.1/
:bash# tar xzvf luawsgi-*.tar.gz -C /usr/share/lua/5.1/
E pronto!
Primeiro exemplo
Crie um arquivo (pode ser em seu homedir) chamado
teste.lua
:require "wsgi"
require "wsgi.session"
require "middleware.auth"
local function authhandler(user, pass)
return user == "eu" and pass == "senha"
end
local function app(environ, start_response)
-- Cria ambiente de sessão
local session = wsgi.session(environ, start_response)
-- Se não houver um login, lê de POST
if not session.login then
print "Criando login com dados de POST"
session.login = environ.params.login
end
print("Usuário: " .. session.login)
-- Configura status e cabeçalho (não envia)
start_response { "200 OK";
["Content-Type"] = "text/html; charset=utf-8",
}
-- Encerra retornando o corpo da página
return {
"<html>\n",
"<head>\n",
"<title>Ola ",
(environ.params.login or ""),
"</title>\n",
"</head>\n",
"<body>\n",
'<form action="" method="POST">\n',
'<input type="text" id="login" ',
'name="login" />\n',
'<input type="submit" />\n',
"</body>\n",
"</html>\n",
}
end
-- Este middleware garante autenticação
app = middleware.auth(app, authhandler)
wsgi.serve { app; host="*", port=8001 }
O submódulo
wsgi.session
permite um acesso fácil a sessão e o submódulo middleware.auth
oferece recursos para autenticação.Salve execute:
bash$ lua teste.lua
E acesse no navegador http://localhost:8001. Veja na função
authhandler()
que o usuário é «eu
» e a senha é «senha
».Descendo o rio… AJAX
Vamos usar AJAX? Para isso temos o submódulo
wsgi.ajax
. Crie testeDeAjax.lua
:require "wsgi"
require "wsgi.ajax"
local function app(environ, start_response)
-- Cria ambiente AJAX
local ajax = wsgi.ajax(environ, start_response)
ajax.export("som", function (x, y) return x + y end)
ajax.export("sub", function (x, y) return x - y end)
ajax.export("mul", function (x, y) return x * y end)
ajax.export("div", function (x, y) return x / y end)
-- Descomente para depuração
--ajax.setdebug()
-- Responde AJAX
if ajax.handle then
return ajax.handle()
end
-- Configura status e cabeçalho
start_response{ "200 OK";
["Content-Type"] = "text/html; charset=utf-8",
["Cache-Control"] = "no-cache, must-revalidate",
["Pragma"] = "no-cache",
}
-- Retorna o corpo da página (recheado de JavaScript!)
return {[[<html>
<head>
<title>Teste de AJAX</title>
<script language="javascript"><!--
]],
ajax.get_js(),
[[
function set_x(x) {
document.getElementById('x').value = x;
document.getElementById('y').value = '';
}
function som() {
var x, y;
x = document.getElementById('x').value;
y = document.getElementById('y').value;
ajax_som(x, y, set_x);
document.getElementById('y').focus();
return true;
}
function sub() {
var x, y;
x = document.getElementById('x').value;
y = document.getElementById('y').value;
ajax_sub(x, y, set_x);
document.getElementById('y').focus();
return true;
}
function mul() {
var x, y;
x = document.getElementById('x').value;
y = document.getElementById('y').value;
ajax_mul(x, y, set_x);
document.getElementById('y').focus();
return true;
}
function div() {
var x, y;
x = document.getElementById('x').value;
y = document.getElementById('y').value;
ajax_div(x, y, set_x);
document.getElementById('y').focus();
return true;
}
//--></script>
<style rel="stylesheet"><!--
body {
font-size: 14pt;
text-align: center;
}
--></style>
</head>
<body onLoad="document.getElementById('x').focus(); return true;">
<input type="txt" id="x" value="2" />
?
<input type="txt" id="y" value="3" />
<br />
<input type="button" value="+" onClick="som()" />
<input type="button" value="-" onClick="sub()" />
<input type="button" value="×" onClick="mul()" />
<input type="button" value="÷" onClick="div()" />
</body>
</html>
]]
}
end
wsgi.serve { app; host="*", port=8001 }
Execute:
bash$ lua testeDeAjax.lua
Agora acesse: http://localhost:8001.
Uma brincadeira com JSON
Fala-se muito atualmente em XML atualmente, que é um recurso interessante e até eficiente para o que se propõe.
Mas é um descaramento afirmar que XML seja pau pra toda obra. Quem diz isso ou está iludido ou está querendo enganar alguém.
Por exemplo, para transferência de dados em tempo real, XML é altamente ineficiente.
Para tanto temos JSON.
Além de LuaWsgi trazer integrado JSON4Lua, o submódulo
wsgi.ajax
possui uma função que adapta o comportamento de uma função para trabalhar com JSON.Vamos ao exemplo
testeDeJson.lua
:require "wsgi"
require "wsgi.ajax"
local function dobro(t)
print(t.val)
return { val = t.val * 2 }
end
local function app(environ, start_response)
local ajax = wsgi.ajax(environ, start_response)
-- Esta é a função JSON-import → jsonport ;)
ajax.jsonport("nada", dobro)
if ajax.handle then
return ajax.handle()
end
start_response{ "200 OK";
["Content-Type"] = "text/html; charset=utf-8",
["Cache-Control"] = "no-cache, must-revalidate",
["Pragma"] = "no-cache",
}
return {[[<html>
<head>
<title>Teste de AJAX</title>
<script
type="text/javascript"
src="http://www.json.org/json.js"
></script>
<script language="javascript"><!--
]],
ajax.get_js(),
[[
function set_x(x) {
var e = x.parseJSON();
document.getElementById("x").value = e.val;
return true;
}
function nada() {
var o = { "val": document.getElementById("x").value };
ajax_nada(o.toJSONString(), set_x);
return true;
}
//--></script>
<style rel="stylesheet"><!--
body {
font-size: 14pt;
text-align: center;
}
--></style>
</head>
<body>
<input type="text" id="x" value=" " />
<input type="button" value="Vai!" onClick="nada()" />
</body>
</html>
]]
}
end
wsgi.serve { app; host="*", port=8001 }
Mesmo esquema!
Conclusão
Ainda há mais o que ver sobre LuaWsgi, como o middleware publicador (
middleware.publisher
) e mais algumas coisinhas interessantes, mas este artigo já se tornou extenso demais. =(Estão todos convidados a experimentar LuaWsgi e, mais importante, a participar de seu desenvolvimento!
[]'s
Rodrigo Cacilhas