Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unexpected hash's behavior

Tags:

ruby

I'm newbie. Help me, please. I write Puppet function Piece of code :

    n_if={}
     over_if = arguments[1]

    over_if.each do |kk,vv|
      weth={}
        puts kk,vv,weth
        weth = arguments[0]
        weth['in_vlan'] = vv['in_vlan']
        weth['options']['MTU'] = vv['mtu']
    n_if['eth'+ kk.to_s]=weth
end

Data readed from 2 files, and passed into arguments[0] and arguments[1] respectively:

# template of ethernet interfaces
eth_: 
  method: "static"
  family: "inet"
  ip: ""
  netmask: "255.255.0.0"
  onboot:  true
  options: 
    MTU: ""
  in_vlan: ""

# values for include into ethernet interfaces
eth_values:
 0:
  mtu: 1500
  in_vlan: 15
 1:
  mtu: 9000
  in_vlan: 125

I expect get hash with keys 'eth0' and 'eth1' as follow:

eth1methodstaticfamilyinetin_vlan125ipnetmask255.255.0.0onboottrueoptionsMTU9000eth0methodstaticfamilyinetin_vlan15ipnetmask255.255.0.0onboottrueoptionsMTU1500

But I get :

eth1methodstaticfamilyinetin_vlan125ipnetmask255.255.0.0onboottrueoptionsMTU9000eth0methodstaticfamilyinetin_vlan125ipnetmask255.255.0.0onboottrueoptionsMTU9000

What is my mistake?

like image 674
user2207683 Avatar asked Jul 30 '26 00:07

user2207683


1 Answers

First, some comments:

  1. Your code is not indented in a way that most others do it, which makes it hard for others to help you. It should look something like this:

    n_if={}
    over_if = arguments[1]
    
    over_if.each do |kk,vv|
      weth={}
      puts kk,vv,weth
      weth = arguments[0]
      weth['in_vlan'] = vv['in_vlan']
      weth['options']['MTU'] = vv['mtu']
      n_if['eth'+ kk.to_s]=weth
    end
    
  2. Perhaps your variable names make sense to you, but they don't make sense to me. What is n_if, weth, over_if, kk and vv?

  3. You assign weth to be a hash inside your each, and then you assign it to be something else. What are you really trying to do?

  4. You say that arguments[0] and arguments[1] are data read in from files. How are these read in? Are these YAML files? It would be helpful if you would include code to actually reproduce your problem. Pare it down to the essentials.

  5. In Ruby it is generally more idiomatic and performant not to concatenate strings, but to use string interpolation:

    n_if["eth#{kk}"] = weth
    

Now, some answers:

My guess is that your setup holds data like this:

arguments = {
  "eth_"=>{
    "method"=>"static",
    "family"=>"inet",
    "ip"=>"",
    "netmask"=>"255.255.0.0",
    "onboot"=>true,
    "options"=>{"MTU"=>""},
    "in_vlan"=>""
  },
  "eth_values"=>{
    0=>{"mtu"=>1500, "in_vlan"=>15},
    1=>{"mtu"=>9000, "in_vlan"=>125}
  }
}

arguments[0] = arguments['eth_']
arguments[1] = arguments['eth_values']

I believe (based on many guesses as to what you have and what you may want) that your problem is this combination:

weth={}
weth=arguments[0]

I think your intent here is to say "weth is a hash type of object; now fill it with values from arguments[0]". What those lines actually say is:

  • Set weth to an empty hash.
  • Nevermind, throw away that empty hash and set weth to the same object as arguments[0].

Consequently, each time through the loop you are modifying the same hash with weth. Instead, I think you want to duplicate the hash for weth. Does the following modified code give you what you need?

n_if={}
over_if = arguments[1]

over_if.each do |kk,vv|
  weth = arguments[0].dup
  weth['in_vlan'] = vv['in_vlan']
  weth['options']['MTU'] = vv['mtu']
  n_if["eth#{kk}"]=weth
end

require 'pp' # for nice wrapping inspection
pp n_if
#=> {"eth0"=>
#=>   {"method"=>"static",
#=>    "family"=>"inet",
#=>    "ip"=>"",
#=>    "netmask"=>"255.255.0.0",
#=>    "onboot"=>true,
#=>    "options"=>{"MTU"=>9000},
#=>    "in_vlan"=>15},
#=>  "eth1"=>
#=>   {"method"=>"static",
#=>    "family"=>"inet",
#=>    "ip"=>"",
#=>    "netmask"=>"255.255.0.0",
#=>    "onboot"=>true,
#=>    "options"=>{"MTU"=>9000},
#=>    "in_vlan"=>125}}

If not, please edit your question with more details on what you ACTUALLY have (hint: p arguments and show us the result) and what you really want as the result.


Edit: For fun, here's a functional transformation instead. It is left as an exercise to the reader to understand how it works and level-up their functional programming skills. Note that I have modified eth_values to match the hierarchy of the template so that simple merging can be applied. I've left the "MTU"=>"" and "in_vlan"=>"" entries in, but note that they are not necessary for the code to work, you could delete both (and the resulting "options"=>{}) and achieve the same result.

args = {
  "eth_"=>{
    "method"=>"static",
    "family"=>"inet",
    "ip"=>"",
    "netmask"=>"255.255.0.0",
    "onboot"=>true,
    "options"=>{"MTU"=>""},
    "in_vlan"=>""
  },
  "eth_values"=>{
    0=>{"options"=>{"MTU"=>1500}, "in_vlan"=>15},
    1=>{"options"=>{"MTU"=>9000}, "in_vlan"=>125}
  }
}

n_if = Hash[
  args['eth_values'].map do |num,values|
    [ "eth#{num}",
      args['eth_'].merge(values) do |k,v1,v2|
      if v1.is_a?(Hash) and v2.is_a?(Hash) then
        v1.merge(v2)
      else
        v2
      end
    end ]
  end
]

pp n_if #=> Same result as in the previous code.
like image 180
Phrogz Avatar answered Aug 01 '26 13:08

Phrogz