Error. There's no 'foo' parameter. Passing a literal hash would require {}.
Hm. Either an error or c = [5], depending on whether or not your want to declare that, once a positional is used, there's no going back after the fact.
a = 1, b = {:b=>2, foo=>5}, since you've passed a literal hash.
Here's the latest test case we had for Sydney. Note that Sydney assumes that all method arguments automatically become keyword arguments. There's no syntax for explicitly declaring that a given method parameter is a valid keyword argument.
···
In message "Re: Confusion Over Keyword Arguments" > on Fri, 3 Mar 2006 00:57:44 +0900, "Berger, Daniel" <Daniel.Berger@qwest.com> writes:
#
# Tests the keyword argument behavior.
#
# Authored by Dan Berger and Evan Webb
#
require 'behavior/keyword'
require 'test/unit'
# This is the class we use within the test case below.
class Foo
MYCONST = 5
behavior KeywordBehavior
attr_reader :x, :y, :z
def bar(x, y, z=3)
@x = x
@y = y
@z = z
end
def grab(x, y, *z)
@x = x
@y = y
@z = z
end
def vars
[@x, @y, @z]
end
def t2(x)
name = "blah"
end
end
# Added to test a method redefinition in a subclass, as well as the
# define_method and instance_method methods.
class Bar < Foo
attr_reader :a, :b, :c
def bar(a, b=2, c=3, d=4)
@a = a
@b = b
@c = c
end
define_method(:baz, instance_method(:bar))
end
TOPLEVEL=5
class TC_Arguments < Test::Unit::TestCase
def setup
@foo = Foo.new
@bar = Bar.new
@arr = [1,2,3]
end
def test_methods_defined
assert_respond_to(@foo, :bar)
assert_respond_to(@bar, :bar)
assert_respond_to(@foo, :grab)
assert_respond_to(@foo, :vars)
assert_respond_to(@foo, :t2)
assert_respond_to(@bar, :baz)
end
# Methods may still take positional arguments, the way they always have
def test_positional_basic
assert_nothing_raised{ @foo.bar(1,2) }
assert_nothing_raised{ @foo.bar(1,2,3) }
end
def test_positional_basic_subclass
assert_nothing_raised{ @bar.bar(1) }
assert_nothing_raised{ @bar.bar(1,2) }
assert_nothing_raised{ @bar.bar(1,2,3) }
assert_nothing_raised{ @bar.bar(1,2,3,4) }
assert_nothing_raised{ @bar.baz(1,2,3) }
end
# You can use named parameters, using the name of the parameter defined in
# in the method itself. It must be the name of the parameter, followed by
# a colon, followed by the value. Spaces between the parameter name, colon
# and value are not allowed.
def test_named_basic
assert_nothing_raised{ @foo.bar(z:1, x:2, y:3) }
assert_equal(2, @foo.x)
assert_equal(3, @foo.y)
assert_equal(1, @foo.z)
end
def test_named_basic_subclass
assert_nothing_raised{ @bar.bar(d:1, a:2, b:3, c:4) }
assert_equal(2, @bar.a)
assert_equal(3, @bar.b)
assert_equal(4, @bar.c)
assert_equal(1, @bar.d)
end
# Ensure that named parameters in a define_method/instance_method work.
# In this case, Bar#baz is actually calling Foo#bar.
def test_named_dynamically_defined
assert_nothing_raised{ @bar.baz(z:3, y:2, x:1) }
assert_equal(1, @bar.x)
assert_equal(2, @bar.y)
assert_equal(3, @bar.z)
end
# Default values in a declaration work the same way they always have. The
# use of named parameters does not change this.
def test_named_basic_default_values
assert_nothing_raised{ @foo.bar(x:1, y:2) }
assert_equal(1, @foo.x)
assert_equal(2, @foo.y)
assert_equal(3, @foo.z) # default
end
# You can mix and match positional and named parameters, though there are
# limitations. Positional parameters must come first (i.e. on the left
# side).
def test_mixed_basic
assert_nothing_raised{ @foo.bar(1, z:2, y:3) }
assert_equal(1, @foo.x)
assert_equal(3, @foo.y)
assert_equal(2, @foo.z)
assert_nothing_raised{ @foo.bar(1, 2, z:4) }
assert_equal(1, @foo.x)
assert_equal(2, @foo.y)
assert_equal(4, @foo.z)
end
# You can splat an array, and the values will be assigned in a left to right
# fashion, as in the current Ruby behavior.
def test_splat
assert_nothing_raised{ @foo.bar(*@arr) }
assert_equal(1, @foo.x)
assert_equal(2, @foo.y)
assert_equal(3, @foo.z)
end
# You can mix and match positional, named and splat arguments. However,
# splat arguments must come last. This is the same as the current behavior
# in Ruby.
def test_mixed_splat
a = [1]
assert_nothing_raised{ @foo.bar(*a) }
assert_nothing_raised{ @foo.bar(1, *a) }
assert_nothing_raised{ @foo.bar(1, y:2, *a)}
assert_nothing_raised{ @foo.bar(1, 2, *a) }
assert_nothing_raised{ @foo.bar(x:1, y:2, *a) }
end
# The '*rest' declarations work the same as they always have. In the case
# of named parameters, the values assigned to the 'rest' argument are
# simply pushed as an array.
def test_def_splat
assert_nothing_raised{ @foo.grab(1, 2, 3, 4) }
assert_equal([1, 2, [3, 4]], @foo.vars)
assert_nothing_raised{ @foo.grab(x:1, y:2, z:4) }
assert_equal([1, 2, [4]], @foo.vars)
assert_nothing_raised{ @foo.grab(1, 2, 3, [1,2,3])
assert_equal([1,2,3,[[1,2,3]]], @foo.vars)
assert_nothing_raised{ @foo.grab(1, 2, 3, z:[1,2,3])
assert_equal([1,2,3,[[1,2,3]]], @foo.vars)
assert_nothing_raised{ @foo.grab(1, 2, 3, z:*[1,2,3])
assert_equal([1,2,3,[1,2,3]], @foo.vars)
end
# If a keyword argument is assigned to a '*rest' parameter, then it
# autovivifies a hash, assigning that hash as the argument to the
# '*rest' parameter. Note double declarations are still illegal.
def test_keyword_in_splat
assert_nothing_raised{ @foo.grab(10, 20, name:"evan", age:99) }
assert_equal([ 10, 20, [{:name => "evan", :age=>99}] ], @foo.vars)
end
# If nothing is assigned to the '*rest' variable, then it is simply empty.
# is the same as the current Ruby behavior.
def test_keyword_no_splat
@foo.grab(x:1, y:2)
assert_equal [1,2,], @foo.vars
end
# The presence of a '*rest' declaration does not alter required arguments.
def test_keyword_splat_with_not_enough
assert_raises(ArgumentError) { @foo.grab(x:1) }
end
# Ensure that the presence of a parameter in one method declaration does
# not interfere with an identically named parameter in another declaration.
def test_no_local_leakage
assert_nothing_raised { @foo.t2 x:8 }
end
# Ensure that "::" doesn't cause problems
def test_works_with_constants
assert_nothing_raised{ @foo.bar(y:1, x:2, z:Foo::MYCONST) }
assert_nothing_raised{ @foo.bar(y: 1, x: 2, z: ::TOPLEVEL) }
end
def test_expected_argument_errors
assert_raises(ArgumentError){ @foo.bar } # x & y missing
assert_raises(ArgumentError){ @foo.bar(1) } # y missing
assert_raises(ArgumentError){ @foo.bar(x:1) } # y missing
assert_raises(ArgumentError){ @foo.bar(y:1) } # x missing
assert_raises(ArgumentError){ @foo.bar(x:1, z:2) } # y missing
assert_raises(ArgumentError){ @foo.bar([1,2,3]) } # y missing
assert_raises(ArgumentError){ @foo.bar(*[1]) } # y missing
assert_raises(ArgumentError){ @foo.bar(x:1,y:1,a:3) } # no 'a' parameter
assert_raises(ArgumentError){ @foo.bar(*[1,2,3,4]) } # too many args
end
# Ensure that the subclass didn't pick up the named parameters
# from the parent
def test_expected_argument_errors_subclass
assert_raises(ArgumentError){ @bar.bar }
assert_raises(ArgumentError){ @bar.bar(x:1) }
assert_raises(ArgumentError){ @bar.bar(y:1) }
assert_raises(ArgumentError){ @bar.bar(z:1) }
end
def test_expected_syntax_errors
assert_raises(SyntaxError){ @foo.bar(x:1, 2) } # positional must be first
assert_raises(SyntaxError){ @foo.bar(*a, 1) } # splat must be last
assert_raises(SyntaxError){ @foo.bar(*a, x:1) } # splat must be last
end
def teardown
@foo = nil
@arr = nil
end
end