sábado, 23 de junho de 2007

LuaWsgi

Lua/Python Como eu havia prometido nas Reflexões de Monte Gasppa e Giulia C., aqui está o artigo sobre LuaWsgi.

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:


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 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
blog comments powered by Disqus