I\'m pretty new to Ruby so apologies if this is an obvious question.
I\'d like to use named parameters when instantiating a Struct, i.e. be able to specify which ite
Have you considered OpenStruct?
require 'ostruct'
person = OpenStruct.new(:name => "John", :age => 20)
p person # #<OpenStruct name="John", age=20>
p person.name # "John"
p person.adress # nil
With newer versions of Ruby you can use keyword_init: true
:
Movie = Struct.new(:title, :length, :rating, keyword_init: true)
Movie.new(title: 'Title', length: '120m', rating: 'R')
# => #<struct Movie title="Title", length="120m", rating="R">
Based on @Andrew Grimm's answer, but using Ruby 2.0's keyword arguments:
class Struct
# allow keyword arguments for Structs
def initialize(*args, **kwargs)
param_hash = kwargs.any? ? kwargs : Hash[ members.zip(args) ]
param_hash.each { |k,v| self[k] = v }
end
end
Note that this does not allow mixing of regular and keyword arguments-- you can only use one or the other.
If your hash keys are in order you can call the splat operator to the rescue:
NavLink = Struct.new(:name, :url, :title)
link = {
name: 'Stack Overflow',
url: 'https://stackoverflow.com',
title: 'Sure whatever'
}
actual_link = NavLink.new(*link.values)
#<struct NavLink name="Stack Overflow", url="https://stackoverflow.com", title="Sure whatever">
You could rearrange the if
s.
class MyStruct < Struct
# Override the initialize to handle hashes of named parameters
def initialize *args
# I think this is called a guard clause
# I suspect the *args is redundant but I'm not certain
return super *args unless (args.length == 1 and args.first.instance_of? Hash)
args.first.each_pair do |k, v|
# I can't remember what having the conditional on the same line is called
self[k] = v if members.include? k
end
end
end
For a 1-to-1 equivalent with the Struct behavior (raise when the required argument is not given) I use this sometimes (Ruby 2+):
def Struct.keyed(*attribute_names)
Struct.new(*attribute_names) do
def initialize(**kwargs)
attr_values = attribute_names.map{|a| kwargs.fetch(a) }
super(*attr_values)
end
end
end
and from there on
class SimpleExecutor < Struct.keyed :foo, :bar
...
end
This will raise a KeyError
if you missed an argument, so real nice for stricter constructors and constructors with lots of arguments, data transfer objects and the like.