Samenvoegen hashes in yaml conf-bestanden

31 juli 2009 door Prashant · Reacties
Geplaatst onder: technologie

YAML is heel handig voor het schrijven van configuratiebestanden. Belangrijkste voordeel is dat, het leest als tekstbestand. Dit werkt heel 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 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') te 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 uitgang locatie is hetzelfde in ontwikkeling en productie deel. Dit is waar yml node identifier komt redden aan. Idee is simpel hebben een set van standaard waarden en ze op verschillende plaatsen te overschrijven.
Je zou kunnen trek het uit elkaar als volgt uit:

  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:
   <<: * Defaults
 productie:
   <<: * Defaults
   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 wat meer duidelijkheid verhandeld voor een beetje magie. Hier is een kleine uitleg: &, * en <<: & dat is anker-tag kan worden opgevat als node identifier, * is knooppunt referentie en <<: staat voor hash samen te voegen.

Voor meer details zie ofwel yaml specs of wikipedia
Tot nu toe zo goed, maar er is een catch hier, deze hash samenvoegingen worden niet recursief. Wat het betekent is dit: laten we zeggen dat u verschillende afzender naam voor e-mail hebben 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:
   <<: * Defaults
   mail:
     SENDER_NAME: sender_dev
 productie:
   <<: * Defaults
   output_location: prod_location
   mail:
     SENDER_NAME: sender_prod 

Laten controleren

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

Oops, ging er iets mis, probleem zoals hierboven vermeld is dat de hash samen te voegen niet recursieve is en terwijl haar samenvoegen vervangen mail in gebreke per post van de productie die op een sleutel heeft. Oplossing / omzeilen is te rollen 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:
   <<: * Defaults
 productie:
   <<: * Defaults
   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"
$

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