Статьи

Феникс для рельсов

Феникс

Мой последний пост был посвящен WebSockets в экосистеме Ruby с особым акцентом на поддержку WebSockets в Rails. Когда я узнал о сложных отношениях между WebSockets и Ruby, я обнаружил веб-инфраструктуру, которая упрощает WebSockets, в то же время позволяя нам делать вещи «в стиле Rails». Это называется Феникс. Хотя WebSockets — это то, как я открыл для себя Phoenix, он может предложить гораздо больше, чем просто «более простые WebSockets».

К сожалению (или к счастью, в зависимости от того, как вы смотрите на вещи), Phoenix не является фреймворком Ruby. Вместо этого он написан и основан на языке Elixir, который построен поверх виртуальной машины Erlang. В этой статье мы рассмотрим, почему имеет смысл рассматривать комбинацию Elixir + Phoenix как Rubyist, а также основы платформы Phoenix.

Эликсир

Чтобы понять, что делает Elixir особенным, мы должны немного понять Erlang и Erlang VM. Erlang изначально разрабатывался в Ericsson (то есть, в большей или меньшей степени, телекоммуникационной компанией) для создания систем, которые бы работали независимо от того, какое безумие происходит в мире. Хотя Erlang не завоевал популярность, как чума, он использовался для некоторых довольно крупных проектов многими компаниями. Некоторые примеры: WhatsApp работает на основе архитектуры Erlang, Facebook изначально использовал Erlang для своей архитектуры чата, а Basho (компания, стоящая за Riak) активно использует Erlang и высоко ценит его . Есть ряд причин, по которым Erlang так хорош для построения высокодоступных распределенных систем (например, концепция супервизоров особенно хорошо выполняется в Erlang). Тем не менее, Erlang является функциональным языком с рядом особенностей, которые делают его немного неприятным для многих.

Erlang работает поверх виртуальной машины Erlang. Если вы знакомы с Java, это похоже на отношения между Java и JVM. Elixir — это другой язык, который работает поверх виртуальной машины Erlang. Итак, Elixir предлагает невероятно простое взаимодействие с Erlang (мы можем вызывать функции Erlang практически без затрат времени выполнения) с другим синтаксисом, который, на мой взгляд, более приятен, чем синтаксис Erlang.

Если вы хотите быстро освоиться с основами эликсира, у нас есть пара статей, которые вы можете быстро просмотреть. Даже если у вас нет опыта работы с Elixir, но у вас есть опыт работы с Ruby, вы поймете, что не так уж и сложно понять, как работает Elixir.

Феникс

Итак, что такое Феникс ? Это подобный Rails фреймворк, созданный для Elixir. Вы можете спросить: в чем смысл изучения еще одной основы еще одного языка, который, кажется, никто еще не использует (пока)? Есть несколько причин. Во-первых, Erlang VM довольно чертовски быстр, и Elixir наследует эту скорость. Есть тесты, которые показывают это. Но, конечно, мы можем сидеть без дела весь день и пробивать дыры в них, так что вам просто нужно создать что-то с использованием обеих технологий, чтобы поверить в это.

Во-вторых, Elixir — интересный язык, потому что ему удается объединить функциональное программирование, ООП и концепцию метапрограммирования, подобную Lisp (макросы Elixir довольно безумно мощны). Наконец, Rails определенно чувствует себя немного закулисным, когда дело доходит до таких вещей, как WebSockets. Феникс идет с этим справа от коробки. Лучше всего то, что вы получаете большинство из этих преимуществ, не оставляя тонкостей Rails. Конечно, для Phoenix не так много пакетов, как для Rails, но сама инфраструктура довольно полнофункциональна. Давайте посмотрим на основы Феникса.

Установка

Мы разберем шаги установки в основном потому, что они скучны, и есть другие ресурсы, которые, вероятно, объяснят их лучше (и мы с ними свяжемся). Прежде всего, вам нужно установить Elixir, который, к счастью, имеет довольно понятную страницу установки . Эликсирный эквивалент пары RubyGems / Bundler называется «Mix». Мы можем использовать это для установки Phoenix, как описано на странице установки Phoenix .

Построить это

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

Итак, что мы будем строить? В течение дня я постоянно забываю о мелочах, и мне хотелось бы, чтобы мне напомнили, пока я набиваю код на своем ноутбуке. Чтобы решить эту проблему, мы собираемся создать веб-приложение, на которое мы можем устанавливать напоминания, и оно будет напоминать нам по электронной почте. Мы могли бы также сделать это с помощью API уведомлений HTML5, но мы продемонстрируем некоторые важные концепции внутри Phoenix с электронной почтой.

Базовая структура

Мы начинаем с «пустого» приложения Phoenix, запустив:

mix phoenix.new remynders

Вы должны получить сообщение об установке, которое заканчивается этим:

 We are all set! Run your Phoenix application:

$ cd remynders
$ mix phoenix.server

Вы также можете запустить его внутри IEx (Interactive Elixir) как:

 $ iex -S mix phoenix.server

Мы можем взглянуть на то, что имеем до сих пор после запуска сервера по адресу http://127.0.0.1:4000 . Мы можем оставить сервер работающим, потому что, как и Rails, сервер Phoenix выполняет перезагрузку живого кода. Чтобы начать работу с нашим приложением «remynders», нам нужно изменить маршрут, чтобы у нас был маршрут, который не заканчивается на странице по умолчанию в Phoenix. Откройте файл web / router.ex (более или менее аналогичный конфигурации Rails / rout.rb ):

 defmodule Remynders.IndexView do
  use Remynders.Web, :view
end

Наконец, у нас есть шаблон в web / templates / index.html.eex :

 <div class="headline">Set Quick Reminders Quickly</div>

<a href="#" class="button">Create a reminder</a>

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

  1. Наше творение в настоящее время выглядит как мусор.
  2. На этой странице еще осталось несколько маркетинговых материалов «Phoenix Framework».

Чтобы решить эти проблемы, мы должны изменить макет и немного CSS.

Макеты

Если вы откроете web / templates / layout / application.html.eex , вы увидите разметку, которая повсюду сбрасывает рекламные материалы Phoenix. Вместо этого мы заменим его нашей разметкой:

 <!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="">
    <meta name="author" content="">

    <title>Remynders</title>
    <link rel="stylesheet" href="<%= static_path(@conn, "/css/app.css") %>">
  </head>

  <body>
    <div class="page_wrapper">
      <div id="logo">
      <h1>Remynders</h1>
      </div>

      <p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p>
      <p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>

      <%= @inner %>
    </div>
    <script src="<%= static_path(@conn, "/js/app.js") %>"></script>
    <script>require("web/static/js/app")</script>
  </body>
</html>

Единственная часть этого, с которой мы должны иметь дело в данный момент, это где наши визуализированные шаблоны будут просто «вставлены» (опять же, невероятно похоже на концепцию Rails application.html.erb ). Итак, мы решили часть нашей проблемы, но эстетика — не лучшее качество Remynders … пока.

Статические файлы

Мы хотим изменить CSS как для макета, так и для данной страницы. Быстрый взгляд на web / static / css / app.scss показывает, что Phoenix поставляется с копией Bootstrap, готовой к работе. Поскольку это небольшое приложение, и мы не слишком заботимся о поддержке множества различных браузеров со сложным стилем, мы просто избавимся от Bootstrap. Мы заменим app.css на:

 body {
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  margin: 0;
  padding: 0;
  border: none;
}


#logo h1 {
  font-size: 20px;
  margin: auto;
  text-align: center;
  padding-bottom: 20px;
  color: #fff;
  background-color: #cc0000;
  padding-top: 10px;
  margin-bottom: 40px;
  text-transform: uppercase;
  letter-spacing: 2px;
}

.headline {
  font-weight: bold;
  text-align: center;
  font-size: 40px;
}

.button {
  display: inline-block;
  border: 4px solid #000;
  padding: 20px;
  color: #000;
  text-transform: uppercase;
  font-weight: bold;
  text-decoration: none;
  font-size: 12px;
  letter-spacing: 1px;
  margin: 35px;
}

.button:hover {
  transition: background-color 0.3s ease;
  background-color: #cc0000;
  color: #fff;
  border: 4px solid #cc0000;
}

.center_block {
  margin: auto;
  text-align: center;
}

Этот CSS делает индексную страницу немного лучше. Как видно, каталог app / static содержит статические элементы нашего веб-приложения (например, Javascript, CSS и т. Д.).

Помощники

Прямо сейчас наша страница индекса более или менее статична. Мы хотим связать его с формой, которая позволит нам создать напоминание. Прежде всего, нам нужно исправить наши маршруты, чтобы мы настроили маршрутизацию ресурсов для «Напоминания» (точно так же, как в Rails):

 resources "/reminders", ReminderController

Это устанавливает маршруты ресурсов, но нам нужен ReminderController Опять же, мы создаем новый контроллер в web / controllers / Remder_controller.ex :

 defmodule Remynders.ReminderController do
  use Remynders.Web, :controller

  plug :action

  def new(conn, _params) do
    render conn, "new.html"
  end
end

Теперь мы исправим ссылку на странице индекса. Во-первых, используйте помощник URL в web / views / index_view.ex :

 defmodule Remynders.IndexView do
  use Remynders.Web, :view
  alias Remynders.Router.Helpers

  def new_reminder_path do
    Helpers.reminder_path(Remynders.Endpoint, :new)
  end
end

Мы можем сделать это, открыв web / templates / index.html.eex и заменив:

 <a href="#" class="button">Create a reminder</a>

с этим:

 <a href="<%= new_reminder_path %>" class="button">

Итак, вот что мы сделали. Прежде всего, мы добавили псевдоним в представлении:

 alias Remynders.Router.Helpers

Это позволяет нам обращаться к Remynders.Router.HelpersHelpers Чисто для удобства. Затем мы использовали реальный помощник URL:

 Helpers.reminder_path(Remynders.Endpoint, :new)

Который определил это как функцию в представлении. Это позволяет нам вызывать функцию из фактического шаблона для настройки ссылки. Все это кажется действительно сложным по сравнению с Rails ‘ link_to В основном мы делали эту функцию представления, чтобы продемонстрировать, как мы можем вызывать методы в представлении из шаблона. Мы действительно можем заменить наши пометить с помощью помощника HTML:

 <%= link "Create Reminder", to: reminder_path(@conn, :new), class: "button" %>

Теперь, если вы перейдете на страницу индекса и нажмете на кнопку, вы получите … страницу с ошибкой. Мы еще не настроили вид!

До скорого

Пока что может показаться, что мы далеко не продвинулись. Мы работали только с несколькими очень простыми страницами через Phoenix и едва разработали помощника по URL. Но мы заложили прочную основу, на которой мы можем быстро строить с помощью помощников форм, моделей, фоновой обработки и API уведомлений HTML5. Мы рассмотрим многое из этого в следующей статье!

Дальнейшее чтение

Некоторые части Phoenix еще не имеют отличной документации, но большую часть того, с чем мы работали, можно найти в руководствах по Phoenix . Phoenix также поставляется с большим количеством справочной документации, которая может быть очень полезна, чтобы понять, как использовать определенную функцию. Большинство методов создания HTML (например, linkзадокументирована отдельно .