Статьи

Эрланг и эликсир, часть 5: феникс

До сих пор мы видели, как использовать базовые типы данных и принципы кодирования виртуальной машины Erlang через язык Elixir. Теперь мы пройдем полный круг и создадим работающее веб-приложение с использованием Phoenix Web Framework.

Phoenix использует серверный шаблон MVC и фактически является верхним слоем многоуровневой модульной системы, включающей Plug (модульная спецификация, используемая для маршрутизации , контроллеров и т. Д.), Ecto (оболочка БД для MongoDB, MySQL, SQLite3, PostgreSQL и MSSQL) и HTTP- сервер (Cowboy).

Структура Феникса покажется знакомой Джанго для Python или Ruby on Rails. И производительность, и скорость разработки приложений были ключевыми факторами при проектировании Phoenix, и в сочетании с функциями реального времени они дают ему мощный потенциал в качестве среды веб-приложений производственного качества.

Требуется эликсир, поэтому, пожалуйста, обратитесь к инструкции по установке в начале этой серии.

Нам также потребуется Hex для работы Phoenix (для установки зависимостей). Вот команда для установки Hex (если у вас уже установлен Hex, он обновит Hex до последней версии):

1
$ mix local.hex

Если вы еще не ознакомились с языком эликсира, я могу порекомендовать вам продолжить чтение первых шагов этого руководства, прежде чем перейти к этой части.

Обратите внимание, что если вы хотите прочитать краткое руководство, вы также можете обратиться к Руководству по обучающему эликсиру и эрлангу , которое предоставляется командой Phoenix.

Примечание. По умолчанию это включено в установку Elixir.

Для запуска Elixir нам понадобится виртуальная машина Erlang, потому что код Elixir компилируется в байтовый код Erlang.

Если вы используете систему на основе Debian, вам может понадобиться явно установить Erlang, чтобы получить все необходимые пакеты.

1
2
3
wget https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb && sudo dpkg -i erlang-solutions_1.0_all.deb
$ sudo apt-get update
$ sudo apt-get install esl-erlang

Теперь, когда мы позаботились об Elixir и Erlang, вы готовы установить архив Mix.

Архив микс действительно похож на Zip-файл, за исключением того, что он содержит приложение, а также скомпилированные файлы BEAM и привязан к определенной версии приложения.

Архив миксов — это то, что мы будем использовать для создания нового базового приложения Phoenix, из которого мы можем построить наше приложение!

Запустите следующее в своем терминале:

1
$ mix archive.install https://github.com/phoenixframework/archives/raw/master/phoenix_new.ez

Если архив Phoenix Mix не будет установлен должным образом с помощью этой команды, мы можем загрузить пакет из архивов Phoenix , сохранить его в файловой системе и запустить: mix archive.install /path/to/local/phoenix_new.ez .

Нам понадобится node.js версии 5 или выше , поскольку Phoenix будет использовать пакет brunch.io для компиляции статических ресурсов, таких как css и js, которые в свою очередь используют npm .

Загрузите Node.js со страницы загрузки . При выборе пакета для загрузки важно отметить, что для Phoenix требуется версия 5.0.0 или выше.

Пользователи Mac OS X также могут установить Node.js с помощью homebrew .

Если у вас возникли проблемы с установкой Node, обратитесь к официальному справочному руководству Phoenix.

По умолчанию Phoenix настраивает приложения для использования сервера базы данных отношений PostgreSQL, но мы можем переключиться на MySQL, передав флаг --database mysql при создании нового приложения.

В дальнейшем, работая с моделями Ecto в этом руководстве, мы будем использовать PostgreSQL и адаптер Postgrex.

Чтобы следовать примерам, вы должны установить PostgreSQL. Вики PostgreSQL содержит руководства по установке для различных операционных систем.

Обратите внимание, что Postgrex — это прямая зависимость от Phoenix, и она будет автоматически установлена ​​вместе с остальными нашими зависимостями при запуске нашего приложения.

Phoenix предполагает, что наша база данных PostgreSQL будет иметь учетную запись пользователя postgres с правильными разрешениями и паролем «postgres». Если это не так, как вы хотите настроить, обратитесь к инструкциям для задачи ecto.create mix, чтобы настроить учетные данные.

Если вы хотите, чтобы ваше приложение Phoenix работало только без костей, без Ecto или Plug (без db или brunch.io), создайте свое приложение со следующими флагами --no-brunch и --no-ecto :

1
mix phoenix.new web —no-brunch —no-ecto

К этому моменту вы должны иметь:

  • Эликсир
  • Erlang (по умолчанию предоставляется при установке Elixir)
  • наговор
  • Установлен архив миксов Phoenix
  • Кроме того , если вы выбрали поддержку БД и статических ресурсов, у вас также будут PostgreSQL и Node.js> = 5.0.0, и в этом случае вы будете готовы к созданию своего приложения.

Вы можете запустить mix phoenix.new из любого каталога, чтобы запустить приложение Phoenix.

Для вашего нового проекта Phoenix примет либо абсолютный, либо относительный путь; при условии, что имя нашего приложения — hello_world , любой из них будет работать нормально:

1
$ mix phoenix.new /home/me/code/hello_world
1
$ mix phoenix.new hello_world

Когда вы будете готовы, запустите команду create, и вы получите следующий результат:

1
2
3
4
5
6
7
8
9
mix phoenix.new hello_world
* creating hello_world/config/config.exs
* creating hello_world/config/dev.exs
* creating hello_world/config/prod.exs
* creating hello_world/web/views/layout_view.ex
* creating hello_world/web/views/page_view.ex
 
Fetch and install dependencies?

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

Когда это будет сделано, мы увидим приглашение с просьбой установить зависимости. Продолжайте с да:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
Fetch and install dependencies?
* running mix deps.get
* running npm install && node node_modules/brunch/bin/brunch build
 
We are all set!
 
    $ cd hello_world
    $ mix phoenix.server
 
You can also run your app inside IEx (Interactive Elixir) as:
 
    $ iex -S mix phoenix.server
 
Before moving on, configure your database in config/dev.exs and run:
 
    $ mix ecto.create

Теперь, когда все загружено, мы можем перейти в каталог, в который Elixir mix ecto.create файлы проекта, и создать базу данных с помощью mix ecto.create .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
$ cd hello_world
$ mix ecto.create
==> connection
Compiling 1 file (.ex)
Generated connection app
==> fs (compile)
Compiled src/sys/inotifywait.erl
Compiled src/sys/fsevents.erl
Compiled src/sys/inotifywait_win32.erl
Compiled src/fs_event_bridge.erl
Compiled src/fs_sup.erl
Compiled src/fs_app.erl
Compiled src/fs_server.erl
Compiled src/fs.erl
 
The database for HelloPhoenix.Repo has been created.

Примечание: если вы запускаете эту команду впервые, Phoenix может также попросить установить Rebar. Продолжайте установку, так как Rebar используется для сборки пакетов Erlang.

Если вы видите следующую ошибку:

1
2
3
4
5
6
State: Postgrex.Protocol
** (Mix) The database for HelloWorld.Repo couldn’t be created: an exception was raised:
    ** (DBConnection.ConnectionError) tcp connect: connection refused — :econnrefused
        (db_connection) lib/db_connection/connection.ex:148: DBConnection.Connection.connect/2
        (connection) lib/connection.ex:622: Connection.enter_connect/5
        (stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3

Убедитесь, что служба PostgreSQL работает и доступна с предоставленными учетными данными пользователя (по умолчанию используется пользователь postgres с паролем «postgres»).

Теперь мы можем запустить сервер для нашего приложения Elixir! Запустите следующее:

1
2
3
$ mix phoenix.server
[info] Running HelloWorld.Endpoint with Cowboy using http on port 4000
23 Nov 05:25:14 — info: compiled 5 files into 2 files, copied 3 in 1724ms

По умолчанию Phoenix принимает запросы через порт 4000.

Посетите http: // localhost: 4000 , и вы увидите страницу приветствия Phoenix Framework.

Страница приветствия The Phoenix Framework

Если вы не видите страницу выше, попробуйте получить к ней доступ через http://127.0.0.1:4000 (если localhost не определен в вашей ОС).

Локально, теперь мы можем видеть запросы, обрабатываемые в нашем терминальном сеансе, когда наше приложение выполняется в сеансе iex . Чтобы остановить это, мы дважды iex ctrl-c , как обычно, чтобы остановить iex .

1
2
3
4
5
6
7
8
9
$ mix phoenix.server
[info] Running HelloWorld.Endpoint with Cowboy using http://localhost:4000
28 Nov 15:32:33 — info: compiling
28 Nov 15:32:34 — info: compiled 6 files into 2 files, copied 3 in 5 sec
[info] GET /
[debug] Processing by HelloWorld.PageController.index/2
  Parameters: %{}
  Pipelines: [:browser]
[info] Sent 200 in 50ms

Когда Phoenix генерирует для нас новое приложение, оно создает структуру каталогов верхнего уровня, как мы увидим в следующем разделе ниже.

Мы создали новое приложение с помощью команды mix phoenix.new , которая сгенерировала новое приложение, включая структуру каталогов следующим образом:

1
2
3
4
5
6
7
├── _build
├── config
├── deps
├── lib
├── priv
├── test
├── web

Сейчас мы будем работать над веб-каталогом, который содержит следующее:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
├── channels
    └── user_socket.ex
├── controllers
│ └── page_controller.ex
├── models
├── static
│ ├── assets
│ |
|
|
|
│ |
├── templates
│ ├── layout
│ │ └── app.html.eex
│ └── page
│ └── index.html.eex
└── views
|
|
|
|
├── router.ex
├── gettext.ex
├── web.ex

Чтобы изменить логотип в верхней части страницы, нам нужно отредактировать статические ресурсы, которые хранятся в priv/static . Логотип хранится в каталоге так: priv/static/images/phoenix.png .

Не стесняйтесь добавлять свою собственную графику здесь; мы свяжем его с CSS и начнем модифицировать шаблон дальше. По умолчанию Phoenix будет компилировать любые статические ресурсы (например, здесь, в каталоге изображений) в рабочий комплект.

Когда нам нужна фаза сборки для js или css, мы app.js ресурсы в web/static , а исходные файлы встраиваются в их соответствующий app.js / app.css в priv/static .

Путь к вашему CSS — это web/static/css/phoenix.css . Чтобы изменить логотип, посмотрите строки 29-36.

01
02
03
04
05
06
07
08
09
10
11
12
/* Custom page header */
.header {
  border-bottom: 1px solid #e5e5e5;
}
.logo {
  width: 519px;
  height: 71px;
  display: inline-block;
  margin-bottom: 1em;
  background-image: url(«/images/phoenix.png»);
  background-size: 519px 71px;
}

Внесите изменения и сохраните файл, и изменения будут автоматически обновлены.

1
2
3
28 Nov 15:49:00 — info: copied gript.png in 67ms
28 Nov 15:49:04 — info: compiled phoenix.css and 1 cached file into app.css in 77ms
28 Nov 15:49:33 — info: compiled phoenix.css and 1 cached file into app.css in 75ms

Перезагрузите веб-браузер или загрузите http: // localhost: 4000 .

Обновленная целевая страница

Чтобы изменить содержимое вашего шаблона, просто просмотрите файлы в web/templates/layout и web/templates/page . Вы можете начать изменять файлы, чтобы увидеть изменения в вашем приложении.

Стандартный шаблонизатор Phoenix использует EEx, что означает Embedded Elixir . Все файлы шаблонов имеют расширение .eex .

Шаблоны ограничены видом, который, в свою очередь, ограничен контроллером.

Phoenix создает каталог web/templates куда мы можем поместить все это. Для организации лучше всего указывать пространство имен, поэтому, если вы хотите создать новую страницу, это означает, что вам нужно создать новый каталог в web/templates а затем создать в нем файл index.html.eex (например, web/templates/<My-New-Page>/index.html.eex ).

Давайте сделаем это сейчас. Создайте web/templates/about/index.html.eex и сделайте так, чтобы это выглядело так:

1
<div class=»jumbotron»> <h2>About my app</h2> </div>

В Фениксе, часть представления парадигмы проектирования MVC выполняет несколько важных заданий.

Для одного представления визуализируют шаблоны. Кроме того, они выступают в качестве уровня представления необработанных данных от контроллера, выступая в качестве посредника при подготовке их для использования в шаблоне.

Для примера возьмем общую гипотетическую структуру данных, которая представляет пользователя с полем first_name полем last_name . Теперь для шаблона мы хотим показать полное имя пользователя.

Для лучшего подхода мы напишем функцию для объединения first_name и last_name и предоставим нам помощника в представлении, чтобы написать чистый, краткий и легко читаемый код шаблона.

Для рендеринга любых шаблонов для нашего AboutController нам нужен AboutView .

Примечание. Здесь важны имена — первая часть имен представления и контроллера должна совпадать.

Создайте web/views/about_view.ex и сделайте так, чтобы это выглядело так:

1
2
3
defmodule HelloWorld.AboutView do
  use HelloWorld.Web, :view
end

Чтобы увидеть новую страницу, вам нужно будет настроить маршрут и контроллер для вашего представления и шаблона.

Поскольку Phoenix работает над парадигмой MVC, нам нужно заполнить все части. Это не так много работы, хотя.

На простом английском языке: Маршруты отображают уникальные пары HTTP глагол / путь к парам контроллер / действие для дальнейшего выполнения.

Phoenix автоматически создает для нас файл роутера в новом приложении на web/router.ex . Здесь мы будем работать для следующего раздела.

Маршрут по умолчанию «Добро пожаловать в Феникс!» Страница выглядит следующим образом.

Это означает, что нужно перехватить все запросы, сделанные при посещении http: // localhost: 4000 / в браузере (который выдает HTTP- GET ) по пути приложения / root, и отправить все эти запросы в функцию index в модуле HelloPhoenix.PageController определено в web/controllers/page_controller.ex .

Страница, которую мы собираемся создать, просто скажет «О моем приложении», когда мы укажем нашему браузеру http: // localhost: 4000 / about . Вы можете добавить больше информации, чтобы соответствовать вашему приложению в шаблоне, так что просто впишите и напишите в своем HTML!

Для нашей страницы о нас нужно определить маршрут. Так что просто откройте web/router.ex в текстовом редакторе. По умолчанию он будет содержать следующее; Для получения дополнительной информации о маршрутизации обратитесь к официальному руководству по маршрутизации .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
defmodule HelloPhoenix.Router do
  use HelloPhoenix.Web, :router
 
  pipeline :browser do
    plug :accepts, [«html»]
    plug :fetch_session
    plug :fetch_flash
    plug :protect_from_forgery
    plug :put_secure_browser_headers
  end
 
  pipeline :api do
    plug :accepts, [«json»]
  end
 
  scope «/», HelloPhoenix do
    pipe_through :browser # Use the default browser stack
 
    get «/», PageController, :index
  end
 
  # Other scopes may use custom stacks.
  # scope «/api», HelloPhoenix do
  # pipe_through :api
  # end
end

Для нашего раздела about, давайте добавим новый маршрут к маршрутизатору для запроса GET в /about . Он будет обработан HelloPhoenix.AboutController , который мы HelloPhoenix.AboutController в следующей части.

Для GET to /about добавьте эту строку в блок scope "/" области действия router.ex :

1
get «/about», AboutController, :index

Полный блок будет выглядеть так:

1
2
3
4
5
6
scope «/», HelloPhoenix do
  pipe_through :browser # Use the default browser stack
 
  get «/», PageController, :index
  get «/about», AboutController, :index
end

Мы настроили маршрут, вид и шаблон. Итак, давайте теперь соберем все части вместе, чтобы мы могли просматривать их в браузере.

Контроллеры определяются как модули Elixir, а действия внутри контроллера являются функциями Elixir. Цель действий — собрать любые данные и выполнить любые задачи, необходимые для рендеринга.

Для маршрута /about нам нужен модуль HelloWorld.AboutController с действием index/2 .

Для этого нам нужно создать web/controllers/about_controller.ex и поместить в него следующее:

1
2
3
4
5
6
7
defmodule HelloWorld.AboutController do
  use HelloWorld.Web, :controller
 
  def index(conn, _params) do
    render conn, «index.html»
  end
end

Для получения дополнительной информации о контроллерах см. Официальное руководство по контроллерам .

Все действия контроллера принимают два аргумента. Первым из них является conn , структура, которая содержит загрузку данных о запросе.

Второе — это params , которые являются параметрами запроса. Здесь мы не используем params и избегаем предупреждений компилятора, добавляя ведущий _ .

Ядром этого действия является render conn, "index.html" . Это говорит Фениксу найти шаблон index.html.eex и отобразить его. Phoenix будет искать шаблон в каталоге, названном в честь нашего контроллера, поэтому web/templates/hello .

Примечание. Использование атома в качестве имени шаблона также будет работать здесь: render conn, :index , например, при использовании атома :index . Но шаблон будет выбран на основе заголовков Accept, например, «index.html» или «index.json».

Посещение http: // localhost: 4000 / about URL теперь отобразит шаблон, контроллер, представление и маршрут, которые мы определили до сих пор!

О странице моего приложения

Итак, теперь мы создали страницу и немного настроили приложение. Но как мы на самом деле делаем что-то с пользовательским вводом? Действия.

Запросы для нашей страницы about будут обрабатываться HelloWorld.AboutController с использованием действия show. Как мы уже определили контроллер на последних шагах, нам просто нужно добавить в код способ сохранения переменной, которая передается через URL-адрес, например: http: // localhost: 4000 / about / weather .

Теперь мы изменим код, чтобы сопоставить новый параметр запроса GET URL через контроллер и, в конечном итоге, с шаблоном, используя сопоставление с образцом Elixir.

Добавьте следующее в модуль в web/controllers/about_controller.ex :

1
2
3
def show(conn, %{«appName» => appName}) do
  render conn, «show.html», appName: appName
end

Несколько интересных мест здесь:

  • Мы сопоставляем шаблон с параметрами, передаваемыми в функцию show, так что переменная appName будет связана со значением из URL.
  • Для нашего примера URL ( http: // localhost: 4000 / about / weather ) переменная appName будет содержать значение weather.
  • В действии show также передается третий аргумент для функции рендеринга: пара ключ / значение, где atom :appName является ключом, а переменная appName передается в качестве значения.

Полный список web/controllers/about_controller.ex так:

01
02
03
04
05
06
07
08
09
10
11
defmodule HelloWorld.AboutController do
  use HelloWorld.Web, :controller
 
  def index(conn, _params) do
    render conn, «index.html»
  end
 
  def show(conn, %{«appName» => appName}) do
    render conn, «show.html», appName: appName
  end
end

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

Создайте файл web/templates/about/show.html.eex и добавьте следующее:

1
<div class=»jumbotron»> <h2>About <%= @appName %></h2>

Мы используем специальный синтаксис EEx <%= %> для Embedded Elixir. Открывающий тег имеет знак = , означающий, что код Elixir между ними будет выполнен, и в свою очередь вывод заменит тег.

Наша переменная для имени приложения отображается как @appName . В этом случае это не атрибут модуля, но на самом деле это особый бит метапрограммированного синтаксиса, который Map.get(assigns, :appName) . Результат намного приятнее для глаз и намного проще работать с шаблоном.

Например, чтобы мы могли видеть маршрут http: // localhost: 4000 / about / weather , нам нужно определить маршрут для связи с действием show для только что определенного контроллера.

1
2
3
4
5
6
7
scope «/», HelloWorld do
  pipe_through :browser # Use the default browser stack.
 
  get «/», PageController, :index
  get «/about», AboutController, :index
  get «/about/:appName», AboutController, :show
end

Теперь наша работа завершена! Попробуйте это, посетив URL http: // localhost: 4000 / about / weather .

Целевая страница для погоды

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

Мы затронули настройку функций Ecto для PostgreSQL, но для более подробного ознакомления с частью модели парадигмы MVC, пожалуйста, продолжите чтение в руководстве Ecto .

Что касается взаимодействия с пользователем и создания аутентификации, например, пожалуйста, продолжите свое обучение в руководстве по Plug на официальной документации Phoenix .