Часть 3. Администратор данных

Часть 1. Введение
Часть 2. Пусконаладка предприятия
Часть 3. Администратор данных
Часть 4. Администратор процессов
Часть 5. Структура приложения
Часть 6. Публикация в GCP

В этой статье я расскажу как написать простую админку для KVS на NITRO и N2O на 5 строчек!.

Elixir прелюдия

Чтобы не писать каждый раз эту прелюдию для каждого приложения приведем на примере PLM проекта, который уже включает почти весь стек: N2O, KVS, BPE, а также другие N2O протоколы CHAT, NITRO. NITRO — является портом Nitrogen Web Framework написанный Расти Клопхаусом.

config/config.exs

use Mix.Config config :n2o, pickler: :n2o_secret, mq: :n2o_syn, port: 8043, proto: CHAT.Server mqtt_services: ['erp', 'plm'], ws_services: ['chat'], upload: "./priv/static", protocols: [:n2o_heart, :n2o_nitro, :n2o_ftp, :bpe_n2o, CHAT.TXT], routes: KVS.Routes config :kvs, dba: :kvs_rocks, dba_st: :kvs_st, schema: [:kvs, :kvs_stream, :bpe_metainfo] config :erp, boot: [:erp_boot, :acc_boot, :pay_boot, :plm_boot, :fin_boot]

mix.exs

defmodule PLM.Mixfile do use Mix.Project def project() do [ app: :plm, version: "0.7.1", elixir: "~> 1.8.1", description: "PLM Product Lifecycle Management", deps: deps() ] end def application() do [ mod: {PLM.Application, []}, applications: [:ranch, :cowboy, :rocksdb, :kvs, :syn, :erp, :bpe, :n2o, :chat] ] end def deps() do [ {:ex_doc, "~> 0.20.2", only: :dev}, {:asn1ex, github: "vicentfg/asn1ex", only: :dev}, {:cowboy, "~> 2.5.0"}, {:rocksdb, "~> 1.2.0"}, {:chat, "~> 3.7.2"}, {:n2o, "~> 6.7.1"}, {:syn, "~> 1.6.3"}, {:kvs, "~> 6.7.4"}, {:erp, "~> 0.7.6"}, {:bpe, "~> 4.7.3"}, {:nitro, "~> 4.7.3"} ] end emd

Админка на Нитрогене

Админку напишем на Эрланге, хотя можно и на Эликсире! Но ничего, в следующей статье покажем как на эликсире писать админку BPE! Контроллер страницы состоит из 5 сообщений.

Красным цветом выделены идентификаторы DOM элементов, которые заменяются на пререндереные на сервере куски HTML сниппетов. Как это происходит в Live View и даже PHP! Но в отличии от и использования Suck and Blow нотации для темплейт энжинов, таких как ASP, PHP, EEX, DTL и других, NITRO предлает встраивание в хост язык или глубокое встаивание HTML5 в Erlang в данном случае и является фреймворком находящимя в своём классе: OCaml Ocsigen, Scala Lift, SML UrWeb, F# WebSharper, Erlang Nitrogen. Основная идея — пишем все на одном языке и обучать новичков надо одному языку причем сразу показывая как писать на нем веб приложения, которые и так все понимают как писать.

lib/events/kvs.ex

defmodule KVS.Index do use N2O, with: [:n2o, :nitro] use FORM use KVS require ERP require Logger def parse(ERP."Employee"(person: ERP."Person"(cn: name))), do: name def parse(_), do: [] def event(:init), do: [:user, :writers, :session, :enode, :disc, :ram] |> Enum.map(fn x -> [NITRO.clear(x), send(self(), {:direct, x})] end) def event(:ram), do: NITRO.update(:ram, span(body: ram(:os.type()))) def event(:user), do: NITRO.update(:user, span(body: parse(:n2o.user()))) def event(:session), do: NITRO.update(:session, span(body: :n2o.sid())) def event(:enode), do: NITRO.update(:enode, span(body: :lists.concat([:erlang.node()]))) def event(:disc), do: NITRO.update(:disc, span(body: hd(:string.tokens(:os.cmd('du -hs rocksdb'), '\t')))) def event({:link, i}), do: [ NITRO.clear(:feeds), :kvs.feed(i) |> Enum.map(fn t -> NITRO.insert_bottom(:feeds, panel(body: NITRO.compact(t))) end) ] def event(:writers), do: :writer |> :kvs.all() |> :lists.sort() |> Enum.map(fn writer(id: i, count: c) -> NITRO.insert_bottom( :writers, panel(body: [link(body: i, postback: {:link, i}), ' (' ++ NITRO.to_list(c) ++ ')'])) end) def event(_), do: [] def ram({_, :darwin}) do mem = :os.cmd('top -l 1 -s 0 | grep PhysMem') [_, l, _, r] = :string.tokens( :lists.filter(fn x -> :lists.member(x, '0123456789MG ') end, mem), ' ') :lists.concat([NITRO.meg(NITRO.num(l)), '/', NITRO.meg(NITRO.num(l) + NITRO.num(r))]) end def ram({_, :linux}) do [t, u, _, _, b, c] = :lists.sublist( :string.tokens( :os.cmd('free'), ' \n'), 8, 6) mem = :erlang.list_to_integer(u) - :erlang.list_to_integer(b) + :erlang.list_to_integer(c) :lists.concat([NITRO.meg(mem * 1000), '/', NITRO.meg(:erlang.list_to_integer(t) * 1000)]) end def ram(os), do: NITRO.compact(os) end

lib/routes.ex

defmodule KVS.Routes do use N2O, with: [:n2o, :nitro] def finish(state, context), do: {:ok, state, context} def init(state, context) do %{path: path} = cx(context, :req) {:ok, state, cx(context, path: path, module: route_prefix(path))} end defp route_prefix(<<"/ws/", p::binary>>), do: route(p) defp route_prefix(<<"/", p::binary>>), do: route(p) defp route_prefix(path), do: route(path) def route(<<"kvs", _::binary>>), do: KVS.Index def route(<<"app/kvs", _::binary>>), do: KVS.Index def route(_), do: KVS.Index end

Контейнер Приложения

priv/static/kvs.htm

<nav> <a href='login.htm'>LOGIN</a> <a href='plm.htm'>PLM</a> <a href='bpe.htm'>BPE</a> <a href='form.htm'>FORM</a> <a href='kvs.htm' style="background:#ededed;">KVS</a> </nav> <aside> <article> <section> <h2>KVS</h2> <p>Abstract chain database</p> <div>USER: <span id=user></span> SSD: <span id=disc></span> RAM: <span id=ram></span><br> SESSION: <span id=session></span> VM: <span id=enode></span></div> </section> <div class=container> <div><h2>WRITERS</h2><div id=writers></div></div> <div><h2>CHAINS</h2><div id=feeds>To display the chain select the writer.</div></div> </div> </article> </aside>

Код приложения можно найти в следующих репозиториях:

enterprizing/plm
o7/bud