Wie erstellen Sie und laden von Modulen zur Laufzeit dynamisch in Elixir, oder Erlang?
Das grundlegende Szenario ist dieses: ich brauche zum laden von text aus einer Datenbank, und drehen Sie dann den text in ein Elixier-Modul (oder ein Erlang-Modul) und dann ruft hinein. Der text ist genauso effektiv wie eine Modul-Datei. Dies ist eine Art von hot-code laden. Ich möchte kompilieren Sie die "Datei", und laden Sie dann das resultierende Modul, dann ruft hinein. Später werde ich zu entladen. Der einzige Unterschied ist der code in einer Datenbank vorhanden ist, anstatt eine Datei auf der Festplatte. (und dann existiert es nicht an der Zeit, dass ich Schreibe den code zu laden.)
Ich weiß, Erlang unterstützt hot-code laden, scheint aber konzentrierte sich auf die Erstellung von Dateien auf der Festplatte und lädt dann die Balken. Ich wünschte, dies zu tun als ein dynamischer Prozess, und ich nicht ersetzen, ausführen von code, sondern laden Sie den code, dann läuft es, dann zu entladen.
Gibt es mehrere Einrichtungen in Elixir für die Bewertung von code zur Laufzeit. Ich versuche, herauszufinden, wie dies zu tun ist mit Ihnen, und die Dokumentation ist etwas spärlich.
Code.compile_string(string, "nofile")
"gibt eine Liste von Tupeln, wobei das erste element ist der name des Moduls und die zweite ist eine Binär". So, jetzt habe ich die Module Namen und Ihre Programme, aber ich weiß nicht, wie laden Sie dann die binaries in die Laufzeit und rufen in Ihnen. Wie soll ich das tun? (Es gibt keine Funktion, die in der Code-Bibliothek, die ich sehen kann.)
Vielleicht könnte ich dann mit der Erlang-Funktion:
:code.load_binary(Module, Filename, Binary) ->
{module, Module} | {error, What}
Ok, das gibt ein Tupel mit dem atom "Modul" und dann das Modul. Wenn der string aus der Datenbank geladen, definiert ein Modul namens "Paris", wie in meinem code würde ich dann ausführen
paris.handler([parameters])
da ich nicht im Voraus wissen, dass es ein Modul namens paris? Konnte ich wissen, indem er die Zeichenfolge "paris" ebenfalls in der Datenbank gespeichert, dass dies der name, aber gibt es eine Möglichkeit der Berufung in ein Modul mit einer Zeichenfolge, die als name für das Modul, das Sie hier aufrufen?
Gibt es auch:
eval(string, binding //[], opts //[])
Das wertet den Inhalt der Zeichenfolge. Können diese Zeichenfolge die gesamte definition von einem Modul? Es erscheint nicht. Ich möchte in der Lage sein, um dieser code zu schreiben ist, die ausgewertet wird in einer solchen Weise, dass es hat mehrere Funktionen, die sich gegenseitig aufrufen--z.B. ein komplettes kleines Programm, mit vordefinierten entry-point (Die könnte ein Haupt, wie "DynamicModule.Griff([parameter-Liste])"
Dann gibt es die EEx-Modul, die hat:
compile_string(source, options //[])
Das ist toll, für den tut Vorlagen. Aber letztlich ist es nur scheint zu funktionieren für den Anwendungsfall, wo ein string und du hast Elixir-code in Sie eingebettet sind. Es wertet die Zeichenfolge, die im Rahmen der Optionen und erzeugt einen string. Ich Suche kompilieren Sie die Zeichenfolge in eine oder mehr Funktionen, die ich dann aufrufen kann. (Wenn ich nur eine Funktion, die ist in Ordnung, das Funktion können Muster übereinstimmen oder Schalter zu tun, die anderen Dinge, die benötigt werden....)
Ich weiß, das ist unkonventionell, aber ich habe meine Gründe, es zu tun auf diese Weise, und Sie sind die guten. Ich bin auf der Suche nach Rat, wie dies zu tun, müssen aber nicht gesagt "Mach das nicht". Es scheint, wie es sollte möglich sein, Erlang unterstützt hot-code laden und Elixier ist ziemlich dynamisch, aber ich weiß einfach nicht, die syntax, oder die richtigen Funktionen. Ich werde die überwachung dieser Frage eng zusammen. Vielen Dank im Voraus!
BEARBEITUNGEN basierend auf den ersten Antworten:
Danke für die Antworten, das ist gute Fortschritte. Als Yuri zeigte, eval definieren können Sie ein Modul, und wie José Punkte aus, kann ich einfach code eval für kleine Teile von code mit Bindungen.
Dem code wird ausgewertet (ob sich in einem Modul, oder nicht) ist ziemlich Komplex. Und seine Entwicklung das beste wäre, mit ZERLEGUNG in Funktionen und den Aufruf der jeweiligen Funktionen.
Helfen, lassen Sie mich Ihnen einige Kontext. Angenommen ich Baue ein web-framework. Der code geladen von der Datenbank-Handler für bestimmte URIs. Also, wenn ein HTTP-Aufruf kommt, könnte ich laden den code für example.com/blog/auf Dieser Seite kann auch bedeuten verschiedene Dinge, wie Kommentare, eine Liste der letzten Beiträge, etc.
Da viele Leute auf die Seite zur gleichen Zeit, ich bin erzeugen eines Prozesses zur Verarbeitung von jeder Seite anzeigen. So gibt es viele Male, wenn dieser code ausgewertet werden kann gleichzeitig für unterschiedliche Anforderungen.
Dem die Modul-Lösung erlaubt es, brechen Sie den code in Funktionen für die verschiedenen Teile der Seite (z.B.: die Liste der Beiträge, Kommentare, etc. ) Und ich würde laden Sie das Modul einmal, beim Start, und lassen Sie viele Prozesse spawnen, dass call-in-es. Das Modul global ist, richtig?
Was passiert, wenn es ein Modul ist bereits definiert? Beispiel: Wenn das Modul verpasst, und es gibt Prozesse fordern bereits, dass Modul.
In iex, ich bin in der Lage, neu zu definieren, ein Modul, das bereits definiert:
iex(20)> Code.eval "defmodule A do\ndef a do\n5\nend\nend"
nofile:1: redefining module A
Was passiert, wenn ich definieren das Modul zur Laufzeit, zu allen Prozessen, die aktuell Berufung in das Modul? Auch wird diese Neudefinition der Arbeit außerhalb der iex im normalen Betrieb?
Unter der Annahme, dass die Neudefinition der module problematisch sein würde, und dass die Module global könnte Probleme mit Namespaces Kollisionen, schaute ich in die Verwendung von eval auf eine Funktion definieren.
Wenn ich kann lediglich den code aus der Datenbank-Funktionen definieren, dann sind diese Funktionen im Rahmen von was auch immer Prozess, und wir haben nicht die Möglichkeit, von globalen Zusammenstößen.
Jedoch, dies scheint nicht zu funktionieren:
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
Ich auch versucht:
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
So, in Zusammenfassung:
-
Können Module neu definiert werden, mithilfe von Code.eval, wenn es-Prozesse aufrufen, die in Ihnen?
-
Ist es möglich mit Code.eval, um Funktionen, deren Umfang gebunden ist, ist der Prozess, in dem Code.eval aufgerufen wurde?
-
Wenn Sie verstehen, was ich versuche zu tun, können Sie vorschlagen, eine bessere Art und Weise, darüber zu gehen?
Auch, wenn es ein besseres forum, wo ich sein sollte, Fragen Sie diese, fühlen Sie sich frei, mich wissen zu lassen. Und wenn es docs oder relevante Beispiele, die ich Lesen sollte, bitte fühlen Sie sich frei, um mich zu Ihnen. Ich versuche nicht, um Sie alle Arbeit tun, ich bin nicht in der Lage, um es herauszufinden selber.
Ich Lerne Elixier speziell für die Fähigkeit, dynamisch evlauate code, aber meine Elixir wissen ist minimal - ich habe gerade angefangen, - und meine erlang ist rusty zu.
Viel Dank!
Du musst angemeldet sein, um einen Kommentar abzugeben.
Wie du Sie beschrieben hast, gibt es viele verschiedene Ansätze, die Sie ergreifen könnten, indem Sie letztlich Kochen unten zu zwei verschiedenen Kategorien: 1) code-Kompilierung und 2) code-Auswertung. Das Beispiel, das Sie oben beschrieben erfordert eine Kompilierung, die definieren ein Modul und dann würden Sie haben, um es aufzurufen. Jedoch, als Sie herausfand, erfordert die Definition einer Modul-name und potenziell Säuberung und verwerfen diese Module, wenn sich die Datenbank ändert. Beachten Sie auch, dass die Definition Module können Abgase, atom-Tabelle, wie ein atom erzeugt wird, für jedes Modul. Ich würde diese Vorgehensweise nur verwenden, wenn Sie brauchen, um zu kompilieren höchstens ein Dutzend Module.
(Eine kleine Anmerkung,
Code.compile_string
bereits definiert, das Modul, so brauchen Sie nicht zu nennenload_binary
).Vielleicht ein einfacher Ansatz ist zu nennen
Code.eval
vorbei an der code aus der Datenbank, somit code-Auswertung. Es funktioniert gut, wenn alle Sie wollen, ist zu bewerten, einige benutzerdefinierte Regeln. DieCode.eval
akzeptiert eine Bindung, die Ihnen erlauben würde, um Parameter an den code. Nehmen wir an, Sie haben "a + b" in der Datenbank gespeichert, woa
undb
sind die Parameter, könnte man es bewerten, wie:BEARBEITEN BASIEREND AUF FRAGE BEARBEITET
Wenn Sie auswerten code, beachten Sie, dass
Code.eval
gibt das Ergebnis der Auswertung der code und die neue Bindung, also dein Beispiel oben wäre besser geschrieben als:Angesichts Ihrer Nutzung Fall, ich würde das nicht als Verwendung von eval, aber ich würde es ja kompilieren eines Moduls. Da die Informationen aus der Datenbank kommen, Sie können diese Tatsache nutzen, um eine eindeutige Module pro Datensatz für Sie. Zum Beispiel, können Sie davon ausgehen Entwickler werden das hinzufügen der Inhalte des Moduls, der Datenbank, so etwas wie:
Nachdem Sie diese information erhalten Sie aus der Datenbank, können Sie es kompilieren mit:
Dem Datensatz alles, was Sie bekommen, wieder aus der Datenbank. Vorausgesetzt, die Datensatz-id ist
1
, es würde definieren Sie ein ModulFromDB.Data1
die dann Sie wäre in der Lage zu berufenfoo
undbar
.Hinsichtlich code-nachladen, beide
defmodule
undModule.create
verwenden:code.load_binary
um ein Modul zu laden. Das bedeutet, dass, wenn Sie ein update für das Modul, die alte version ist noch immer herum, bis wieder eine neue version geladen wird.Eines der Dinge, die Sie sollte hinzufügen, sowie den cache-Ablauf, so dass Sie brauchen nicht zu kompilieren eines Moduls auf jede Anfrage. Diese kann getan werden, wenn Sie eine lock_version in der Datenbank, erhöhen Sie jedes mal, wenn Sie ändern Sie die Datensatz-Inhalte. Der endgültige code wäre so etwas wie:
module
in diesem Fall wirdFromDB.Data1
(nicht Binär, sondern ein atom repräsentiert das Modul). Das heißt, wir können aufrufen von Funktionen in der it. Alles, was Sie tun müssen, ist sicherzustellen, dass mindestens eine Funktion mit dem Namenmain
(oder alles, was Sie bevorzugen) ist verfügbar in der Datenbank, und starten Sie es so:module.main(a, b, c)
.Code.eval kann verwendet werden, um zu definieren ein Modul:
Funktioniert diese Hilfe?
Haben Sie überprüft:
Dynamische Bibliothek Kompilieren von Jacob Vorreuter
. Siehe Beispiel untenAuch, überprüfen Sie heraus dieses Frage und seine Antwort