Ни одно достаточно крупное приложение не обходится без конфигурационных файлов. Сегодня мы познакомимся с пакетом configurator , который позволяет не только банально парсить конфиги в Haskell, но и делать другие интересные вещи. Например, узнавать об изменении конфигов и автоматически перечитывать их. Автором пакета является широко известный в узких кругах Bryan O’Sullivan .
Создадим у себя в домашнем каталоге простой конфиг с именем test.conf:
host = «127.0.0.1»
name = «mydatabase»
user = «admin»
pass = «qwerty»
}
Как видите, синтаксис конфига прост и понятен. Также в конфиге можно импортировать другие конфиги:
Основные функции для работы с конфигурационными файлами следующие:
Создаем новый пустой конфиг.
Читаем конфигурационные файлы с диска. Тип Worth определяется примерно как data Worth a = Required a | Optional a
.
Получение части конфига.
Перечитываем конфиги. При этом конфиги, полученные с помощью функции subconfig, также обновляются.
Чтение параметра из конфига.
Аналогично предыдущей функции, только в случае отсутствия параметра будет использовано значение по умолчанию.
Аналогично lookup, только в случае отсутствия параметра вместо того, чтобы вернуть Nothing, будет брошено исключение .
Вспомогательная функция для вывода конфига.
Преобразуем конфиг в простой HashMap. Удобно, например, для последующей передачи этого HashMap в чистые функции.
Теперь дружно скажем cabal install configurator
и запустим ghci:
ghci > import qualified Data . Text as T
Загрузим наш конфиг:
Выведем его на экран:
( «» , fromList [ ( «database.user» , String «admin» ) , ( «database.name» , String «mydatabase» ) , ( «database.pass» , String «qwerty» ) , ( «database.host» , String «127.0.0.1» ) ] )
Получим имя пользователя:
ghci > user <- C . lookup conf key :: IO ( Maybe String )
ghci > user
Just «admin»
Попробуем поработать с сабконфигом:
ghci > pass <- C . require subconf ( T . pack «pass» ) :: IO String
ghci > pass
«qwerty»
… и использовать значения по умолчанию:
ghci > port
5432
Совсем не сложно, правда?
Теперь попробуем разобраться с автоматическим обновлением конфигов. Типы и функции, которые нам понадобятся, следующие:
interval :: Int
, onError :: SomeException -> IO ( )
}
Этот тип определяет, как часто следует перечитывать конфиг и что делать в случае возникновения ошибки.
Возвращает значение по умолчанию типа AutoConfig. Интервал равен одной секунде, ошибки игнорируются.
Создает автоматически обновляемый конфиг.
Посмотрим на все это хозяйство в действии. Говорим:
ghci > ( conf2 , _ ) <- C . autoReload C . autoConfig paths
ghci > C . getMap conf2
fromList [ ( «database.user» , String «admin» ) , …
Открываем конфиг, заменяем имя пользователя на «guest», снова говорим:
fromList [ ( «database.user» , String «guest» ) , …
Кажется, работает! Ну, почти. На момент написания этих строк в configurator было несколько неисправленных багов. Например, если использовать в пути к конфигу переменную $(HOME)
, он не будет обновляться. Ознакомиться со списком известных багов можно здесь .
Также configurator позволяет подписаться на обновления определенных частей конфига.
Возвращает шаблон для подписки на определенную часть конфига.
Создает шаблон для подписки на один конкретный параметр.
Тип функции, вызываемой при внесении изменений в конфиге.
Подписаться на изменения.
Проверяем:
Prelude C T | let changeHandler name val = do
Prelude C T | putStrLn $ show name ++ » changed to » ++ show val
Prelude C T | : }
ghci > let prfx = ( C . prefix $ T . pack «database» )
ghci > let exct = ( C . exact $ T . pack «database.port» )
ghci > C . subscribe conf2 prfx changeHandler
ghci > C . subscribe conf2 exct changeHandler
Открываем конфиг и дописываем в него строчку:
В ghci видим:
«database.port» changed to Just ( Number ( 5432 % 1 ) )
Вот такой занятный пакет этот configurator. А чем вы парсите конфиги в Haskell?
Дополнение: Haskell и хождение в базы данных с помощью HDBC