Haskell, Snap: Простая конструкция с защелкой. Когда мы используем Snaplet и когда библиотека? - PullRequest
9 голосов
/ 30 марта 2012

Я пытаюсь понять простую конструкцию с защелкой. Кроме того, когда мне на самом деле нужно сделать снимок, а когда простую боковую библиотеку? И если мне нужен один, как мне сделать это из библиотеки?

Например, у меня есть несколько функций БД, где я оборачиваю свой код SQL, как показано ниже.

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

Каждая функция инициирует новое соединение и закрывает соединение после фиксации. Я полагаю, что создание моментальных снимков - это способ избежать соединения в каждой функции? В моем обработчике я бы использовал это так:

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
           }

Пока это работает. Мой вопрос (ы): Когда мне действительно нужно превратить библиотеку в брелок? Нужно ли мне превращать мою простую библиотеку БД в брелок, чтобы инициализировать соединение, а не устанавливать соединение в каждой функции?

Теперь, если я сделаю снимок ... На веб-сайте Snap есть крошечный пример sanaplet верхнего уровня, но нет никаких следов того, как создать собственный простой плаглет.

Итак, я добавил функцию инициализации моментального снимка в мою библиотеку БД.

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

Это правильный способ сделать это? Это все, что мне нужно, чтобы превратить его в съемную заглушку?

Затем я складываю этот моментал БД в основное приложение

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

Теперь все, что я получаю, это доступное соединение для моих обработчиков запросов, верно? Так что мой обработчик становится:

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
           }

Это не похоже на работу. Что я делаю неправильно? Это правильный способ извлечь соединение из дескриптора (dbcon)? Является ли это в общем правильное направление для создания простого щелчка? Мне действительно нужна кнопка в моем случае?

Спасибо.

1 Ответ

4 голосов
/ 30 марта 2012

Handler является экземпляром MonadState: MonadState v (Handler b v).

Handler также является экземпляром MonadSnaplet и поэтому предоставляет метод with:
with :: Lens v (Snaplet v') -> m b v' a -> m b v a

dbcon является Lens App (Snaplet Connection).

Таким образом, чтобы добраться до Connection, мы можем использовать:
conn <- with dbcon get

Как правило, вы создаете брелок, если предоставляете функциональные возможности, которые могут быть полезны всем. В вашем случае, вероятно, лучше воспользоваться преимуществом HDBC Snaplet , который вы можете использовать для подключения к базе данных sqlite3.

Оформление заказа http://norm2782.github.com/snaplet-hdbc.html для хорошего руководства по использованию HDBC-флешки.

...