crystal-lang

What does the ** (Double splat) in Crystal lang do?


What does the ** prefix do in this method call using Crystal-lang? This is from the shrine file package. Can you explain how I would use a double splat?

class FileImport::AssetUploader < Shrine
  def generate_location(io : IO | UploadedFile, metadata, context, **options) HERE
    name = super(io, metadata, **options)

    File.join("imports", context[:model].id.to_s, name)
  end
end

FileImport::AssetUploader.upload(file, "store", context: { model: YOUR_ORM_MODEL } })

Solution

  • According to the official docs:

    A double splat (**) captures named arguments that were not matched by other parameters. The type of the parameter is a NamedTuple.

    def foo(x, **other)
      # Return the captured named arguments as a NamedTuple
      return other
    end
    
    foo 1, y: 2, z: 3    # => {y: 2, z: 3}
    foo y: 2, x: 1, z: 3 # => {y: 2, z: 3}
    
    

    The usefulness of the double splat is that it captures all named arguments. For example, you may create a function that handles any number of keyword arguments.

    def print_any_tuple_with_any_keys(**named_tuple)
      named_tuple.each { |k, v| puts "Options #{k}: #{v}" }
    end
     
    print_any_tuple_with_any_keys(api: "localhost")
    print_any_tuple_with_any_keys(fruit: "banana", color: "yellow")
    print_any_tuple_with_any_keys(hash: "123", power: "2", cypher: "AES")
    
    

    This will output:

    Options api: localhost
    Options fruit: banana
    Options color: yellow
    Options hash: 123
    Options power: 2
    Options cypher: AES
    

    In the code you provided, all the other named arguments passed to generate_location that do not match io, metadata, or context will be passed down to the super function that is calling the parent class, in this case, a Shrine class. The use for Shrine specifically, is that they provide a generic upload function for different storage engines, any extra arguments may or may not be used down the call tree, and in the case of AWS S3 storage, there may be a metadata argument that adds metadata to the file.