ruby md5 digest

Crear un hash md5 de un número, cadena, matriz o hash en Ruby



digest (5)

Necesito crear una cadena de firma para una variable en Ruby, donde la variable puede ser un número, una cadena, un hash o una matriz. Los valores de hash y los elementos de matriz también pueden ser cualquiera de estos tipos.

Esta cadena se usará para comparar los valores en una base de datos (Mongo, en este caso).

Lo primero que pensé fue crear un valor hash MD5 de un valor codificado JSON, así: (body es la variable mencionada anteriormente)

def createsig(body) Digest::MD5.hexdigest(JSON.generate(body)) end

Esto casi funciona, pero JSON.generate no codifica las claves de un hash en el mismo orden cada vez, por lo que createsig({:a=>''a'',:b=>''b''}) no siempre es igual a createsig({:b=>''b'',:a=>''a''}) .

¿Cuál es la mejor manera de crear una cadena de firma que se ajuste a esta necesidad?

Nota: para el detalle orientado entre nosotros, sé que no puede JSON.generate() un número o una cadena. En estos casos, simplemente llamaría a MD5.hexdigest() directamente.


Aquí está mi solución. Camino por la estructura de datos y construyo una lista de piezas que se unen en una sola cadena. Para garantizar que los tipos de clases observados afecten al hash, inyecto un único carácter Unicode que codifica información de tipo básico a lo largo del camino. (Por ejemplo, queremos ["1", "2", "3"]. Objsum! = [1,2,3] .objsum)

Lo hice como un refinamiento en Object, es fácilmente portado a un parche de mono. Para usarlo solo necesita el archivo y ejecuta "usando ObjSum".

module ObjSum refine Object do def objsum parts = [] queue = [self] while queue.size > 0 item = queue.shift if item.kind_of?(Hash) parts << "//000" item.keys.sort.each do |k| queue << k queue << item[k] end elsif item.kind_of?(Set) parts << "//001" item.to_a.sort.each { |i| queue << i } elsif item.kind_of?(Enumerable) parts << "//002" item.each { |i| queue << i } elsif item.kind_of?(Fixnum) parts << "//003" parts << item.to_s elsif item.kind_of?(Float) parts << "//004" parts << item.to_s else parts << item.to_s end end Digest::MD5.hexdigest(parts.join) end end end


Dependiendo de tus necesidades, puedes llamar a ary.inspect o ary.to_yaml , incluso.


Estoy codificando lo siguiente bastante rápido y no tengo tiempo para probarlo realmente en el trabajo, pero debería hacer el trabajo. Avíseme si encuentra algún problema y lo revisaré.

Esto debería aplanarse y ordenar las matrices y los hash, y tendrías que tener algunas cuerdas bastante extrañas para que haya colisiones.

def createsig(body) Digest::MD5.hexdigest( sigflat body ) end def sigflat(body) if body.class == Hash arr = [] body.each do |key, value| arr << "#{sigflat key}=>#{sigflat value}" end body = arr end if body.class == Array str = '''' body.map! do |value| sigflat value end.sort!.each do |value| str << value end end if body.class != String body = body.to_s << body.class.to_s end body end > sigflat({:a => {:b => ''b'', :c => ''c''}, :d => ''d''}) == sigflat({:d => ''d'', :a => {:c => ''c'', :b => ''b''}}) => true


Si solo pudieras obtener una representación de cuerdas del body y no hacer que el hash Ruby 1.8 volviera con diferentes órdenes de una vez a la otra, podrías hash confiablemente esa representación de cadena. Vamos a ensuciarnos las manos con algunos parches de mono:

require ''digest/md5'' class Object def md5key to_s end end class Array def md5key map(&:md5key).join end end class Hash def md5key sort.map(&:md5key).join end end

Ahora cualquier objeto (de los tipos mencionados en la pregunta) responde a md5key devolviendo una clave confiable para usarla para crear una suma de comprobación, entonces:

def createsig(o) Digest::MD5.hexdigest(o.md5key) end

Ejemplo:

body = [ { ''bar'' => [ 345, "baz", ], ''qux'' => 7, }, "foo", 123, ] p body.md5key # => "bar345bazqux7foo123" p createsig(body) # => "3a92036374de88118faf19483fe2572e"

Nota: Esta representación hash no codifica la estructura, solo la concatenación de los valores. Por lo tanto, ["a", "b", "c"] marcará lo mismo que ["abc"].


Solo mis 2 centavos:

module Ext module Hash module InstanceMethods # Return a string suitable for generating content signature. # Signature image does not depend on order of keys. # # {:a => 1, :b => 2}.signature_image == {:b => 2, :a => 1}.signature_image # => true # {{:a => 1, :b => 2} => 3}.signature_image == {{:b => 2, :a => 1} => 3}.signature_image # => true # etc. # # NOTE: Signature images of identical content generated under different versions of Ruby are NOT GUARANTEED to be identical. def signature_image # Store normalized key-value pairs here. ar = [] each do |k, v| ar << [ k.is_a?(::Hash) ? k.signature_image : [k.class.to_s, k.inspect].join(":"), v.is_a?(::Hash) ? v.signature_image : [v.class.to_s, v.inspect].join(":"), ] end ar.sort.inspect end end end end class Hash #:nodoc: include Ext::Hash::InstanceMethods end