Использовать 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. В документации есть небольшой фрагмент этого кода.

Другие вопросы по тегам