Структура Yesod-проекта: базовая конфигурация

Приветствую!

После первого знакомства с Yesod пришла пора заглянуть внутрь нашего проекта. Должны же мы узнать, что сделала команда yesod init, не так ли? Давайте же заглянем в корень:

Application.hs
Foundation.hs
Model.hs
Settings.hs
devel.hs
webhs.cabal
Import.hs
Handler/
config/
messages/
templates/
Settings/
app/
deploy/
dist/
static/
test/
yesod-devel/

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

Вот содержимое, созданное по умолчанию:

favicon.ico
robots.txt
postgresql.yml
keter.yml
models
routes
settings.yml

Полагаю, назначение файлов favicon.ico и robots.txt вопросов не вызывает, не правда ли? С файлом postgresql.yml вы уже познакомились, когда настраивали параметры доступа приложения к Postgres.

Сетевая конфигурация

Вглянем на содержимое важного файла settings.yml:

Default: &defaults
  host: "*4" # any IPv4 host
  port: 3000
  approot: "http://localhost:3000"
  copyright: Insert copyright statement here
  #analytics: UA-YOURCODE

Development:
  <<: *defaults

Testing:
  <<: *defaults

Staging:
  <<: *defaults

Production:
  #approot: "http://www.example.com"
  <<: *defaults

Помните, в прошлой заметке я упомянул о том, что приложение можно запустить не через yesod devel, а непосредственно? И там упоминались четыре возможные окружения: Development, Testing, Staging и Production. Полагаю, теперь, когда вы увидели содержимое файла settings.yml, всё встало на свои места: для каждого из окружений мы можем задать свои сетевые параметры. Например, можно переопределить значение параметра approot, чтобы различные версии нашего приложения ассоциировали себя с разными URL-ами, и то же самое можно сделать с параметрами host и port. Вот возможный результат:

Default: &defaults
  host: "127.0.0.1"
  #analytics: UA-YOURCODE

Development:
  port: 3001
  approot: "http://dev.my-service.com"
  <<: *defaults

Testing:
  port: 3002
  approot: "http://test.my-service.com"
  <<: *defaults

Staging:
  port: 3003
  approot: "http://stage.my-service.com"
  <<: *defaults

Production:
  port: 3000
  approot: "http://my-service.com"
  <<: *defaults

Разворачивание через Keter

Содержимое файла keter.yml мы сейчас рассматривать не будем. Это конфигурационный файл для утилиты keter, предназначенной для автоматического разворачивания нашего приложения в виде специального бандла. Подробнее об этом рассказано здесь. Однако на данный момент я деплою приложение другим (более простым и понятным мне) способом. Кстати, об этом способе мы поговорим в следующих заметках.

Маршруты

Теперь взглянем на файл routes. Этот файл - один из самых важных для нашего приложения. Взглянем внутрь:

/static         StaticR     Static getStatic
/auth           AuthR       Auth   getAuth

/favicon.ico    FaviconR    GET
/robots.txt     RobotsR     GET

/               HomeR       GET POST

Перед нами маршруты (routes). Обычный маршрут определяет три сущности (слева направо):

  1. Путь, то есть URL (начиная от корня), по которому мы обращаемся к той или иной странице нашего приложения.
  2. Ресурс, объединяющий обработчик(и) запросов, направленных по тому или иному пути.
  3. Типы запросов, которые мы можем направить по тому или иному пути.

Рассмотрим корневой маршрут:

/               HomeR       GET POST

Путь понятен - это корень нашего приложения (домашняя страница). Ресурс HomeR отвечает за обработку запросов, направленных к корню приложения. И, как видите, домашняя страница нашего приложения способна принимать как GET, так и POST-запросы.

Таким образом, по мере расширения функциональности нашего веб-приложения мы будем довольно часто обращаться к файлу routes, чтобы дописать новые маршруты.

Вы спросите, почему же первые два маршрута выглядят необычно? Например:

/auth           AuthR       Auth   getAuth

Об этом будет подробнее рассказано в следующих заметках. В частности, путь /auth скрывает несколько возможных запросов, связанных, как вы уже поняли, с авторизацией.

Модели

Взглянем на содержимое файла models:

User
    ident Text
    password Text Maybe
    UniqueUser ident
    deriving Typeable
Email
    email Text
    user UserId Maybe
    verkey Text Maybe
    UniqueEmail email

-- By default this file is used in Model.hs (which is imported by Foundation.hs)

Перед нами - модели для работы с БД. Но об этом мы поговорим особо. Поверьте, эта тема достойна отдельной заметки.

Расположение

Содержимое каталога config уже вполне пригодно для реального использования. Осталось уточнить одну деталь, касающуюся расположения этого каталога, ведь, как уже было сказано выше, без конфигурационных файлов, расположенных в нём, наше приложение не сможет работать.

Попробуем запустить наше приложение вот так:

$ cd dist/build/webhs
$ ./webhs Development -p 3001

Ага, разбежались! Получите ошибку:

webhs: InvalidYaml (Just (YamlException "Yaml file not found: config/settings.yml"))

Файл config/settings.yml не найден, и это вполне ожидаемо. Наше приложение рассчитывает на то, что каталог config расположен не абы где, а в строго определённом месте. Правило очень простое: этот каталог должен располагаться в том же месте, откуда мы запускаем наше приложение.

Например, мы могли бы расположить этот каталог рядом с исполняемым файлом:

$ cd dist/build/webhs
$ cp -R ../../../config .
$ ./webhs Development -p 3001

Так заработает. Но можно просто запустить из того места, где config уже лежит:

./dist/build/webhs/webhs Development -p 3001

Впрочем, я опять вас немного обманул, потому что исполняемый файл рассчитывает не только на каталог config, но и ещё на кое-что. Однако об этом в отдельной заметке.