Publicidad:
La Coctelera

un rato de sol

i will work harder, ja ja ja, no ahora en serio.

4 Julio 2009

RESTful JSONP in Rails using Rack middleware

Rails 2.3 has finally included support for Rack. The Rack project (http://rack.rubyforge.org/) consists of an abstraction layer on top of the webserver that provides a common set of services for developers while hiding the complexity of the web server software whatever it should be (mongrel, glassfish, thin, etc).

The support for Rack is a major accomplishment of the Rail Core Team and opens a lot of interesting new capabilities for the framework users.

In this post entry I will comment a possible use of Rack to provide support for RESTful-like requests for JSONP in Rails.

As you surely know, current desktop browsers only give support for GET and POST HTTP requests. This is a serious difficulty for clients trying to follow REST design guidelines in browser clients, since PUT and DELETE methods are needed to carry request to the server asking for the update and deletion of a resource.

Rails address this problem allowing to pass a '_method' parameter in POST requests that overwrites with its value the POST method of the request. In this way, PUT and DELETE requests can be sent from a HTML form or from an AJAX request.

But this feature only solves half of the problem. Web browsers implement a security model known as the Same Origin Policy (http://en.wikipedia.org/wiki/Same_origin_policy), where only requests to the same domain of the current URL are allowed. This policy effectively forbids cross domain AJAX requests. At least until a common specification for cross domain XMLHTTPRequests can be agreeded and implemented (http://dev.w3.org/2006/webapi/XMLHttpRequest-2/).

One work-around for this problem has been found in the use of JSONP (http://bob.pythonmac.org/archives/2005/12/05/remote-json-jsonp/) a technique where a new SCRIPT tag is inserted into the web page DOM with a callback parameter. The server answers with a script where the callback parameter value is used as a function name that is invoked with the data from the server passed as the actual parameter invokation value.

JSONP, though nothing more than a hack, is been used by many web pages and services to provide data to javascript clients and mashups. A big problem with JSONP appears when you try to use JSONP in a RESTful way. Including the SCRIPT tag in the page initiates a new GET request to the server, so if you wants to use JSONP to send a request for creating a new resource (POST), updating it (PUT), or deleting it (DELETE), you must use some work-around like the use of the _method parameter in Rails. But the overwriting of the HTTP method in Rails only works with POST request: no RESTful JSONP for you my friend.

Of course, you can always try to modify the behaviour of the method override mechanism of Rails with a little bit of monkey patching. Solutions in these direction has been proposed (http://www.actsasflinn.com/2008/06/13/cross-domain-restful-json-p-with-rails) but they are very likely to break with each new Rails release.

With the support for Rack in Rails 2.3 the situation has changed and a cleaner way to change the behavior of MethodOverride in Rails can be built.

Now, MethodOverride is a class of the Rack Rails middleware stack (http://rack.rubyforge.org/doc/classes/Rack/MethodOverride.html). Rack Rails allow developers to insert new middleware classes in the stack (http://guides.rubyonrails.org/rails_on_rack.html), so you can easily add a new middleware before MethodOverride that allows the use of _method parameters with GET requests, without the need of monkey patching:

 module SemanticResource
   
   # Allows overwriting of HTTP method also in
   # GET HTTP requests in order to allow
   # RESTful JSONP calls
   class RestfulJsonpMiddleware
   
     HTTP_METHODS = %w(GET HEAD PUT POST DELETE OPTIONS)
     METHOD_OVERRIDE_PARAM_KEY = "_method".freeze
     HTTP_METHOD_OVERRIDE_HEADER = "HTTP_X_HTTP_METHOD_OVERRIDE".freeze
     
     attr_accessor :method_parameter_name
     
     def initialize(app)
       @app = app
       @method_parameter_name = METHOD_OVERRIDE_PARAM_KEY
     end
 
     # We check if the method parameter is in the request
     # and set up the request to allow the execution of the
     # overwritten HTTP method
     def call(env)
       req = ActionController::Request.new(env)
       method = req.params[@method_parameter_name]
       method = method.to_s.upcase
       if HTTP_METHODS.include?(method)
         env["rack.methodoverride.original_method"] = env["REQUEST_METHOD"]
         env["REQUEST_METHOD"] = "POST"
         env[HTTP_METHOD_OVERRIDE_HEADER] = method
       end
      @app.call(env)
     end
   end
 end
 

And now we can add the middleware in config.rb

 config.middleware.insert_before(Rack::MethodOverride,SemanticResource::RestfulJsonpMiddleware)

That's it, cross domain RESTful AJAX for Rails in a little bit cleaner way! You must take some time to consider the security implications of this change in your application though.

servido por Antonio sin comentarios compártelo

11 Marzo 2009

π calculus

A la hora de afrontar cualquier problema lo suficientemente complicado, uno de los principales problemas a los que nos enfrentamos es el de conseguir pensar con suficiente claridad acerca del mismo. Esto implica caracterizarlo de forma adecuada, eliminando todos los elementos superfluos o subproblemas no esenciales, lo que supone casi siempre, siguiendo el ejemplo de uno mis héroes intelectuales (http://en.wikipedia.org/wiki/Wittgenstein), conseguir un lenguaje que nos permita razonar de forma exacta sobre dicho problema.

Ese lenguaje siempre es el de las matemáticas y cuando el problema tiene que ver con la computación, toma forma de cálculo, no como el cálculo diferencial, sino en la acepción que se usa en lógica: la de un sistema formal para la manipulación de expresiones simbólicas. El 'calculus' más relevante para la computación y el más conocido es el cálculo lambda de Alonzo Church y compañía, que supone la base de cualquier lenguaje de programación funcional, pero otros muchos calculi han sido construidos para el estudio de problemas concretos.

El Cálculo Pi, es uno de ellos. Pretende faciliar el estudio de procesos paralelos de computación que evolucionan dinámicamente. A pesar de lo ambicioso del objetivo, los componentes del cálculo π no podían ser más simples:

Un proceso se identifica por una letra mayúscula:

P, Q, R...

Cuando los procesos se ejecutan en paralelo el proceso se identifican por el símbolo '|':

P | Q | R

Así se expresarían tres procesos que se ejecutan paralelamente. Pero lo interesante viene cuando los procesos empiezan a comunicarse entre sí:

P = <y>x.P'

Expresa que el proceso P envía el nombre 'x' por el canal 'y' y sigue comportandose como P'.  Por el contrario:

P = y(x).P'

Indica que el proceso P espera recibir el nombre 'x' por el canal 'y' y a continuación se sigue comportando como P'. A través de los canales se pueden enlazar los procesos, por ejemplo:

<y>x.P | y(z).Q

En este ejemplo el proceso P envía por el canal 'y' el nombre 'x', el proceso Q recibe el nombre por el canal 'y' y en el contexto de Q renombra 'z' con el nombre 'x' enviado {x/z}. Si expandimos la expresión:

<y>x.P | y(z).Q -> P|Q{x/z}

Tendríamos el proceso P ejecutándose en paralelo con el proceso Q donde el nombre 'z' pasa a valer 'x'.

El calculo tiene sólo unos cuantos símbolos más: P! sería un proceso recursivo de tal forma que P! = P! | P, y una forma de expresar que un canal es privado a dos procesos.

La aportación seminal del Cálculo Pi como cálculo de procesos, es la brillante idea de tratar tanto canales como símbolos de la misma manera, como nombres que se pueden enviar por otros canales con lo que se puede "enviar el proceso" pasando canales de comunicación, si hay colisión de nombres se hace una alfa-conversión como en el cálculo lambda:

P | Q | R

<x1>y1.y1(z1) | x1(y2).<z2>y2  |  z2(x3).<x3>"hola"

Si computamos este sistema:

y1(z1) | {y1/y2}.<z2>y2 | z2(x3).<x3>"hola"

y1(z1) |  <z2>y1 | z2(x3).<x3>"hola"

y1(z1) |  0  |  {y1/x3}.<x3>"hola"

y1(z1) | 0 | <y1>"hola"

{"hola"/z1} | 0 | 0

"hola" | 0 | 0

0 | 0 |0

Podemos observar como el proceso P le ha pasado un enlace hacia si mismo a R a través de Q y R luego le ha enviado la cadena "hola" a través del enlace enviado.

Lo atractivo del Cálculo Pi es que su álgebra asociada nos permite tratar formalmente problemas de computación paralela y responder a preguntas como "¿se comportan dos sistemas de la misma manera?" lo que se conoce como el problema de bisimulación, Y problemas de computación paralela los hay muy interesantes, piénsese en el web y el envío de enlaces hacia servidores a través de documentos HTML.

servido por Antonio 1 comentario compártelo

4 Febrero 2009

processing javascript

Por esas cosas que tiene la vida me tengo que leer Visualizing Data de Ben Fry, uno de los popes del idem, o de la analítica visual, o de como como quiera que llamen a eso de intentar meter datos de 15 dimensiones en 400x400 pixels.

El caso es que el amigo Ben, se pasa el segundo capítulo de su libro hablando de un lenguaje de manipulación de gráficos llamado Processing, un lenguaje de scripting bastante apañado que hace muy sencillo hacer dibujitos y animaciones interactivas. Cuando digo que lo hace sencillo, me refiero a que intenta conseguir que diseñadores gráficos y "artistas multimedia" escriban código.

El caso es que jugar con el entorno de processing es bastante divertido y se consiguen resultados bastante vistosos con muy poco esfuerzo. Me recuerda un montón a la versión de LOGO con la que escribí mis primeras líneas en un ordenador (¡qué tiempos aquellos!).

La tortuga de LOGO en toda su gloria

Processing está escrito sobre Java. Esto es una ventaja pero también un inconveniente, sobre todo cuando quieres colgar una de tus animaciones en la web, ya que tienes que hacerlo vía applet. Generar el applet es muy sencillo con Processing, pero aún así un applet es, bueno, un applet.

Afortunadamente, recordé que el gran Javi Santana me envío en algún momento un mail hablándome de processing.js, un port de parte de la API de processing a javascript, escrito por John Resig, el autor de JQuery, al que tanto debemos todos los que hacemos programación web.

Processing.js funciona usando el objeto canvas que ya soportan algunos navegadores (mis pruebas las he hecho con Firefox 3.0.6) y funciona bastante bien. La API tiene dos formas de trabajar, puedes suministrarle código processing a un objeto javascript y un parser compuesto por un puñado de expresiones regulares lo transforma en código canvas javascript que luego evalúa, o se puede trabajar con un objeto javascript Processing, e ir invocando sobre él las funciones de la API de processing.
Este último modo resulta quizás más interesante, ya que permite en el repintado interaccionar con otros elementos HTML de la página. Como ejemplo os paso el código de una de las demos del sitio de processing reescrito con processing.js, con un pequeño extra que hace posible repintar el fondo del canvas usando los valores suministrados desde el HTML de la página.

Para hacerlo funcionar cambiad la ruta hasta processing.js del código:

 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
 <html> <head>
 <title></title>
 <script type="text/javascript"
 src="../processing/processing.js"></script>
 <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js" type="text/javascript"></script>
 <script type="text/javascript">
  window.onload = function(){
    var p = Processing("canvasElement");
    p.setup = function() {
       p.size(400,400);
       p.stroke(255);
       p.background(0,0,0);
    };
    p.draw = function() {
       p.background($("#r").val(),$("#g").val(),$("#b").val());
       p.line(150,25,p.mouseX,p.mouseY);
    }
 
    p.init();
 };
 </script>
 </head>
 
 <body>
 <h1>Canvas demo</h1>
 
 <p>
 <label for="r">red</label>
   <input type="text" id="r"></input>
 </p>
 <p>
 <label for="g">green</label>
   <input type="text" id="g"></input>
 </p>
 <p>
 <label for="b">blue</label>
   <input type="text" id="b"></input>
 
 </p>
 <br/>
 
 <canvas id="canvasElement" height="150" width="600"/>
 
 </body> </html>
 

Os recomiendo que le probéis tanto processing, como processing.js, seguro que pasáis un buen rato.

servido por Antonio 2 comentarios compártelo

28 Enero 2009

Haml en Clojure

Acabo de subir a Github clj-haml, un apaño para tener algo parecido a Haml (http://haml.hamptoncatlin.com/) para clojure.


Haml:

%div#things
     %span#rice Chicken Fried
     %p.beans{ :food => 'true' } The magical fruit
     %h1.class.otherclass#id La La La
 

clj-haml:

(h= :div#things
              (h= :span#rice "Chicken Fried")
              (h= :p.beans {:food "true"} "The magical fruit")
              (h= :h1.class.otherclass#id "La La La"))
 

El HTML generado en ambos casos:

<div id='things'>
   <span id='rice'>Chicken Fried</span>
   <p class='beans' food='true'>The magical fruit</p>
   <h1 id='id' class='class otherclass'>La La La</h1>
  </div>

Sólo hay una función:

(h= :selector attributes contents /)
 

Todos los parámetros son opcionales, :selector es un keyword que puede incluir el nombre de una etiqueta HTML seguido de un id y algunas clases especificadas siguiendo la notación CSS: :p#myid.class1.clas2, :.class1#myid, :br

Attributes es un mapa asociativo con pares clave valor para nombre de atributo y valor, por ejemplo: {:href "/test.html"}.

Contents son unos cuantos forms, potencialmente conteniendo otras llamadas (h=) que se evaluarán y cuyo valor devuelto se insertará en la etiqueta que se está generando.

Por último, el / final opcional, si presente, hace que la etiqueta generada sea con autocierre (h= :br /) -> <br/>, (h= :br) -> <br></br>.

Como en Haml vamos.

La podéis descargar aquí (http://github.com/antoniogarrote/clj-haml/tree/master).

Tags: haml, ruby, clojure

servido por Antonio sin comentarios compártelo

24 Enero 2009

El núcleo (mónadico) de un framework web clojure

En las últimas semanas, mi pequeña biblioteca clojure para grizzly ha ido creciendo y ya puede aceptar cualquier petición web y renderizar una respuesta.

Desde el punto de vista de cliente, el proceso es ahora mismo este:

Enrutamos una petición a una función que procesará la petición:

  (route! (url-pattern GET "greetings" :name)
             say-hello)
 

Con la anterior expresión estamos enrutando las peticiones "GET /greetings/PARAMETRO" a la función say-hello, que además recibirá el valor de PARAMETRO con el nombre :name.

Definamos ahora la función say-hello:

 (defn say-hello
   ([] (render (if (= (parameter :name) "Helena")
                        "Hola chica guapa!"
                        (str "Hola " (parameter :name)))
                    200
                    {:Content-Type "text/plain"})))
 

En esta función básicamente decimos que queremos renderizar un mensaje u otro en función del valor del parámetro :name de la URL.


Hasta aquí todo sencillo, pero lo interesante es que hay debajo de todo esto ¿qué hacen las llamadas a render y parameter? Bien, la respuesta es que toda la lógica de servicio de una petición se encuentra encerrado dentro de una mónada, la Mónada WebIO. Esta mónada se define como:

 ;;
 ;; WebIO X = WebIOUnfinished X |
 ;;           WebIOFinished X
 ;;           WebIOtFailed
 ;;
 
 (defstruct web-request-processing :environment ;; The Rack request
                                   :response ;; The response of the request
                                   :parameters) ;; the parameters hash
 

La petición, la respuesta y los parámetros se guardan en la mónada que puede tener tres estados pendiente, terminada o fallida que establecen el estado de la respuesta.

La función de binding para la mónada WebIO, define como se deben combinar funciones monádicas según el resultado de aplicar la petición contenida en la mónda:

 (defmethod >>= :WebIO [f m]
   "Instance of the >>= function for the WebIO monad. If the computation
    result of m is WebIO Unfinished, the next function f is applied to the
    content of the m. If the result of m is WebIO Failed, next function
    f is not applied and m is returned. If the result of m is WebIO
    Finished, f is not invoked"
   (if (or (web-io-failed? m) (web-io-finished? m))
     m
     (f (:content m))))
 

Para poder realizar el procesamiento de la petición sólo se necesitan dos cosas una función que transforme una petición web, a través de la interfaz Rack que usamos, en una mónada WebIO y algún método para transformar una función normal como la anterior usada como ejemplo (say-hello), que no sabe nada sobre mónada alguna en una función monádica capaz de ser procesada por el operador de binding (>>=).

De lo primero se encarga la función (wrap-request) que usa la función general monádica (return) para envolver una petición Rack, además de protegerla con una referencia para poder ser usada facilmente mediante el mecanismo de memoria transaccional software (STM) que implementa Clojure:

 (defn wrap-request
   "Wrapper around return that builds the new request monad initializing it
    with the values of the rack-request"
   {:monad :WebIO}
   ([rack-request rack-response parameters subtype]
      (return :WebIO subtype
            (struct web-request-processing
                    rack-request
                    (if (= (class rack-response) clojure.lang.Ref)
                      rack-response
                      (ref rack-response))
                    parameters)))
   ([rack-request]
     (wrap-request rack-request (create-rack-response) {} :Unfinished))
   ([rack-request rack-response]
     (wrap-request rack-request rack-response {} :Unfinished))
   ([rack-request rack-response parameters]
     (wrap-request rack-request rack-response parameters :Unfinished)))
 

El segúndo requisito, transformar una función normal en una función monádica, lo realiza la función (with-web-io) y se conoce normalmente como "lifting". En este caso además, define unas variables accesibles sólo por el hilo que ejecuta esa función, para la petición, respuesta y parámetros, de forma que no sea necesario para el programador que implementa la función con la lógica de procesamiento, tener que pasar explícitamente estos parámetros a funciones como (parameter) o (render):

 (defn with-web-io
   "Lifts a call to a function for a web-io-processing struct into the web-io monad"
   {:monad :WebIO}
   ([f web-io-processing]
      (try
       (let [result (binding [*request* (:environment web-io-processing)
                              *response* (:response web-io-processing)
                              *parameters* (:parameters web-io-processing)]
                      (apply f []))]
         (if (web-io-monad? result)
           result
           (wrap-request (:environment web-io-processing)
                         (:response web-io-processing)
                         (:parameters web-io-processing)
                         :Unfinished)))
       (catch Exception ex (raise (:environment web-io-processing)
                                  500
                                  (. ex getMessage))))))
 

Sobre estas funciones, el mecanismo que sirve una petición resulta trivial, el proceso cuenta con los siguientes pasos:

  • Recibir la petición Rack
  • Extraer la ruta y método de la petición y comprobar si se puede enrutar a alguna función de respuesta
  • Transformar la petición Rack en una mónada WebIO
  • Crear una colección con los filtros anteriores, la función de respuesta, y los filtros posteriores a los que se va a enrutar la petición
  • Hacer un lift de cada una de estas funciones a funciones monádicas a través de (with-web-io)
  • Ejecutar la secuencia de funciones monádicas con el operador de binding (>>=)
  • Devolver el resultado a través de Rack

servido por Antonio 1 comentario compártelo

12 Enero 2009

Programación concurrente en Clojure: agentes

Clojure es un lenguaje de programación pensado por y para la concurrencia. Las características funcionales del lenguaje, como la inmutabilidad de sus estructuras de datos, contribuyen a que mantener varios hilos de ejecución sea sencillo, cada uno con su propia copia inmutable de los datos, ya que de esta manera, es imposible que un hilo modifique el estado de la computación de otro hilo.

Sin embargo, hay veces que es necesario compartir el estado entre varios hilos o, dado que Clojure se ejecuta sobre la máquina virtual de Java y es posible acceder a todos los objetos y clases de Java, trabajamos con estructuras de datos no inmutables.

Clojure ofrece diferentes formas para tratar estas situaciones automáticamente, de forma que se eliminen los problemas de concurrencia que aparecen en estas circunstancias, los agentes son una de ellas.

Un agente es una abstracción que mantiene el estado de una computación, actualizándolo mediante la ejecución de forma asíncrona de funciones sobre ese estado que son enviadas por otros hilos mediante la función (send). Estas funciones se van almacenando en una estructura FIFO y van siendo aplicadas secuencialmente por el agente en su propio pool de hilos.
Si habéis trabajado con Erlang, podéis pensar en los agentes Clojure como una versión recortada de los servidores Erlang.

Un agente clojure se declara con la función (agent):

 (agent initial-state)
 

Donde initial-state puede ser cualquier estructura de datos. Por ejemplo, vamos a construir un logger asíncrono y thread-safe. Nuestro logger mantendrá pues un nivel de logging y un sitio donde escribir el log, por defecto le daremos el nivel "1" y la salida estándar:

(def *logger* (agent {:level 1 :output *out*}))
 

Para loguear los mensajes, necesitamos una función (log), que recibirá un nivel de logging y algo que loguear, como usar números para los niveles de log queda algo feo, definamos una función que transforma claves para los niveles de log en enteros:

 (defn level-name-to-int
   "Returns a numeric identifier for the levels of the logger"
   ([level]
      (let [levels {:debug 0
                    :info 1
                    :warning 2
                    :error 3
                    :critical 4}]
        (let [to-return (level levels)]
          (if (nil? to-return) 6 to-return)))))
 

Ahora que tenemos niveles de log, necesitamos alguna forma de cambiar el nivel de logging actual almacenado en el agente del logger. Como ya mencionamos, para cambiar el estado de un agente, necesitamos una función que será enviada al agente para que la ejecute en su pool de hilos sobre el estado actual almacenado. La función de actualización debe recibir como primer argumento el estado del agente, una serie de parámetros opcionales y devolver un estado del agente modificado. Usando notación Haskell:

 updateFunction :: AgentState -> [Args] -> AgentState
 

En nuestro caso, nuestra función para modificar el nivel de logging del agente puede ser:

 (defn reset-logger-level
   "Initializes the logger with a given level"
   ([level]
      (send *logger* (fn [a l] {:level (level-name-to-int l) :output (:output a)}) level)))
 

Como se puede ver, enviamos al agente *logger* una función lambda, donde se transforma la estructura de dats almacenada en el agente, sustituyendo el viejo nivel de logeo con el suministrado en el argumento "level".

Ahora, necesitamos la función de logueo. Crearemos dos versiones, una que recibe un nivel y el código a loguear, y si el nivel del agente logger es adecuado loguea el resultado del código pasado a la función de log y lo devuelve tal cual. La segunda, recibirá además una descripción que logueará junto al resultado de ejecutar el códigos suministrado como argumento:

 (defn log
   "logs something in the logger with the provided level of log"
   ([level to-log]
      (do
        (send *logger* (fn [a level msg]
                         (do
                           (when (<= (:level a)
                                     (level-name-to-int level))
                             (let [to-write (str (. (keyword-to-string level) toUpperCase) " "
                                                 (. (new java.util.Date) toString) " => "(str msg) "\n")
                                   writer (:output a)]
                               (do
                                 (. writer (write to-write 0 (. to-write length)))
                                 (. writer flush))))
                           a))
              level to-log)
        to-log))
   ([level desc to-log]
      (do
        (send *logger* (fn [a level msg]
                         (do
                           (when (<= (:level a)
                                     (level-name-to-int level))
                             (let [to-write (str (. (keyword-to-string level) toUpperCase) " "
                                                 (. (new java.util.Date) toString) " => " desc " " (str msg) "\n")
                                   writer (:output a)]
                               (do
                                 (. writer (write to-write 0 (. to-write length)))
                                 (. writer flush))))
                           a))
              level to-log)
        to-log)))
 

En este caso, como en el anterior, enviamos una función lambda al agente con send para que haga el trabajo, esta función loguea, el resultado de la ejecución comprobando el nivel de log y añadiendo alguna información extra como la hora.

Es interesante notar que la función log no es bloqueante, en cuanto se ha enviado la función lambda al agente, retorna y la ejecución de la computación continúa, se haya logueado el mensaje o no, además como la ejecución de funciones en el pool del agente es FIFO, los mensajes se van a loguear en el orden de ejecución, sin que se "cuelen" mensajes en el orden de ejecución (la función send es atómica).

Aquí se puede comprobar como funciona en ejecución nuestro flamante logger:

 user=> (reset-logger-level :debug)
 (reset-logger-level :debug)
 #=(clojure.lang.Agent. "clojure.lang.Agent@219009")
 
 user=> (log :info "hola?" (+ 1 2))
 (log :info "hola?" (+ 1 2))
 3
 
 user=> INFO Mon Jan 12 00:25:54 CET 2009 => hola? 3
 (reset-logger-level :error)
 (reset-logger-level :error)
 #=(clojure.lang.Agent. "clojure.lang.Agent@219009")
 
 user=> (log :info "ahora no... " (* 3 3))
 (log :info "ahora no... " (* 3 3))
 9
 user=>  
 

Como se puede ver, se puede meter llamadas a log en medio de la ejecución del código ya que es transparente para una llamada a función, además se puede comprobarla ejecución asíncrona del logger, comprobando como se cuela el logueo de la llamada para (log :info "hola?" (+ 1 2)) antes de que la shell de clojure muestre el cursor para la siguiente entrada de datos por parte del usuario.

Para terminar, vamos a hacer que en vez de loguear en la salida estándar, mande el log a un fichero. Como ya hemos dicho, Clojure es 100% java, y la variable especial de salida estándar *out*, no es más que un objeto java.io.OutputStreamWriter, así que vamos a hacer un par de funciones, una para cambiar el destino de logueo almacenado en el estado del agente, y otro para poder iniciarlo con un fichero de salida:

 (defn reset-logger
   "Initializes the logger with a given level and output writer"
   ([level writer]
      (send *logger* (fn [a l w] {:level (level-name-to-int l) :output w}) level writer)))
 
 (defn reset-logger-with-file-output
   "Resets the logger setting the output to the file whose path is provided as an argument
    with the level specified"
   ([level file-path]
      (reset-logger level (new FileWriter (new File file-path) true))))
 

Ya tenemos terminado nuestro logger, de ultimísima tecnología, con el que no tendremos problemas como el que te puedes encontrar en Merb, cuando el tiempo que tarda en mostrar el log de tu aplicación web acaba provocando un timeout por parte del cliente. ;)

Por último, queda comentar sobre los agentes que les ocurre cuando la función de actualización lanza una excepción: intentemos loguear en null, algo que seguro no es una buena idea:

 (send *logger* (fn [_] {:output nil :level 1}))
 (send *logger* (fn [_] {:output nil :level 1}))
 #=(clojure.lang.Agent. "clojure.lang.Agent@219009")
 user=> 
 (log :error "????")
 "????"
 

El mensaje no ha sido loguead, como esperábamos, pero tampoco parece que haya explotado nada, intentemos loguear algo más

 user=> (log :error "are you alive?")
  (log :error "are you alive?")
  java.lang.Exception: Agent has errors (NO_SOURCE_FILE:0)
  user=> 
 

Vaya, parece que nuestro agente se ha quedado en un estado de error. Este es el comportamiento de los agentes Clojure ante los errores, se quedan suspendidos hasta que reseteemos el estado del mismo con la función (clear-agent-errors)

 user=> (clear-agent-errors *logger*)
 (clear-agent-errors *logger*)
 nil
 

¡Listo para usar otra vez!

El código, como siempre, lo podéis descargar de Github

servido por Antonio sin comentarios compártelo

8 Enero 2009

Creando individuos en la ABox: validaciones en una semántica de mundo abierto

Más cosas que voy implementando en mi biblioteca OWL para Clojure.

En otro post, mostré como se podía definir la TBox, con un procedimiento similar al de usar migraciones para definir el esquema de una base de datos relacional. Ahora vamos mostrar lo que sería el equivalente ontológico a insertar filas en las tablas del esquema relacional, es decir a insertar aserciones en la ABox.

Creamos una TBox de prueba:

 (describe-tbox
   (describe-owl-class :test "Person")
   (describe-owl-datatype-property :test "name" (xsd-string))
   (describe-owl-datatype-property :test "age" (xsd-decimal))
   (describe-owl-class-has-property :test "Person" :test "name")
   (describe-owl-class-has-property :test "Person" :test "age"))
 

Una vez guardada la TBox en un repositorio e iniciada (con alias :test-Person, :test-name y :test-age para los recursos), podremos crear individuos en la ABox que sean tipos de las clases OWL contenidas en la TBox, para ello usamos la función (abox-create-individual)

 (abox-create-individual :test-Person
                         "http://test.com/abox/people#"
                         { :test-name "Helena",
                           :test-age 24 })
 

Esta función crea un nuevo individuo con URI "http://test.com/abox/people#IDENTIFIER", donde IDENTIFIER es un identificador universal único (UUID) que se genera con la llamada a la función. La función inserta los tripletes RDF necesarios para asertar que el individuo recién creado es del tipo :test-Person, así como de todas sus superclases (en este caso owl:Thing). Además da valores a las propiedaded test:name y test:age.

Los tripletes RDF equivalentes serían (con notación Turtle):

@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
 @prefix test: <http://test.com/tbox/people#> .
 @prefix owl: <http://www.w3.org/2002/07/owl#> .
 @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
 
 <"http://test.com/abox/people#IDENTIFIER"> rdf:type test:Person ;
                                            rdf:type owl:Thing ; 
                                            test:age "24"^^xsd:decimal ;
                                            test:name "Helena"^^xsd:string .
 

A la hora de crear individuos en la ABox, hay que tener en cuenta que se pueden crear individuos con valores sólo para algunas propiedades, o con propiedades no definidas en la clase OWL o ni siquiera en la TBox. Una de las características de las lógicas descriptivas, la base formal de OWL, es que tienen una semántica denominada de "mundo abierto" (ver el "Cambridge Handbook of DL"). Siguiendo una semántica "open world", si una información no se ha asertado en la ontología, no quiere decir que no exista, sino que todavía no se conoce. Lo mismo sucede para la información extra. Es interesante comparar esta semántica con una semántica de "mundo cerrado", como la que siguen las bases de datos relacionales, donde si un dato no está en la tabla, significa un error, no desconocimiento. Esta situación se agrava cuando contamos con pocos constructores OWL y no hay soporte para un razonador, si no que se trabaja solo con las aserciones explícitas de la ontología.

Una forma de poner restricciones a lo que insertamos en la ABox, sin recurrir a hacer un test de consistencia con un reasoner tras realizar las aserciones, es permitir añadir validaciones a los recursos descritos en la TBox. Estas validaciones se realizan cuando se quiere hacer una aserción en la ABox para ver si la aserción está permitida o no, controlando qué es válido y qué no es válido asertar. Aún así, esta solución no deja de ser una pequeña chapuza, ya que perdemos la información de las validaciones que no está contenida en la ontología, por lo que siempre que sea posible, se debe modelar correctamente la ontología, insertando la información posible y después añadir las validaciones.

Estas validaciones, son predicados y se pueden realizar en el registro de la TBox. y pueden ir asociadas a cualquier acción y recurso. Por ejemplo:

 (tbox-register-name! :owl-thing (owl-Thing) :connection)
 (tbox-register-name! :test-Person :test "Person"  :connection)
 (tbox-register-name! :test-age :test "age"  :connection)
 (tbox-register-name! :test-name :test "name"  :connection)
 
 (tbox-register-validation-on! :create 
                               :test-age 
                               (fn [age]
                                   (> (:value age) 18)))
 

En este caso, comprobamos que al crear un nuevo individuo que da valor a la propiedad test:age, el valor de la edad es mayor que 18. En este caso la validación es para un propiedad, y se comprobaría cuando se realizase una acción sobre cualquier clase con esa propiedad, de esta manera se pueden reutilizar las validaciones entre diferentes clases. Cuando la validación es a nivel de clase, pero no común para las propiedad compartida entre clases, se puede registrar la validación a nivel de clase:

 (tbox-register-name! :test-AdultPerson :test "AdultPerson" :connection)
 (tbox-register-validation-on! :create
                               :test-AdultPerson
                               (fn [properties] 
                                   (> (:value (:test-age properties)) 18)))
 

Así se comprobaría el predicado cuando se crea un individuo de la clase test:AdultPerson, pero no cuando ce crea cualquier otro individuo de una clase relacionada con la propiedad test:age.

Con las validaciones se pueden ofrecer una semántica para casi todo: propiedades obligatorias, formato correcto de valores ... siempre que no sean excusa para perder información que podría haberse asertado algún constructor de OWL, como allValuesFrom, etc.

Tags: clojure, owl

servido por Antonio sin comentarios compártelo

5 Enero 2009

Una capa de persistencia de datos semántica basada en OWL

Hoy en día la norma para desarrollar la capa de persistencia de una aplicación web consiste en desarrollar un esquema para algún sistema de base de datos relacional en el que almacenar los datos.
Esto supone básicamente darle golpes a tus objetos hasta convertirlos en conjuntos de enteros, cadenas, etc que después se vuelven a encajar en tablas, indicando relaciones entre esas tablas duplicando valores en columnas especiales de esas tablas. Estamos tan acostumbrados a hacerlo de esta manera que ni nos damos cuenta de la gran distancia que media entre nuestro modelo objetual, con el que trabajamos en nuestro código, y el nivel de abstracción mucho más bajo con el que almacenamos una representación persistente de esos datos.

Sin embargo, esto no tendría porque ser así. Cuando creamos y modelamos nuestro dominio de datos para la aplicación, no hacemos más que crear una ontología, una descripción de los recursos del dominio y sus relaciones, y en los últimos años se ha estandarizado un lenguaje para la descripción de ontologías por parte de la W3C: OWL.

OWL nace del mundo de la lógica descriptiva y permite describir las dos partes que componen una ontología:

  • Una TBox: o terminological box, donde describimos una terminología, los tipos de recursos y relaciones, por ejemplo, existen cosas llamadas personas, existe una relación entre una cosa y un número llamada tener edad, etc...
  • Una ABox: o assertional box, donde hacemos aserciones usando los términos de la TBox, por ejemplo: Juan es una persona, Juan tiene una edad de 15, etc...

A continuación mostraré una biblioteca para describir una TBox en Clojure lisp.

Nuestra primera tarea es tener un sitio donde almacenar la TBox, para ello debemos tener una conexión con un repositorio. Con el siguiente código creamos un repositorio en memoria y lo registramos con un nombre para usarlo posteriormente:

  (register-repository! :test (init-memory-repository!))
 

A continuación podemos pasar a describir nuestra TBox para la ontología que vamos a describir, la famosa aplicación para escribir blogs, para ello usamos las funciones (describe-owl-*):

 (write-graph-in-repository
    (describe-tbox
       ;; algunas propiedades de datos
       (describe-owl-datatype-property :superblog "title" (xsd-string))
       (describe-owl-datatype-property :superblog "content" (xsd-string))
       (describe-owl-datatype-property :superblog "name" (xsd-string))
                 
       ;; algunas clases
       (describe-owl-class :superblog "Blog")
       (describe-owl-class :superblog "Post")
       (describe-owl-class :superblog "Author")
                
       ;; asignamos propiedades a clases
       (describe-owl-class-has-property :superblog "Blog" :superblog "title")
       (describe-owl-class-has-property :superblog "Post" :superblog "title")
       (describe-owl-class-has-property :superblog "Post" :superblog "content")
       (describe-owl-class-has-property :superblog "Author" :superblog "name")
 
       ;; algunas propiedades de objetos
       (describe-owl-object-property :superblog "contains")
       (describe-owl-object-property :superblog "writes")
 
       ;; relaciones entre clases
       (describe-owl-class-has-property :superblog "Blog" :superblog "contains")
       (describe-owl-property-range :superblog "contains" :superblog "Post")
       (describe-owl-class-has-property :superblog "Author" :superblog "writes")
       (describe-owl-property-range :superblog "writes" :superblog "Post"))
 
    ;; seleccionamos la conexion con el repositorio anteriormente registrado
    (connection! :test))
 

El código anterior es equivalente a escribir una migración en Ruby on Rails, pero el hecho de usar OWL en vez de directamente una base de datos relacional, nos aporta una serie de beneficios, por ejemplo, la reutilización del esfuerzo empleado en modelar el dominio. En el ejemplo anterior podemos darnos cuenta de que las relaciones como título, ser autor de, etc, ya han sido modelados y estandarizados dentro del Dublin Core Metadata Initiative (http://en.wikipedia.org/wiki/Dublin_Core), ¿por qué no utilizar el soporte para el Dublin Core que incluye la biblioteca? La reutilización se basa en el concepto de kits de vocabularios, que incluyen clases y propiedades y funciones que trabajan sobre estas partículas OWL:

 (use 'com.agh.webserver.framework.persistence.rdf.vocabularies.dc)
 
 ;; cargamos la descripción del Dublin Core en el repositorio de test
 (import-vocabulary-kit :dc (connection! :test)) 
 
 ;; buscamos sinonimos entre nuestro dominio y el DC
 (write-graph-in-repository
    (describe-tbox
       (describe-owl-equivalent-properties :superblog "title" (dc-title))
       (describe-owl-equivalent-properties :superblog "writes" (dc-creator)))
    (connection! :test))
 

La descripción de la TBox de la ontología está en este momento almacenada en el repositorio, para poder usar la TBox de forma eficiente debemos iniciar la TBox al empezar a usar nuestra aplicación declarando que recursos de la ontología almacenada en el repositorio queremos utilizar:

   ;; iniciamos la TBox
  (tbox-register-name! :owl-thing (owl-Thing) :test)
  (tbox-register-name! :superblog-Post "http://superblog.com/Post" :test)
  (tbox-register-name! :superblog-Blog "http://superblog.com/Blog" :test)
  (tbox-register-name! :superblog-Author "http://superblog.com/Author" :test)
  (tbox-register-name! :superblog-content "http://superblog.com/content" :test)
  (tbox-register-name! :superblog-title "http://superblog.com/title" :test)
  (tbox-register-name! :superblog-name "http://superblog.com/name" :test)
  (tbox-register-name! :superblog-post "http://superblog.com/contains" :test)
  (tbox-register-name! :superblog-post "http://superblog.com/writes" :test)
 
  ;; iniciamos el kit de Dublin Core con los nombre por defecto que asigna la 
  :: biblioteca
  (tbox-register-vocabulary-dc :test)
 
   ;; el codigo de la aplicacion empezaria aqui
 

Ahora ya podríamos acceder a los recursos mediante su URI o mediante el nombre con el que se ha registrado. Un test que muestra la interacción entre las diferentes partes, además de la inferencia entre propiedades y subclases, podría ser:

 (deftest test-class-subclassing
   (let [orig-tbox @*tbox*
         orig-conn @*connections*
         orig-repos @*repositories-registry*
         repo (init-memory-repository!)
         graph (describe-tbox
                (describe-owl-datatype-property "http://test.com/prop_a" (xsd-float))
                (describe-owl-datatype-property "http://test.com/prop_b" (xsd-float))
                (describe-owl-class "http://test.com/class_a")
                (describe-owl-class "http://test.com/class_b")
                (describe-owl-class "http://test.com/class_c")
                (describe-owl-subclass "http://test.com/class_a" "http://test.com/class_b")
                (describe-owl-subclass "http://test.com/class_a" "http://test.com/class_c")
                (describe-owl-class-has-property "http://test.com/class_a" "http://test.com/prop_a")
                (describe-owl-class-has-property "http://test.com/class_b" "http://test.com/prop_b"))]
     (do (tbox-clear!)
         (register-repository! :test repo)
         (write-graph-in-repository graph (connection! :test))
         (tbox-register-name! :owl-thing (owl-Thing) :test)
         (tbox-register-name! :class_a "http://test.com/class_a" :test)
         (tbox-register-name! :class_b "http://test.com/class_b" :test)
         (tbox-register-name! :class_c "http://test.com/class_c" :test)
         (tbox-register-name! :prop_a "http://test.com/prop_a" :test)
         (tbox-register-name! :prop_b "http://test.com/prop_b" :test)
         (let [class-recovered (tbox-find-class-by-uri! "http://test.com/class_a")]
           (do 
               (is (= class-recovered
                      {:name :class_a
                       :uri "http://test.com/class_a"
                       :subclass-of [{:name :class_a
                                      :uri {:prefix "", :value "http://www.w3.org/2002/07/owl#Thing"}
                                      :subclass-of []
                                      :datatype-properties #{}
                                      :object-properties #{}
                                      :repository-name :test}
                                     {:name :class_b
                                      :uri {:prefix "", :value "http://test.com/class_b"}
                                      :subclass-of [{:name :owl-Thing
                                                     :uri {:prefix "", :value "http://www.w3.org/2002/07/owl#Thing"}
                                                     :subclass-of []
                                                     :datatype-properties #{}
                                                     :object-properties #{}
                                                     :repository-name :test}]
                                      :datatype-properties #{{:name nil
                                                              :uri
                                                              {:prefix "", :value "http://test.com/prop_b"}
                                                              :range {:prefix "", :value "http://www.w3.org/2001/XMLSchema#float"}
                                                              :equivalent-properties #{}
                                                              :repository-name :test}}
                                      :object-properties #{}
                                      :repository-name :test}
                                     {:name :class_c
                                      :uri {:prefix "", :value "http://test.com/class_c"}
                                      :subclass-of [{:name :owl-Thing
                                                     :uri {:prefix "", :value "http://www.w3.org/2002/07/owl#Thing"}
                                                     :subclass-of []
                                                     :datatype-properties #{}
                                                     :object-properties #{}
                                                     :repository-name :test}]
                                      :datatype-properties #{}
                                      :object-properties #{}
                                      :repository-name :test}]
                       :datatype-properties #{{:name :prop_b
                                               :uri {:prefix "", :value "http://test.com/prop_b"}
                                               :range {:prefix "", :value "http://www.w3.org/2001/XMLSchema#float"}
                                               :equivalent-properties #{}
                                               :repository-name :test}
                                              {:name :prop_a
                                               :uri
                                               {:prefix "", :value "http://test.com/prop_a"}
                                               :range {:prefix "", :value "http://www.w3.org/2001/XMLSchema#float"}
                                               :equivalent-properties #{}
                                               :repository-name :test}}
                       :object-properties #{}
                       :repository-name :test}
               (tbox-restore! orig-tbox)
               (repositories-register-restore! orig-repos)
               (connections-restore! orig-conn))))))
 

Al igual que OWL se situa sobre una pila de protocolos de la W3C para la web semántica, en concreto, una ontología OWL se codifica como un grafo RDF que a su vez se puede describir como un documento XML, la biblioteca OWL usa la bibliteca RDF que comenté anteriormente para describir los recursos en términos de constructores de un grafo RDF y recupera su información transformando ese grafo en consultas SPARQL.

En siguientes posts, veremos como usar esta TBox para hacer aserciones sobre objetos concretos dentro de la ABox.

Aunque todavía se encuentra muy lejos de ser usable para nada, si queréis hacer pruebas y juguetear con él, el código se encuentra disponible en mi repositorio de Github.

Tags: lisp, clojure, owl

servido por Antonio sin comentarios compártelo


Sobre mí

Avatar de Antonio

un rato de sol

Barcelona/Salamanca, España
ver perfil »
contacto »
Trabajador del metal y del acero, en la gloriosa XING AG, escribo software con el que poder ganarme el jornal. En mi tiempo libre sigo tecleando código de bonitos colores a medio camino entre lo sublime y lo terrible. Últimamente me gustan mucho los gatos.

Fotos

Antonio Garrote Hernández todavía no ha subido ninguna foto.

¡Anímale a hacerlo!

Buscar

suscríbete

Selecciona el agregador que utilices para suscribirte a este blog (también puedes obtener la URL de los feeds):

¿Qué es esto?

Crea tu blog gratis en La Coctelera