Rails load YAML to hash and reference by symbol

Ruby on-RailsHashYamlSymbols

Ruby on-Rails Problem Overview


I am loading a YAML file in Rails 3.0.9 like this:

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

It loads the all of the contents like hierarchical hashes, no problem. The part I don't like is the fact that the hashes can only be accessed with single or double quotes but not a symbol.

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

Any thoughts?

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

Try using the HashWithIndifferentAccess like

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

Solution 2 - Ruby on-Rails

An alternative solution is to have the keys which you wish to access as a symbol prepended with a colon. For example:

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

development:
  <<: *default

test:
  <<: *default

production:
  <<: *default

Later you can then access these like so:

APP_CONFIG[:symbol]
APP_CONFIG['string']

Note that I am using YAML::ENGINE.yamler = "syck". Not sure if this works with psych. (Psych definitely won't support key merging as I showed in the example though.)

About using HashWithIndifferentAccess: using it has the side effect of creating duplicate keys: one for symbol access and one for string access. This might be nefarious if you pass around YAML data as arrays. Be aware of this if you go with that solution.

Solution 3 - Ruby on-Rails

If you are working in Ruby on Rails, You might want to take a look at symbolize_keys(), which does exactly what the OP asked for. If the hash is deep,you can use deep_symbolize_keys(). Using this approach, the answer is

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

Solution 4 - Ruby on-Rails

This is the same from the selected answer, but with a better syntax:

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

Solution 5 - Ruby on-Rails

Psych (a.k.a. YAML) provides the keyword argument :symbolize_names to load keys as symbols. See method reference

file_path = File.expand_path('../app.yml', __FILE__)
yaml_contents = File.read(file_path)
    
APP_CONFIG = YAML.safe_load(yaml_contents, symbolize_names: true)

Solution 6 - Ruby on-Rails

There is another potential answer I discovered while digging around.

You can forgo HashWithIndifferentAccess.new by instead adding this to the top of your YAML files:

--- !map:HashWithIndifferentAccess

then simply YAML.load like normal. The only trick is that rails needs to already be loaded if you are doing this in your environment for use in initializers, etc. (like I am).

Solution 7 - Ruby on-Rails

  1. Rails has a special method to symbolize keys.
  2. You can use load_file method and get rid of File.read
  3. Not sure if you need expand_path also, the default directory is rails root.

I'd write it that simple:

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

Solution 8 - Ruby on-Rails

If you're using pure Ruby (i.e. no Rails), you could intermediately change to JSON format. The JSON lib's parse method can symbolize keys.

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

Here's what I mean:

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

Note: This adds overhead of conversion to and from json.

Solution 9 - Ruby on-Rails

Just use appropriate option in your YAML parser. For instance, symbolize_names in Psych:

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

See RDoc: https://ruby-doc.org/stdlib-2.6.1/libdoc/psych/rdoc/Psych.html#method-c-load.

Solution 10 - Ruby on-Rails

You are probably used to the params hash in Rails, which is actually a HashWithIndifferentAccess rather than a standard ruby Hash object. This allows you to use either strings like 'action' or symbols like :action to access the contents.

With a HashWithIndifferentAccess, you will get the same results regardless of what you use, but keep in mind this only works on HashWithIndifferentAccess objects.

So to make this work with YAML, you'll have to load the result of YAML.load into a HashWithIndifferentAccess, like so:

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

Solution 11 - Ruby on-Rails

I usually don't use HashWithIndifferentAccess just to avoid confusion and prevent inconsistencies in the way that it is accessed, so what I would do instead is tack on a .deep_symbolize_keys to get the whole thing in symbol key form.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMichael K MadisonView Question on Stackoverflow
Solution 1 - Ruby on-RailsRob Di MarcoView Answer on Stackoverflow
Solution 2 - Ruby on-RailsistvanpView Answer on Stackoverflow
Solution 3 - Ruby on-RailszentralmaschineView Answer on Stackoverflow
Solution 4 - Ruby on-RailsfotanusView Answer on Stackoverflow
Solution 5 - Ruby on-RailsbbennoView Answer on Stackoverflow
Solution 6 - Ruby on-RailsMichael K MadisonView Answer on Stackoverflow
Solution 7 - Ruby on-RailsVladimir KrivchenkoView Answer on Stackoverflow
Solution 8 - Ruby on-RailsNavView Answer on Stackoverflow
Solution 9 - Ruby on-RailsskaleeView Answer on Stackoverflow
Solution 10 - Ruby on-RailsTiloView Answer on Stackoverflow
Solution 11 - Ruby on-RailslobatiView Answer on Stackoverflow