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