O introducere în aplicațiile de elixir

În articolele mele anterioare am discutat diferitele termeni ai lui Elixir și am scris o cantitate mare de cod. Ceea ce nu am discutat, totuși, este modul de structurare și organizare a codului dvs., astfel încât să fie ușor de întreținut și eliberat. 

Aplicațiile sunt foarte frecvente pentru Erlang și Elixir și sunt folosite pentru a construi componente reutilizabile care se comportă ca unități independente. O aplicație poate avea propriul arbore de supervizare și configurație și se poate baza pe alte aplicații care sunt disponibile fie la nivel local, fie pe un server de la distanță. În general, lucrul cu aplicațiile nu este atât de complex, iar oamenii care au venit, de exemplu, din lumea Ruby, vor găsi multe concepte familiare.

În acest articol veți afla ce aplicații sunt, cum pot fi create, cum să specificați și să instalați dependențe și cum să furnizați valori de mediu. La sfârșitul articolului vom face niște exerciții și vom crea un calculator bazat pe web. 

Voi folosi Elixir 1.5 în acest articol (a fost lansat acum câteva luni), dar toate conceptele explicate ar trebui să se aplice și versiunii 1.4.

Aplicații?

Unii ar putea argumenta că termenul "aplicație" nu este foarte potrivit, deoarece în Erlang și Elixir înseamnă de fapt o componentă sau un cod care are o grămadă de dependențe. Aplicația însăși poate fi utilizată ca o dependență, de asemenea, în lumea Ruby pe care o numim o "bijuterie".

În general, aplicațiile sunt foarte frecvente în Elixir și vă permit să construiți componente reutilizabile, asigurând în același timp o gestionare ușoară a dependenței. Ele constau dintr-unul sau mai multe module cu zero sau mai multe dependențe și sunt descrise de fișierul de resurse al aplicației. Acest fișier conține informații despre numele aplicației, versiunea, modulele, dependențele și alte chestii. Puteți crea fișierul de resurse manual, dar este mult mai ușor să faceți acest lucru cu ajutorul instrumentului de mixare care va pregăti, de asemenea, o structură corectă pentru dvs.. 

Deci, să vedem cum putem crea o nouă aplicație Elixir!

Aplicatie noua

Pentru a crea o nouă aplicație, tot ce trebuie să faceți este să executați următoarea comandă:

amestecați noul app_name

De asemenea, putem oferi --cina steag pentru a crea un supraveghetor gol pentru noi. Să creați o nouă aplicație numită Probă pe aici:

amestecați un eșantion nou -

Această comandă va crea a probă directorul pentru tine cu o mână de fișiere și foldere înăuntru. Permiteți-mi să vă ghidez rapid prin ele:

  • config dosarul conține un singur fișier config.exs care, după cum puteți ghici, oferă configurație pentru aplicație. Inițial are câteva comentarii utile, dar nici o configurație. Rețineți, apropo, că configurația furnizată în acest fișier este limitată doar la aplicația în sine. Dacă încărcați aplicația ca o dependență, aceasta este config.exs vor fi efectiv ignorate.
  • lib este dosarul principal al aplicației care conține a sample.ex fișier și a probă dosar cu un application.ex fişier. application.ex definește un modul de apel invers cu un începe / 2 care creează un supervizor gol.
  • Test este dosarul care conține teste automate pentru aplicație. Nu vom discuta despre teste automate în acest articol.
  • mix.exs este fișierul care conține toate informațiile necesare despre aplicație. Există mai multe funcții aici. În interiorul proiect funcția, furnizați numele aplicației (ca atom), versiunea și mediul. cerere funcția conține informații despre dependența de apel inversă modulul de aplicație și despre timpul de execuție. În cazul nostru, Sample.Application este setată ca apel invers al modulului de aplicație (care poate fi tratat ca punct de intrare principal) și trebuie să definească a începe / 2 funcţie. După cum sa menționat deja mai sus, această funcție a fost deja creată pentru noi de către amesteca instrument. În cele din urmă, dependențele liste de funcții dependente de construire-timp.

dependenţe

Este destul de important să se facă distincția între dependența de execuție și dependența de timp. Condițiile de construire sunt încărcate de către amesteca în timpul procesului de compilare și sunt în esență compilate în aplicația dvs.. 

Acestea pot fi preluate de la un serviciu cum ar fi GitHub, de exemplu, sau de pe site-ul web hex.pm, un manager de pachete externe care stochează mii de componente pentru Elixir și Erlang. Perioadele de execuție sunt pornite înainte de începerea aplicației. Ele sunt deja compilate și disponibile pentru noi.

Există câteva moduri de a specifica dependențele de construire în timp mix.exs fişier. Dacă doriți să utilizați o aplicație de pe site-ul web hex.pm, pur și simplu spuneți:

: dependency_name, "~> 0.0.1"

Primul argument este întotdeauna un atom care reprezintă numele aplicației. A doua este cerința, o versiune pe care doriți să o utilizați - este analizată de modulul Versiune. În acest exemplu, ~> înseamnă că dorim să descărcăm cel puțin versiunea 0.0.1 sau mai mare, dar mai mică decât 0.1.0. Dacă spunem ~> 1,0, înseamnă că ne-ar plăcea să folosim o versiune mai mare sau egală cu 1.0 dar mai puțin decât 2.0. Există și operatori de genul ==, >, <, > =, și <= disponibil.

De asemenea, este posibil să specificați direct a : git sau a :cale opțiune:

: gettext, git: "https://github.com/elixir-lang/gettext.git", etichetă: "0.1" : dependență locală, cale: "path / to / local_dependency"

Este deasemenea o : github o comandă rapidă care ne permite să furnizăm doar numele proprietarului și numele repo:

: gettext, github: "elixir-lang / gettext"

Pentru a descărca și a compila toate dependențele, executați:

mix deps.get

Acest lucru va instala un client Hex dacă nu aveți unul și apoi verificați dacă una dintre dependențe trebuie să fie actualizată. De exemplu, puteți specifica Poison-o soluție pentru a analiza JSON-ca o dependență ca aceasta:

 defp deps do [: poison, "~> 3.1"] sfarsit

Apoi rulați:

mix deps.get

Veți vedea o ieșire similară:

Executarea rezolvării dependenței ... Rezolvarea dependenței a fost finalizată: otravă 3.1.0 * Obținerea de otrăvire (pachet Hex) Verificarea pachetului (https://repo.hex.pm/tarballs/poison-3.1.0.tar) Pachet preluat

Poison este acum compilat și disponibil pe PC. Mai mult, a mix.lock fișierul va fi creat automat. Acest fișier furnizează versiunile exacte ale dependențelor de utilizat atunci când aplicația este pornită. 

Pentru a afla mai multe despre dependențe, executați următoarea comandă:

amesteca ajutorul deps

Comportament din nou

Aplicațiile sunt comportamente, la fel ca GenServer și supervizorii, despre care am vorbit în articolele anterioare. Așa cum am menționat deja, oferim un modul de apel invers în interiorul mix.exs fișier în modul următor:

 aplicația def face [mod: Sample.Application, []] sfârșit

Sample.Application este numele modulului, în timp ce [] poate conține o listă de argumente pentru a trece la începe / 2 funcţie. începe / 2 trebuie să fie implementată pentru ca aplicația să pornească corect.

application.ex conține modulul de apel invers care arată astfel:

defmodule Sample.Application utilizează Aplicația def start (_type, _args) do copii = [] opts = [strategie:: one_for_one, name: Sample.Supervisor] Supervisor.start_link (copii, opts)

începe / 2 trebuie să se întoarcă ok, pid (cu o stare opțională ca al treilea element) sau : eroare, motiv.

Un alt lucru care merită menționat este faptul că aplicațiile nu necesită deloc modul de apel invers. Aceasta înseamnă că funcția de aplicare din interiorul mix.exs fișierul poate deveni cu adevărat minimalist:

Definați cererea []

Astfel de aplicații sunt numite aplicații de bibliotecă. Ei nu au nici un copac de supraveghere, dar pot fi utilizați ca dependențe de alte aplicații. Un exemplu de aplicație de bibliotecă ar fi Poison, pe care am specificat-o ca fiind o dependență în secțiunea anterioară.

Pornirea unei aplicații

Cel mai simplu mod de a porni aplicația dvs. este să executați următoarea comandă:

iex -S mix

Veți vedea o ieșire similară cu aceasta:

Compilarea a 2 fișiere (.ex) Aplicație generată pentru eșantioane

A _construi director va fi creat în interiorul probă pliant. Acesta va conține .grindă fișiere, precum și alte fișiere și foldere.

Dacă nu doriți să începeți o coajă de Elixir, o altă opțiune este să rulați:

mișcați rularea

Problema, totuși, este că aplicația se va opri imediat start funcția își termină treaba. Prin urmare, puteți furniza --fără oprire cheie pentru a menține aplicația în funcțiune atâta timp cât este necesar:

amestecați rularea - nu se oprește

Același lucru se poate realiza folosind elixir comanda:

elixir -S mix-run -no-halt

Rețineți totuși că aplicația se va opri de îndată ce închideți terminalul unde a fost executată această comandă. Acest lucru poate fi evitat prin pornirea aplicației într-un mod detașat: 

elixir - se amestecă rula - nu se oprește - este decodificată

Mediul de aplicare

Uneori este posibil ca utilizatorul unei aplicații să stabilească un anumit parametru înainte ca aplicația să fie încărcată. Acest lucru este util când, de exemplu, utilizatorul ar trebui să poată controla portul pe care ar trebui să îl asculte un server web. Astfel de parametri pot fi specificați în mediul de aplicații, care este o simplă stocare în cheie în memorie. 

Pentru a citi un parametru, utilizați fetch_env / 2 funcție care acceptă o aplicație și o cheie:

Application.fetch_env (: sample,: some_key) 

Dacă cheia nu poate fi găsită, a :eroare atomul este returnat. Există, de asemenea, a fetch_env! / 2 funcția care generează o eroare în schimb și get_env / 3 care pot oferi o valoare implicită.

Pentru a stoca un parametru, utilizați put_env / 4:

Application.put_env (: probă,: cheie,: valoare)

A patra valoare conține opțiuni și nu este necesară setarea.

În cele din urmă, pentru a șterge o cheie, folosiți delete_env / 3 funcţie:

Application.delete_env (: sample,: key)

Cum oferim o valoare pentru mediu când pornim o aplicație? Ei bine, acești parametri sunt stabiliți folosind --Erl tastați în modul următor:

iex -erl "- valoarea cheie a eșantionului" -S mix

Puteți apoi să preluați cu ușurință valoarea:

Application.get_env: sample,: key # =>: valoare

Ce se întâmplă dacă un utilizator uită să specifice un parametru la pornirea aplicației? Ei bine, cel mai probabil trebuie să furnizăm o valoare implicită pentru astfel de cazuri. Există două posibile locuri în care puteți face acest lucru: în interiorul config.exs sau în interiorul mix.exs fişier.

Prima opțiune este cea preferată deoarece config.exs este fișierul care este destinat să stocheze diverse opțiuni de configurare. Dacă aplicația dvs. are o mulțime de parametri de mediu, trebuie să fiți sigur config.exs:

utilizați configurația Mix.Config: eșantion, cheie:: valoare

Cu toate acestea, pentru o aplicație mai mică, este bine să oferiți valori de mediu chiar în interior mix.exs prin ajustarea funcției de aplicare:

 Aplicația def nu [extra_applications: [: logger], mod: Sample.Application, [], env: [# <==== key: :value ] ] end

Exemplu: Crearea unui CalcServer bazat pe Web

Bine, pentru a vedea aplicațiile în acțiune, să modificăm exemplul care a fost deja discutat în articolele GenServer și Supervisors. Acesta este un simplu calculator care permite utilizatorilor să efectueze diverse operații matematice și să obțină rezultatul destul de ușor. 

Ceea ce vreau să fac este să fac acest calculator bazat pe web, astfel încât să putem trimite cereri POST pentru a efectua calcule și o cerere GET pentru a apuca rezultatul.

Creaza un nou lib / calc_server.ex fișier cu următorul conținut:

defmodule Sample.CalcServer folosesc GenServer def start_link (initial_value) do GenServer.start_link (__ MODULE__, initial_value, nume: __MODULE__) sfarsit def init (initial_value) atunci cand is_number (initial_value) do : ok, initial_value sfarsit def init (_ : stop, "Valoarea trebuie să fie un număr întreg!" end end add (număr) genServer.cast (__ MODULE__, : add, number) end result def genServer.call (__ MODULE__, result) (: add, number -> : noreply, state + number _ -> : result, _, state) stop, "nu este implementat", state end end end terminate (_reason, _state) face IO.puts "sfârșitul serverului" capăt sfârșit

Vom adăuga doar suport pentru adăuga operațiune. Toate celelalte operații matematice pot fi introduse în același mod, așa că nu le voi lista aici pentru a face codul mai compact.

CalcServer utilizează GenServer, așa că ajungem child_spec automat și poate porni din funcția de apel invers astfel:

 def start (_type, _args) do copii = [Sample.CalcServer, 0] opts = [strategie:: one_for_one, name: Sample.Supervisor]

0 aici este rezultatul inițial. Trebuie să fie un număr, altfel CalcServer se va termina imediat.

Acum, întrebarea este cum adăugăm asistența web? Pentru a face acest lucru, vom avea nevoie de două dependențe de la terți: Plug, care va funcționa ca o bibliotecă de abstractizare, și Cowboy, care va funcționa ca un server web real. Desigur, trebuie să specificăm aceste dependențe în interiorul mix.exs fişier:

 defp deps nu [: cowboy, "~> 1.1", : plug, "~> 1.4"]

Acum putem porni aplicația Plug sub propriul nostru fir de supraveghere. Optimizați funcția de pornire astfel:

 def, start, (_type, _args) do copii = [Plug.Adapters.Cowboy.child_spec (: http, Sample.Router, [], [port: Application.fetch_env! , 0] # ... sfârșit

Aici oferim child_spec și setarea Sample.Router pentru a răspunde la solicitări. Acest modul va fi creat într-un moment. Ceea ce nu-mi place în legătură cu această configurație este însă faptul că numărul portului este codat greu, ceea ce nu este cu adevărat convenabil. S-ar putea să vreau să-l tweak la pornirea aplicației, deci să-l stocăm în mediul înconjurător:

Plug.Adapters.Cowboy.child_spec (: http, Sample.Router, [], [port: Application.fetch_env! (: Sample,: port)])

Acum furnizați valoarea implicită a portului în interiorul config.exs fişier:

config: eșantion, port: 8088

Grozav! 

Cum rămâne cu routerul? Creaza un nou lib / router.ex fișier cu următorul conținut:

defmodule Sample.Router nu folosiți Plug.Router plug: mufa de meci: expediere sfârșitul

Acum trebuie să definim câteva rute pentru a efectua adăugarea și a prelua rezultatul:

 pentru a obține "/ result" nu conn |> ok (to_string (Sample.CalcServer.result)) end post "/ add" do fetch_number (conn) |> Sample.CalcServer.add conn |

Noi folosim obține și post macro-uri pentru a defini /rezultat și /adăuga rute. Aceste macrocomenzi vor seta Conn obiect pentru noi. 

O.K și fetch_number sunt funcții private definite în felul următor:

 (conn), parametrii de execuție a fișierului (conn), parametrii de contorizare a conectorului (conn)

fetch_query_params / 2 returnează un obiect cu toți parametrii de interogare. Suntem interesați doar de numărul pe care utilizatorul îl trimite la noi. Toți parametrii inițial sunt șiruri, deci trebuie să-l convertim la întreg.

send_resp / 3 trimite un răspuns clientului cu codul de stare furnizat și un organism. Nu vom efectua nici o verificare a erorilor aici, așa că codul va fi întotdeauna 200, ceea ce înseamnă că totul este în regulă.

Și, asta este! Acum puteți porni aplicația în oricare dintre modalitățile de mai sus (de exemplu, tastând iex -S mix) și utilizați răsuci instrument pentru a efectua solicitările:

curl http: // localhost: 8088 / result # => 0 curl http: // localhost: 8088 / add? number = 1 -X POST # => OK curl http: // localhost: 8088 / rezultat #

Concluzie

În acest articol am discutat aplicațiile Elixir și scopul acestora. Ați învățat cum să creați aplicații, să furnizați diverse tipuri de informații și să listați dependențele în interiorul mix.exs fişier. De asemenea, ați văzut cum să stocați configurația în mediul aplicației și ați învățat câteva moduri de a începe să aplicați. În sfârșit, am văzut aplicațiile în acțiune și am creat un simplu calculator bazat pe web.

Nu uitați că site-ul hex.pm afișează multe sute de aplicații terță parte gata pentru a fi utilizate în proiectele dvs. Asigurați-vă că navigați în catalog și alegeți soluția potrivită! 

Sperăm că ați găsit acest articol util și interesant. Vă mulțumesc că ați rămas cu mine și până la următoarea dată.

Cod