Search code examples
rubyyamljruby

Update value in key value pair in yml file using ruby


I have a YAML file:

DATA_SVC_GEN_CONF_ATTR:
   -
     -   
       - 
         name: validationCodeNum
         value: 1
       - 
         name: validationCodeSymbol
         value: "val1"
       - 
         name: sql
         value: Test
       - 
         name: sqlByGridArea
         value: Test1

I want to replace the value of the key sql from "Test" to "Select * from table".

What I am trying is:

asset.getAttributes.getList.each{|a|

  if a.getName == "sqlByGridArea"
     a.setValue "Select * from table"

    (attrib_defs).map!{|a| a.map{|k,v| "#{k}=#{v}"}.join(',')}
  end
   asset.with_attributes attrib_defs

Solution

  • Here's how to test whether your YAML is valid, and how I generate YAML for use in files or elsewhere:

    require 'yaml'
    
    str = <<EOT
    DATA_SVC_GEN_CONF_ATTR:
       -
         -   
           - 
             name: validationCodeNum
             value: 1
           - 
             name: validationCodeSymbol
             value: "val1"
           - 
             name: sql
             value: Test
           - 
             name: sqlByGridArea
             value: Test1
    EOT
    
    data = YAML.load(str)
    # => {"DATA_SVC_GEN_CONF_ATTR"=>
    #      [[[{"name"=>"validationCodeNum", "value"=>1},
    #         {"name"=>"validationCodeSymbol", "value"=>"val1"},
    #         {"name"=>"sql", "value"=>"Test"},
    #         {"name"=>"sqlByGridArea", "value"=>"Test1"}]]]}
    

    YAML would complain if it couldn't parse the data. If it could and the data wasn't what you expect the output help you diagnose the problem.

    When I want to create a YAML file, especially one that's complex, I start with the actual data structure and let YAML generate the serialized version of it:

    data = {
      'DATA_SVC_GEN_CONF_ATTR' =>
      [
        [
          [
            {
              'name' => 'validationCodeNum',
              'value' => 1
            },
            {
              'name' => 'validationCodeSymbol',
              'value' => 'val1'
            },
            {
              'name' => 'sql',
              'value' => 'Test'
            },
            {
              'name' => 'sqlByGridArea',
              'value' => 'Test1'
            }
          ]
        ]
      ]
    }
    puts data.to_yaml
    # >> ---
    # >> DATA_SVC_GEN_CONF_ATTR:
    # >> - - - name: validationCodeNum
    # >>       value: 1
    # >>     - name: validationCodeSymbol
    # >>       value: val1
    # >>     - name: sql
    # >>       value: Test
    # >>     - name: sqlByGridArea
    # >>       value: Test1
    

    Moving on... To change elements in a YAML file you can treat the file as text, read it line-by-line, read it by slurping it, or have YAML open it and change the resulting object then have YAML rewrite it.

    Slurping files is OK if you're sure the data will fit in memory and you can easily find the section you want to change. Reading it line-by-line often makes it easier to find specific text because you're dealing with individual lines, not the whole file.

    Using YAML is the easiest I think as it reduces the change of a badly written regex doing the wrong thing.

    data = <<EOT
    ---
    DATA_SVC_GEN_CONF_ATTR:
    - - - name: validationCodeNum
          value: 1
        - name: validationCodeSymbol
          value: val1
        - name: sql
          value: Test
        - name: sqlByGridArea
          value: Test1
    EOT
    

    Pretending that data was the content of a YAML file on disk, it'd be loaded and parsed using YAML.load_file. In this example I'm loading as a string so I have to use load:

    object = YAML.load(data)
    

    The data is now parsed so it can be manipulated easily:

    object['DATA_SVC_GEN_CONF_ATTR'].first.first[2]['name'] = "Select * from table"
    

    Then it can be written back out:

    puts object.to_yaml # => nil
    # >> ---
    # >> DATA_SVC_GEN_CONF_ATTR:
    # >> - - - name: validationCodeNum
    # >>       value: 1
    # >>     - name: validationCodeSymbol
    # >>       value: val1
    # >>     - name: Select * from table
    # >>       value: Test
    # >>     - name: sqlByGridArea
    # >>       value: Test1
    

    If this is a mission-critical (production) file, you'd want to do the appropriate rename, write, delete steps to protect the data in-case of errors, but that's the gist of it.

    Your data structure is very questionable though. You're using a nested array-of-array-of-array just to hold your hashes, which is not very common or easily understood. Instead it should be reduced to a single array:

    data = <<EOT
    ---
    DATA_SVC_GEN_CONF_ATTR:
     - name: validationCodeNum
       value: 1
     - name: validationCodeSymbol
       value: val1
     - name: sql
       value: Test
     - name: sqlByGridArea
       value: Test1
    EOT
    
    object = YAML.load(data)
    # => {"DATA_SVC_GEN_CONF_ATTR"=>
    #      [{"name"=>"validationCodeNum", "value"=>1},
    #       {"name"=>"validationCodeSymbol", "value"=>"val1"},
    #       {"name"=>"sql", "value"=>"Test"},
    #       {"name"=>"sqlByGridArea", "value"=>"Test1"}]}
    object['DATA_SVC_GEN_CONF_ATTR'][2]['name'] = "Select * from table"