在snaplet初始化期间使用subsnaplet?

时间:2012-07-03 09:46:17

标签: haskell haskell-snap-framework

我有一些像这样的snaplet:

data DB b = DB
  {_pgsql :: Snaplet Postgresql
  ,dbCache :: Map Text Text
  }

我希望从postgresql数据库中填充dbCache。似乎在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 monad中使用Initializer从db中读取数据?

1 个答案:

答案 0 :(得分:2)

snaplet-postgresql-simple提供的数据库访问函数在任何作为HasPostgres类型类实例的monad中运行。通常,这将是您的应用程序的Handler monad。

您无法在Handler内使用Initializer个功能。 Initializer monad的重点是设置运行Web服务器和Handler monad所需的初始状态数据类型。因此,在初始化程序中运行处理程序确实是不可能的 - 除非您从另一个Web服务器内部运行一个Web服务器... 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)

你可以在snaplet-postgresql-simple auth backend中看到一个真实的例子。

更新

我刚刚将一个新版本的snaplet-postgresql-simple上传到hackage,提供HasPostgres instance for ReaderT。这使您可以使用runReaderT更简单地完成此操作。文档中有一个小的代码片段。