Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to output sorted hash in ruby template

Tags:

ruby

puppet

I'm building a config file for one of our inline apps. Its essentially a json file. I'm having a lot of trouble getting puppet/ruby 1.8 to output the hash/json the same way each time.

I'm currently using

<%= require "json"; JSON.pretty_generate data %>

But while outputting human readable content, it doesn't guarantee the same order each time. Which means that puppet will send out change notifications often for the same data.

I've also tried

<%= require "json"; JSON.pretty_generate Hash[*data.sort.flatten] %>

Which will generate the same data/order each time. The problem comes when data has a nested array.

data => { beanstalkd => [ "server1", ] }

becomes

"beanstalkd": "server1",

instead of

"beanstalkd": ["server1"],

I've been fighting with this for a few days on and off now, so would like some help

like image 439
Gavin Mogan Avatar asked Mar 16 '12 08:03

Gavin Mogan


1 Answers

Since hashes in Ruby are ordered, and the question is tagged with ruby, here's a method that will sort a hash recursively (without affecting ordering of arrays):

def sort_hash(h)
  {}.tap do |h2|
    h.sort.each do |k,v|
      h2[k] = v.is_a?(Hash) ? sort_hash(v) : v
    end
  end
end

h = {a:9, d:[3,1,2], c:{b:17, a:42}, b:2 }
p sort_hash(h)
#=> {:a=>9, :b=>2, :c=>{:a=>42, :b=>17}, :d=>[3, 1, 2]}

require 'json'
puts sort_hash(h).to_json
#=> {"a":9,"b":2,"c":{"a":42,"b":17},"d":[3,1,2]}

Note that this will fail catastrophically if your hash has keys that cannot be compared. (If your data comes from JSON, this will not be the case, since all keys will be strings.)

like image 185
Phrogz Avatar answered Sep 28 '22 04:09

Phrogz