How to convert a Ruby Hash to YAML and keeping comments?

78 Views Asked by At

How to convert a Ruby Hash to YAML and keeping comments?

For the following ruby example

require 'yaml'

h = {
  # The garage in Miami
  'garage_A' => {
    'car_x' => {
      # Bought in Gotham
      'name' => 'Batmobile',
      'color' => 'black'
    }
  },
  'garage_B' => {
    # Mike's bike
    'bike_m' => {
      # Cherry red
      'color' => 'red',
      'wheels' => 2
    }
  }
}

puts YAML.dump(h)

I would like to obtain the following result:

---
# The garage in Miami
garage_A:
  car_x:
    # Bought in Gotham
    name: Batmobile
    color: black
garage_B:
  # Mike's bike
  bike_m:
    # Cherry red
    color: red
    wheels: 2

Here is what I'm getting currently:

---                                                                                                                                                                                                                  
garage_A:                                                                                                                                                                                                            
  car_x:                                                                                                                                                                                                             
    name: Batmobile                                                                                                                                                                                                  
    color: black                                                                                                                                                                                                     
garage_B:                                                                                                                                                                                                            
  bike_m:                                                                                                                                                                                                            
    color: red                                                                                                                                                                                                       
    wheels: 2  

I could also be interested in the reverse operation.

Use case: A Ruby hash with teh default configuration for a tool. When the configuration file does not exist, it dumps the hash to YAML and write the configuration file. For user-friendlyness it would be better to have the comments in the configuration file rather than to have to refer to the documentation.

1

There are 1 best solutions below

0
Todd A. Jacobs On

Add Comment Keys to Each Hash

You should really template your YAML files as here-documents, or use a templating language like erb. If possible, you should also use more intention-revealing names for your Hash elements so that the comments are unnecessary.

Should you find you still genuinely need the comments, remember that neither JSON nor Ruby Hash objects natively support comments as part of their syntax. As a result, you'll need to create a "comment" key/value pair for each nested object in your Ruby Hash in order to display it within the JSON or YAML you generate from the Hash object. For example:

require "yaml"

# Assign a comment key to each nested element.
hash_with_comments = {
  "comment"  => "nested hashes contain 'comment' keys",
  "garage_a" => {
    "comment" => "The garage in Miami",
    "car_x"   => {
      "comment" => "Bought in Gotham",
      "name"    => "Batmobile",
      "color"   => "black",
    }
  }
}

Now hash_with_comments.to_yaml will yield the following YAML:

---
comment: nested hashes contain 'comment' keys
garage_a:
  comment: The garage in Miami
  car_x:
    comment: Bought in Gotham
    name: Batmobile
    color: black

Depending on your use case, you could stop there and just ignore comment keys in whatever code parses the YAML. Otherwise, you can use the String#gsub block syntax (you must use a block to avoid polluting $1 for non-matching lines) to convert the comment elements into actual comments. For example, calling:

hash_with_comments.to_yaml.gsub(/^(\s*)comment:/) { "#{$1}#" }

will yield the following YAML after converting the comment keys to actual comments:

---
# each Hash contains a 'comment' key
garage_a:
  # The garage in Miami
  car_x:
    # Bought in Gotham
    name: Batmobile
    color: black