web-dev-qa-db-de.com

Schienen laden YAML in Hash und Referenz durch Symbol

Ich lade eine YAML-Datei in Rails 3.0.9 wie folgt:

APP_CONFIG = YAML.load(File.read(File.expand_path('../app.yml', __FILE__)))

Es lädt den gesamten Inhalt wie hierarchische Hashes, kein Problem. Was ich nicht mag, ist die Tatsache, dass auf die Hashes nur mit einfachen oder doppelten Anführungszeichen, nicht aber mit einem Symbol zugegriffen werden kann. 

APP_CONFIG['mailer']['username']  # works fine
APP_CONFIG[:mailer][:username]    # doesn't

Irgendwelche Gedanken?

58

Versuchen Sie es mit HashWithIndifferentAccess

APP_CONFIG = HashWithIndifferentAccess.new(YAML.load(File.read(File.expand_path('../app.yml', __FILE__))))
74
Rob Di Marco

Alternativ können Sie die Schlüssel, auf die Sie als Symbol zugreifen möchten, mit einem Doppelpunkt versehen. Zum Beispiel:

default: &default
  :symbol: "Accessed via a symbol only"
  string: "Accessed via a string only"

development:
  <<: *default

test:
  <<: *default

production:
  <<: *default

Später können Sie dann wie folgt auf diese zugreifen:

APP_CONFIG[:symbol]
APP_CONFIG['string']

Beachten Sie, dass ich YAML::ENGINE.yamler = "syck" verwende. Nicht sicher, ob dies mit psych funktioniert. (Psycho wird das Zusammenführen von Schlüsseln definitiv nicht unterstützen, wie ich im Beispiel gezeigt habe.)

Über die Verwendung von HashWithIndifferentAccess: Die Verwendung von es hat den Nebeneffekt, doppelte Schlüssel zu erstellen: einen für den Symbolzugriff und einen für den Stringzugriff. Dies ist möglicherweise schädlich, wenn Sie YAML-Daten als Arrays übergeben. Seien Sie sich dessen bewusst, wenn Sie sich für diese Lösung entscheiden. 

31
istvanp

Wenn Sie in Ruby on Rails arbeiten, möchten Sie vielleicht einen Blick auf symbolize_keys () werfen, der genau das tut, was das OP verlangt. Wenn der Hash tief ist, können Sie deep_symbolize_keys() verwenden. Mit diesem Ansatz lautet die Antwort

APP_CONFIG = YAML.load(File.read(File.expand_path('../app.yml', __FILE__))).deep_symbolize_keys
25
zentralmaschine

Dies ist dasselbe wie bei der ausgewählten Antwort, jedoch mit einer besseren Syntax:

YAML.load(File.read(file_path)).with_indifferent_access 
15
fotanus

Es gibt eine andere mögliche Antwort, die ich beim Graben entdeckt habe. 

Sie können auf HashWithIndifferentAccess.new verzichten, indem Sie dies anstelle Ihrer YAML-Dateien hinzufügen:

--- !map:HashWithIndifferentAccess

dann einfach YAML.load wie gewohnt. Der einzige Trick besteht darin, dass Rails bereits geladen sein muss, wenn Sie dies in Ihrer Umgebung für die Verwendung in Initialisierern usw. (wie ich) tun. 

11
  1. Rails hat eine spezielle Methode zum Symbolisieren von Schlüsseln.
  2. Sie können die Methode load_file verwenden und File.read entfernen
  3. Nicht sicher, ob Sie expand_path auch benötigen, das Standardverzeichnis ist Rails root.

Ich würde es so einfach schreiben:

YAML::load_file('app.yml').symbolize_keys

7

Sie sind wahrscheinlich an den Params-Hash in Rails gewöhnt, der eigentlich ein HashWithIndifferentAccess und kein Standard-Ruby-Hash-Objekt ist. Auf diese Weise können Sie entweder Zeichenfolgen wie "Aktion" oder Symbole wie: Aktion verwenden, um auf den Inhalt zuzugreifen.

Mit einem HashWithIndifferentAccess erhalten Sie unabhängig davon, was Sie verwenden, die gleichen Ergebnisse. Denken Sie jedoch daran, dass dies nur für HashWithIndifferentAccess-Objekte funktioniert.

Damit dies mit YAML funktioniert, müssen Sie das Ergebnis von YAML.load in einen HashWithIndifferentAccess laden, wie dies so ist:

APP_CONFIG = HashWithIndifferentAccess.new(   YAML.load(File.read(File.expand_path('../app.yml', __FILE__)))   )
2
Tilo

Wenn Sie reines Ruby verwenden (d. H. Keine Rails), können Sie zwischenzeitlich in das JSON-Format wechseln. Die parse-Methode der JSON-Bibliothek kann Schlüssel symbolisieren.

http://Ruby-doc.org/stdlib-2.0.0/libdoc/json/rdoc/JSON.html#method-i-parse

Das meine ich damit:

JSON.parse(JSON.dump(YAML.load_file(File.expand_path('../app.yml', __FILE__))), symbolize_names: true)

Note: Dies erhöht den Aufwand für die Konvertierung zu und von Json.

2
Nav

Normalerweise verwende ich HashWithIndifferentAccess nicht, nur um Verwirrung zu vermeiden und Inkonsistenzen beim Zugriff zu vermeiden. Ich würde stattdessen einen .deep_symbolize_keys verwenden, um das Ganze in Form eines Symbolschlüssels zu erhalten.

0
lobati