Reputation: 1100
I am trying to create a method that can be called in the following manner:
instance.respond('some command', 'command arg 1', 'command arg 2', 'command arg 3')
instance.respond(true, 'some command', 'command arg 1', 'command arg 2', 'command arg 3')
instance.respond(false, 'some command', 'command arg 1', 'command arg 2', 'command arg 3')
The true
/false
values indicate if the command should be sent immediately or buffered, respectively.
I tried to define the method as in the following example:
class Foo
def bar1(arg1=true, arg2)
puts 'this works'
end
def bar2(arg1, *args)
puts 'this works as well'
end
def bar3(arg1=true, arg2, *args)
puts 'but this fails'
end
end
Running this gives the error:
/opt/dl/testcase.rb:10: syntax error, unexpected *
def bar3(arg1=true, arg2, *args)
^
/opt/dl/testcase.rb:13: syntax error, unexpected keyword_end, expecting end-of-input
Is there a way to resolve this, or to work around it?
Upvotes: 2
Views: 67
Reputation: 110755
@simplaY has shown how named arguments can be used here. One has a few options if versions of Ruby prior to 2.0 (when named arguments made their debut) must be supported.
#1
def respond(*args)
flag = true # default
flag = args.shift if [true, false].include? args.first
puts "flag = #{flag}"
puts "args = #{args}"
if flag
# code
else
# code
end
end
respond "dog", "cat"
flag = true
args = ["dog", "cat"]
respond false, "dog", "cat"
flag = false
args = ["dog", "cat"]
This of course requires that the permitted value of the first argument is true
or false
if and only if it is the value of flag
.
#2
def respond(flag=true, args)
if flag
# code
else
# code
end
end
called
respond ["dog", "cat"]
respond true, ["dog", "cat"]
respond true, ["dog", "cat"]
Whenever a method has a variable number of arguments the rule is that it will be unambiguous to Ruby if and only if it is unambiguous to you.
#3
def respond(options)
flag = options.fetch(:flag, true)
if flag
# code
else
# code
end
end
called
respond(arg1: 'dog', arg2: 'cat')
respond(flag: false, arg1: 'dog', arg2: 'cat')
This last approach, which was widely used prior to Ruby 2.0, has been largely supplanted by the use named arguments, in part because named arguments display the keys whose values are being passed, whereas in this last approach only the hash is identified among the method's arguments.
Upvotes: 1
Reputation: 161
Since you haven't specified which ruby version you are using, I'm going to assume that a version >= 2 is fine. If this question is just about fixing the syntax error, then you could write something like
class A
def respond(flag = true, b = '', *args)
"flag=#{flag}, b=#{b} args=#{args.join(',')}"
end
end
However, there are some issues you'd encounter:
A.new.respond('some command', 'command arg 1', 'command arg 2', 'command arg 3')
#=> flag=some command, b=command arg 1 args=command arg 2,command arg 3
A.new.respond(true, 'some command', 'command arg 1', 'command arg 2', 'command arg 3')
#=> flag=true, b=some command args=command arg 1,command arg 2,command arg 3
A.new.respond(false, 'some command', 'command arg 1', 'command arg 2', 'command arg 3')
#=> flag=false, b=some command args=command arg 1,command arg 2,command arg 3
An approach that would make use of named arguments would be safer. You could for example do something like
class B
def respond(flag: true, b:, **args)
"flag=#{flag}, b=#{b} args=#{args.values.join(',')}"
end
end
args = {
c1: "command arg 1",
c2: "command arg 2",
c3: "command arg 3"
}
B.new.respond(b: 'some command', **args)
#=> flag=true, b=some command args=command arg 1,command arg 2,command arg 3
B.new.respond(flag: true, b: 'some command', **args)
#=> flag=true, b=some command args=command arg 1,command arg 2,command arg 3
B.new.respond(flag: false, b: 'some command', **args)
#=> flag=false, b=some command args=command arg 1,command arg 2,command arg 3
Upvotes: 3