4

I'd like to allow the user to pass an option to a method that can be either a single object or an array. The below code works, assuming `opts[:variable_length_opt] is defined:

def initialize(opts={})
  @ivar = *opts[:variable_length_opt]
end

But I'd also like to be able to set a default if the option is not set. But this code does not work:

def initialize(opts={})
  @ivar = (opts[:variable_length_opt] ? *opts[:variable_length_opt] : default_value)
end

It throws an unexpected tSTAR error. I understand there are other, more verbose methods to accomplish what I'm after, but I am wondering if there are other, as-short alternatives. Also, what are the limits of the splat? I can't think of a good reason that it should be unavailable here.

Sean Mackesey
  • 10,701
  • 11
  • 40
  • 66

2 Answers2

8

I think splats are only available in assignments (and indirectly, in method calls). You can't call splat directly either:

1.9.3p286 :045 > *[1,2,3,4]
SyntaxError: (irb):45: syntax error, unexpected '\n', expecting tCOLON2 or '[' or '.'
    from /Users/fcoury/.rvm/rubies/ruby-1.9.3-p286/bin/irb:16:in `<main>'

In your case, you can do something like:

def initialize(opts={})
  @ivar = *(opts[:variable_length_opt] ? opts[:variable_length_opt] : [default_value])
end

That is almost as short.

But you usually use the splat to assign multiple variables from an array, like

a = [1,2,3,4]
b, c, d, e = *a
b #=> 1
c #=> 2 ...

Why do you need the splat in this case?

kolrie
  • 12,562
  • 14
  • 64
  • 98
  • 1
    Thanks kolrie. I didn't think of putting the splat on the outside, but that's just what I need-- actually it should even work without putting default_value in list brackets. As for why I need the splat, the purpose is to allow the user to pass either an array or a single object as the option. The splat will wrap the single object in an array and leave an array untouched. This allows me to write the rest of my code treating @ivar as an array. One more thing-- for your multiple assignment example above, you actually don't need a splat (at least in Ruby 1.9.3). Try it and see! – Sean Mackesey Nov 10 '12 at 04:32
  • You're absolutely right about the assignment and it works even in 1.8.7 :) – kolrie Nov 10 '12 at 08:25
0

Instead of using ternary actually you could initialize your opts hash with a default value -

class InitOptions
    attr_reader :ivar

    def initialize(opts=Hash.new("default_value"))
        @ivar = *opts[:variable_length_opt]
    end
end

p InitOptions.new({:variable_length_opt => [1,2,3,4]}).ivar #=> [1, 2, 3, 4]
p InitOptions.new.ivar # => ["default_value"]

What Hash.new("default_value") does is that instead of returning nil when some key in not present in the hash, it returns whatever value you passed to initialize it, in this case default_value

saihgala
  • 5,724
  • 3
  • 34
  • 31
  • Thanks Ashish. This works quite well if I have only one item in the options hash, but it doesn't scale to multiple items (which require their own, likely different, defaults). Not that I mentioned that in my question, though. – Sean Mackesey Nov 10 '12 at 06:56