Nei miei articoli precedenti abbiamo discusso vari termini di Elisir e scritto una quantità considerevole di codice. Ciò che non abbiamo discusso, tuttavia, è come strutturare e organizzare il codice in modo che sia facile da mantenere e rilasciare.
Le applicazioni sono molto comuni per Erlang ed Elixir e vengono utilizzate per creare componenti riutilizzabili che si comportano come unità indipendenti. Un'applicazione può avere la propria struttura di supervisione e configurazione e può fare affidamento su altre applicazioni disponibili localmente o su un server remoto. Tutto sommato, lavorare con le applicazioni non è così complesso, e le persone che sono venute, ad esempio, dal mondo di Ruby troveranno molti concetti familiari.
In questo articolo imparerai quali sono le applicazioni, come possono essere create, come specificare e installare le dipendenze e come fornire i valori dell'ambiente. Alla fine dell'articolo faremo un po 'di pratica e creeremo un calcolatore basato sul web.
Userò Elixir 1.5 in questo articolo (è stato rilasciato un paio di mesi fa), ma tutti i concetti spiegati dovrebbero essere applicati anche alla versione 1.4.
Qualcuno potrebbe obiettare che il termine "applicazione" non è molto appropriato perché in Erlang ed Elixir significa in realtà un componente, o un codice che ha un mucchio di dipendenze. L'applicazione stessa può essere usata anche come dipendenza: nel mondo Ruby la chiameremmo una "gemma".
Tutto sommato, le applicazioni sono molto comuni in Elixir e consentono di creare componenti riutilizzabili, fornendo al tempo stesso una facile gestione delle dipendenze. Sono costituiti da uno o più moduli con zero o più dipendenze e sono descritti dal file di risorse dell'applicazione. Questo file contiene informazioni sul nome dell'applicazione, la versione, i suoi moduli, le dipendenze e altre cose. Puoi creare manualmente il file di risorse, ma è molto più facile farlo con lo strumento di mix che preparerà anche una struttura di cartelle corretta per te.
Quindi vediamo come possiamo creare una nuova applicazione Elixir!
Per creare una nuova applicazione, tutto ciò che devi fare è eseguire il seguente comando:
mescola il nuovo nome app
Possiamo anche fornire il --cenare
flag per creare un supervisore vuoto per noi. Creiamo una nuova applicazione chiamata Campione
per di qua:
mixare nuovo campione --sup
Questo comando creerà a campione directory per te con una manciata di file e cartelle all'interno. Lascia che ti guidi rapidamente attraverso di loro:
avviare / 2
funzione che crea un supervisore vuoto.progetto
funzione, fornisci il nome dell'app (come un atomo), la versione e l'ambiente. Il applicazione
la funzione contiene informazioni sul callback del modulo dell'applicazione e sulle dipendenze di runtime. Nel nostro caso, Sample.Application
è impostato come callback del modulo dell'applicazione (che può essere considerato come il punto di ingresso principale) e deve definire a avviare / 2
funzione. Come già accennato in precedenza, questa funzione è stata già creata per noi dal mescolare
strumento. Infine, il dipendenze
la funzione elenca le dipendenze di build-time.È abbastanza importante distinguere tra le dipendenze di runtime e build-time. Le dipendenze di build sono caricate da mescolare
strumento durante la compilazione e sono fondamentalmente compilati nella tua applicazione.
Possono essere scaricati da un servizio come GitHub, per esempio, o dal sito web hex.pm, un gestore di pacchetti esterno che memorizza migliaia di componenti per Elixir ed Erlang. Le dipendenze del runtime vengono avviate prima dell'avvio dell'applicazione. Sono già compilati e disponibili per noi.
Esistono un paio di modi per specificare le dipendenze del build-time in a mix.exs file. Se desideri utilizzare un'applicazione dal sito web hex.pm, dì semplicemente:
: dependency_name, "~> 0.0.1"
Il primo argomento è sempre un atomo che rappresenta il nome dell'applicazione. Il secondo è il requisito, una versione che si desidera utilizzare: viene analizzata dal modulo Version. In questo esempio, ~>
significa che vogliamo scaricare almeno la versione 0.0.1
o superiore ma inferiore a 0.1.0
. Se diciamo ~> 1.0
, significa che vorremmo usare una versione maggiore o uguale a 1.0
ma meno di 2.0
. Ci sono anche operatori come ==
, >
, <
, > =
, e <=
a disposizione.
È anche possibile specificare direttamente a :idiota
o a :sentiero
opzione:
: gettext, git: "https://github.com/elixir-lang/gettext.git", tag: "0.1" : local_dependency, percorso: "percorso / a / local_dependency"
C'è anche un : GitHub
scorciatoia che ci consente di fornire solo il nome del proprietario e del repository:
: gettext, github: "elixir-lang / gettext"
Per scaricare e compilare tutte le dipendenze, eseguire:
mescola deps.get
Questo installerà un client esadecimale se non ne hai uno e quindi verifica se è necessario aggiornare una qualsiasi delle dipendenze. Ad esempio, è possibile specificare Poison, una soluzione per analizzare JSON, come una dipendenza come questa:
defp deps do [: poison, "~> 3.1"] end
Quindi esegui:
mescola deps.get
Vedrai un risultato simile:
Esecuzione della risoluzione delle dipendenze ... Risoluzione delle dipendenze completata: veleno 3.1.0 * Avvelenamento (pacchetto Hex) Controllo del pacchetto (https://repo.hex.pm/tarballs/poison-3.1.0.tar) Pacchetto recuperato
Poison è ora compilato e disponibile sul tuo PC. Inoltre, a mix.lock il file verrà creato automaticamente. Questo file fornisce le versioni esatte delle dipendenze da utilizzare all'avvio dell'applicazione.
Per saperne di più sulle dipendenze, eseguire il seguente comando:
mescolare le istruzioni di aiuto
Le applicazioni sono comportamenti, proprio come GenServer e supervisori, di cui abbiamo parlato negli articoli precedenti. Come ho già detto sopra, forniamo un modulo di callback all'interno di mix.exs file nel seguente modo:
l'applicazione def fa [mod: Sample.Application, []] end
Sample.Application
è il nome del modulo, mentre []
può contenere un elenco di argomenti da passare a avviare / 2
funzione. Il avviare / 2
la funzione deve essere implementata affinché l'applicazione si avvii correttamente.
Il application.ex contiene il modulo di callback che assomiglia a questo:
defmodule Sample.Application do use Applicazione def start (_type, _args) do children = [] opts = [strategia:: one_for_one, nome: Sample.Supervisor] Supervisor.start_link (child, opts) end end
Il avviare / 2
la funzione deve essere restituita : ok, pid
(con uno stato opzionale come terzo elemento) o : errore, motivo
.
Un'altra cosa che vale la pena ricordare è che le applicazioni non richiedono affatto il modulo di callback. Significa che la funzione dell'applicazione all'interno di mix.exs il file potrebbe diventare davvero minimalista:
l'applicazione def fa [] fine
Tali applicazioni sono chiamate applicazioni di libreria. Non hanno alcun albero di supervisione ma possono ancora essere utilizzati come dipendenze da altre applicazioni. Un esempio di un'applicazione di libreria sarebbe Poison, che abbiamo specificato come dipendenza nella sezione precedente.
Il modo più semplice per avviare l'applicazione è eseguire il seguente comando:
mix iS-S
Vedrai un risultato simile a questo:
Compilazione di 2 file (.ex) App campione generata
UN _costruire la directory verrà creata all'interno di campione cartella. Conterrà .fascio file così come altri file e cartelle.
Se non vuoi avviare una shell di elisir, devi eseguire un'altra opzione:
mescolare corsa
Il problema, tuttavia, è che l'applicazione si fermerà non appena il inizio
la funzione finisce il suo lavoro. Pertanto, è possibile fornire il --no-halt
chiave per mantenere l'applicazione in esecuzione per tutto il tempo necessario:
mix run --no-halt
Lo stesso può essere ottenuto usando il elisir
comando:
elixir -S mix run --no-halt
Notare, tuttavia, che l'applicazione si arresterà non appena si chiude il terminale in cui è stato eseguito questo comando. Questo può essere evitato avviando l'applicazione in modalità distaccata:
elixir -S mix run --no-halt --detached
A volte potresti desiderare che l'utente di un'applicazione imposti alcuni parametri prima che l'app venga effettivamente avviata. Ciò è utile quando, ad esempio, l'utente deve essere in grado di controllare quale porta deve essere ascoltata da un server web. Tali parametri possono essere specificati nell'ambiente dell'applicazione che è una semplice memoria con valori-chiave in memoria.
Per leggere alcuni parametri, usa il fetch_env / 2
funzione che accetta un'app e una chiave:
Application.fetch_env (: sample,: some_key)
Se la chiave non può essere trovata, a :errore
atomo viene restituito. Ci sono anche a fetch_env! / 2
funzione che solleva un errore invece e get_env / 3
che può fornire un valore predefinito.
Per memorizzare un parametro, utilizzare put_env / 4
:
Application.put_env (: sample,: chiave,: valore)
Il quarto valore contiene opzioni e non è necessario impostarlo.
Infine, per eliminare una chiave, utilizzare il delete_env / 3
funzione:
Application.delete_env (: sample,: chiave)
Come forniamo un valore per l'ambiente all'avvio di un'app? Bene, tali parametri sono impostati usando --Erl
digitare nel modo seguente:
iex --erl "-campione chiave campione" -S mix
È quindi possibile recuperare facilmente il valore:
Application.get_env: sample,: key # =>: value
Cosa succede se un utente si dimentica di specificare un parametro all'avvio dell'applicazione? Bene, molto probabilmente abbiamo bisogno di fornire un valore predefinito per tali casi. Ci sono due possibili luoghi in cui puoi fare questo: all'interno del config.exs o dentro il mix.exs file.
La prima opzione è quella preferita perché config.exs è il file che in realtà è pensato per memorizzare varie opzioni di configurazione. Se la tua applicazione ha molti parametri ambientali, dovresti assolutamente attenervisi config.exs:
usa la configurazione di Mix.Config: sample, key:: value
Per un'applicazione più piccola, tuttavia, è abbastanza normale fornire valori ambientali all'interno mix.exs modificando la funzione dell'applicazione:
applicazione def fare [extra_applications: [: logger], mod: Sample.Application, [], env: [# <==== key: :value ] ] end
Ok, per vedere le applicazioni in azione, modifichiamo l'esempio già discusso nei miei articoli su GenServer e Supervisor. Questa è una semplice calcolatrice che consente agli utenti di eseguire varie operazioni matematiche e di recuperare il risultato abbastanza facilmente.
Quello che voglio fare è rendere questa calcolatrice basata sul web, in modo che possiamo inviare richieste POST per eseguire calcoli e una richiesta GET per afferrare il risultato.
Crea un nuovo lib / calc_server.ex file con il seguente contenuto:
defmodule Sample.CalcServer usa GenServer def start_link (initial_value) do GenServer.start_link (__ MODULE__, initial_value, name: __MODULE__) end def init (initial_value) quando is_number (initial_value) do : ok, initial_value end def init (_) do : stop, "Il valore deve essere un numero intero!" end def add (numero) do GenServer.cast (__ MODULE__, : add, number) end def result do GenServer.call (__ MODULE__,: result) end def handle_call (: result, _, state) do : reply, state, state end def handle_cast (operazione, stato) do case operation do : add, number -> : noreply, state + number _ -> : stop, "Non implementato", stato end end def terminate (_reason, _state) do IO.puts "The server terminated" end end
Aggiungeremo solo il supporto per il Inserisci
operazione. Tutte le altre operazioni matematiche possono essere introdotte allo stesso modo, quindi non le elencherò qui per rendere il codice più compatto.
Il CalcServer
utilizza GenServer
, così otteniamo child_spec
automaticamente e può avviarlo dalla funzione di callback in questo modo:
def start (_type, _args) do children = [Sample.CalcServer, 0] opts = [strategia:: one_for_one, nome: Sample.Supervisor] Supervisor.start_link (child, opts) end
0
ecco il risultato iniziale. Deve essere un numero, altrimenti CalcServer
terminerà immediatamente.
Ora la domanda è: come aggiungere il supporto web? Per fare ciò, abbiamo bisogno di due dipendenze di terze parti: Plug, che fungerà da libreria di astrazione e Cowboy, che fungerà da server web effettivo. Ovviamente, dobbiamo specificare queste dipendenze all'interno di mix.exs file:
defp deps do [: cowboy, "~> 1.1", : plug, "~> 1.4"] fine
Ora possiamo avviare l'applicazione Plug sotto il nostro albero di supervisione. Modifica la funzione di avvio in questo modo:
def start (_type, _args) do children = [Plug.Adapters.Cowboy.child_spec (: http, Sample.Router, [], [port: Application.fetch_env! (: sample,: port)]), Sample.CalcServer , 0] # ... fine
Qui stiamo fornendo child_spec
e impostazione Sample.Router
rispondere alle richieste. Questo modulo verrà creato in un momento. Ciò che non mi piace di questa configurazione, tuttavia, è che il numero di porta è hard-coded, il che non è veramente conveniente. Potrei volerlo modificare quando avvii l'applicazione, quindi salviamolo invece nell'ambiente:
Plug.Adapters.Cowboy.child_spec (: http, Sample.Router, [], [port: Application.fetch_env! (: Sample,: port)])
Ora fornisci il valore predefinito della porta all'interno di config.exs file:
config: sample, port: 8088
grande!
E il router? Crea un nuovo lib / router.ex file con il seguente contenuto:
defmodule Sample.Router usa la spina Plug.Router: match plug: fine invio
Ora dobbiamo definire un paio di percorsi per eseguire l'aggiunta e recuperare il risultato:
ottieni "/ result" fai conn |> ok (to_string (Sample.CalcServer.result)) end post "/ add" fai fetch_number (conn) |> Sample.CalcServer.add conn |> ok end
Stiamo usando ottenere
e inviare
macro per definire il /risultato
e /Inserisci
itinerari. Quelle macro imposteranno il conn
oggetto per noi.
ok
e fetch_number
sono funzioni private definite nel modo seguente:
defp fetch_number (conn) do Plug.conn.fetch_query_params (conn) .params ["numero"] |> String.to_integer end defp ok (conn, data \\ "OK") do send_resp conn, 200, fine dati
fetch_query_params / 2
restituisce un oggetto con tutti i parametri della query. Siamo interessati solo al numero che l'utente ci invia. Tutti i parametri inizialmente sono stringhe, quindi dobbiamo convertirlo in numero intero.
send_resp / 3
invia una risposta al client con il codice di stato fornito e un corpo. Qui non eseguiremo alcun controllo degli errori, quindi il codice sarà sempre 200
, il che significa che tutto va bene.
E questo è tutto! Ora puoi avviare l'applicazione in uno dei modi elencati sopra (ad esempio, digitando mix iS-S
) e utilizzare il arricciare
strumento per eseguire le richieste:
curl http: // localhost: 8088 / result # => 0 curl http: // localhost: 8088 / add? number = 1 -X POST # => OK curl http: // localhost: 8088 / result # => 1
In questo articolo abbiamo discusso le applicazioni di elisir e il loro scopo. Hai imparato come creare applicazioni, fornire vari tipi di informazioni e elencare le dipendenze all'interno di mix.exs file. Hai anche visto come memorizzare la configurazione nell'ambiente dell'app e imparato un paio di modi per avviare la tua applicazione. Infine, abbiamo visto le applicazioni in azione e creato un semplice calcolatore basato sul web.
Non dimenticare che il sito web hex.pm elenca molte centinaia di applicazioni di terze parti pronte per l'uso nei tuoi progetti, quindi assicurati di sfogliare il catalogo e scegliere la soluzione più adatta a te!
Spero che tu abbia trovato questo articolo utile e interessante. Ti ringrazio per essere stato con me e fino alla prossima volta.