NEWS
[Frage] 'adapter' funktionen nicht überall verfügbar
-
Hallo zusammen,
um den code des Adapters etwas übersichtlicher zu halten habe ich mir ein zusätzliches Modul erstellt, welches im Unterordner 'lib' abgelegt ist. Leider lassen sich nicht alle 'adapter' Funktionen von dort ausführen.
adapter.setObject ````Funktioniert einwandfrei. EDIT:```` adapter.getObject ````Geht auch.
adapter.getState
[TypeError: adapter.getState is not a function]
Stellt sich die Frage ist das bekannt und so gewollt oder muss hier anders vorgegangen werden?
-
Ich hab das im tradfri-Adapter so gelöst (Code etwas vereinfacht):
Im Ordner lib ein Modul "global" erstellt:
// Singleton-Pattern let __instance = null; class Global { constructor() { if (__instance) { return __instance; } __instance = this; } get adapter() { return this._adapter; } set adapter(adapter) { this._adapter = adapter; // [...] Adapter-Objekt um eine Handvoll promisifizierte Funktionen ergänzt } // [...] weitere Helferfunktionen } const stuff = new Global(); export default stuff;
Im Hauptteil des Programms muss die Variable einmal befüllt werden:
import _ from "./lib/global"; // [...] // Adapter-Objekt erstellen const adapter = utils.adapter({ name: "tradfri", // Wird aufgerufen, wenn Adapter initialisiert wird ready: function () { // Adapter-Instanz global machen _.adapter = adapter; //... } // ... });
Dann in allen Modulen, die auf adapter Zugriff benötigen:
import _ from "./global"; // Zugriff auf Adapter per _.adapter.getState _.adapter.setState // etc.
Import und export ist ES6-Syntax, du solltest aber bei Google finden, wie sich das mit älterer Syntax darstellen lässt.
-
Ich denke der saubere Weg ist dir die adapter-Instanz (also Variable "adapter") mit an die Funktionen in deinem Lib-File zu übergeben wo Du Sie brauchst.
In "lib"-Files lagert man ja üblicherweise Funktionen aus die man dann aufruft.
Wenn in lib eine Klasse liegt dann der Klasse beim Konstruktur übergeben und als eigene Skript-Variable ablegen. Dann kannst Du es verwenden.
In JavaScript hat jedes .js File erstmal seinen eigenen Variablenscope …
-
Danke für die Antworten.
Die Lösung von @apollon77 ist wirklich einfach, damit geht es.
der Klasse beim Konstruktur übergeben `
Kannst du mir das bitte anders erklären, ich glaube genau das gemacht zu haben was du mir damit sagen willst.Ich hab mir einfach eine Funktion gebaut die beim Start des Adapters die Variable 'adapter' in meine 'lib' an eine Variable übergibt.
@AlCalzone: Deine Lösung übersteigt mein Verständnis, ich verstehe gerade mal Ansatzweise wie die Zusammenhänge sind.
Abschließend bleibt die Frage warum ein Teil der Funktionen aus 'adapter' nicht verfügbar sind wenn ich sie mithilfe von````
var utils = require(__dirname + '/utils');
var adapter = utils.adapter('upnp'); -
@Jey Cee: Im Prinzip wollte ich genau das vermeiden, was apollon77 vorgeschlagen hat. Der Ansatz stammt ursprünglich aus einem anderen Projekt, wo der Code in so viele verschiedene Komponenten aufgeteilt ist, dass ich zig bis hunderte Male die Adapter-Instanz übergeben müsste, teils von Komponente in Komponente in Komponente…
Der Ansatz mal in einfachen Worten zusammengefasst:
Im Modul /lib/global wird eine Klasse definiert, die als eine Eigenschaft die Adapter-Instanz (Inhalt der Variable "adapter" aus dem main-file) zugewiesen bekommt.
! Diese Klasse ist ein Singleton, d.h. egal wie oft sie per require/import eingebunden wird, wird immer auf das identische Objekt zugegriffen (das ist der Code am Anfang mit __instance). Wenn die Klasse erstmalig erstellt wird, wird ihr Verweis in __instance gespeichert. Bei zukünftigen Zugriffen wird sie nicht neu erstellt, sondern der Verweis in __instance stattdessen zurückgegeben.
! Das geht möglicherweise auch ohne dieses Singleton-Pattern, bin mir nicht 100% sicher, ob das bei require() eh nicht schon passiert.
Diese Klasse wird zu Beginn jeder Code-Datei, die Zugriff auf die Adapter-Instanz benötigt, eingebunden und ihr der Variablenname _ zugewiesen (Schreibfaulheit). Das sollte auch per````
var _ = require("./global");Im Main-Modul bei der Adapter-Erstellung wird diese Variable einmalig beschrieben, sodass anschließend jeder Verweis auf _.adapter auf die eigentliche Instanz zugreift. ` > ```` > var adapter = utils.adapter('upnp'); > ```` ` Ist das so gedacht, bzw. sollte das funktionieren? Ich dachte utils.adapter erstellt eine ****neue**** Adapter-Instanz und gibt diese zurück.
-
Ok jetzt versteh ich das besser. Danke für die gute Erklärung.
CODE: ALLES AUSWÄHLEN
var adapter = utils.adapter('upnp');
Ist das so gedacht, bzw. sollte das funktionieren? Ich dachte utils.adapter erstellt eine neue Adapter-Instanz und gibt diese zurück. `
Das ist eine Interessante Frage, daran merkt man halt das ich wenig Ahnung JavaScript/Node.js hab. Für mich war es immer nur ein Verweis auf eine Klasse/Funktionsbibliothek.Was ich jetzt weiss ist das utils.adapter definitiv die Instanz zurück gibt, aber daneben auch alle verfügbaren Funktionen die in der adapter.js festgelegt sind.
-
Ja, der code baut eine neue (zweite) Instanz. Würde ich also so nicht machen. Ob es wirklich ein Problem ist weiss ich aber auch nicht.
Die Lösung von AlCazone geht auch. Am Ende ist es auch einfach eine Funktion "setAdapter" in deinem Lib-File die du dann über exports bekannt machst und aus dem hauptscript aufrufst und die nix anderes macht als
var adapter;
function setAdapter(adapt) {
adapter=adapt;
}
-
Ich finde beide Lösungen zur Sichtbarmachung des Adapter-Instanzobjektes adpater in eigenen Modulen haben Ihre Berechtigung. Die Lösung von AlCalzone finde ich sehr elegant, insbesondere dann, wenn ich das instanzierte Adapterobjekt noch dynamisch anpassen möchte (z.B. einige Methoden promisifizieren möchte, etc.). Dann kann ich das schön übersichtlich in global.js machen. Wenn ich nur adapter in wenigen eigenen Modulen benötige, mache ich es eigentlich immer so, wie von apollon77 vorgeschlagen. Das geht schnell und ist sehr überschaubar. Bei der Lösung von AlCalzone muss mann sich immer bewusst machen, dass Klassen erst ab Nodeversion 4.8.3 zur Verfügung stehen, dass eine Kompatibilität zur Nodeversion 0.12.18, die ja auch noch vielfach genutzt wird, nicht gegeben ist.
Im Umgang mit adapter sollte man auch wissen, dass mit
const utils = require(path.join(__dirname, '/lib/utils')); var adapter = utils.adapter(options);
am Anfang von main.js die Erzeugung von adapter asynchron erfolgt. Dies liegt an dem Aufruf von
initObjects(callback);
innerhalb des Adapter-Konstruktors. Hierdurch werden im adapter alle Methoden in
adapter.objects adapter.states
(in der Regel als ObjectsInMemClient und StatesInMemClient) für die Abstraktionsschicht zur Verfügung gestellt.
Das hat zur Folge, dass z.B. folgender Aufruf am Anfang von main.js
const Promise = require('bluebird'); const utils = require(path.join(__dirname, '/lib/utils')); var adapter = utils.adapter(options); adapter = Promise.promisifyAll(adapter);
nicht vollständig funktionieren muss, da adapter.states zu diesem Zeitpunkt noch nicht erzeugt wurde.
Man muss also immer das ready-Event abwarten, um Änderungen in adapter vorzunehmen, d.h. erst in main() ist wirklich sichergestellt, dass adapter vollständig zur Verfügung steht.
Grüße
Carsten