Mostrando postagens com marcador Lua. Mostrar todas as postagens
Mostrando postagens com marcador Lua. Mostrar todas as postagens

sábado, 8 de agosto de 2015

Instalando MoonScript sobre LuaJIT

Atualizado no blog novo.

MoonScript
Colocar MoonScript para trabalhar com LuaJIT não é trivial. É preciso uma série de pequenos hacks pra funcionar.

Vamos começar pelas dependências.

Dependências

MoonScript depende de quatro outros módulos para funcionar:
Instale alt-getopt normalmente no LUA_PATH de seu LuaJIT. Aqui para mim é /usr/share/lua/jit.

Para instalar LuaFileSystem, clone o repositório do GitHUB e não se esqueça de editar o arquivo config. As mudanças principais são:
PREFIX=/usr
LUA_LIBDIR=$(PREFIX)/lib/lua/jit
LUA_INC=$(PREFIX)/include/luajit-2.0

Isso considerando que seu LUA_CPATH esteja em /usr/lib/lua/jit.

Compile e instale normalmente.

Já LPeg merece uma atenção extra, já que ele não funciona com LuaJIT. No lugar, use LPegLJ.

Clone e instale LPegLJ no seu LUA_PATH, depois execute o seguinte comando:
cd /usr/share/lua/jit/
sudo ln -s lpeglj.lua lpeg.lua

Isso fará com que MoonScript pense tratar-se do LPeg original.

Instalando MoonScript

Com as três dependências instaladas, clone o projeto do GitHUB. Edite o Makefile, substituindo as ocorrências de lua5.1 e lua por luajit.

Remova as entradas local e global do Makefile.

Edite o hashbang dos arquivos bin/moon e bin/moonc trocando lua por luajit.

Execute make compile.

Copie os diretórios moon/ e moonscript/ para seu LUA_PATH.

Copie os arquivos bin/moon e bin/moonc para o diretório /usr/bin do sistema.

E pronto! Já deve estar funcionando! Qualquer dúvida, me avisem pra eu revisar o texto.

[]’s

segunda-feira, 22 de junho de 2015

MoonScript

Adaptado no blog novo.
MoonScript Há muito pouco tempo atrás um amigo meu me sugeriu dar atenção à MoonScript. Admito que eu via a linguagem com preconceito, mesmo assim resolvi dar uma olhada.

Resultado: todos os meus códigos em Lua acabaram convertidos para MoonScript. :-)

A linguagem é extremamente enxuta, limpa e poderosa. Não vou entrar em detalhes, se estiver curioso, leia o guia da linguagem.

Mas também não puxei assunto por nada, vou dar alguns exemplos.

Paradigma Funcional

Para lidar com o paradigma funcional, a sintaxe de MoonScript é bem mais concisa e que a de Lua. Por exemplo, o combinador Y.

Lua é uma linguagem estrita e não suporta o combinador Y em sua forma lazy. Como MoonScript é compilado para código Lua, sofre do mesmo mal. Assim, é preciso usar o combinador Z, que é versão estrita do combinador Y.

O combinador Z é definido como: λf.(λx.xx)(λx.f(λv.xxv)). Isso é facilmente representado em MoonScript:
Z = (f using nil) -> ((x) -> x x) (x) -> f (...) -> (x x) ...

Com isso é possível, por exemplo, implementar facilmente o quick sort:
Z = (f using nil) -> ((x) -> x x) (x) -> f (...) -> (x x) ...

tconcat = (...) -> [e for t in *{...} for e in *t]

Z (qsort using tconcat) ->
    (xs) ->
        if #xs <= 1
            xs

        else
            x, xs = xs[1], [e for e in *xs[2,]]
            lesser = [e for e in *xs when e <= x]
            greater = [e for e in *xs when e > x]
            tconcat (qsort lesser), {x}, (qsort greater)

Orientação a Objetos

Em Lua é preciso toda uma ginástica com metatabelas para simular eficientemente orientação a objetos. MoonScript tem suporte a classes na linguagem.

Por exemplo, podemos criar uma pilha (stack) facilmente:
import bind_methods from assert require "moon"

class Stack
    list: nil

    set: (x) =>
        @list =
            next: @list
            value: x

    get: =>
        v = nil
        {value: v, next: @list} = @list if @list
        v

bind_methods Stack!

LuaJIT

Como MoonScript compila para código Lua tradicional, você pode gerar código para virtualmente qualquer plataforma que rode Lua, como LuaJIT e LÖVE.

Por exemplo, você pode usar C-structs e metatipos:
ffi = assert require "ffi"
import sqrt from math

ffi.cdef [[
    typedef struct { double x, y; } point_t;
]]

local Point = ffi.metatype "point_t",
    __add: (o) => Point @x + o.x, @y + o.y
    __len: => @\hypot!
    __tostring: => "(#{@x}, #{@y})"
    __index:
        area: => @x * @y
        hypot: => sqrt @x*@x + @y*@y

Point

bash$ luajit
LuaJIT 2.0.3 -- Copyright (C) 2005-2014 Mike Pall. http://luajit.org/
JIT: ON CMOV SSE2 SSE3 SSE4.1 fold cse dce fwd dse narrow loop abc sink fuse
> moon = assert(require "moonscript.base")
> Point = (moon.loadfile "point.moon")()
> p = Point(3, 4)
> = tostring(p)
(3, 4)
> = p:area()
12
> = #p
5
>
Bem, acho que já fiz propaganda suficiente da linguagem. ;-)

[]’s

domingo, 13 de março de 2011

Decoradores em Lua

@



Um recurso extremamente útil em Python é o uso de decoradores.

Por exemplo, um decorador que memoíze o resultado de uma função:

def memo(func, buffer={}):
def wrapper(*args):
if args in buffer:
response = buffer[args]
else:
response = buffer[args] = func(*args)
return response

return wrapper


Assim funções como o cálculo recursivo da sequência de Fibonacci se tornam menos custosas:
@memo
def fibonacci(n):
if n < 2:
return 1
else:
return fibonacci(n - 2) + fibonacci(n - 1)


Observação: memoização consome muita memória!

É possível ver que, em Python, decoradores são extremamente fáceis de se implementar e usar, o que permite usos bastantes complexos, como a orientação a aspecto.

Lua também suporta decoradores, mas de uma forma um pouco menos intuitiva, mais complexa e totalmente macetada.

Para a criação de decoradores em Lua, é usada a sobrecarga do operador de concatenação, como sugerido na literatura de referência.

Para criarmos então o decorador de memoização, precisamos de uma metatabela que sobrecarregue o operador:
local mt = {
__concat = function(self, func)
return function(...)
local response
local args = serialize(...)
if self[args] then
response = self[args]
else
response = func(...)
self[args] = response
end
return response
end
end,
}


A função serialize() não existe, então temos de criá-la:
local function serialize(...)
local response = ""
local i, v
for i, v in ipairs {...} do
response =
response .. "[" .. ("%g"):format(i) .. "] = " ..
("%g"):format(v) .. ", "
end
return response
end


Agora criamos o decorador:
function memo()
return setmetatable({}, mt)
end


Isso tudo pode ser colocado em módulo, chamado memo.lua por exemplo. Na última linha do módulo coloque:
return memo


Agora, para usar o decorador:
require "memo"

fibonacci =
memo() ..
function(n)
if n < 2 then
return 1
else
return fibonacci(n - 2) + fibonacci(n - 1)
end
end


O uso do decorador está na linha: memo() .. – a sintaxe não é tão elegante quanto a de Python – @memo –, mas funciona da mesma forma, inclusive a criação de cadeias de decoradores.

A criação de decoradores em Lua também não é tão intuitiva e simples quanto em Python, em função da diferença de filosofias entre as duas linguagens, mas com o hábito se torna um recurso poderoso.

[]’s
Cacilhας, La Batalema

quinta-feira, 20 de novembro de 2008

Base64

De vez enquando, não tem como não, nós programadores sempre esbarramos na Base64.

Base64 é um protocolo de codificação que usa apenas seis bits, o que significa um conjunto de sessenta e quatro (64) elementos – daí Base64.

A conversão de oito (byte) para seis bits é feita da seguinte forma:
xxxxxx.xx xxxx.xxxx xx.xxxxxx


Ou ainda:
aaaaaabb bbbbcccc ccdddddd


Os elementos usados são caracteres simples, começando com as letras maiúsculas, A (0) a Z (25), então as letras minúsculas, a (26) a z (51), os números, 0 (52) a 9 (61), e os caracteres + (62) e / (63).

Na conversão de bytes (8b) para Base64 (6b), cada três bytes é convertido em quatro dígitos, então um código Base64 é sempre pensado em grupos de quatro. Se o tamanho de um código Base64 não for múltiplo de quatro, caracteres = são acrescentados ao final até que o tamanho seja múltiplo de quatro.

Python


Em Python, Base64 é tão simples que nem tem graça:
>>> print "Kodumaro".encode("base64")
S29kdW1hcm8=
>>> print "S29kdW1hcm8=".decode("base64")
Kodumaro


Lua


Em Lua, é preciso baixar o módulo Mime do LuaSocket:
> require "mime"
> print(mime.b64 "Kodumaro")
S29kdW1hcm8=
> print(mime.unb64 "S29kdW1hcm8=")
Kodumaro


Smalltalk


Sendo muito sincero sobre o assunto, não sei como fazer conversão de Base64 em Smalltalk. =(

Mas sei que os módulos do Seaside providenciam isso!

Se alguém souber, por favor informe!

[update 2009-11-27]
Sugestão do Hugo:

No Squeak tem isso aqui:
(Base64MimeConverter mimeEncode: 'base64' readStream) contents
(Base64MimeConverter mimeDecode: 'S29kdW1hcm8' as: ByteString) contents


E no Pharo tem métodos para strings:
'base64' base64Encoded
'S29kdW1hcm8' base64Decoded


A implementação usa as coisas do Squeak:
String>>base64Decoded
↑ (Base64MimeConverter mimeDecode: self as: self class)

String>>base64Encoded
↑ (Base64MimeConverter mimeEncode: self readStream) contents


Para outros Smalltalks eu não sei…

Legal né? =)

Muito legal sim, Hugo, valeu!
[/update]


C


Aha! Aqui começa de verdade a brincadeira!

É claro que você pode usar as funcionalidades de Base64 da gLibC, mas descobri que nem todas as versões dela apresentam tais funcionalidades:
#include <glib/gbase64.h>


Vamos precisar usar os cabeçalhos stdlib.h, string.h e sys/types.h. Como também vamos criar um cabeçalho para «compartilhar» algumas funções, ele também será incluído no início de nosso arquivo base64.c:
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include "base64.h"


Agora vamos criar um array com todos os possíveis caracteres Base64 em sua ordem natural:
static const char b64all[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef"
"ghijklmnopqrstuvwxyz0123456789+/";


Também vamos precisar de três funções locais: uma para obter o índice de um elemento (_getindex), outra para codificar para Base64 um grupo de três bytes (_encode) e mais uma para decodificar um grupo de quatro elementos Base64 (_decode):
int _decode(u_int8_t *, const u_int8_t *);
void _encode(u_int8_t *, const u_int8_t *, int);


Não é preciso uma função para _getindex:
#define _getindex(c) (int) (index(b64all, c) - b64all)


Repara que, em vez de char, estamos usando u_int8_t, que é mais conveniente quando queremos lidar com bytes enquanto bytes, não caracteres.

A partir daqui, se preferir, organize as funções em ordem alfabética – ou na ordem que quiser.

A primeira função que implementaremos será para codificar uma string C (const char *) para Base64. Como a string pode não ser bem formada, a função deverá receber também seu tamanho:
const char *b64encode(const char *original, int length) {
// Se o tamanho não for informado, consideramos uma string bem
// formada
if (length == 0)
length = strlen(original);

// Inteiro com o tamanho do código a ser gerado
int b64length = ((length + 2) / 3) * 4 + 1;

// Contadores para percorrer as strings
int i=0, j=0;

// Alocando memória para o código
char *b64 = (char *) malloc(sizeof(char) * b64length);
memset(b64, 0, b64length);

while (i < length) {
// Codifica um grupo de três bytes...
_encode(
(u_int8_t *) b64 + j,
(const u_int8_t *) original + i,
(length - i)
);

// E segue para o próximo grupo
i += 3;
j += 4;
}

// Retorna o código
return (const char *) b64;
}


A próxima função deve fazer o contrário, converter um código Base64 para uma string. Como a string resultante pode não ser bem formada – pode não ser terminada em carácter nulo ou possuir caracteres nulos no meio –, é preciso uma forma de informar seu tamanho, então ela receberá um ponteiro para um inteiro:
const char *b64decode(const char *b64, int *length) {
// Inteiro com o tamanho do código
int b64length = strlen(b64);

// Se não for múltiplo de quatro, há algo errado
if (b64length % 4 != 0)
return NULL;

// Tamanho máximo da string decifrada
int prob = (b64length / 4) * 3 + 1;

// Contadores para percorrer as strings
int i=0, j=0;

// Alocando memória para o resultado
char *s = (char *) malloc(sizeof(char) * prob);

while (j < b64length) {
// Decifra um grupo de quatro elementos
// e conta o resultado
i += _decode(
(u_int8_t *) s + i,
(const u_int8_t *) b64 + j
);

// Segue para o próximo grupo
j += 4;
}

// Se foi fornecido um inteiro para contagem, informa o tamanho
if (length != NULL)
*length = i;

// Retorna a string decifrada
return (const char *) s;
}


Agora precisamos das funções específica para as conversões.

Primeiro para codificar:
void _encode(u_int8_t *dest, const u_int8_t *src, int len) {
// Menor que 1, nada a fazer
if (len < 1)
return;

// Dados a serem retornados
int aux[] = { 0, 0, 0, 0 };

// Primeiro elemento: os 6 bits mais significativos do primeiro
// byte
aux[0] = src[0] >> 2;

// Segundo elemento: os 2 bits menos significativos do primeiro e
// os quatro bits mais significativos do segundo byte
aux[1] = (src[0] & 0x03) << 4;

if (len > 1) {
// SE houver um segundo...
aux [1] |= (src[1] & 0xf0) >> 4;

// Terceiro elemento: os quatro bits menos significativos do
// segundo e os dois mais significativos do terceiro byte
aux [2] = (src[1] & 0x0f) << 2;

if (len > 2) {
// Se houver um terceiro...
aux[2] |= src[2] >> 6;

// Quarto elemento: os seis bits menos significatos do
// terceiro byte
aux[3] = src[2] & 0x3f;
}
}

// Codifica agora os valores numéricos para string
dest[0] = b64all[aux[0]];
dest[1] = b64all[aux[1]];
dest[2] = '=';
dest[3] = '=';
if (len > 1) {
dest[2] = b64all[aux[2]];
if (len > 2)
dest[3] = b64all[aux[3]];
}
}

int _decode(u_int8_t *dest, const u_int8_t *src) {
// Representação numérica do código
int aux[] = { 0, 0, 0, 0 };

// Contador
int i, c = 1;

// Converte código para valores numéricos
for (i = 0; i < 4; ++i)
aux[i] = _getindex(src[i]);

// Primeiro byte: primeiro elemento seguido dos quatro bits mais
// significativos do segundo
dest[0] = (u_int8_t) (aux[0] << 2) | ((aux[1] & 0x30) >> 4);

// Zera os bytes seguintes
dest[1] = '\0';
dest[2] = '\0';

if (aux[2] != -1) {
// Se houver um terceiro elemento...
++c;

// Segundo byte: quatro bits menos significativos do segundo
// elemento seguidos pelos quatro bits mais significativos do
// terceiro
dest[1] = (u_int8_t) ((aux[1] & 0x0f) << 4) | (aux[2] >> 2);

if (aux[3] != -1) {
// Se houver um quarto elemento...
++c;

// Terceiro byte: dois bits menos significativos do
// terceiro elemento seguidos pelo quarto elemento
dest[2] = (u_int8_t)
((aux[2] & 0x03) << 6) |
aux[3];
}
}

// Retorna o tamanho da string
return c;
}


Cabeçalho


Por último criamos o cabeçalho base64.h, tornando públicas as duas funções de codificação e decodificação:
#ifndef _BASE64_H
#define _BASE64_H

const char *b64decode(const char *, int *);
const char *b64encode(const char *, int);

#endif


Conclusão


Mesmo que ninguém vá implementar uma biblioteca de conversão Base64, espero que este artigo sirva para ajudar a entender melhor do que se trata Base64.

[update]

Se quiser acrescentar algum açucar sintático, coloque em base64.c:
#ifdef C_PLUS_PLUS
const char *b64decode(const char *b64, int &length) {
return b64decode(b64, &length);
}
#endif


E mude o conteúdo de base64.h para:
#ifndef _BASE64_H
#define _BASE64_H

#ifdef C_PLUS_PLUS
extern "C" {
#endif

const char *b64decode(const char *, int *);
const char *b64encode(const char *, int);

#ifdef C_PLUS_PLUS
}
const char *b64decode(const char *, int &);
#else
#define _b64decode(s, len) b64decode(s, &len)
#endif

#endif

[/update]


[]'s
Cacilhas, La Batalema

quinta-feira, 16 de outubro de 2008

Portando GDBM para Lua

Lua Espero que este artigo seja interessante. =)

Além de ser um linguagem de programação brasileira, desenvolvida na PUC Rio, Lua possui uma poderosa API com C, na minha opinião, sua maior vantagem.

A outra parte importante deste artigo é GDBM, um conjunto de sub-rotinas de banco de dados baseado em hash, uma alternativa livre ao Unix DBM.

A ideia aqui é demonstrar um pouco dos recursos da biblioteca GDBM e como é simples estender a linguagem Lua.

O objeto


Pessoalmente, quando crio extensões para Lua, gosto de criar primeiro uma classe que represente meu objeto principal, ou uma classe para cada tipo de objeto que será usado nos scripts; depois crio um módulo que porta cada recurso para Lua.

Neste caso vamos criar uma classe GdbmObject que faça a interface com a base de dados, GDBM_FILE.

É claro que poderia ter usado direto GDBM_FILE, aliás comecei fazendo assim, mas acabei voltando ao procedimento que me é natural.

Quanto a objetos datum, não vi necessidade de criar uma interface, já que datum não passa de uma representação mais flexível de string.

O cabeçalho, gdbmobj.h, começa então com as instruções de preprocessamento triviais e a inclusão do cabeçalho gdbm.h, essencial para lidar com GDBM:
#ifndef _GDBMOBJ_H_
#define _GDBMOBJ_H_

#include <gdbm.h>


Podemos então criar a classe e começar os métodos públicos pelo construtor e o destruidor.

O Construtor precisa criar o GDBM_FILE. Para tanto, temos a função gdbm_open, que recebe cinco parâmetros:
  1. o nome do arquivo
  2. o tamanho de blocos para leitura e gravação em bytes (mínimo 512)
  3. o modo de leitura e gravação
  4. as permissões do arquivo, para caso ele seja criado
  5. uma função de callback a ser chamada quando ocorrer um erro fatal


Assim, pelo menos alguns desses parâmetros precisam ser argumentos do construtor. Para simplificar, vamos reduzir: caso o callback seja nulo, é usada uma função padrão. Vamos ficar com ela:
class GdbmObject {
public:
GdbmObject(
const char *name,
int block=512,
int mode=GDBM_READER,
int perm=0644
);
~GdbmObject();


Nosso construtor então recebe os elementos essencias para gdbm_open.

Os outros métodos públicos importantes precisam representar as funções de gdbm.h que queremos acessar, em ordem alfabética: gdbm_close, gdbm_delete, gdbm_exists, gdbm_fetch, gdbm_firstkey, gdbm_nextkey, gdbm_reorganize, gdbm_setopt, gdbm_store e gdbm_sync:
        bool gclose(void);
bool gdelete(const char *);
bool gexists(const char *);
double gfetch_number(const char *);
const char *gfetch_string(const char *);
const char *gnext(const char *key=NULL);
bool greorganize(void);
bool gsetopt(int, int);
bool gstore(const char *, const char *);
bool gstore(const char *, double);
bool gsync(void);


Agora um método para verificar se a base está fechada um método para retornar a última mensagem de erro:
        bool isclosed(void) const;
const char *last_error(void) const;


Entrando em território privado, precisamos de dois atributos: um flag que indique se a base está fechada e o GDBM_FILE em si:
    private:
bool closed;
GDBM_FILE fd;


Então um método privado para converter datum em string e outro para fazer o gdbm_fetch comum a gfetch_number e gfetch_string:
        static const char *get_string(datum);
datum gfetch(const char *);
};

#endif /* _GDBMOBJ_H_ */


Código dos métodos


A lógica em si vai no arquivo gdbmobj.cc, que deve iniciar incluindo nosso cabeçalho e o cabeçalho cstring, para que possamos usar strlen nas conversões entre strings e datum.
#include <cstring>
#include "gdbmobj.h"


O código do construtor é simples: basta pegar os argumentos e passá-lo como parâmetro para gdbm_close – não esqueça de ajudar closed como false!
GdbmObject::GdbmObject(
const char *name, int block, int mode, int perm
): closed(false)
{
this->fd = gdbm_open(
const_cast<char *>(name),
block, mode, perm, NULL
);
}


O destruidor deve apenas fechar a base, caso esqueçam de fazê-lo:
GdbmObject::~GdbmObject() {
this>gclose();
}


Precisamos fazer o gclose. Ele deve:
  1. verificar se a base já não está fechada
  2. fechar a base
  3. verificar se não houve erro
  4. marcar o objeto como fechado

bool GdbmObject::gclose(void) {
if (this->closed)
return false;

gdbm_close(this->fd);

if (gdbm_errno == GDBM_NO_ERROR) {
this->closed = true;
return true;
} else
return false;
}


Agora o método gdelete: primeiro verificar se já não está fechado – todos os métodos precisam verificar isso –, em seguida converter a chave string em datum – para isso o cabeçalho cstring – e então fazer o que tem de ser feito:
bool GdbmObject::gdelete(const char *key) {
if (this->close)
return false;

datum dkey;
dkey.dptr = const_cast<char *>(key);
dkey.dsize = strlen(key);

gdbm_delete(this->fd, dkey);

return gdbm_errno == GDBM_NO_ERROR;
}


O próximo método verifica se uma chave existe e funciona similar ao gdelete, só o retorno é que tem um significado diferente:
bool GdbmObject::gexists(const char *key) {
if (this->close)
return false;

datum dkey;
dkey.dptr = const_cast<char *>(key);
dkey.dsize = strlen(key);

return static_cast<bool>(gdbm_exists(this->fd, dkey));
}


Os métodos gfetch_number e gfetch_string devem retornar um valor para uma chave, um retornando double e outro string (const char *), ambos usando o método privado gfetch para obter a chave em formato datum antes:
double GdbmObject::gfetch_number(const char *key) {
datum data = this->gfetch(key);
double resp = -1.;

if (data.dsize == sizeof(double)) {
double *aux = reinterpret_cast<double *>(data.ptr);
resp = *aux;
}

delete[] data.dptr;
return resp;
}

const char *GdbmObject::gfetch_string(const char *key) {
if (this->closed)
return NULL;

datum data = this->gfetch(key);
const char *aux = get_string(data);

delete[] data.dptr;
return aux;
}


O método gnext é mais complexo, pois faz interface com gdbm_firstkey (quando a chave for nula) e com gdbm_nextkey.
const char *GdbmObject::gnext(const char *key) {
if (this->closed)
return NULL;

datum next;
next.dptr = NULL;
next.dsize = 0;

if (key == NULL)
next = gdbm_firstkey(this->fd);

else {
datum current;
current.dptr = const_cast<char *>(key);
current.dsize = strlen(key);
next = gdbm_nextkey(this->fd, current);
}

if (next.dptr != NULL) {
const char *aux = get_string(next);

delete[] next.dptr;
return aux;
} else
return NULL;
}


O método greorganize é similar a gclose:
bool GdbmObject::greorganize(void) {
if (this->closed)
return false;

gdbm_reorganize(this->fd);
return gdbm_errno == GDBM_NO_ERROR;
}


O método gsetopt precisa tornar transparente algumas peculiaridades de gdbm_setopt:
bool GdbmObject::gsetopt(int option, int value) {
if (this->closed)
return false;

gdbm_setopt(this->fd, option, &value, sizeof(int));
return gdbm_errno == GDBM_NO_ERROR;
}


Temos dois métodos gstore: um para armazenar números, outro para strings, mas a lógica é a mesma: converter tudo pra datum e usar gdbm_store para armazenar. Vamos usar apenas o flag GDBM_REPLACE:
bool GdbmObject::gstore(const char *key, double value) {
if (this->closed)
return false;

datum dkey, data;

dkey.dptr = const_cast<char *>(key);
dkey.dsize = strlen(key);
data.dptr = reinterpret_cast<char *>(&value);
data.dsize = sizeof(double);

gdbm_store(this->fd, dkey, data, GDBM_REPLACE);
return gdbm_errno == GDBM_NO_ERROR;
}

bool GdbmObject::gstore(const char *key, const char *value) {
if (this->closed)
return false;

datum dkey, data;

dkey.dptr = const_cast<char *>(key);
dkey.dsize = strlen(key);
data.dptr = const_cast<char *>(&value);
data.dsize = sizeof(double);

gdbm_store(this->fd, dkey, data, GDBM_REPLACE);
return gdbm_errno == GDBM_NO_ERROR;
}


O método gsync é quase idêntico a greorganize:
bool GdbmObject::gsync(void) {
if (this->closed)
return false;

gdbm_sync(this->fd);
return gdbm_errno == GDBM_NO_ERROR;
}


Agora os métodos constantes, isclosed (está fechado) e last_error (último erro):
bool GdbmObject::isclosed(void) const {
return this->closed;
}

const char *GdbmObject::last_error(void) const {
if (this->closed)
return "database closed";

else
return const_cast<const char *>(gdbm_strerror(gdbm_errno));
}


O método gfetch faz a verdadeira interface com gdbm_fetch para gfetch_number e gfetch_string:
datum GdbmObject::gfetch(const char *key) {
datum data;
data.dptr = NULL;
data.dsize = 0;

if (!this->closed) {
datum dkey;
dkey.dptr = const_cast<char *>(key);
dkey.dsize = strlen(key);

data = gdbm_fetch(this->fd, dkey);
}

return data;
}


O método estático get_string precisa converter datum em string:
const char *GdbmObject::get_string(datum data) {
if (data.dptr == NULL)
return NULL;

char *aux = new char[data.dsize + 1];

for (int i=0; i<data.dsize; ++i)
aux[i] = data.dptr[i];
aux[data.dsize] = '\0';

return const_cast<const char *>(aux);
}


Aqui cabe um comentário para justificar o uso de for em lugar de strcpy:

O ponteiro de datum não é uma string, é um ponteiro para um grupo de bytes – mais ou menos similiar a uma string de Pascal, que tem a vantagem de suportar carácter nulo como parte. Portanto pode não ter um carácter nulo ou ter caracteres nulos dentro do grupo de bytes.

Então a função strcpy não é aplicável.

Que entre a Lua!


Vamos introduzir então a API com Lua, no arquivo luagdbm.cc.

O código precisa começar incluindo o cabeçalho de nossa classe e o cabeçalho de Lua para C++:
#include <lua.hpp>
#include "gdbmobj.h"


Algumas configurações importantes são dados de versão e nome do módulo:
#define VERSION "1.0"
#define MODULE "gdbm"
const char *modulename = MODULE;


Quando Lua carrega uma extensão, chama a função luaopen_nome_da_função, no caso luaopen_gdbm, que deve receber um ponteiro para um estado Lua e deve responder com um inteiro representando a quantidade de elementos empilhados para o estado Lua ao final.

Outra função interessante é set_info, que serve para separar de luaopen_* os dados de diretos, strings e números.

Também vamos criar uma função para empilhar um objeto GdbmObject para o estado Lua:
extern "C" int luaopen_gdbm(lua_State *);
static void set_info(lua_State *);
static void pushdb(lua_State *, GdbmObject *);


Agora vamos declarar uma função para cada método público de GdbmObject – é nossa interface com Lua:
extern "C" {
static int luagdbm_close(lua_State *);
static int luagdbm_delete(lua_State *);
static int luagdbm_exists(lua_State *);
static int luagdbm_fetch_number(lua_State *);
static int luagdbm_fetch_string(lua_State *);
static int luagdbm_isclosed(lua_State *);
static int luagdbm_next(lua_State *);
static int luagdbm_open(lua_State *);
static int luagdbm_reorganize(lua_State *);
static int luagdbm_setopt(lua_State *);
static int luagdbm_store(lua_State *);
static int luagdbm_sync(lua_State *);
}


Podemos implementar cada uma dessas funções.

Função de carregamento


Como já foi dito, a função de carrgamento é luaopen_gdbm.

Ela deve criar um vetor de luaL_reg relacionando cada função com as chaves no módulo em lua, terminando com nulo:
int luaopen_gdbm(lua_State *L) {
static const luaL_reg gdbm_funcs[] = {
{"close", luagdbm_close },
{"delete", luagdbm_delete },
{"exists", luagdbm_exists },
{"fetch_number", luagdbm_fetch_number },
{"fetch_string", luagdbm_fetch_string },
{"isclosed", luagdbm_isclosed },
{"next", luagdbm_next },
{"open", luagdbm_open },
{"reorganize", luagdbm_reorganize },
{"setopt", luagdbm_setopt },
{"store", luagdbm_store },
{"sync", luagdbm_sync },
{ NULL, NULL }
};


Então o módulo deve ser registrado com as funções. Para poder usar o módulo como metatabela para os objetos GdbmObject, vamos também criar também a chave __index apontando para o próprio módulo:
    luaLregister(L, modulename, gdbm_funcs);
lua_pushliteral(L, "__index");
lua_getglobal(L, modulename);
lua_settable(L, -3);


Agora só falta chamar set_info e informar que uma estrutura – o módulo – foi empilhada:
    set_info(L);
return 1;
}


Função para empilhar um objeto de banco de dados


É a função pushdb, que recebe a pilha e um ponteiro para o objeto a ser empilhado.

A função também deve associar ao objeto o próprio módulo como metatabela:
void pushdb(lua_State *L, GdbmObject *db) {
lua_pushlightuserdata(L, db);
lua_getglobal(L, modulename);
lua_setmetatable(L, -2);
}


Vamos deixar set_info para o final…

Funções de interface com os métodos público


Praticamente todas funções devem:
  1. carregar o ponteiro para objeto GdbmObject do estado
  2. verificar se ele ele não está nulo
  3. fazer o que tem de ser feito
  4. retornar o que foi solicitado ou uma mensagem de erro


Repare que todas as funções retornam a quantidade de elementos empilhados (lua_push*).

Assim como começamos pelo método gclose, vamos começar pela função luagdbm_close:
int luagdbm_close(lua_State *L) {
GdbmObject *db =
reinterpret_cast<GdbmObject *>(lua_touserdata(L, 1));

if (db == NULL) {
lua_pushnil(L);
lua_pushstring(L, "no database");
return 2;
}

if (db->gclose()) {
delete db;
lua_pushboolean(L, 1);
return 1;

} else {
lua_pushnil(L);
lua_pushstring(L, db->last_error());
return 2;
}
}


A função luagdbm_delete ainda precisa recuperar a chave a ser deletada:
int luagdbm_delete(lua_State *L) {
GdbmObject *db =
reinterpret_cast<GdbmObject *>(lua_touserdata(L, 1));

if (db == NULL) {
lua_pushnil(L);
lua_pushstring(L, "no database");
return 2;
}

const char *key = lua_tostring(L, 2);

if (db->gdelete(key)) {
lua_pushboolean(L, 1);
return 1;

} else {
lua_pushnil(L);
lua_pushstring(L, db->last_error());
return 2;
}
}


A função luagdbm_exists é similar à anterior, só que mais simples:
int luagdbm_exists(lua_State *L) {
GdbmObject *db =
reinterpret_cast<GdbmObject *>(lua_touserdata(L, 1));

if (db == NULL) {
lua_pushnil(L);
lua_pushstring(L, "no database");
return 2;
}

const char *key = lua_tostring(L, 2);

lua_pushboolean(L, static_cast<int>(db->gexists(key)));
return 1;
}


As funções luagdbm_fetch_number e luagdbm_fetch_string são similares, só que, assim como os métodos gfetch_number e gfetch_string, retornam número e string:
int luagdbm_fetch_number(lua_State *L) {
GdbmObject *db =
reinterpret_cast<GdbmObject *>(lua_touserdata(L, 1));

if (db == NULL) {
lua_pushnil(L);
lua_pushstring(L, "no database");
return 2;
}

const char *key = lua_tostring(L, 2);
double value = db->getch_number(key);

lua_pushnumber(L, value);

if (value == -1.) {
lua_pushstring(L, db->last_error());
return 2;

} else
return 1;
}

int luagdbm_fetch_string(lua_State *L) {
GdbmObject *db =
reinterpret_cast<GdbmObject *>(lua_touserdata(L, 1));

if (db == NULL) {
lua_pushnil(L);
lua_pushstring(L, "no database");
return 2;
}

const char *key = lua_tostring(L, 2);
const char *value = db->getch_string(key);

if (value == NULL) {
lua_pushnil(L);
lua_pushstring(L, db->last_error());
return 2;

} else {
lua_pushstring(L, value);
return 1;
}
}


A função luagdbm_isclose é extremamente simples:
int luagdbm_isclosed(lua_State *L) {
GdbmObject *db =
reinterpret_cast<GdbmObject *>(lua_touserdata(L, 1));

if (db == NULL) {
lua_pushnil(L);
lua_pushstring(L, "no database");
return 2;
}

lua_pushboolean(L, static_cast<int>(db->isclosed()));
return 1;
}


A função luagdbm_next também não é complicada, apenas é preciso verificar se o parâmetro chave foi fornecido:
int luagdbm_next(lua_State *L) {
GdbmObject *db =
reinterpret_cast<GdbmObject *>(lua_touserdata(L, 1));

if (db == NULL) {
lua_pushnil(L);
lua_pushstring(L, "no database");
return 2;
}

const char *key = NULL;
if (lua_type(L, 2) == LUA_TSTRING)
key = lua_tostring(L, 2);

const char *value = db->gnext(key);

if (value == NULL) {
lua_pushnil(L);
lua_pushstring(L, db->last_error());
return 2;

} else {
lua_pushstring(L, value);
return 1;
}
}


Vamos deixar a função luagdbm_open, devido a sua complexidade, então passamos direto para luagdbm_reorganize:
int luagdbm_reorganize(lua_State *L) {
GdbmObject *db =
reinterpret_cast<GdbmObject *>(lua_touserdata(L, 1));

if (db == NULL) {
lua_pushnil(L);
lua_pushstring(L, "no database");
return 2;
}

if (db->greorganize()) {
lua_pushboolean(L, 1);
return 1;

} else {
lua_pushnil(L);
lua_pushstring(L, db->last_error());
return 2;
}
}


A função luagdbm_setopt precisa obter dois inteiros, representando a opção e o valor a ser ajustado:
int luagdbm_setopt(lua_State *L) {
GdbmObject *db =
reinterpret_cast<GdbmObject *>(lua_touserdata(L, 1));

if (db == NULL) {
lua_pushnil(L);
lua_pushstring(L, "no database");
return 2;
}

int option = static_cast<int>(lua_tonumber(L, 2));
int value = static_cast<int>(lua_tonumber(L, 3));

if (db->gsetopt(option, value)) {
lua_pushboolean(L, 1);
return 1;
} else {
lua_pushnil(L);
lua_pushstring(L, db->last_error());
return 2;
}
}


Outra função um pouco mais complicada é luagdbm_store, que deve armazenar um par chave-valor.

A complexidade não está no armazenamento em si, mas na tomada de decisão do tipo da chave, número ou string.

Começamos como as outras funções:
int luagdbm_store(lua_State *L) {
GdbmObject *db =
reinterpret_cast<GdbmObject *>(lua_touserdata(L, 1));

if (db == NULL) {
lua_pushnil(L);
lua_pushstring(L, "no database");
return 2;
}

const char *key = lua_tostring(L, 2);
bool resp = false;


Então precisamos decidir se o próximo parâmetro é número ou string e usar uma variável do tipo conveniente:
    switch (lua_type(L, 3)) {
case LUA_TSTRING: {
const char *aux = lua_tostring(L, 3);
resp = db->gstore(key, aux);
break;
}

case LUA_TNUMBER: {
double aux = lua_tonumber(L, 3);
resp = db->gstore(key, aux);
break;
}

default:
lua_pushnil(L);
lua_pushstring(L, "not compatible data");
return 2;
}


Podemos então retornar convenientemente:
    if (resp) {
lua_pushboolean(L, 1);
return 1;

} else {
lua_pushnil(L);
lua_pushstring(L, db->last_error());
return 2;
}
}


A última função é luagdbm_sync, que não tem segredos:
int luagdbm_sync(lua_State *L) {
GdbmObject *db =
reinterpret_cast<GdbmObject *>(lua_touserdata(L, 1));

if (db == NULL) {
lua_pushnil(L);
lua_pushstring(L, "no database");
return 2;
}

if (db->gsync()) {
lua_pushboolean(L, 1);
return 1;

} else {
lua_pushnil(L);
lua_pushstring(L, db->last_error());
return 2;
}
}


Abertura de uma base de dados


Finalmente chegamos à função luagdbm_open!

Ela é bem complexa, pois pode receber parâmetros diferentes.

Se o parâmetro for string, será considerada como o nome do arquivo de base de dados e para todas as demais configurações são usados os valores por defeito.

Se for uma tabela:
  • o valor para a chave name representará o nome do arquivo;
  • o valor para a chave block representará o tamanho do bloco;
  • o valor para a chave mode representará o modo de leitura e/ou gravação;
  • o valor para a chave permission representará as permissões para a criação do arquivo.


No entanto, mais de um parâmetros podem ser passados para o modo, como por exemplo GDBM_WRCREAT e GDBM_SYNC. Para esses casos, o valor para a chave mode deve ser uma tabela indexada contendo os modos desejados – e a função precisa estar preparada para processar isso!

A primeira parte da função é carregar os valores por defeito:
int luagdbm_open(lua_State *L) {
const char *name = NULL;
int block = 512;
int mode = GDBM_READER;
int perm = 0644;


Então precisamos verificar se o parâmetro é string (nome do arquivo, tudo mais ajustado por defeito) ou tabela:
    switch(lua_type(L, 1)) {
case LUA_TSTRING:
name = lua_tostring(L, 1);
break;

case LUA_TTABLE: {
lua_getfield(L, 1, "name");

if (lua_type(L, -1) == LUA_TSTRING) {
name = lua_tostring(L, -1);


Agora precisamos pegar o valor de block:
                lua_getfield(L, 1, "block");
if (lua_type(L, -1) == LUA_TNUMBER)
block = static_cast<int>(lua_tonumber(L, -1));


Ao trazermos o valor de mode para cima, precisamos ainda verificar seu tipo:
                lua_getfield(L, 1, "mode");
switch (lua_type(L, -1)) {
case LUA_TNUMBER:
mode = static_cast<int>(lua_tonumber(L, -1));
break;


Agora, caso seja uma tabela, é preciso percorrer seus valores enquantos estes forem números:
                    case LUA_TTABLE: {
int i = 1;
lua_rawgeti(L, -1, i);
mode = 0;
while (lua_type(L, -1) == LUA_TNUMBER) {
mode |= static_cast<int>(
lua_tonumber(L, -1)
);
lua_pop(L, 1);
lua_rawgeti(L, -1, ++i);
}

mode = (mode == 0) ? GDBM_READER : mode;
break;
}


Repare que lua_rawgeti(L, -1, i) empilha o i-ésimo elemento da tabela no fim da pilha (-1) e lua_pop(L, 1) desempilha para voltarmos à tabela.

Agora, caso não seja nem número, nem tabela, ignora:
                    default:
break;
}


Obter as permissões é tão simples quanto obter o tamanho do bloco. Aproveitamos e já encerramos o switch o parâmetro ignorando outros tipos:
                lua_getfield(L, 1, "permission");
if (lua_type(L, -1) == LUA_TNUMBER)
perm = static_cast<int>(lua_tonumber(L, -1));
}

break;
}

default:
break;
}


Repare bem em uma coisa: lua_to*(L, 1) retorna o primeiro parâmetro passado à função, lua_to*(L, 2) retorna o segundo parâmetro. lua_to*(L, -1) retorna o último elemento empilhado por lua_push*, lua_getglobal, lua_getfield, lua_rawget, lua_rawgeti ou qualquer outra função da API que empilhe elementos.

Agora é preciso verificar se, após todo esse trabalho, o nome do arquivo foi realmente ajustado:
    if (name == NULL) {
lua_pushnil(L);
lua_pushstring(L, "file not supplied");
return 2;


Finalmente podemos abrir o arquivo de banco de dados e, se tudo correr bem, retorná-lo para o estado Lua – caso contrário, retornar um erro:
    } else {
GdbmObject *db = new GdbmObject(name, block, mode, perm);

if (gdbm_errno == GDBM_NO_ERROR) {
if (db == NULL) {
lua_pushnil(L);
lua_pushstring(L,
"no error, but no database returned"
);
return 2;

} else {
pushdb(L, db);
return 1;
}
} else {
delete db;
lua_pushnil(L);
lua_pushstring(L, gdbm_strerror(gdbm_errno));
return 2;
}
}
}


Informações


A função set_info é na verdade a mais chata: primeiro ajusta dados padrão sobre o módulo, depois carrega as constantes usadas pelo GDBM com nomes similares.

É bastante extenso e se não quiser, não precisa ler agora. Está aqui apenas para quem quiser programá-lo.
void set_info(lua_State *L) {
lua_pushliteral(L, "_COPYRIGHT");
lua_pushliteral(L, "Copyright (C) 2008 Rodrigo Cacilhas");
lua_settable(L, -3);
lua_pushliteral(L, "_DESCRIPTION");
lua_pushliteral(L, "GDBM interface");
lua_settable(L, -3);
lua_pushliteral(L, "_NAME");
lua_pushliteral(L, MODULE);
lua_settable(L, -3);
lua_pushliteral(L, "_VERSION");
lua_pushliteral(L, VERSION);
lua_settable(L, -3);

lua_pushliteral(L, "READER");
lua_pushnumber(L, GDBM_READER);
lua_settable(L, -3);
lua_pushliteral(L, "WRITER");
lua_pushnumber(L, GDBM_WRITER);
lua_settable(L, -3);
lua_pushliteral(L, "WRCREAT");
lua_pushnumber(L, GDBM_WRCREAT);
lua_settable(L, -3);
lua_pushliteral(L, "NEWDB");
lua_pushnumber(L, GDBM_NEWDB);
lua_settable(L, -3);
lua_pushliteral(L, "FAST");
lua_pushnumber(L, GDBM_FAST);
lua_settable(L, -3);
lua_pushliteral(L, "SYNC");
lua_pushnumber(L, GDBM_SYNC);
lua_settable(L, -3);
lua_pushliteral(L, "NOLOCK");
lua_pushnumber(L, GDBM_NOLOCK);
lua_settable(L, -3);

lua_pushliteral(L, "INSERT");
lua_pushnumber(L, GDBM_INSERT);
lua_settable(L, -3);
lua_pushliteral(L, "REPLACE");
lua_pushnumber(L, GDBM_REPLACE);
lua_settable(L, -3);


lua_pushliteral(L, "CACHESIZE");
lua_pushnumber(L, GDBM_CACHESIZE);
lua_settable(L, -3);
lua_pushliteral(L, "FASTMODE");
lua_pushnumber(L, GDBM_FASTMODE);
lua_settable(L, -3);
lua_pushliteral(L, "SYNCMODE");
lua_pushnumber(L, GDBM_SYNCMODE);
lua_settable(L, -3);
lua_pushliteral(L, "CENTFREE");
lua_pushnumber(L, GDBM_CENTFREE);
lua_settable(L, -3);
lua_pushliteral(L, "COALESCEBLKS");
lua_pushnumber(L, GDBM_COALESCEBLKS);
lua_settable(L, -3);


lua_pushliteral(L, "NO_ERROR");
lua_pushnumber(L, GDBM_NO_ERROR);
lua_settable(L, -3);
lua_pushliteral(L, "MALLOC_ERROR");
lua_pushnumber(L, GDBM_MALLOC_ERROR);
lua_settable(L, -3);
lua_pushliteral(L, "BLOCK_SIZE_ERROR");
lua_pushnumber(L, GDBM_BLOCK_SIZE_ERROR);
lua_settable(L, -3);
lua_pushliteral(L, "FILE_OPEN_ERROR");
lua_pushnumber(L, GDBM_FILE_OPEN_ERROR);
lua_settable(L, -3);
lua_pushliteral(L, "FILE_WRITE_ERROR");
lua_pushnumber(L, GDBM_FILE_WRITE_ERROR);
lua_settable(L, -3);
lua_pushliteral(L, "FILE_SEEK_ERROR");
lua_pushnumber(L, GDBM_FILE_SEEK_ERROR);
lua_settable(L, -3);
lua_pushliteral(L, "FILE_READ_ERROR");
lua_pushnumber(L, GDBM_FILE_READ_ERROR);
lua_settable(L, -3);
lua_pushliteral(L, "BAD_MAGIC_NUMBER");
lua_pushnumber(L, GDBM_BAD_MAGIC_NUMBER);
lua_settable(L, -3);
lua_pushliteral(L, "EMPTY_DATABASE");
lua_pushnumber(L, GDBM_EMPTY_DATABASE);
lua_settable(L, -3);
lua_pushliteral(L, "CANT_BE_READER");
lua_pushnumber(L, GDBM_CANT_BE_READER);
lua_settable(L, -3);
lua_pushliteral(L, "CANT_BE_WRITER");
lua_pushnumber(L, GDBM_CANT_BE_WRITER);
lua_settable(L, -3);
lua_pushliteral(L, "READER_CANT_DELETE");
lua_pushnumber(L, GDBM_READER_CANT_DELETE);
lua_settable(L, -3);
lua_pushliteral(L, "READER_CANT_STORE");
lua_pushnumber(L, GDBM_READER_CANT_STORE);
lua_settable(L, -3);
lua_pushliteral(L, "READER_CANT_REORGANIZE");
lua_pushnumber(L, GDBM_READER_CANT_REORGANIZE);
lua_settable(L, -3);
lua_pushliteral(L, "UNKNOWN_UPDATE");
lua_pushnumber(L, GDBM_UNKNOWN_UPDATE);
lua_settable(L, -3);
lua_pushliteral(L, "ITEM_NOT_FOUND");
lua_pushnumber(L, GDBM_ITEM_NOT_FOUND);
lua_settable(L, -3);
lua_pushliteral(L, "REORGANIZE_FAILED");
lua_pushnumber(L, GDBM_REORGANIZE_FAILED);
lua_settable(L, -3);
lua_pushliteral(L, "CANNOT_REPLACE");
lua_pushnumber(L, GDBM_CANNOT_REPLACE);
lua_settable(L, -3);
lua_pushliteral(L, "ILLEGAL_DATA");
lua_pushnumber(L, GDBM_ILLEGAL_DATA);
lua_settable(L, -3);
lua_pushliteral(L, "OPT_ALREADY_SET");
lua_pushnumber(L, GDBM_OPT_ALREADY_SET);
lua_settable(L, -3);
lua_pushliteral(L, "OPT_ILLEGAL");
lua_pushnumber(L, GDBM_OPT_ILLEGAL);
lua_settable(L, -3);
}


Compilando


Uma forma legal de compilar é através de um arquivo Makefile.

Esse arquivo começa com um comentário informando data e seu criador, e logo em seguida os recursos usados: compilador (CC), link-editor (LD), flags de compilação (CFLAGS) e bibliotecas envolvidas (LIBS), no caso:
# $Id: luagdbm,v 1.0 2008/10/16 23:25:31 cacilhas Exp $

CC ?= c++
LD ?= c++
RM ?= rm -f

CFLAGS ?=
LIBS ?= -lm -llua -lgdbm -lstdc++

# **********************************************************************


Então os procedimentos de compilação.

O procedimento para compilar os arquivos fontes (*.cc) em objetos (*.o) é:
.cc.o:
$(CC) -c $< $(CFLAGS) -o $@


Finalmente fazemos a link-edição de tudo na biblioteca compartilhada gdbm.so:
gdbm.so: gdbmobj.o luagdbm.o
$(LD) -shared $? ($LIBS) -o $@


Agora precisamos informar ao make o que fazer por defeito:
all: gdbm.so


E como limpar a bagunça:
clean:
$(RM) luagdbm.o
$(RM) gdbmobj.o


Feito isso já podemos compilar:
bash$ make


Se tudo correu bem, a biblioteca gdbm.so terá sido gerada, se não, verifique os erros e volte corrigindo-os. Caso encontre algum erro neste artigo, por favor informe para que eu possa corrigi-lo!

Fazendo funcionar


Agora mova a biblioteca para um diretório onde Lua possa encontrá-la. Aqui para mim é /usr/lib/lua/5.1/, mas aí para você pode ser qualquer coisa. O conteúdo da variável de ambiente LUA_CPATH pode dar uma dica.

Na ausência dessa variável, entrando no interpretador Lua, a chave package.cpath informa o local correto.

Feito isso, podemos iniciar um interpretador Lua e fazer testes:
bash$ lua
Lua 5.1.3 Copyright (C) 1994-2008 Lua.org, PUC-Rio
> require "gdbm"
> db = assert(gdbm.open { name = "dbtest.db", mode = gdbm.WRCREAT })
> db:store("first", "a")
> db:store("second", "b")
> db:store("thirt", "c")
> db:close()


Saindo do interpretador, você poderá ver o arquivo dbtest.db criado. Vamos reler seu conteúdo:
> db = assert(gdbm.open "dbtest.db")
> k = db:next()
> while k do print(k, db:fetch_string(k)) k = db:next(k) end
first a
second b
thirt c
> db:close()


A ordem de gdbm_next não é necessariamente a ordem de registro.

Conclusão


Acho que este artigo está cheio de informações e é bastante longo, mas se você conseguiu lê-lo até aqui, significa que consegui fazê-lo útil.

Para saber mais, leia a manpage de gdbm e o PiL.

[]'s
Cacilhas, La Batalema

[update 2008-10-23]Valeu pela divulgação no BR-Linux.org, Augusto![/update]

segunda-feira, 24 de dezembro de 2007

Lua-Gtk

A linguagem de programação Lua foi planejada para ser uma linguagem de extensão para aplicações escritas em C.

O lado negativo disso é a linguagem não foi pensada para o desenvolvimento de aplicações totalmente em Lua, mas por outro lado a grande vantagem é que tudo que existe em C pode ser portado para Lua.

Assim, mesmo não havendo um toolkit gráfico padrão para Lua, a biblioteca GTK+ foi portada para Lua, sob o nome Lua-Gtk.

Lua-Gtk implementa perfeitamente a versão 2.x do GTK+, com ênfase na orientação a objetos.

Para quem conhece a biblioteca GTK+, o módulo Lua-Gtk não traz segredo algum. Depois de ter instalado Lua-Gtk, vamos implementar um exemplo «Olá Mundo!» clássico.

Em C, nosso hello.c fica assim:
#include <gtk/gtk.h>

static void hello(GtkWidget *, gpointer);
static gboolean delete_event(GtkWidget *, GdkEvent *, gpointer);

int main(int argc, char **argv) {
GtkWidget *window;
GtkWidget *button;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

g_signal_connect(
G_OBJECT(window), "delete_event",
G_CALLBACK(delete_event), NULL
);
gtk_container_set_border_width (GTK_CONTAINER(window), 10);

button = gtk_button_new_with_label("Hello World");

g_signal_connect(
G_OBJECT(button), "clicked",
G_CALLBACK(hello), NULL
);
g_signal_connect_swapped(
G_OBJECT(button), "clicked",
G_CALLBACK(gtk_widget_destroy), G_OBJECT (window)
);
gtk_container_add(GTK_CONTAINER(window), button);
gtk_widget_show(button);
gtk_widget_show (window);

gtk_main ();
return 0;
}

static void hello(GtkWidget *widget, gpointer data) {
g_print("Hello World\n");
gtk_main_quit();
}

static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer data) {
g_print("delete event occurred\n");
return TRUE;
}


Para compilá-lo:
bash$ gcc hello.c -o hello `pkg-config --cflags --libs gtk+-2.0`
bash$ ./hello


A versão Lua, hello.lua é muito parecida, se você comparar linha a linha:
require "gtk"

function hello(widget, data)
print "Hello World"
gtk.main_quit()
end

function delete_event(widget, event, data)
print "dete event occurred"
return true
end

gtk.init(#{...}, {...})

window = gtk.window_new(gtk.GTK_WINDOW_TOPLEVEL)

window:connect("delete_event", delete_event)
window:set_border_width(10)

button = gtk.button_new_with_label("Hello World")

button:connect("clicked", hello)
window:add(button)
button:show()
window:show()

gtk.main()


Para executar:
bash$ lua hello.lua


Fica aqui a dica! Para saber mais, veja o manual de referência.

Se quiserem, depois escrevo mais sobre o assunto.

[]'s
Cacilhas, La Batalema

domingo, 9 de setembro de 2007

Criando um módulo binário em Lua

Lua Uma das coisas que tornam a linguagem de programação Lua interessante é justamente sua poderosa API com a linguagem C.

Com ela é possível criar módulos em C, explorando todo poder e flexibilidade dessa incrível linguagem.

De fato a linguagem Lua não passa de uma linguagem de extensão para C e o interpretador Lua é uma programa escrito em C para ser uma máquina virtual.

A criação de um módulo de baixo nível – como são conhecidos os módulos em C para Lua – não tem nenhum truque ou sortilégio. =)

Basicamente você precisa de uma função int luaopen_módulo(lua_State *) que chame a função lua_register() para registrar o módulo. Essa função recebe três parâmetros: o estado Lua, o nome do módulo e um vetor do tipo luaL_reg com a lista de funções a serem exportadas. A função deve retornar 1.

As funções a serem exportadas devem ser estáticas, recebendo o estado Lua e retornando inteiro.

Os parâmetros vindos de Lua são recolhidos com as funções lua_totipo() e o retorno é dado pelas funções lua_pushtipo(). A função C real deve retornar um número representando a quantidade de valores a serem retornados para Lua.

Confuso até aqui, né? Mas tudo se esclarecerá com um exemplo.

Exemplo


Vamos criar um módulo que permita mudar e informar o diretório atual. Para tanto, precisaremos do cabeçalho unistd.h.

Precisaremos também do cabeçalho stdlib.h – para o malloc() – e dos cabeçalhos de Lua:
#include <stdlib.h>
#include <unistd.h>

#include <lua.h>
#include <luaxlib.h>
#include <lualib.h>


Agora podemos declarar os cabeçalhos das funções para mudar e informar o diretório atual, e uma função para ajustar informações sobre o módulo:
static int dir_chdir(lua_State *);
static int dir_cwd(lua_State *);
static void set_info(lua_State *);


Repare que todas as funções recebem como único argumento o estado Lua.

Vamos agora ao vetor que define as funções do módulo. Deve ser um vetor do tipo luaL_reg. Esse tipo é uma estrutura onde o primeiro elemento é uma string representando o nome da funções e o segundo elemento a função em si. O último elemento do vetor deve ser equivalente a nulo.

O último elemento do vetor deve ser nulo:
static const luaL_reg Funcs[] = {
{ "chdir", dir_chdir },
{ "cwd", dir_cwd },
{ NULL, NULL }
}


Agora já podemos criar a função de abertura do módulo, como já vimos, luaopen_dir():
int luaopen_dir(lua_State *L) {
luaL_register(L, "dir", Funcs);
set_info(L);
return 1;
}


Em set_info(), o que vemos é lua_pushliteral(), que empilha dados literais no estado, depois «agrupamos» os dados literais com lua_settable() – não vou entrar em detalhes sobre isso.
static void set_info(lua_State *L) {
lua_pushliteral(L, "_COPYRIGHT");
lua_pushliteral(L, "Copyright (C) 2007 Rodrigo Cacilhas");
lua_settable(L, -3);
lua_pushliteral(L, "_DESCRIPTION");
lua_pushliteral(L, "Directory control");
lua_settable(L, -3);
lua_pushliteral(L, "_NAME");
lua_pushliteral(L, "dir");
lua_settable(L, -3);
lua_pushliteral(L, "_VERSION");
lua_pushliteral(L, "1.0");
lua_settable(L, -3);
}


As funções exportadas


Vamos agora às funções exportadas para Lua.

O cabeçalho da primeira função é:
static int dir_chdir(lua_State *L) {
int r;


Usaremos r para fornecer o retorno.

Em Lua, a função chdir() deverá receber um parâmetro string. Para pegar esse parâmetro na função em C usamos:
    char *path = (char *) lua_tostring(L, 1);


A chamada da função lua_tostring() recebe como primeiro parâmetro o estado Lua que executou a função e, como segundo parâmetro, o índice do parâmetro passado para a função Lua – 1 indica o primeiro parâmetro.

Então a linha acima recolherá o primeiro parâmetro passado em Lua como uma string.

Agora é código C simples:
    if (chdir(path) == 0)
r = 1;
else
r = 0;


Ou seja, se o status for zero, o retorno será 1 (verdadeiro), senão será 0 (falso).

Agora precisamos empilhar a resposta no estado Lua como booleano:
    lua_pushboolean(L, r);


Por último avisamos o estado de que um valor foi retornado:
    return 1;
}


Vamos agora à segunda função: ela retornará uma string representando o diretório atual ou nulo caso ocorra um erro – veja que é possível retornar quantidades e tipos diferentes em chamadas diferentes:
static int dir_cwd(lua_State *L) {
char *path = (char *) malloc(sizeof(char) * 1024);
getcwd(path, 1023);

if (path == NULL) {
lua_pushnil(L);
lua_pushstring(L, "getcwd error");
return 2; // dois retornos
} else {
lua_pushstring(L, path);
return 1; // um retorno
}
}


Compilando


Agora é preciso compilar o módulo – chamei o arquivo de dir.c:
bash$ gcc -fPIC -Wall -c dir.c
bash$ ld -lm -shared dir.o -o dir.so


Agora, como superusuário, mova a biblioteca compartilhada dir.so para o diretório $LUA_CPATH – geralmente /usr/local/lib/lua/5.1/ ou /usr/lib/lua/5.1/.

Testando


Vamos então testar! Levante a máquina virtual Lua e experimente:
lua> require "dir"
lua> print(dir.chdir "/tmp")
true
lua> print(dir.cwd())
/tmp


Se tudo correu bem, você terá algo similar a isso. =)

Dúvidas nos comentários, por favor!

[]'s
Cacilhas

sábado, 28 de julho de 2007

OpenID

OpenID Passei essa semana quebrando a cabeça no trabalho para desenvolver um protótipo de módulo relying party em Lua.

Relying party – também chamado consumer – é um dos nós que participam de uma autenticação OpenID.

O que é OpenID?


OpenID é um sistema livre descentralizado de autenticação e autorização centrado no usuário.

Ou seja, atualmente em cada sistema com autenticação, você precisa fazer um cadastro. Então você usuário é obrigado a manter uma redundância inconveniente de dados, que eventualmente precisam ser atualizados.

Para piorar os sistemas centralizam dados de todos os usuários, inclusive as senhas. Daí você usuário precisa ter uma senha para cada sistema, tendo de lembrar toda uma fauna de sEnH45 complicadas – ou de 123mudar.

Alguns arriscam sua identidade digital usando a mesma senha para tudo, o que não é aconselhável nem de longe.

OpenID surge em meio a essa realidade como uma solução para o problema.

Você usuário centraliza todos seus dados e sua senha em um único servidor de sua confiança, chamado server-agent ou OpenID provider.

Então você pode autenticar-se em sistemas que ofereçam suporte a OpenID – chamados relying parties ou consumers – sem que o sistema conheça sua senha, apenas fornecendo uma URL conhecida como identidade OpenID (identity). Ainda mais: quando for atualizar seus dados, só é preciso atualizar no agente servidor, pois não há redundância.

É um sistema descentralizado porque você não é obrigado a cadastrar seus dados no sistema onde deseja ser autenticado.

Protocolo


Há ferramentas prontas para Python, JSP, Ruby e PHP.

No entanto Lua é uma linguagem com uma comunidade muito pequena ainda para reagir tão rápido às novidades. Considerando-se ainda a filosofia da linguagem – SIMPLE: simple, light-weight and extensible, simples, leve e extensível –, é compreensível a necessidade de desenvolver os próprios módulos.

Se por um lado é inconveniente, por outro, se você realmente gosta de programar, Lua se torna uma linguagem muito divertida.

Em miúdos: se você trabalha com programação mas não gosta, faz isso só pra ganhar dinheiro, pare de ler por aqui e vai trabalhar. Espere alguém fazer um módulo por você para poder usar OpenID. =P


Continuando então o artigo – agora para programadores de verdade e curiosos –, o problema para desenvolver um módulo consumidor é entender o protocolo.

O protocolo OpenID negocia tunelado dentro do protocolo HTTP.

Resumidamente, a lógica é a seguinte:
  1. O cliente (navegador) acessa o consumidor;
  2. O consumidor envia um formulário ao cliente solicitando sua identidade OpenID;
  3. O cliente informa ao consumidor sua identidade;
  4. O consumidor normaliza a identidade – fica algo como http://user.example.com/;
  5. O consumidor acessa via HTTP a URL da identidade e pega o código HTML;
  6. O consumidor procura na URL marcadores link com atributos rel contendo openid.server, openid.delegate, openid2.provider e openid2.local_id – esses marcadores informam através do atributo href quem é o agente servidor e qual a identidade real do cliente;
  7. O consumidor acessa via HTTP o agente servidor em busca das chaves de comunicação – esse processo é chamado associação;
  8. Tendo sido feita a associação, o consumidor usa as chaves para redirecionar o cliente para a página de autenticação do agente servidor;
  9. O cliente se autentica no agente servidor informando sua senha, se já não estiver atenticado;
  10. O agente servidor pergunta ao cliente se ele autoriza o consumidor a ter acesso a seu perfil;
  11. Se o cliente permitir, o agente servidor redireciona o cliente para o consumidor, passando os dados solicitados.


Vamos então para o primeiro passo que precisamos implementar:

Formulário


O action do formulário deve apontar para o script que irá negociar com o agente servidor e precisa ter uma entrada texto para a identidade.

Esta entrada geralmente tem a identificação openid_login e seu formato CSS padrão pode ser encontrado aqui.

Normalização


O próximo passo já é executado no script.

Ele deve pegar a URL e normalizá-la.

Isto é feito assim:
  • =id não muda;
  • xri://=id vira =id;
  • xri://$dns*id.ex.com vira http://id.ex.com/;
  • xri://$ip*a.b.c.d vira http://a.b.c.d/;
  • http://id.ex.com/ e https://id.ex.com/ não muda;
  • id.ex.com vira http://id.ex.com/.


Na verdade estamos aqui neste artigo interessados apenas nas normalizações que geram URLs HTTP e HTTPS.

Associação


Na associação o consumidor acessa o agente servidor via HTTP.

O agente servidor é obtido de um desses marcadores tirados da consulta ao HTML da identidade:
  • <link rel="openid.server" href="http://example.com/server" />
  • <link rel="openid2.provider" href="http://example.com/server" />
  • <link rel="openid.server openid2.provider" href="http://example.com/server" />


Observação: para uma consulta HTTP em Lua você precisa da função socket.http.request() de LuaSocket.

Na associação envie ao agente servidor via GET os seguintes parâmetros:
  • openid.mode=associate
  • openid.assoc_type=HMAC-SHA1
  • openid.sesion_type=no-encryption


Quanto a criptografia, o ideal é usar DH-SHA1 (Diffie Hellman), mas, até que alguém faça um módulo de baixo nível para Lua de gerenciamento de chaves grandes, Lua é muito lenta para lidar com tais chaves e somos obrigados a trabalhar sem criptografia. =(

Feita esta consulta, o agente servidor deve retornar duas chaves no corpo:
  • assoc_handle – «maçaneta» (alguma tradução melhor?) de associação;
  • mac_key – chave de código de autenticação de mensagem.


Guarde essas informações!

Redirecionamento


Agora é preciso redirecionar o cliente para a página de login do agente servidor.

Faça isso contruindo uma URL apontando para o agente servidor e levando os seguintes parâmetros (via GET mesmo):
  • openid.ns – indica a versão de OpenID usada, para 2.0: http://specs.openid.net/auth/2.0
  • openid.mode=checkid_setup
  • openid.identity – a identidade do cliente
  • openid.claimed_id – novamente a identidade
  • openid.assoc_handle – a «maçaneta» de associação recebida
  • openid.return_to – a URL do script que irá tratar a resposta


Depois disso o cliente vai negociar diretamente com o agente servidor.

Recebendo de volta


No final o cliente será redirecionado de volta para o consumidor, mais especificamente para a URL informada por openid.return_to.

Esse script receberá parâmetros POST, sendo o principal openid.mode, que, se for id_res, o cliente está autenticado e autorizou o consumidor.

Outros parâmetros


Todos os parâmetros podem ser encontrados na especificação do protocolo.

TODO


Este artigo aborda o básico do básico da atenticação.

Com ele ainda não é possível recolher dados de SRE (informações de perfil), que deveriam ser passados nos subparâmetros de openid.sreg.

Mas para tanto é preciso usar criptografia – pelo que entendi, posso estar errado.

De qualquer forma, aqui está uma boa explicação do funcionamento do protocolo OpenID.

[]'s
Cacilhas

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 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