Samenvoegen hashes in YAML conf bestanden

31 juli 2009 door Prashant · Reacties
Geplaatst onder: technologie

YAML is heel handig voor het schrijven van configuratiebestanden. Primair voordeel is dat, het leest als tekstbestand. Dit werkt echt goed als je config bestand is plat (geen hiërarchie) en heeft geen herhalingen.
Als uw configuratie bestand herhalingen dan is het zinvol deze te scheiden van die elementen en ze opnieuw te gebruiken. Wat ik bedoel is dit - laten we zeggen dat je je config bestand ziet er als volgt uit:

  ontwikkeling:
   input_location: common_input
   output_location: dev_location
   mail:
     smtp_server: uw_server
     login: your_login
     wachtwoord: top_secret
 productie:
   input_location: common_input
   output_location: dev_location
   mail:
     smtp_server: uw_server
     login: your_login
     wachtwoord: top_secret 

Ervan uitgaande dat bovenstaande code in / tmp / test.yml hier is hoe je kunt lezen in Python en Ruby
$cat readyml.py

 #! / Usr / bin / env python
 van pprint import pprint als pp
 # In debian moet python-yaml te installeren
 van yaml import belasting, load_all, dump
 hash = belasting (open ("/ tmp / test.yml '))
 pp (hash ['ontwikkeling']) 


$ cat readyml.rb

  #! / Usr / bin / env ruby
 require 'pp'
 hash = YAML :: load (File.open ('/ tmp / test.yml'). lezen)
 pp hash ['ontwikkeling'] 

Hier is een handige one-liner voor Ruby versie
$ ruby -rpp -e 'pp YAML::load(File.open("/tmp/a.yml"))["development"]' of u kunt hetzelfde proberen in irb of python console.

Merk op dat in het bovenstaande stukje code, alles is anders dan uitvoerlocatie hetzelfde is in ontwikkeling en productie deel. Dit is waar yml node identifier komt te redden is. Idee is simpel beschikken over een set van standaard waarden en ze op andere plaats te overschrijven.
Je zou kunnen trek het uit elkaar als volgt:

  standaard: & defaults
   input_location: common_input
   output_location: dev_location
   mail:
     SENDER_NAME: afzender
     smtp_server: uw_server
     login: your_login
     wachtwoord: top_secret
 ontwikkeling:
   <<: * Standaard
 productie:
   <<: * Standaard
   output_location: prod_location 


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

Geweldig, het werkt (tm)!.
Ongetwijfeld hebben we geruild enige duidelijkheid voor een beetje magie. Hier is een kleine uitleg: &, * en <<: & dat is ankertag kan worden opgevat als node identifier, * is knooppunt referentie-en <<: staat voor hash samenvoegen.

Voor meer details zie ofwel yaml specs of wikipedia
Tot nu toe zo goed, maar er is hier een vangst, deze hash fuseert zijn niet recursief. Wat het betekent is dit: stel dat u verschillende afzender naam hebben voor de post in twee omgevingen, kunt u in de verleiding komen het volgende doen:

  standaard: & defaults
   input_location: common_input
   output_location: dev_location
   mail:
     SENDER_NAME: afzender
     smtp_server: uw_server
     login: your_login
     wachtwoord: top_secret
 ontwikkeling:
   <<: * Standaard
   mail:
     SENDER_NAME: sender_dev
 productie:
   <<: * Standaard
   output_location: prod_location
   mail:
     SENDER_NAME: sender_prod 

Laten we bekijken

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

Oeps, er ging iets mis, probleem zoals hierboven vermeld is dat de hash samenvoegen niet recursief is en bij het samenvoegen van deze vervangen mail in gebreke per post van de productie die op een sleutel heeft. Oplossing / werk in de buurt is om uitgerold nog een niveau:

  common_settings: & common_settings
 input_location: common_input
 output_location: dev_location
 mail_defaults: & mail_defaults
  SENDER_NAME: afzender
   smtp_server: uw_server
   login: your_login
   wachtwoord: top_secret

 standaard: & defaults
   << * Common_settings
   mail:
     << * Mail_defaults
 ontwikkeling:
   <<: * Standaard
 productie:
   <<: * Standaard
   mail:
     << * Mail_defaults
     SENDER_NAME: sender_prod

Laten we controleer opnieuw

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

Heb je zeggen dat je nog een niveau van nesting, goed je kan zeker uitrollen nog een niveau, maar dan wordt het een puinhoop. Dus, als je niet probeert om oplossing voor Torens van Hanoi in een conf bestand te schrijven, is het beter om conf bestand restucture dan graven in yaml of iets anders. Maar dat is uw oproep toch.