Introduzione alle applicazioni con elisir

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.

applicazioni?

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!

Nuova applicazione

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:

  • config la cartella contiene un solo file config.exs che, come puoi intuire, fornisce la configurazione per l'applicazione. Inizialmente ha alcuni commenti utili, ma nessuna configurazione. Nota, a proposito, che la configurazione fornita in questo file è limitata solo all'applicazione stessa. Se stai caricando l'applicazione come dipendenza, la sua config.exs sarà effettivamente ignorato.
  • lib è la cartella principale dell'applicazione che contiene a sample.ex file e a campione cartella con un application.ex file. application.ex definisce un modulo di callback con a avviare / 2 funzione che crea un supervisore vuoto.
  • test è la cartella contenente test automatici per l'applicazione. Non discuteremo di test automatizzati in questo articolo.
  • mix.exs è il file che contiene tutte le informazioni necessarie sull'applicazione. Ci sono molteplici funzioni qui. Dentro il 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.

dipendenze

È 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

Comportamento di nuovo

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.

Avvio di un'applicazione

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

Ambiente applicativo

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

Esempio: creazione di un CalcServer basato sul Web

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

Conclusione

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.