Merging-Hashes in yaml conf-Dateien

31. Juli 2009 von Prashant · Kommentare
Abgelegt unter: Technik

YAML ist sehr nützlich für das Schreiben von Konfigurationsdateien. Primärer Vorteil ist, dass es wie Text-Datei liest. Dies funktioniert wirklich gut, wenn deine config-Datei ist flach (keine Hierarchie) und hat keine Wiederholungen.
Wenn Ihre Konfigurationsdatei hat Wiederholungen dann macht es Sinn zu trennen und diese Elemente wiederverwenden. Was ich meine ist diese - sagen wir mal du deine config-Datei wie folgt aussieht:

  Entwicklung:
   input_location: common_input
   output_location: dev_location
   Mail:
     smtp_server: Ihr_Server
     Anmeldung: your_login
     Kennwort: top_secret
 Produktion:
   input_location: common_input
   output_location: dev_location
   Mail:
     smtp_server: Ihr_Server
     Anmeldung: your_login
     Kennwort: top_secret 

Unter der Annahme obigen Code in / tmp / test.yml hier ist, wie Sie in Python und Ruby zu lesen
$cat readyml.py

 #! / Usr / bin / env python
 von pprint Import pprint als PP
 # In Debian müssen python-yaml installieren
 von YAML Import Last, load_all, dump
 hash = load (open ('/ tmp / test.yml'))
 pp (Hash ['Entwicklung']) 


$ cat readyml.rb

  #! / Usr / bin / env ruby
 require 'pp'
 hash = YAML :: load (File.open ('/ tmp / test.yml'). lesen)
 PP-Hash ['Entwicklung'] 

hier ist ein handliches Einzeiler für Ruby-Version
$ ruby -rpp -e 'pp YAML::load(File.open("/tmp/a.yml"))["development"]' oder Sie können die gleiche oder im IRB-Python-Konsole ausprobieren.

Beachten Sie, dass in dem obigen Code-Schnipsel, alles andere als Ausgang gleichen Standort in Entwicklung und Produktion ist ein Teil ist. Dies ist, wo yml Knotenkennung kommt zu retten. Idee ist einfach über einen Satz von Standardwerten zu überschreiben und sie an anderer Stelle.
Sie könnten auseinanderziehen wie folgt:

  Standardwerte: Standardwerte &
   input_location: common_input
   output_location: dev_location
   Mail:
     sender_name: Absender
     smtp_server: Ihr_Server
     Anmeldung: your_login
     Kennwort: top_secret
 Entwicklung:
   <<: * Standardwerte
 Produktion:
   <<: * Standardwerte
   output_location: prod_location 


$ ruby -rpp -e 'pp YAML::load(File.open("/tmp/a.yml"))["development"]["mail"]["login"]'
"your_login"
$

Große, funktioniert es (tm)!.
Wohl haben wir gehandelt etwas Klarheit für ein bisschen Magie. Hier eine kleine Erläuterung: &, * und <<: & das Anker-Tag ist als Knoten-Kennung verstanden werden kann, ist * Knoten-Referenz und <<: steht für Hash-Merge.

Weitere Details finden Sie entweder yaml specs oder wikipedia
So weit so gut, aber es gibt einen Haken hier, sind diese Hash-Zusammenführungen nicht rekursiv. Was es bedeutet, ist dies: Angenommen, Sie möchten unterschiedliche Absender-Namen für E-Mail in beiden Umgebungen haben, könnten Sie versucht, die Folgendes leistet:

  Standardwerte: Standardwerte &
   input_location: common_input
   output_location: dev_location
   Mail:
     sender_name: Absender
     smtp_server: Ihr_Server
     Anmeldung: your_login
     Kennwort: top_secret
 Entwicklung:
   <<: * Standardwerte
   Mail:
     sender_name: sender_dev
 Produktion:
   <<: * Standardwerte
   output_location: prod_location
   Mail:
     sender_name: sender_prod 

Lets prüfen

$ ruby -rpp -e 'pp YAML::load(File.open("/tmp/a.yml"))["development"]["mail"]["login"]'
nil
$

Hoppla, etwas schief gelaufen ist, wie oben erwähnt Problem ist, dass die Hash-Merge nicht rekursiv ist und während der Zusammenführung es ersetzt Mail des Verzugs mit der Post der Produktion, die nur einen Schlüssel hat. Lösung / Workaround ist es, entrollen eine weitere Ebene:

  common_settings: & common_settings
 input_location: common_input
 output_location: dev_location
 mail_defaults: & mail_defaults
  sender_name: Absender
   smtp_server: Ihr_Server
   Anmeldung: your_login
   Kennwort: top_secret

 Standardwerte: Standardwerte &
   <<: * Common_settings
   Mail:
     <<: * Mail_defaults
 Entwicklung:
   <<: * Standardwerte
 Produktion:
   <<: * Standardwerte
   Mail:
     <<: * Mail_defaults
     sender_name: sender_prod

Lets erneut prüfen

$ ruby -rpp -e 'pp YAML::load(File.open("/tmp/a.yml"))["development"]["mail"]["login"]'
"your_login"
$

Wussten Sie sagen, Sie haben noch eine weitere Ebene der Verschachtelung, gut man kann definitiv entrollen ein weiteres Level, aber dann wird es ein Durcheinander. Also, wenn Sie nicht versuchen, Lösung für Türme von Hanoi in einer conf-Datei zu schreiben, ist es besser, conf-Datei als Graben in yaml oder etwas anderes restucture. Aber das ist Ihr Anruf trotzdem.