Использовать subsnaplet во время инициализации snaplet?
У меня есть несколько слов, как это:
data DB b = DB
{_pgsql :: Snaplet Postgresql
,dbCache :: Map Text Text
}
И я хочу заполнить dbCache
из базы данных postgresql. Кажется, что это естественно делать во время инициализации Snaplet.
initDB :: SnapletInit b (DB b)
initDB = makeSnaplet "db" "cached database" Nothing $ do
pgs <- nestSnaplet "pgsql" pgsql pgsInit
cache <- getSomeDataPlease pgs
return $ DB pgs cache
Итак, вопрос: как это возможно использовать pgs :: Snaplet Postgres
в Initializer
Монада для чтения данных из БД?
1 ответ
Функции доступа к БД, предоставляемые snaplet-postgresql-simple, запускаются в любой монаде, которая является экземпляром HasPostgres
тип класс. Как правило, это будет Handler
Монада для вашего приложения.
Вы не можете использовать Handler
функции внутри Initializer
, Весь смысл монады Initializer состоит в том, чтобы установить тип данных начального состояния, который необходим для запуска веб-сервера и монады Handler. Так что действительно невозможно запускать обработчики внутри инициализатора - если, конечно, вы не запускаете один веб-сервер внутри другого веб-сервера... ick.
Таким образом, у вас есть два возможных варианта. Вы могли бы создать HasPostgres
экземпляр для вашего Initializer
, Но это не имеет особого смысла, если вы не подключаетесь к статическому серверу. Это может быть приемлемо, если вы делаете отладку. Иногда я делаю это для IO, чтобы сделать тривиальным тестирование моих функций базы данных:
instance HasPostgres IO where
getPostgresState = do
pool <- createPool (connect $ ConnectInfo "127.0.0.1" ...) ...
return $ Postgres pool
Но в общем случае не имеет смысла создавать такой экземпляр для использования в производственном коде. Это означает, что если вы хотите получить доступ к базе данных в Initializer
Вы должны использовать функции postgresql-simple напрямую, а не оболочки, предоставляемые snaplet-postgresql-simple. Вот почему я экспортировал функцию доступа pgPool. Это будет выглядеть примерно так:
initDB :: SnapletInit b (DB b)
initDB = makeSnaplet "db" "cached database" Nothing $ do
pgs <- nestSnaplet "pgsql" pgsql pgsInit
let pool = pgPool $ extract pgs
results <- liftIO $ withResource pool (\conn -> query_ conn myQuery)
Вы можете увидеть реальный живой пример этого в auth backend-функции snaplet-postgresql-simple.
Обновить:
Я только что загрузил новую версию snaplet-postgresql-simple для взлома, которая предоставляет экземпляр HasPostgres для ReaderT. Это позволяет вам сделать это проще с помощью runReaderT. В документации есть небольшой фрагмент этого кода.