2011-05-08 18 views
6

Trato específicamente de generar la plantilla para que las funciones crud funcionen con el almacén de datos de Google App Engine utilizando appengine-magic en Clojure. Tengo dificultades para generar valores a partir de un modelo que he reproducido a continuación.Uso de macros en Clojure

(def *model* {:users [{:name "Adam" 
         :email "[email protected]" 
         :registered-on "07-05-2011"} 
         {:name "Greg" 
         :email "[email protected]" 
         :registered-on "11-05-2011"}] 
       :post [{:title "A" 
         :authour "Adam"} 
        {:title "B" 
         :author "Greg"}]}) 

Soy bastante nuevo a AppEngine-magia, sino que proporciona un defentity que le permite definir las entidades que se pueden poner en el almacén de datos y guardar! que le permite guardar entidades predefinidas en el almacén de datos.

Éstos toman la forma de:

(ds/defentity Post [title author]) 
(ds/save! (Post. title author)) 

Ahora acaba de empezar con he definido:

(defn list-entities [model] 
    "Takes a representation of the model and lists the entities in preparation for generating defentities" 
    (interleave (vec (map first (partition 1 (map (comp symbol capitalize #(str % ".") name) (keys model))))) 
    (map vec (map keys (map first (vals model)))))) 

Llamando con:

(list-entities *model*) 

Salidas:

(Users. [:name :email :registered-on] Post. [:title :author]) 

Ahora estoy teniendo dificultades para definir gen-entidades que tomarán el resultado anterior y llamo repetidamente a ds/defentities para definir tantas entidades como requiera mi modelo.

(defmacro gen-entities [entity fields] 
    `(ds/defentity 'entity 'fields)) 

Además, de ninguna manera estoy seguro de que este sea un modo razonable de resolver este problema. Todavía soy muy nuevo en macros y probablemente cometo varios errores. Cualquier ayuda/claridad sería apreciada.

NOTA:

Ese modelo me he dado cuenta está mal diseñado, la de abajo es mucho mejor:

(def *model* {:users [:name :email :registered-on] 
       :post [:title :author]}) 

Sin embargo es más complejo en términos de escribir una macro así que voy a dejar como es.

+1

¿Por qué no querría hacer una función que transforme la entrada en llamadas a ds/defentity directamente? La única razón por la que necesita macros aquí es debido a la salida que genera su función. – Gert

+0

Bueno, me podría estar perdiendo algo aquí, lo intenté y parece que no funciona. Así que pensé que este era un caso en el que tendría que usar macros. Soy consciente de que no deberíamos usarlos a menos que lo necesitemos, así que pensé en preguntar aquí al respecto. – toofarsideways

Respuesta

1

Creo que se requiere una macro, porque defentity parece definir un tipo.

(defmacro gen-entities 
    [model] 
    `(do 
    [email protected](for [[entity-kw values] model] 
     (let [entity-sym (-> entity-kw name capitalize symbol) 
       fields  (map (comp symbol name) (keys (first values)))] 
      `(ds/defentity ~entity-sym [[email protected]]))))) 

No tiene que manipular las teclas y los valores entre sí, solo para unirlos de nuevo con intercalación. El mapeo sobre un mapa le dará la clave y el valor correspondiente de una vez.

user=> (macroexpand-1 `(gen-entities ~model)) 
(do 
    (ds/defentity Users [name registered-on email]) 
    (ds/defentity Post [title authour])) 

Nota: esto no funcionará con el modelo almacenado en un Var. Deberá especificar el modelo en la llamada al gen-entities.

user=> (macroexpand-1 '(gen-entities model)) 
(
#<IllegalArgumentException java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.lang.Symbol> 
+0

Parece que me falta algo, sin embargo, cuando ejecuto macroexpand-1 como lo ha demostrado obtengo el resultado. Sin embargo, si trato de evaluar la llamada (gen-entities inserting-model-here) en lugar de macroexpandirlo, no funciona, dándome una java.lang.ClassCastException: clojure.lang.La palabra clave no se puede convertir a clojure.lang.IObj [Clase lanzada java.lang.RuntimeException]. ¿Alguna idea de lo que me estoy perdiendo? – toofarsideways

+0

También parece extraño que no pueda usar un Var predefinido, ¿hay algún motivo para esa limitación? – toofarsideways

+0

@toofarsideways Se corrigió un error estúpido. Debería funcionar ahora. No funciona con Var porque la macro no ve el valor real de Var, sino el símbolo-'modelo 'en este caso. – kotarak