Estoy tratando de entender la construcción simple de snaplet. Además, ¿cuándo realmente necesito hacer una instantánea y cuándo una biblioteca auxiliar simple? Y si lo necesito, ¿cómo puedo salir de una biblioteca?Haskell, Ajustar: construcción sencilla snaplet. ¿Cuándo usamos snaplet y cuándo biblioteca?
Por ejemplo, tengo un montón de funciones de base de datos donde envuelvo mi código SQL como a continuación.
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
Cada función inicia una nueva conexión y cierra la conexión después de la confirmación. Supongo que hacer un snaplet es la forma de evitar la conexión en todas las funciones. En mi manejador lo usaría como esto:
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
}
Funciona hasta ahora. Mi pregunta (s) es/are: ¿Cuándo realmente necesito convertir una biblioteca en una instantánea? ¿Debo convertir mi simple biblioteca de bases de datos en una instantánea solo para inicializar la conexión en lugar de hacer la conexión en todas las funciones?
Ahora, si hago el snaplet ... En el sitio web de Snap hay un pequeño ejemplo de un sangrado de nivel superior pero no hay ningún rastro de cómo hacer un snaplet de pluggble propio.
por lo que añade la función de inicialización snaplet a mi 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
Es esta la forma correcta de hacerlo? ¿Es esto todo lo que necesito para convertirlo en un snaplet pluggble?
Entonces Stack snaplet DB en la aplicación principal
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
Ahora, todo lo que gano es la conexión disponible a mis controladores de solicitudes, ¿verdad? Así que mi controlador se convierte en:
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
}
Esto no parece funcionar. ¿Qué estoy haciendo mal? ¿Es esta la forma correcta de extraer la conexión del controlador de snaplet (dbcon)? ¿Esta es, en general, la dirección correcta para construir un simple snaplet? ¿Realmente necesito un snaplet aquí en mi caso?
Gracias.
Gracias. He visto el snaplet de HDBC y he jugado con él. Quiero hacerlo desde cero, esta es la única forma de aprender para mí. Primero traté de encontrar la forma de usar una biblioteca simple dentro de mis manejadores y ahora quiero aprender cómo construir una opción de snaplet. Quiero entender cómo se construye un simple snaplet, cómo interactúan y por qué/cuándo necesito uno ... –
¿Puede decirme si esta es la dirección correcta para construir un snaplet? ¿Es el dbInit todo lo que necesito? –
@ r.sendecky Como un snaplet muy simple, diría que está bien. Sin embargo, los snaplets normalmente se utilizan para ejecutar acciones desde una mónada que ha creado. En tu caso, funciona porque solo usas acciones IO. Recomiendo analizar mightybytes [AcidState snaplet] (http://hackage.haskell.org/packages/archive/snaplet-acid-state/0.2/doc/html/src/Snap-Snaplet-AcidState.html#Acid). Es un buen ejemplo de cómo/por qué construir un snaplet. – qubital