Arquivo de Setembro de 2007
postmodern-utils-0.0.1
Fiz um pacote que é um conjunto de funções e macros para facilitar o uso do Postmodern.
Download: http://www.lucindo.com.br/lisp/postmodern-utils.tar.gz
É possível instalar com ASDF-Install:
* (require :asdf-install) * (asdf-install:install "http://lucindo.com.br/lisp/postmodern-utils.tar.gz")
Com ele é possível fazer o seguinte:
(eval-when (:execute) (setf postmodern-utils:*db-name* “db-name” postmodern-utils:*db-user* “username” postmodern-utils:*db-pass* “password” postmodern-utils:*db-host* “server”))
Depois disso você pode usar a macro with-pooled-connection para todo código que precise acessar o banco, como por exemplo:
(defmacro update-dao-attribute (type id accessor-fn new-value) (let ((dao (gensym))) `(with-pooled-connection (with-transaction () (let ((,dao (get-dao type id))) (progn (setf (,accessor-fn ,dao) ,new-value) (save-dao ,dao)))))))
Nesse pacote ainda temos a função create-table-if-not-exists, a macro with-new-connection. A função que motivou o desenvolvimento dessa lib foi select-daos. Ela funciona como a select-dao do Postmodern, só que aceita alguns parâmetros (keyword) a mais:
(with-new-connection (select-daos 'entity :test '(:> attr1 100))
Mais exemplos:
(select-daos 'entity) (select-daos 'entity :limit 20) (select-daos 'entity :limit 20 :offset 40) (select-daos 'entity :order-by 'attr2) (select-daos 'entity :order-by '(:desc attr2)) (select-daos 'entity :test '(:> attr1 100) :order-by '(:desc attr2) :limit 100 :offset 200)Sem comentários »
Ajax simples
Warning: Isso deve ser muito manjado, eu que nunca tinha visto. Estou escrevendo só para não esquecer
Imagine o seguinte caso de uma aplicação web: o usuário executa uma ação e o servidor apenas precisa ser notificado, todos os dados necessários para uma eventual alteração da tela já estão presentes. É o caso por exemplo de marcar um item (star-it), votar em um sistema de ratings, adicionar uma tag a um item, e por aí vai.
Podemos fazer da seguinte maneira: um link que o usuário vai clicar para executar uma ação:
<a id=’a_theid’ onclick=“return ajax(this)” href=“/action?user=x&a=y”>
Nesse link, no campo id colocamos uma codificação simples de um eventual objeto a ser alterado, nesse caso usamos a_theid, onde deve um existir um objeto na página com o id theid que poderá ser alterado. A URL que fará a notificação da execução no server esta no href. Dessa forma podemos implementar a função JavaScript ajax assim:
function ajax(node) { var v = node.id.split(/_/); // a_theid var item = v[1]; // theid // Executa a alteração de tela document.getElementById(item).innerHTML = … // notifica o servidor var action = new Image(); action.src = node.href; return false; }
O browser carrega imagens de forma assíncrona, então essa função não bloca, a alteração na tela é feita na hora e o server é notificado. A vantagem é que isso funciona em qualquer browser, mesmo alguns bem velhos.
Bom, a desvantagem é que você não sabe se o request falhou. Mas isso pode acontecer em poucos casos como:
- O servidor está fora do ar: assim o usuário não conseguirá continuar navegando no sistema por muito tempo sem perceber isso, e o número de usuários que acessou o sistema antes dele cair e tem como executar umas ações que vão falhar (sem ele saber) é reduzido. E nessa caso o que você ia fazer? Cuspir um
alertna cara do usuário com “Ooops, We did it again!“? - Existe um problema de rede entre a máquina do usuário e o servidor: isso recai no problema acima, tem o mesmo efeito.
- O servidor está se comportando de maneira inesperada, gerando erros: aí amigo, o AJAX é o menor dos seus problemas.
Bom, eu vi isso no código do YCombinator News (Hacker News). Dá uma olhada lá para ver isso funcionando. Já o Digg e o Reddit usam Prototype. Alias, o Reddit poderia usar isso, pois eles não passam uma função de callback no caso de erro do Ajax.Request. Já o Digg passa uma função com um alert “ERROR: …”
Common Lisp e PostgreSQL
Brincando um pouco com Postmodern:
(eval-when (:compile-toplevel :load-toplevel :execute) (require :postmodern)) (defpackage :pg-test (:use :cl :postmodern)) (in-package :pg-test) (defparameter *db-name* “lucindo”) (defparameter *db-user* “lucindo”) (defparameter *db-pass* “”) (defparameter *db-host* “localhost”) ;; global connection (eval-when (:execute) (connect-toplevel *db-name* *db-user* *db-pass* *db-host*)) ;; defines a table (and a CLOS class) (deftable user () ((id :type integer :initarg :id :accessor id) (name :type (or string db-null) :initarg :name :initform :null :accessor name) (points :type (numeric 10 3) :initarg :points :accessor points)) (:indices id points) (:class-name user)) ;; creates the table if not exists (defun setup () (when (not (table-exists-p :user)) (create-table ‘user))) (defun create-update-user (id name &optional (points 0.0)) (save-dao (make-instance ‘user :id id :name name :points points))) (defun get-user-by-id (id) (get-dao ‘user id))
Se você precisar escrever SQL pode usar o mapeamento para expressões Lisp, como:
(:select ‘relname :from ‘pg-catalog.pg-class :inner-join ‘pg-catalog.pg-namespace :on (:= ‘relnamespace ‘pg-namespace.oid) :where (:and (:= ‘relkind “r”) (:not-in ‘nspname (:set “pg_catalog” “pg_toast”)) (:pg-catalog.pg-table-is-visible ‘pg-class.oid)))
A lib é bem completa, com connection pools, prepared statements, … e 100% Common Lisp (você não precisa ter nada do PostgreSQL instalado na máquina cliente).
Update: deftable não tem suporte a foreign keys
Common Lisp e AJAX
E não é que funciona!
Usei:
(eval-when (:compile-toplevel :load-toplevel :execute) (require :hunchentoot) (require :cl-who) (require :ht-ajax)) (defpackage :ajax-test (:use :cl :hunchentoot :cl-who) (:export #:start-web #:stop-web)) (in-package :ajax-test) ;; local directory with lokris.js (defparameter */static-local-dir* “/Users/lucindo/Documents/Lisp/tmp/”) ;; can be any url (defparameter *ajax-handler-url* “/ajax”) ;; using lokris (very small) (defparameter *ajax-processor* (ht-ajax:make-ajax-processor :type :lokris :server-uri *ajax-handler-url* :js-file-uris “/static/lokris.js”)) ;; hunchentoot handlers setup (eval-when (:compile-toplevel :load-toplevel :execute) (setf *dispatch-table* (list ‘dispatch-easy-handlers (create-folder-dispatcher-and-handler “/static/” */static-local-dir* “text/plain”) (create-prefix-dispatcher *ajax-handler-url* (ht-ajax:get-handler *ajax-processor*)) (create-prefix-dispatcher “/js” ‘java-script) (create-prefix-dispatcher “/” ‘main-page)))) (defparameter *web-server* nil) (defun start-web (&optional (port 4242)) (setf *web-server* (start-server :port port))) (defun stop-web () (stop-server *web-server*)) ;; eval and print a lisp expression (defun testfunc (command) (prin1-to-string (eval (read-from-string command nil)))) (ht-ajax:export-func *ajax-processor* ‘testfunc :method :post) (defun java-script () “function command_clicked(txt) { var command = document.getElementById(’command’).value; ajax_testfunc_set_element(’result’, command); }”) (defun main-page () (with-html-output-to-string (*standard-output* nil :prologue t) (:html (:head (:script :type “text/javascript” :src “/js”) (:title “AJAX test”)) (fmt “~a” (ht-ajax:generate-prologue *ajax-processor*)) (:body (:h1 “AJAX test”) (:table :width “50%” (:tr (:td :colspan “2″ (:span :id “result” (:i “no results yet”)))) (:tr (:td :width “70%” (:input :type “text” :size “70″ :name “command” :id “command”)) (:td (:input :type “button” :value “Eval” :onclick “javascript:command_clicked();”))))))))
Source: ajax-test.lisp (nele mudei para usar Prototype. O legal é o que o HT-AJAX suporta vários processadores ajax: Lokris, Prototype, Dojo, Yahoo! [YUI]).
Se você também não gosta de HTML junto com código pode usar HTML-TEMPLATE. Se você não gosta mesmo de HTML pode usar o Weblocks em que o design fica em CSS e o resto é codigo Lisp (o framework gera todo o HTML e JavaScript para AJAX).
Quase tudo tendo como base Ediware.
2 comentários »