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

Viewed 60

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 } })
1 Answers

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.

Related