terça-feira, 13 de maio de 2008

Programação para Web com Chicken e Ajax

Chicken possui uma extensão interessante para desenvolvimento para a Web: o egg ajax. Essa extensão é, na realidade, um wrapper para a biblioteca prototype.

O uso do egg ajax é bem fácil depois que se entende como ele funciona. Confesso que quebrei um pouco a cabeça para começar a usá-lo, e a dificuldade foi agravada pelo fato de eu não conhecer muito bem a biblioteca prototype na época.

Para facilitar o entendimento do egg ajax, vale a pena mencionar um recurso interessante do egg http: a possibilidade de definição de recursos (resources). Com isso, é possível associar uma URL a um procedimento que será executado no servidor. Esta colocação, embora pareça "solta" agora, é necessária para entender como o egg ajax funciona (espero que ela venha a fazer sentido até o final deste texto...).

Uma das funções mais práticas do egg ajax é a remote-link. Com ela, é possível realizar várias operações no cliente com base no resultado do procedimento executado no servidor. A seguir está um exemplo de aplicação básica de remote-link:

(remote-link
"Clique aqui"
(lambda ()
(print "Oi"))
update: "resultado")

remote-link recebe três argumentos:

  • O texto do link

  • Uma função sem argumentos a ser executada no servidor

  • O tipo de ação a ser executada no cliente e em qual elemento da página aplicar o resultado da função executada no servidor. O tipo de ação a ser executada é determinado pelo parâmetro de palavra-chave escolhido (update no exemplo) e o elemento a aplicar o resultado é a string associada ao parâmetro de palavra-chave (resultado no exemplo).


O exemplo com remote-link, então, gera os códigos HTML e Javascript necessários para atualizar o elemento de identificador resultado com o texto Oi quando o usuário clicar no link Clique aqui. O código gerado é mostrado abaixo:

<a href='#' onclick="new Ajax.Updater('resultado', '/9fa328569dc39078f13e29086bbf3b93', { });">Clique aqui</a>

O código Javascript gerado corresponde a criação de um objeto Updater da bilioteca prototype, cujos argumentos são o identificador do elemento a ser atualizado no cliente depois da execução do procedimento associado à URL usada como segundo argumento (/9fa328569dc39078f13e29086bbf3b93). Essa URL é gerada pelo egg ajax, usando a funcionalidade de recursos do egg http, mencionada no início deste texto. A URL fica associada à função usada como argumento para remote-link.

Vamos ver um exemplo prático com o servidor web Spiffy. São necessários os seguintes eggs:


Os eggs podem ser instalados da seguinte forma:

$ chicken-setup spiffy web-scheme ajax spiffy-utils

Todas as dependências serão instaladas automaticamente.

O programa para execução do servidor web está abaixo (web-server.scm):

(use spiffy web-scheme web-scheme-handler ajax spiffy-utils)

(spiffy-file-ext-handlers `(("ws" . ,web-scheme-handler)))
(spiffy-tcp-port 8080)
(spiffy-root-path ".")
(spiffy-debug-mode #t)
(start-server)

Para usar o egg ajax, é necessária a biblioteca prototype, que acompanha a extensão (o arquivo encontra-se em CHICKEN_PREFIX/lib/chicken/3/prototype.js, onde CHICKEN_PREFIX é o diretório onde o sistema Chicken foi instalado). Copie o arquivo prototype.js para o mesmo diretório onde foi criado o arquivo web-server.scm.

O arquivo para gerar a página HTML (index.ws) é mostrado abaixo:

(ws:page
(string-append
(div 'id "resultado")
(remote-link
"Clique aqui"
(lambda ()
(print "Oi"))
update: "resultado"))
additional-headers: (ajax))

Para ver o resultado, basta executar o servidor web:

$ csi -s web-server.scm

e acessar http://localhost:8080 no seu navegador preferido (algum que tenha suporte a Javascript).

A função ajax usada no exemplo gera o código HTML necessário para referenciar a biblioteca prototype na página.

Obviamente, é possível fazer algo de mais útil com isso. A seguir é mostrado um exemplo mais elaborado (tamanho.ws): uma aplicação que mostra em uma caixa do tipo select os arquivos no diretório raíz do servidor e exibe o tamanho quando são selecionados.

(use posix)

(ws:page
(string-append
(ws:select
'id "file-box"
'onchange (remote-action
(lambda ()
(let ((file (post-var "file")))
(print "Tamanho de " file " = "
(file-size file) " bytes")))
update: "resultado"
arguments: "'file=' + $F('file-box')")
(string-intersperse
(map (lambda (opt)
(option 'value opt opt))
(glob (make-pathname
(spiffy-root-path) "*")))
""))
(div 'id "resultado"))
additional-headers: (ajax))

O código acima monta uma caixa de opções (select de HTML) com os arquivos no diretório raíz do servidor (parâmetro (spiffy-root-path)) e usa remote-action para registrar a função que é executada no lado do servidor e executá-la quando um arquivo for selecionado na caixa de seleção.

O parâmetro de palavra-chave arguments especifica as variáveis a serem passadas através dos métodos GET e POST do HTTP. Se nenhum método for especificado, POST é usado (o parâmetro de palavra-chave method permite especificar o método a ser usado). A função $F em Javascript, da biblioteca prototype, retorna o conteúdo do widget cujo identificador é passado como argumento.

Para ver a aplicação em funcionamento, acesse http://localhost:8080/tamanho.ws no seu navegador

Mais detalhes sobre o funcionamento do egg ajax podem ser obtidos na documentação da extensão, em http://www.call-with-current-continuation.org/eggs/3/ajax.html.

Nenhum comentário: