2013-06-02 13 views
5

Clojure offre una buona interoperabilità Java. Tuttavia, ho voglia di avere questo:Punti di partenza per metamorfosi regolare Servlet che codifica sul mio DSL

(servlet IndexServlet 
    (service[parmas] ....) 
    (do-post[params] ....) 
    (do-get [params] ....)) 

(servlet-filter SecurityFilter 
    (do-filter [params] ....)) 

Credo che sia quello che ha chiamato un DSL e nel mondo Lisp è fatto tramite macro.

Non so come/dove iniziare. il refiy e l'estensione delle forme hanno sicuramente un ruolo importante in questo caso, ma non so come si inserisca in Macro.

Come iniziare a fare questo DSL?
Uno snippet, suggerimenti e trucchi sono molto apprezzati.

risposta

3

Si potrebbe voler esaminare l'adattatore del Ring's Jetty per un esempio di implementazione di un servlet in Clojure. La fonte è disponibile here (collegamento al sorgente per la versione 1.1). In particolare, la prima funzione definita in tale spazio dei nomi, proxy-handler restituisce un gestore basato su una classe astratta fornita da Jetty.

Se si sceglie di implementare un approccio simile (basando il proprio servlet su una classe Java che fornisce alcuni impli di metodo già pronti), è necessario utilizzare proxy; se hai solo bisogno di implementare interfacce (senza sottoclassi), probabilmente vorrai invece reify. L'utilità o meno dei macro dipende da quali parti dell'implementazione risulteranno risolte; L'adattatore di Ring's Jetty non trarrebbe alcun vantaggio dall'uso di macro, ma potreste (ad esempio se desiderate rendere la classe da estendere/l'interfaccia da implementare in un argomento, come la domanda sembra indicare).

In ogni caso, qualsiasi funzionalità che si sceglie di implementare dovrà far parte di un'interfaccia o di un protocollo. Così, l'implementazione javax.servlet.Servlet più un ulteriore operazione foo potrebbe essere simile a questo:

(import (javax.servlet Servlet ServletRequest ServletResponse)) 

(defprotocol PFoo 
    (foo [this x y z])) 

(reify 
    Servlet 
    (service [this ^ServletRequest req ^ServletResponse res] 
    ...) 
    ;; other Servlet methods here... 
    PFoo 
    (foo [this x y z] 
    ...)) 

Si potrebbe quindi avvolgere questo in una macro di fornire qualsiasi zucchero sintattico desiderato. Si noti che reify in realtà non si preoccupano del modo in cui si Interleave nomi di interfaccia/protocollo e definizioni dei metodi all'interno del suo corpo, così si potrebbe avere la macro Emit

(reify 
    Servlet PFoo ... ; other interfaces & protocols 
    (service [...] ...) 
    (foo [...] ...) 
    ;; other methods 
) 

se questo è più conveniente.

Un abbozzo di una macro prendere un nome di un'interfaccia servlet per implementare (presumibilmente estendentesi javax.servlet.Servlet) ed iniettare un protocollo con alcuni metodi aggiuntivi:

(defprotocol PFancyServlet 
    (do-get [this ...]) 
    (do-post [this ...])) 

(defmacro servlet [servlet-iface & meths] 
    `(reify ~servlet-iface PFancyServlet 
     [email protected])) 

meths dovranno includere do-get e do-post nonché Metodi servlet-iface; potresti aggiungere una convalida di argomenti per assicurarti che sia così. Un esempio di chiamata:

(servlet SomeServletInterface 
    (service [this ...] ...) 
    ;; ... 
    (do-get [this ...] ...) 
    (do-post [this ...] ...)) 
+0

Impressionante! Come posso avere funzioni senza "questo" come primo parametro? (servlet IndexServlet (do-get [richiesta risposta])) – Chiron

+0

Potrebbe essere necessario utilizzare 'this' in corpi del metodo (se i metodi si chiamano, per esempio). Detto questo, la tua macro può eseguire il preprocesso di 'meths' in modo arbitrario, in particolare aggiungendo' this' di nuovo in se lo ometti nella sintassi a livello di utente: '(let [meth-name params & body] meth] \' (~ nome-meth ~ (vec (cons 'this params)) ~ @ body)) 'o qualcosa di simile. Ciò produrrebbe un'espansione in cui "questo" sarebbe disponibile come "parametro magico" in ciascuno dei corpi dei metodi. Oppure potresti usare '(gensym" this __ ")' al posto di ''this' nell'espansione per" nascondere "' this' se è quello che vuoi. –

+0

Inoltre, 'proxy' rende già' this' implicito (si veda '(doc proxy)'), come 'definterface',' gen-interface' e 'gen-class'; 'reify',' deftype', 'defrecord',' defprotocol' no. (Ri: 'gen-class', sono le dichiarazioni del metodo in un formato' gen-class' che omettono 'this'; le funzioni di implementazione devono renderlo esplicito.) –