In meinen vorherigen Artikeln haben wir verschiedene Elixir-Begriffe besprochen und eine Menge Code geschrieben. Was wir jedoch nicht besprochen haben, ist, wie Sie Ihren Code so strukturieren und organisieren, dass er leicht zu verwalten und zu veröffentlichen ist.
Anwendungen sind für Erlang und Elixir sehr häufig und werden zum Erstellen wiederverwendbarer Komponenten verwendet, die sich als eigenständige Einheiten verhalten. Eine Anwendung verfügt möglicherweise über eine eigene Überwachungsstruktur und -konfiguration und kann sich auf andere Anwendungen verlassen, die entweder lokal oder auf einem Remote-Server verfügbar sind. Alles in allem ist das Arbeiten mit Anwendungen nicht so komplex, und die Leute, die beispielsweise aus der Welt von Ruby stammen, werden viele bekannte Konzepte finden.
In diesem Artikel erfahren Sie, was Anwendungen sind, wie sie erstellt werden können, wie Abhängigkeiten angegeben und installiert werden und wie Umgebungswerte bereitgestellt werden. Am Ende des Artikels werden wir ein wenig üben und einen webbasierten Rechner erstellen.
Ich werde Elixir 1.5 in diesem Artikel verwenden (es wurde vor einigen Monaten veröffentlicht), aber alle erläuterten Konzepte sollten auch für Version 1.4 gelten.
Einige mögen argumentieren, dass der Begriff "Anwendung" nicht sehr passend ist, da er in Erlang und Elixir tatsächlich eine Komponente oder einen Code mit einer Reihe von Abhängigkeiten bedeutet. Die Anwendung selbst kann als Abhängigkeit verwendet werden - in Ruby-Welt würden wir sie "Juwel" nennen..
Alles in allem sind Anwendungen in Elixir sehr verbreitet und ermöglichen Ihnen die Erstellung wiederverwendbarer Komponenten und bieten zudem ein einfaches Abhängigkeitsmanagement. Sie bestehen aus einem oder mehreren Modulen mit null oder mehr Abhängigkeiten und werden von der Anwendungsressourcendatei beschrieben. Diese Datei enthält Informationen zum Namen, zur Version, zu den Modulen, zu den Abhängigkeiten der Anwendung und zu anderen Informationen. Sie können die Ressourcendatei manuell erstellen, dies ist jedoch mit dem Mix-Tool, das eine korrekte Ordnerstruktur vorbereitet, viel einfacher.
Schauen wir uns also an, wie wir eine neue Elixir-Anwendung erstellen können!
Um eine neue Anwendung zu erstellen, müssen Sie nur den folgenden Befehl ausführen:
mischen Sie neuen app_name
Wir können auch die --sup
Flag, um einen leeren Supervisor für uns zu erstellen. Lassen Sie uns eine neue Anwendung erstellen Probe
diesen Weg:
neue Probe mischen --sup
Dieser Befehl erstellt eine Probe Verzeichnis für Sie mit einer Handvoll Dateien und Ordnern. Lass mich dich schnell durch sie führen:
Start / 2
Funktion, die einen leeren Supervisor erstellt.Projekt
Bei dieser Funktion geben Sie den Namen der App (als Atom), die Version und die Umgebung an. Das Anwendung
Die Funktion enthält Informationen zu den Anwendungsmodul-Callback- und Laufzeitabhängigkeiten. In unserem Fall, Sample.Application
wird als Callback des Anwendungsmoduls (das als Haupteinstiegspunkt behandelt werden kann) festgelegt, und es muss a definiert werden Start / 2
Funktion. Wie bereits erwähnt, wurde diese Funktion bereits von der mischen
Werkzeug. Zuletzt die deps
Funktion listet Abhängigkeiten der Bauzeit auf.Es ist sehr wichtig, zwischen den Abhängigkeiten der Laufzeit und der Buildzeit zu unterscheiden. Build-Time-Abhängigkeiten werden vom geladen mischen
Tool während der Kompilierung und werden grundsätzlich in Ihre Anwendung kompiliert.
Sie können beispielsweise von einem Dienst wie GitHub oder von der Hex.pm-Website abgerufen werden, einem externen Paketmanager, der Tausende von Komponenten für Elixir und Erlang speichert. Laufzeitabhängigkeiten werden vor dem Start der Anwendung gestartet. Sie sind bereits zusammengestellt und für uns verfügbar.
Es gibt verschiedene Möglichkeiten, um Build-Time-Abhängigkeiten in a anzugeben mix.exs Datei. Wenn Sie eine Anwendung von der hex.pm-Website verwenden möchten, sagen Sie einfach:
: dependency_name, "~> 0.0.1"
Das erste Argument ist immer ein Atom, das den Namen der Anwendung darstellt. Die zweite ist die Anforderung, eine Version, die Sie verwenden möchten. Sie wird vom Version-Modul analysiert. In diesem Beispiel, ~>
bedeutet, dass wir mindestens die Version herunterladen möchten 0.0.1
oder höher aber weniger als 0,1,0
. Wenn wir sagen ~> 1,0
, es bedeutet, dass wir Version größer oder gleich verwenden möchten 1,0
aber weniger als 2,0
. Es gibt auch Betreiber wie ==
, >
, <
, > =
, und <=
verfügbar.
Es ist auch möglich, eine direkt anzugeben : git
oder ein :Pfad
Möglichkeit:
: gettext, git: "https://github.com/elixir-lang/gettext.git", Tag: "0,1" : local_dependency, path: "path / to / local_dependency"
Da ist auch ein : github
Abkürzung, die es uns erlaubt, nur den Namen des Besitzers und eines Repos anzugeben:
: gettext, github: "elixir-lang / gettext"
Um alle Abhängigkeiten herunterzuladen und zu kompilieren, führen Sie Folgendes aus:
mix deps.get
Dadurch wird ein Hex-Client installiert, falls Sie noch keinen haben, und prüfen Sie, ob eine der Abhängigkeiten aktualisiert werden muss. Zum Beispiel können Sie Poison-eine Lösung angeben, um JSON-als eine Abhängigkeit wie folgt zu analysieren:
defp deps tun [: poison, "~> 3.1"] ende
Dann renne:
mix deps.get
Sie sehen eine ähnliche Ausgabe:
Ausführen der Abhängigkeitsauflösung… Auflösung der Abhängigkeit abgeschlossen: poison 3.1.0 * poison erhalten (Hex-Paket) Paket überprüfen (https://repo.hex.pm/tarballs/poison-3.1.0.tar) Paket abgerufen
Poison ist jetzt kompiliert und auf Ihrem PC verfügbar. Was mehr ist, a Mix.lock Die Datei wird automatisch erstellt. Diese Datei enthält die genauen Versionen der Abhängigkeiten, die beim Booten der Anwendung verwendet werden sollen.
Führen Sie den folgenden Befehl aus, um mehr über Abhängigkeiten zu erfahren:
mix help deps
Anwendungen sind Verhaltensweisen, genau wie GenServer und Supervisors, über die wir in den vorherigen Artikeln gesprochen haben. Wie ich oben bereits erwähnt habe, bieten wir ein Callback-Modul im mix.exs Datei auf folgende Weise:
def application do [mod: Sample.Application, []] ende
Sample.Application
ist der Name des Moduls, während []
kann eine Liste von Argumenten enthalten, die an das übergeben werden sollen Start / 2
Funktion. Das Start / 2
Funktion muss implementiert werden, damit die Anwendung ordnungsgemäß startet.
Das application.ex enthält das Callback-Modul, das folgendermaßen aussieht:
defmodule Sample.Application verwendet Application def start (_type, _args) do children = [] opts = [strategie:: one_for_one, name: Sample.Supervisor] Supervisor.start_link (children, opts) end end
Das Start / 2
Funktion muss entweder zurückkehren : ok, pid
(mit optionalem Status als drittes Element) oder : Fehler, Grund
.
Erwähnenswert ist auch, dass Anwendungen das Callback-Modul überhaupt nicht wirklich benötigen. Es bedeutet, dass die Anwendung innerhalb der mix.exs Datei kann sehr minimalistisch werden:
def application do [] ende
Solche Anwendungen werden aufgerufen Bibliotheksanwendungen. Sie haben keinen Überwachungsbaum, können aber trotzdem von anderen Anwendungen als Abhängigkeiten verwendet werden. Ein Beispiel für eine Bibliotheksanwendung wäre Poison, das wir im vorherigen Abschnitt als Abhängigkeit angegeben haben.
Die einfachste Möglichkeit, Ihre Anwendung zu starten, besteht darin, den folgenden Befehl auszuführen:
iex -S mix
Sie sehen eine Ausgabe, die dieser ähnelt:
2 Dateien kompilieren (.ex) Generierte Beispielanwendung
EIN _bauen Das Verzeichnis wird innerhalb von erstellt Probe Mappe. Es wird enthalten .Strahl Dateien sowie einige andere Dateien und Ordner.
Wenn Sie keine Elixir-Shell starten möchten, können Sie eine andere Option ausführen:
Mischlauf
Das Problem ist jedoch, dass die Anwendung beendet wird, sobald die Start
Funktion beendet seine Arbeit. Daher können Sie die --ohne halt
Schlüssel, um die Anwendung so lange wie nötig laufen zu lassen:
Mixlauf - no-halt
Dasselbe kann mit der erreicht werden Elixier
Befehl:
Elixier-Mix-Mix - no-halt
Beachten Sie jedoch, dass die Anwendung angehalten wird, sobald Sie das Terminal schließen, an dem dieser Befehl ausgeführt wurde. Dies kann vermieden werden, indem Sie Ihre Anwendung in einem getrennten Modus starten:
Elixier-Mix-Mix --no-halt - Detached
In manchen Fällen möchten Sie möglicherweise, dass der Benutzer einer Anwendung einige Parameter einstellt, bevor die App tatsächlich gestartet wird. Dies ist nützlich, wenn der Benutzer beispielsweise steuern kann, welchen Port ein Webserver überwachen soll. Solche Parameter können in der Anwendungsumgebung angegeben werden, bei der es sich um einen einfachen Schlüsselwertspeicher handelt.
Um einige Parameter zu lesen, verwenden Sie die fetch_env / 2
Funktion, die eine App und einen Schlüssel akzeptiert:
Application.fetch_env (: sample,: some_key)
Wenn der Schlüssel nicht gefunden werden kann, eine :Error
Atom wird zurückgegeben. Es gibt auch eine fetch_env! / 2
Funktion, die stattdessen einen Fehler auslöst und get_env / 3
das kann einen Standardwert liefern.
Um einen Parameter zu speichern, verwenden Sie put_env / 4
:
Application.put_env (: sample,: key,: value)
Der vierte Wert enthält Optionen und muss nicht festgelegt werden.
Um einen Schlüssel zu löschen, verwenden Sie die delete_env / 3
Funktion:
Application.delete_env (: sample,: key)
Wie liefern wir einen Wert für die Umgebung, wenn Sie eine App starten? Nun, solche Parameter werden mit der eingestellt --erl
Schlüssel auf folgende Weise eingeben:
iex --erl "-sample key value" -S mix
Sie können dann einfach den Wert abrufen:
Application.get_env: sample,: key # =>: value
Was passiert, wenn ein Benutzer beim Starten der Anwendung die Angabe eines Parameters vergisst? Nun, höchstwahrscheinlich müssen wir für solche Fälle einen Standardwert angeben. Es gibt zwei mögliche Orte, an denen Sie dies tun können: in der config.exs oder in der mix.exs Datei.
Die erste Option ist die bevorzugte, weil config.exs ist die Datei, die eigentlich verschiedene Konfigurationsoptionen speichern soll. Wenn Ihre Anwendung viele Umgebungsparameter hat, sollten Sie unbedingt dabei bleiben config.exs:
benutze Mix.Config config: sample, key:: value
Für eine kleinere Anwendung ist es jedoch in Ordnung, Umgebungswerte direkt im Inneren anzugeben mix.exs indem Sie die Anwendungsfunktion anpassen:
def application do [extra_applications: [: logger], mod: Sample.Application, [], env: [# <==== key: :value ] ] end
Okay, um Anwendungen in Aktion zu sehen, ändern wir das Beispiel, das bereits in meinen GenServer- und Supervisors-Artikeln beschrieben wurde. Dies ist ein einfacher Rechner, mit dem Benutzer verschiedene mathematische Operationen ausführen und das Ergebnis ganz einfach abrufen können.
Was ich tun möchte, ist, diesen Rechner webbasiert zu machen, damit wir POST-Anfragen senden können, um Berechnungen durchzuführen, und eine GET-Anfrage, um das Ergebnis zu erhalten.
Erstelle eine neue lib / calc_server.ex Datei mit folgendem Inhalt:
defmodule Sample.CalcServer verwendet GenServer. def start_link (initial_value) do GenServer.start_link (__ MODULE__, initial_value, name: __MODULE__) end def init (initial_value), wenn is_number (initial_value) do : ok, initial_value do def: (_) : stop, "Der Wert muss eine ganze Zahl sein!" end def add (number) do GenServer.cast (__ MODULE__, : add, number) end def Ergebnis do GenServer.call (__ MODULE__,: result) ende def handle_call (: result, _, state) do : antworten, state, state end def handle_cast (operation, state) do case operation do : add, number -> : noreply, state + number _ -> : stop, "nicht implementiert", state end end def terminate (_reason, _state) do IO.puts "Server beendet" end end
Wir werden nur Unterstützung für die hinzufügen
Operation. Alle anderen mathematischen Operationen können auf dieselbe Weise eingeführt werden. Ich werde sie hier nicht aufführen, um den Code kompakter zu gestalten.
Das CalcServer
nutzt GenServer
, also bekommen wir child_spec
automatisch und kann es von der Rückruffunktion wie folgt starten:
def start (_type, _args) do children = [Sample.CalcServer, 0] opts = [strategie:: one_for_one, name: Sample.Supervisor] Supervisor.start_link (children, opts) endet
0
Hier ist das erste Ergebnis. Ansonsten muss es eine Zahl sein CalcServer
wird sofort beendet.
Nun stellt sich die Frage, wie wir Web-Support hinzufügen. Dazu benötigen wir zwei Abhängigkeiten von Drittanbietern: Plug, der als Abstraktionsbibliothek fungiert, und Cowboy, der als tatsächlicher Webserver fungiert. Natürlich müssen wir diese Abhängigkeiten innerhalb von angeben mix.exs Datei:
defp deps tun [: cowboy, "~> 1.1", : plug, "~> 1.4"] ende
Nun können wir die Plug-Anwendung unter unserem eigenen Überwachungsbaum starten. Passen Sie die Startfunktion folgendermaßen an:
def start (_type, _args) do children = [Plug.Adapters.Cowboy.child_spec (: http, Sample.Router, [], [port: Application.fetch_env! (: sample,: port)])), Sample.CalcServer , 0] #… Ende
Hier bieten wir an child_spec
und Einstellung Sample.Router
um auf Anfragen zu antworten. Dieses Modul wird in Kürze erstellt. Was mir an diesem Setup nicht gefällt, ist, dass die Portnummer hartcodiert ist, was nicht wirklich bequem ist. Ich möchte es vielleicht beim Start der Anwendung optimieren, also speichern wir es stattdessen in der Umgebung:
Plug.Adapters.Cowboy.child_spec (: http, Sample.Router, [], [port: Application.fetch_env! (: Sample,: port)])
Geben Sie jetzt den Standard-Port-Wert in ein config.exs Datei:
config: Beispiel, Port: 8088
Großartig!
Was ist mit dem Router? Erstelle eine neue lib / router.ex Datei mit folgendem Inhalt:
defmodule Sample.Router verwendet Plug.Router-Plug: Match-Plug: Versandende
Nun müssen wir einige Routen definieren, um die Addition durchzuführen und das Ergebnis abzurufen:
get "/ result" do conn |> ok (to_string (Sample.CalcServer.result)) ende post "/ add" do fetch_number (conn) |> Sample.CalcServer.add conn |> ok Ende
Wir benutzen erhalten
und Post
Makros zur Definition der /Ergebnis
und /hinzufügen
Routen. Diese Makros setzen die conn
Objekt für uns.
OK
und Abrufnummer
sind private Funktionen, die auf folgende Weise definiert sind:
defp fetch_number (conn) do Plug.Conn.fetch_query_params (conn) .params ["number"] |> String.to_integer end defp ok (conn, Daten \\ "OK") do send_resp conn, 200, Datenende
fetch_query_params / 2
gibt ein Objekt mit allen Abfrageparametern zurück. Uns interessiert nur die Nummer, die der Benutzer an uns sendet. Alle Parameter sind anfangs Strings, daher müssen wir sie in eine Ganzzahl konvertieren.
send_resp / 3
sendet eine Antwort mit dem bereitgestellten Statuscode und einem Body an den Client. Wir werden hier keine Fehlerprüfung durchführen, daher wird der Code immer so sein 200
, Das heißt, alles ist in Ordnung.
Und das ist es! Jetzt können Sie die Anwendung auf eine der oben genannten Arten starten (z. B. durch Eingabe) iex -S mix
) und benutze die locken
Werkzeug zur Durchführung der Anfragen:
curl http: // localhost: 8088 / result # => 0 curl http: // localhost: 8088 / add? number = 1 -X POST # => OK curl http: // localhost: 8088 / result # => 1
In diesem Artikel haben wir Elixir-Anwendungen und deren Zweck beschrieben. Sie haben gelernt, wie Sie Anwendungen erstellen, verschiedene Arten von Informationen bereitstellen und Abhängigkeiten auflisten mix.exs Datei. Sie haben auch erfahren, wie Sie die Konfiguration in der App-Umgebung speichern und wie Sie Ihre Anwendung starten können. Zuletzt haben wir Anwendungen in Aktion gesehen und einen einfachen webbasierten Rechner erstellt.
Vergessen Sie nicht, dass die hex.pm-Website viele Hunderte von Anwendungen von Drittanbietern enthält, die zur Verwendung in Ihren Projekten bereit sind. Stellen Sie also sicher, dass Sie den Katalog durchsuchen und die für Sie passende Lösung auswählen!
Hoffentlich fanden Sie diesen Artikel nützlich und interessant. Ich danke dir, dass du bei mir bleibst und bis zum nächsten Mal.