class ChefCLI::PolicyfileLock

def canonicalize_elements(item)

def canonicalize_elements(item)
  case item
  when Hash
    # Hash keys will sort differently based on the encoding, but after a
    # JSON round trip everything will be UTF-8, so we have to normalize the
    # keys to UTF-8 first so that the sort order uses the UTF-8 strings.
    item_with_normalized_keys = item.inject({}) do |normalized_item, (key, value)|
      validate_attr_key(key)
      normalized_item[key.encode("utf-8")] = value
      normalized_item
    end
    elements = item_with_normalized_keys.keys.sort.map do |key|
      k = '"' << key << '":'
      v = canonicalize_elements(item_with_normalized_keys[key])
      k << v
    end
    "{" << elements.join(",") << "}"
  when String
    '"' << item.encode("utf-8") << '"'
  when Array
    elements = item.map { |i| canonicalize_elements(i) }
    "[" << elements.join(",") << "]"
  when Integer
    item.to_s
  when Float
    unless item.finite?
      raise InvalidPolicyfileAttribute, "Floating point numbers cannot be infinite or NaN. You gave #{item.inspect}"
    end
    # Support for floats assumes that any implementation of our JSON
    # canonicalization routine will use IEEE-754 doubles. In decimal terms,
    # doubles give 15-17 digits of precision, so we err on the safe side
    # and only use 15 digits in the string conversion. We use the `g`
    # format, which is a documented-enough "do what I mean" where floats
    # >= 0.1 and < precsion are represented as floating point literals, and
    # other numbers use the exponent notation with a lowercase 'e'. Note
    # that both Ruby and Erlang document what their `g` does but have some
    # differences both subtle and non-subtle:
    #
    # ```ruby
    # format("%.15g", 0.1) #=> "0.1"
    # format("%.15g", 1_000_000_000.0) #=> "1000000000"
    # ```
    #
    # Whereas:
    #
    # ```erlang
    # lists:flatten(io_lib:format("~.15g", [0.1])). %=> "0.100000000000000"
    # lists:flatten(io_lib:format("~.15e", [1000000000.0])). %=> "1.00000000000000e+9"
    # ```
    #
    # Other implementations should normalize to ruby's %.15g behavior.
    Kernel.format("%.15g", item)
  when NilClass
    "null"
  when TrueClass
    "true"
  when FalseClass
    "false"
  else
    raise InvalidPolicyfileAttribute,
      "Invalid type in attributes. Only Hash, Array, String, Integer, Float, true, false, and nil are accepted. You gave #{item.inspect} (#{item.class})"
  end
end