Lo scenario di base è questo: ho bisogno di caricare il testo da un database, e quindi trasformare quel testo in un modulo Elixir (o un modulo di Erlang) e quindi effettuare chiamate in esso. Il testo è effettivamente lo stesso di un file modulo. Quindi questa è una forma di caricamento di hot code. Voglio compilare il "file" e quindi caricare il modulo risultante, quindi effettuare chiamate in esso. Più tardi lo scaricherò. L'unica differenza è che il codice esiste in un database anziché in un file sul disco. (e non esiste al momento in cui sto scrivendo il codice che verrà caricato.)Come si creano e si caricano i moduli dinamicamente in fase di runtime in Elixir o Erlang?
So che Erlang supporta il caricamento di codice caldo, ma sembra concentrato sulla compilazione di file su disco e quindi sul caricamento dei raggi. Desidero farlo come un processo più dinamico e non sostituirò il codice in esecuzione, ma caricando il codice, eseguendolo, quindi scaricandolo.
Ci sono diversi servizi in Elixir per la valutazione del codice in fase di esecuzione. Sto cercando di capire come farlo con loro, e la documentazione è un po 'scarsa.
Code.compile_string(string, "nofile")
"restituisce una lista di tuple in cui il primo elemento è il nome del modulo e il secondo è il suo binario". Quindi, ora ho i nomi dei moduli e i loro binari, ma non conosco un modo per poi caricare i binari nel runtime e chiamarli. Come potrei farlo? (Non c'è alcuna funzione per questo nella libreria di codice che posso vedere.)
Forse ho potuto quindi utilizzare la funzione di Erlang:
:code.load_binary(Module, Filename, Binary) ->
{module, Module} | {error, What}
Ok, quindi questo restituisce una tupla con il "modulo" dell'atomo e quindi il modulo. Se la stringa caricata dal database definito un modulo chiamato "Parigi", come nel mio codice dovrei quindi eseguire
paris.handler([parameters])
dato che non so in anticipo che ci sarà un modulo chiamato Paris? Potrei sapere, avendo la stringa "paris" anche memorizzata nel database che questo è il nome, ma c'è un modo per chiamare in un modulo, usando una stringa come nome del modulo che stai chiamando?
C'è anche:
eval(string, binding // [], opts // [])
che valuta il contenuto della stringa. Questa stringa può essere l'intera definizione di un modulo? Non sembra. Mi piacerebbe essere in grado di scrivere questo codice che viene valutato in modo tale da avere più funzioni che si chiamano a vicenda - ad es. un piccolo programma completo, con un punto di ingresso predefinito (che potrebbe essere un principale, come ad esempio "DynamicModule.handle ([parametro, l'elenco])"
Poi c'è il modulo EEx, che ha:
compile_string(source, options // [])
Che è ottimo per fare modelli, ma alla fine sembra funzionare solo per il caso d'uso in cui c'è una stringa e in esso è incorporato il codice Elixir. Valuta la stringa nel contesto delle opzioni e produce una stringa Sto cercando di compilare la stringa in una o più funzioni che posso quindi chiamare. (Se posso solo fare una funzione che va bene, quella funzione può pattern match o passare a fare le altre cose che sono necessarie ....)
So che questo non è convenzionale, ma ho le mie ragioni per farlo in questo modo e sono buoni. Sto cercando consigli su come farlo, ma non c'è bisogno di sentirmi dire "non farlo". Sembra che dovrebbe essere possibile, Erlang supporta il caricamento di hot code e Elixir è piuttosto dinamico, ma io non conosco la sintassi o le funzioni giuste.Seguirò da vicino questa domanda. Grazie in anticipo!
EDITS sulla base delle prime risposte:
Grazie per le risposte, questo è un buon progresso. Come ha mostrato Yuri, eval può definire un modulo, e come sottolinea José, posso semplicemente usare la valutazione del codice per piccole parti di codice con associazioni.
Il codice che viene valutato (trasformato in modulo o meno) sarà piuttosto complesso. E il suo sviluppo sarebbe meglio coinvolgere scomposizione in funzioni e chiamando quelle funzioni.
Per aiutare, lasciatemi fornire un contesto. Supponiamo che sto costruendo un framework web. Il codice caricato dal database è gestori per URI specifici. Quindi, quando arriva una chiamata HTTP, potrei caricare il codice per esempio.com/blog/ Questa pagina potrebbe comportare diverse cose, come commenti, un elenco di post recenti, ecc.
Poiché molte persone stanno colpendo la pagina allo stesso tempo, sto generando un processo per gestire ogni visualizzazione di pagina. Quindi ci sono molte volte in cui questo codice può essere valutato simultaneamente, per richieste diverse.
La soluzione del modulo consente di suddividere il codice in funzioni per diverse parti della pagina (ad esempio: l'elenco di post, commenti, ecc.) E vorrei caricare il modulo una volta, all'avvio, e lasciare molti processi generano quella chiamata in esso. Il modulo è globale, corretto?
Cosa succede se c'è già un modulo definito? EG: Quando il modulo cambia, e ci sono processi che già chiamano quel modulo.
In iex, sono in grado di ridefinire un modulo che è già stato definito:
iex(20)> Code.eval "defmodule A do\ndef a do\n5\nend\nend"
nofile:1: redefining module A
Cosa succede se ridefinire il modulo in fase di esecuzione, a tutti i processi attualmente rimettere in quel modulo? Inoltre, questa ridefinizione funzionerà al di fuori di iex, nel normale funzionamento?
Supponendo che la ridefinizione del modulo sarebbe problematica e che i moduli che sono globali potrebbero incontrare problemi con le collisioni nello spazio dei nomi, ho cercato di utilizzare eval per definire una funzione.
Se posso semplicemente fare in modo che il codice dal database definisca le funzioni, allora tali funzioni rientrano nell'ambito di qualsiasi processo e non abbiamo la possibilità di collisioni globali.
Tuttavia, questo non sembra funzionare:
iex(31)> q = "f = function do
...(31)> x, y when x > 0 -> x+y
...(31)> x, y -> x* y
...(31)> end"
"f = function do\nx, y when x > 0 -> x+y\nx, y -> x* y\nend"
iex(32)> Code.eval q
{#Fun<erl_eval.12.82930912>,[f: #Fun<erl_eval.12.82930912>]}
iex(33)> f
** (UndefinedFunctionError) undefined function: IEx.Helpers.f/0
IEx.Helpers.f()
erl_eval.erl:572: :erl_eval.do_apply/6
src/elixir.erl:110: :elixir.eval_forms/3
/Users/jose/Work/github/elixir/lib/iex/lib/iex/loop.ex:18: IEx.Loop.do_loop/1
iex(33)> f.(1,3)
** (UndefinedFunctionError) undefined function: IEx.Helpers.f/0
IEx.Helpers.f()
erl_eval.erl:572: :erl_eval.do_apply/6
erl_eval.erl:355: :erl_eval.expr/5
src/elixir.erl:110: :elixir.eval_forms/3
/Users/jose/Work/github/elixir/lib/iex/lib/iex/loop.ex:18: IEx.Loop.do_loop/1
Ho anche provato:
iex(17)> y = Code.eval "fn(a,b) -> a + b end"
{#Fun<erl_eval.12.82930912>,[]}
iex(18)> y.(1,2)
** (BadFunctionError) bad function: {#Fun<erl_eval.12.82930912>,[]}
erl_eval.erl:559: :erl_eval.do_apply/5
src/elixir.erl:110: :elixir.eval_forms/3
/Users/jose/Work/github/elixir/lib/iex/lib/iex/loop.ex:18: IEx.Loop.do_loop/1
Così, in sintesi:
può essere ridefinito utilizzando i moduli di codice .eval quando ci sono processi che chiamano in loro?
È possibile utilizzare Code.eval per creare funzioni il cui ambito è associato al processo in cui è stato chiamato Code.eval?
Se capisci cosa sto cercando di fare, puoi suggerire un modo migliore per farlo?
Inoltre, se c'è un forum migliore in cui dovrei chiedere questo, non esitate a farmelo sapere. E se ci sono documenti o esempi pertinenti che dovrei leggere, per favore sentitevi liberi di indicarmi. Non sto cercando di farti fare tutto il lavoro, non riesco proprio a capirlo da solo.
Sto imparando l'elisir appositamente per l'abilità di svelare dinamicamente il codice, ma la mia conoscenza dell'elisir è minima ora- ho appena iniziato- e anche il mio erlang è arrugginito.
Grazie mille!
Ciò imporrebbe una forma predeterminata sul codice, in modo tale che sia suddivisa in piccole stringhe per la valutazione. Ho modificato la mia domanda per spiegare ulteriormente, ma se potessi definire le funzioni all'interno del codice e poi chiamarle all'interno della stringa, e anche dall'esterno, sarebbe probabilmente l'ideale. Ma non ha funzionato quando l'ho provato. Mi piacerebbe che STRING fosse in grado di definire le funzioni A, B e C, ad esempio, e il codice esterno definisca D, E e F. E dall'interno della stringa inviata a Code.eval e all'esterno, essere in grado di chiamare A, B, C, D, E & F. EG: tutte le funzioni sono mirate al processo. È possibile? – nirvana
Ho appena risposto alle tue nuove domande. –
Solo un altro po 'di confusione. Guardando il tuo codice, l'ultima riga è semplicemente "modulo" che è effettivamente "FromDB.Data1" in questo esempio. Quando si ha "modulo" su una linea in questo modo, è l'equivalente di chiamare FromDB.Data1.main() o qualche punto di ingresso predefinito? In altre parole, il codice crea un modulo il cui nome è basato sulle informazioni del database, il che va bene, come faccio a chiamarlo dal codice che è al di fuori di esso? Posso scrivere "module.renderRequest (a, b, c)" e chiamerà la funzione renderRequest definita nel modulo caricato dal database? – nirvana