2012-03-30 11 views
8

Sto cercando di capire la semplice costruzione di snaplet. Inoltre, quando devo effettivamente creare uno snaplet e una semplice libreria laterale? E se ne ho bisogno uno come faccio a uscire da una biblioteca?Haskell, Snap: semplice costruzione a scatto. Quando usiamo lo snaplet e quando la libreria?

Ad esempio, ho un sacco di funzioni DB in cui avvolgo il mio codice SQL come di seguito.

data Person = Person {personName :: ByteString, personAge :: Int} 

connect :: IO Connection 
connect = connectSqlite3 "/somepath/db.sqlite3" 

savePerson :: Person -> IO() 
savePerson p = do 
c <- connect 
run c "INSERT INTO persons (name, age) \ 
     \VALUES (?, ?)" 
     [toSql (personName p), toSql (personAge p)] 
commit c 
disconnect c 

Ogni funzione avvia una nuova connessione e chiude la connessione dopo il commit. Immagino che creare uno snaplet sia il modo per evitare la connessione in ogni funzione? Nel mio gestore vorrei utilizzare in questo modo:

insertPerson :: Handler App App() 
insertPerson = do 
    par <- getPostParams 
    let p = top par 
    liftIO $ savePerson p 
where 
    top m = 
    Person {personName = head (m ! (B.pack "name")) 
      ,personAge = read (B.unpack (head (m ! (B.pack "age")))) :: Int 
      } 

Funziona così lontano. Le mie domande sono/sono: Quando devo effettivamente trasformare una libreria in uno snaplet? Devo trasformare la mia libreria DB semplice in uno snaplet solo per inizializzare la connessione invece di effettuare la connessione in ogni funzione?

Ora, se si crea lo snaplet ... Sul sito Web di Snap vi è un piccolo esempio di un sanaplet di livello superiore ma non c'è traccia di come realizzare un semplice snaplet pluggble.

così ho aggiunto la funzione di inizio snaplet alla mia biblioteca DB

dbInit :: SnapletInit b Connection 
dbInit = makeSnaplet "DB" "My DB Snaplet" Nothing $ do 
    dbc <- liftIO $ connectSqlite3 "/somepath/db.sqlite3" 
    onUnload $ disconnect dbc 
    return $ dbc 

E 'questo il modo corretto di farlo? È tutto ciò di cui ho bisogno per trasformarlo in uno snaplet pluggble?

Poi ho impilare questo DB snaplet in app principale

data App = App 
    { _heist :: Snaplet (Heist App), 
    _dbcon :: Snaplet (Connection) 
    } 

makeLens ''App 

app :: SnapletInit App App 
app = makeSnaplet "app" "My app" Nothing $ do 
    h <- nestSnaplet "heist" heist $ heistInit "templates" 
    d <- nestSnaplet "" dbcon dbInit 
    addRoutes routes 
    return $ App h d 

Ora, tutto quello che guadagno è disponibile la connessione alla mia richiesta gestori, giusto? Quindi il mio gestore diventa:

insertPerson :: Handler App App() 
insertPerson = do 
    par <- getPostParams 
    let person = top par 
    connection <- gets _dbcon 
    liftIO $ savePerson connection person 
where 
    top m = 
    Person {personName = head (m ! (B.pack "name")) 
      ,personAge = read (B.unpack (head (m ! (B.pack "age")))) :: Int 
      } 

Questo non sembra funzionare. Che cosa sto facendo di sbagliato? È questo il modo corretto per estrarre la connessione dall'impugnatura dello snaplet (dbcon)? Questa è generalmente la direzione corretta per costruire un semplice snaplet? Ho davvero bisogno di uno snaplet qui nel mio caso?

Grazie.

risposta

3

Handler è un'istanza di MonadState: MonadState v (Handler b v).

Handler è anche un'istanza di MonadSnaplet e quindi fornisce il metodo with:
with :: Lens v (Snaplet v') -> m b v' a -> m b v a

dbcon è un Lens App (Snaplet Connection).

Quindi, per arrivare alla Connection possiamo utilizzare:
conn <- with dbcon get

Che normalmente creare uno snaplet se si stesse fornendo funzionalità che ognuno possa trarre beneficio. Nel tuo caso, probabilmente è meglio sfruttare lo HDBC snaplet, che puoi utilizzare per connettersi a un db sqlite3.

Acquista http://norm2782.github.com/snaplet-hdbc.html per un buon tutorial sull'utilizzo dello snaplet HDBC.

+1

Grazie. Ho visto lo snaplet HDBC e ci ho giocato. Voglio farlo io stesso da zero, questo è l'unico modo per imparare per me. Per prima cosa ho cercato di capire come utilizzare una semplice libreria all'interno dei miei gestori e ora voglio imparare come costruirne uno snaplet oput. Voglio capire come viene costruito un semplice snaplet, come interagiscono e perché/quando ne ho bisogno ... –

+0

Puoi dirmi se questa è la direzione corretta per costruire uno snaplet? Il dbInit è tutto ciò di cui ho bisogno? –

+0

@ r.sendecky Come uno snaplet molto semplice, direi che va bene. Tuttavia gli snaplet sono normalmente usati per eseguire azioni da una monade che hai creato. Nel tuo caso funziona perché usi solo azioni IO. Raccomando di analizzare mightybytes [AcidState snaplet] (http://hackage.haskell.org/packages/archive/snaplet-acid-state/0.2/doc/html/src/Snap-Snaplet-AcidState.html#Acid). È un buon esempio di come/perché costruire uno snaplet. – qubital