2016-04-19 12 views
9

Es scheint, dass die Art, wie Konfigurationsdateien in Phoenix geladen und kompiliert werden, ein Problem darstellt, wenn Module von Drittanbietern in config.exs oder dev.exs/prod.exs/test.exs verwendet werden.Elixir/Phoenix: Wie man Module von Drittanbietern in Konfigurationsdateien verwendet?

Beispiel: Zum Einrichten Guardian für JWT-Authentifizierung Ich versuche, das JOSE.JWK Modul für JWK Erstellung/Laden in meinem config.exs zu verwenden. Ich kann das Modul in der Konsole in Ordnung mit iex -S mix phoenix.server verwenden. Es ist natürlich als Abhängigkeit installiert. Der Fehler Ich erhalte ist

** (Mix.Config.LoadError) could not load config config/config.exs 
    ** (UndefinedFunctionError) undefined function JOSE.JWK.from_file/2 (module JOSE.JWK is not available) 

Dies ist der Code in meinem config.exs

# Configure Guardian for JWT Authentication 
config :guardian, Guardian, 
    allowed_algos: ["HS512"], # optional 
    verify_module: Guardian.JWT, # optional 
    issuer: "MyApp", 
    ttl: { 30, :days }, 
    verify_issuer: true, # optional 
    secret_key: System.get_env("GUARDIAN_KEY_PASSPHRASE") |> JOSE.JWK.from_file(System.get_env("GUARDIAN_KEY_FILE")), 
    serializer: MyApp.GuardianSerializer 

Es funktioniert, wenn ich den Anruf zu JOSE.JWK.from_file/2 in einer anonymen Funktion wickeln. Aber natürlich ist der Wert von Guardian.config (: secret_key) ist dann die anonyme Funktion selbst und nicht deren Rückgabewert:

# Configure Guardian for JWT Authentication 
config :guardian, Guardian, 
    allowed_algos: ["HS512"], # optional 
    verify_module: Guardian.JWT, # optional 
    issuer: "MyApp", 
    ttl: { 30, :days }, 
    verify_issuer: true, # optional 
    secret_key: fn -> System.get_env("GUARDIAN_KEY_PASSPHRASE") |> JOSE.JWK.from_file(System.get_env("GUARDIAN_KEY_FILE")) end, 
    serializer: MyApp.GuardianSerializer 

Das ist in Ordnung, in diesem Beispiel, da Wächter eine Funktion für diesen Konfigurationswert akzeptiert. Aber ich kann mir andere Situationen vorstellen, in denen das ein Problem sein könnte.

Ist diese Beschränkung absichtlich? Fehle ich etwas? Gibt es einen Weg dahin?

Antwort

13

Da die Konfiguration ausgewertet wird, bevor die Abhängigkeiten kompiliert werden, können Sie keinen Code aus Abhängigkeiten in der Konfiguration verwenden.

Der Grund ist einfach: Konfiguration könnte ändern, wie eine Abhängigkeit kompiliert wird. Sie müssen entscheiden, was Sie zuerst tun müssen - kompilieren Sie, um Konfigurationen auszuwerten. Die Entscheidung wurde getroffen, um die Konfiguration zuerst auszuwerten, da es viel nützlicher (und häufiger) ist, die Kompilierung über Konfigurationen zu manipulieren als Abhängigkeiten zu verwenden, um andere Anwendungen zu konfigurieren - am häufigsten ist die Konfiguration nur Rohdaten.

+0

Die gleiche Frage gilt für den Zugriff auf Funktionen von der App selbst, anstatt eine Abhängigkeit. Dies verursacht Schmerzen bei https://github.com/trenpixster/addict/issues/105 –

+0

Sind Sie sicher? Wie funktioniert 'config: my_app, MyApp.Endpoint,'? – asiniy

+0

@asiniy 'MyApp.Endpoint' ist nur ein Atom. Wenn es "MyApp.Endpoint.foo()" wäre, wäre es ein Funktionsaufruf und würde erfordern, dass das abhängige Modul kompiliert wird. – Emil

1

Wenn Sie zuerst ein Modul kompilieren, das außerhalb von lebt, bevor Sie es mischen, und es sich im Elixir-Suchpfad befindet, wird es gefunden. Tun Sie einfach Whatever.foo(:bar)

Es wäre sinnvoller, die Vorkompilierung als Mix-Task zu beenden, und rufen Sie Mix, um sicherzustellen, Config Deps sind auf dem neuesten Stand vor dem Aufruf einer 3rd-Party dep.

Es wäre hilfreich, wenn es einen Pre-Config-Mix-Hook und/oder einen config/lib gibt, der vor dem Auswerten der Config vorkompiliert wird. Andernfalls wachsen die Konfigurationen und werden zu widerlichen Codeanhäufungen, selbst wenn Sie import_config verwenden, wodurch Sie Code nicht modularisieren und austrocknen können.

Eine weitere hackaround ist eine minimale mix-Aufgabe, die gerade genug Ihrer App lädt, um Werte zu erhalten und sie in stdout zu schreiben, und dann einen Port verwenden, um sie woanders hin zu bekommen (Rails macht dies an einigen Stellen).

Verwandte Themen