Ruby Code explained

前端 未结 3 1657
时光取名叫无心
时光取名叫无心 2020-12-10 18:42

Could somebody please explain this piece of Ruby code:

def add_spec_path_to(args) # :nodoc:
  args << {} unless Hash === args.last
  args.last[:spec_pa         


        
相关标签:
3条回答
  • 2020-12-10 19:06

    I'm presuming that args is an Array.

    Hash is the name of a class - the first line pushes an empty hash {} onto args unless the last element of args is already a Hash (the === operator for classes tests whether an object is of a certain class).

    The ||= operator is similar to the += operator: it's more or less equivalent to:

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

    So, it will set args.last[:spec_path] if and only if it is currently unset.

    The caller method returns info about the calling method.

    0 讨论(0)
  • 2020-12-10 19:09

    In a little shorter way:

    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
    
    0 讨论(0)
  • 2020-12-10 19:27

    ||= is a common Ruby idiom: it assigns the value only if it is not already set. The effect is the same as code like

    if some_variable == nil
       some_variable = some_value
    end
    

    or

    some_variable= some_value unless some_variable
    

    ===, when not overridden, compares two objects for identity. In the case of Hash === args.last, Hash (which is an object of type Class) is checking to see if it matches the class of the last item in the args Array. The code is making use of the apparent fact that the implementation of Class#=== forces a check on the class of the compared object.

    It doesn't work the other way around, for example:

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

    The trailing arguments to a method may be supplied as the contents of a hash without needing to provide the {}

    So you can do this:

    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
    

    It's often used to provide a variable-length list of optional parameters to the function.

    Are you sure you transcribed the original code precisely, btw? In order to get an array of arguments, you would normally add an * to the argument as declared, otherwise args would have to be input as an array, whcih would rather defeat the object.

    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: Expanding further on the *args thing, try this:

    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
    

    ... using *args causes args to be presented to the method as an array. If I don't use the *, like this, for example:

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

    ... then args has to be an array before I call the method, or I'll get an "ArgumentError: wrong number of arguments" for anything but one thing passed. So it has to look like this:

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

    ...with the Hash explicitly created with {}. And it's ugly.

    All the above tested with MRI 1.8.6, btw.

    0 讨论(0)
提交回复
热议问题