2009-03-04 16 views
5

Podría alguien por favor explicar este pedazo de código Ruby:Ruby Code explicó

def add_spec_path_to(args) # :nodoc: 
    args << {} unless Hash === args.last 
    args.last[:spec_path] ||= caller(0)[2] 
end 

he visto el operador << utilizado para las cadenas de concatenación o como un operador de bits en otros idiomas, pero podría alguien explicar en este contexto. ¿De alguna manera está agregando un lamda en blanco a Args o estoy totalmente equivocado?

también puedo veo utiliza como esto:

before_parts(*args) << block 

¿Está el Hash una palabra clave?

Tampoco estoy seguro de lo que dice el operador ||=.

Estoy igualmente en la oscuridad en cuanto a lo que caller(0)[2] es.

Respuesta

11

|| = es una expresión común de Ruby: asigna el valor solo si no está ya configurado. El efecto es el mismo que el código como

if some_variable == nil 
    some_variable = some_value 
end 

o

some_variable= some_value unless some_variable 

===, cuando no anulado, compara dos objetos de la identidad. En el caso de Hash === args.last, Hash (que es un objeto de tipo Clase) está comprobando si coincide con la clase del último elemento en Array Array. El código está haciendo uso del hecho aparente de que la implementación de Class # === obliga a controlar la clase del objeto comparado.

No funciona al revés, por ejemplo:

a = [{}] 
Hash === a.last #=> true 
a.last === Hash #=> false 

Los argumentos de cola a un método se pueden suministrar como el contenido de un hash sin necesidad de proporcionar la {}

Entonces puede hacer esto:

def hello(arg1, arg2, arg3) 
    puts [arg1.class, arg2.class, arg3.class].join(',') 
end 

hello 1,2,3 #=> Fixnum,Fixnum,Fixnum 
hello :a, "b", :c => 1, :d => 99 #=> Symbol,String,Hash 

A menudo se utiliza para proporcionar una lista de longitud variable de parámetros opcionales para la función.

¿Estás seguro de que transcribiste el código original con precisión, por cierto? Para obtener una matriz de argumentos, normalmente agregaría un * al argumento como declarado, de lo contrario args tendría que ingresarse como una matriz, que preferiría derrotar al objeto.

def add_spec_path_to(*args)    # now args is an array 
    args << {} unless Hash === args.last # if trailing arguments cannot be 
             # interpreted as a Hash, add an empty 
             # Hash here so that following code will 
             # not fail 
    args.last[:spec_path] ||= caller(0)[2] # Set the spec_path option if it's not 
             # already set 
end 

EDIT: Ampliando aún más en la cosa * args, intente esto:

def x(*args) 
    puts args.join(',') 
    puts args.map{|a| a.class }.join(',') 
end 

x 1,2,:a=>5,:b=>6 
1,2,a5b6 
Fixnum,Fixnum,Hash 

... usando args * Causas argumentos que se presentarán al método como una matriz. Si yo no uso el *, de esta manera, por ejemplo:

def y(args) 
    puts args.join(',') 
    puts args.map{|a| a.class }.join(',') 
end 

... entonces args tiene que ser una matriz antes de que llame al método, o voy a conseguir un "ArgumentError: número incorrecto de argumentos "para todo menos una cosa aprobada". Por lo tanto, debe verse así:

y [1,2,{:c=>3,:d=>4}] 

... con el hash creado explícitamente con {}. Y es feo

Todo lo anterior probado con MRI 1.8.6, por cierto.

+0

No, pasando de una matriz tiene sentido, ya que no se devuelve argumentos, por lo contrario, un hash anexado se perdería. – rampion

+0

No estoy seguro de entender: llamarías al método así: some_method ([arg1, arg2, arg3], options_hash)? Eso es horrible: vea la respuesta editada arriba ... –

+0

Gran explicación, Mike. –

14

Supongo que args es un Array.

Hash es el nombre de una clase - la primera línea empuja un hash vacío {} en argsmenos el último elemento de args es ya una (el operador === para las pruebas de clases si un objeto es de una cierta clase) Hash .

El operador ||= es similar al operador +=: es más o menos equivalente a:

args.last[:spec_path] = args.last[:spec_path] || caller(0)[2] 

Así, se establecerá args.last[:spec_path] si y sólo si se encuentra actualmente desactivada.

El método caller devuelve información sobre el método de llamada.

1

En un pequeño camino más corto:

def add_spec_path_to(args) # :nodoc: 

... 

# Append an empty hash to args UNLESS the last arg is a hash.. in which case do nothing 
args << {} unless Hash === args.last # so we need a hash. If it is not there, make an empty one and put it there. 

... 

#if args.last[:spec_path] equals nil or false, set it to caller(0)[2]... 

#so inside that hash from the first part, if :spec_path is not there, create it by using caller(0)[2]. 

args.last[:spec_path] ||= caller(0)[2] 

... 

end