diff options
Diffstat (limited to 'spec/ruby/core/module/ruby2_keywords_spec.rb')
-rw-r--r-- | spec/ruby/core/module/ruby2_keywords_spec.rb | 327 |
1 files changed, 256 insertions, 71 deletions
diff --git a/spec/ruby/core/module/ruby2_keywords_spec.rb b/spec/ruby/core/module/ruby2_keywords_spec.rb index 34c45cb1bc..aca419f522 100644 --- a/spec/ruby/core/module/ruby2_keywords_spec.rb +++ b/spec/ruby/core/module/ruby2_keywords_spec.rb @@ -1,112 +1,297 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -ruby_version_is "2.7" do - describe "Module#ruby2_keywords" do - it "marks the final hash argument as keyword hash" do - obj = Object.new +describe "Module#ruby2_keywords" do + class << self + ruby2_keywords def mark(*args) + args + end + end - obj.singleton_class.class_exec do - def foo(*a) a.last end - ruby2_keywords :foo - end + it "marks the final hash argument as keyword hash" do + last = mark(1, 2, a: "a").last + Hash.ruby2_keywords_hash?(last).should == true + end - last = obj.foo(1, 2, a: "a") - Hash.ruby2_keywords_hash?(last).should == true + it "makes a copy of the hash and only marks the copy as keyword hash" do + obj = Object.new + obj.singleton_class.class_exec do + def regular(*args) + args.last + end end - ruby_version_is "2.7" ... "3.0" do - it "fixes delegation warnings when calling a method accepting keywords" do - obj = Object.new + h = {a: 1} - obj.singleton_class.class_exec do - def foo(*a) bar(*a) end - def bar(*a, **b) end - end + last = mark(**h).last + Hash.ruby2_keywords_hash?(last).should == true + Hash.ruby2_keywords_hash?(h).should == false - -> { obj.foo(1, 2, {a: "a"}) }.should complain(/Using the last argument as keyword parameters is deprecated/) - - obj.singleton_class.class_exec do - ruby2_keywords :foo - end + last2 = mark(**last).last # last is already marked + Hash.ruby2_keywords_hash?(last2).should == true + Hash.ruby2_keywords_hash?(last).should == true + last2.should_not.equal?(last) + Hash.ruby2_keywords_hash?(h).should == false + end - -> { obj.foo(1, 2, {a: "a"}) }.should_not complain + it "makes a copy and unmark the Hash when calling a method taking (arg)" do + obj = Object.new + obj.singleton_class.class_exec do + def single(arg) + arg end end - it "returns nil" do - obj = Object.new + h = { a: 1 } + args = mark(**h) + marked = args.last + Hash.ruby2_keywords_hash?(marked).should == true - obj.singleton_class.class_exec do - def foo(*a) end + after_usage = obj.single(*args) + after_usage.should == h + after_usage.should_not.equal?(h) + after_usage.should_not.equal?(marked) + Hash.ruby2_keywords_hash?(after_usage).should == false + Hash.ruby2_keywords_hash?(marked).should == true + end - ruby2_keywords(:foo).should == nil + it "makes a copy and unmark the Hash when calling a method taking (**kw)" do + obj = Object.new + obj.singleton_class.class_exec do + def kwargs(**kw) + kw end end - it "raises NameError when passed not existing method name" do - obj = Object.new + h = { a: 1 } + args = mark(**h) + marked = args.last + Hash.ruby2_keywords_hash?(marked).should == true - -> { - obj.singleton_class.class_exec do - ruby2_keywords :not_existing - end - }.should raise_error(NameError, /undefined method `not_existing'/) - end + after_usage = obj.kwargs(*args) + after_usage.should == h + after_usage.should_not.equal?(h) + after_usage.should_not.equal?(marked) + Hash.ruby2_keywords_hash?(after_usage).should == false + Hash.ruby2_keywords_hash?(marked).should == true + end - it "acceps String as well" do + ruby_version_is "3.2" do + it "makes a copy and unmark the Hash when calling a method taking (*args)" do obj = Object.new - obj.singleton_class.class_exec do - def foo(*a) a.last end - ruby2_keywords "foo" + def splat(*args) + args.last + end + + def splat1(arg, *args) + args.last + end + + def proc_call(*args) + -> *a { a.last }.call(*args) + end end - last = obj.foo(1, 2, a: "a") - Hash.ruby2_keywords_hash?(last).should == true + h = { a: 1 } + args = mark(**h) + marked = args.last + Hash.ruby2_keywords_hash?(marked).should == true + + after_usage = obj.splat(*args) + after_usage.should == h + after_usage.should_not.equal?(h) + after_usage.should_not.equal?(marked) + Hash.ruby2_keywords_hash?(after_usage).should == false + Hash.ruby2_keywords_hash?(marked).should == true + + args = mark(1, **h) + marked = args.last + after_usage = obj.splat1(*args) + after_usage.should == h + after_usage.should_not.equal?(h) + after_usage.should_not.equal?(marked) + Hash.ruby2_keywords_hash?(after_usage).should == false + Hash.ruby2_keywords_hash?(marked).should == true + + args = mark(**h) + marked = args.last + after_usage = obj.proc_call(*args) + after_usage.should == h + after_usage.should_not.equal?(h) + after_usage.should_not.equal?(marked) + Hash.ruby2_keywords_hash?(after_usage).should == false + Hash.ruby2_keywords_hash?(marked).should == true + + args = mark(**h) + marked = args.last + after_usage = obj.send(:splat, *args) + after_usage.should == h + after_usage.should_not.equal?(h) + after_usage.should_not.equal?(marked) + Hash.ruby2_keywords_hash?(after_usage).should == false + Hash.ruby2_keywords_hash?(marked).should == true end + end - it "raises TypeError when passed not Symbol or String" do + ruby_version_is ""..."3.2" do + # https://bugs.ruby-lang.org/issues/18625 + it "does NOT copy the Hash when calling a method taking (*args)" do obj = Object.new + obj.singleton_class.class_exec do + def splat(*args) + args.last + end - -> { - obj.singleton_class.class_exec do - ruby2_keywords Object.new + def splat1(arg, *args) + args.last end - }.should raise_error(TypeError, /is not a symbol nor a string/) + + def proc_call(*args) + -> *a { a.last }.call(*args) + end + end + + h = { a: 1 } + args = mark(**h) + marked = args.last + Hash.ruby2_keywords_hash?(marked).should == true + + after_usage = obj.splat(*args) + after_usage.should == h + after_usage.should_not.equal?(h) + after_usage.should.equal?(marked) # https://bugs.ruby-lang.org/issues/18625 + Hash.ruby2_keywords_hash?(after_usage).should == true # https://bugs.ruby-lang.org/issues/18625 + Hash.ruby2_keywords_hash?(marked).should == true + + args = mark(1, **h) + marked = args.last + after_usage = obj.splat1(*args) + after_usage.should == h + after_usage.should_not.equal?(h) + after_usage.should.equal?(marked) # https://bugs.ruby-lang.org/issues/18625 + Hash.ruby2_keywords_hash?(after_usage).should == true # https://bugs.ruby-lang.org/issues/18625 + Hash.ruby2_keywords_hash?(marked).should == true + + args = mark(**h) + marked = args.last + after_usage = obj.proc_call(*args) + after_usage.should == h + after_usage.should_not.equal?(h) + after_usage.should.equal?(marked) # https://bugs.ruby-lang.org/issues/18625 + Hash.ruby2_keywords_hash?(after_usage).should == true # https://bugs.ruby-lang.org/issues/18625 + Hash.ruby2_keywords_hash?(marked).should == true + + args = mark(**h) + marked = args.last + after_usage = obj.send(:splat, *args) + after_usage.should == h + after_usage.should_not.equal?(h) + send_copies = RUBY_ENGINE == "ruby" # inconsistent with Proc#call above for CRuby + after_usage.equal?(marked).should == !send_copies + Hash.ruby2_keywords_hash?(after_usage).should == !send_copies + Hash.ruby2_keywords_hash?(marked).should == true end + end - it "prints warning when a method does not accept argument splat" do - obj = Object.new - def obj.foo(a, b, c) end + it "applies to the underlying method and applies across aliasing" do + obj = Object.new - -> { - obj.singleton_class.class_exec do - ruby2_keywords :foo - end - }.should complain(/Skipping set of ruby2_keywords flag for/) + obj.singleton_class.class_exec do + def foo(*a) a.last end + alias_method :bar, :foo + ruby2_keywords :foo + + def baz(*a) a.last end + ruby2_keywords :baz + alias_method :bob, :baz end - it "prints warning when a method accepts keywords" do - obj = Object.new - def obj.foo(a:, b:) end + last = obj.foo(1, 2, a: "a") + Hash.ruby2_keywords_hash?(last).should == true - -> { - obj.singleton_class.class_exec do - ruby2_keywords :foo - end - }.should complain(/Skipping set of ruby2_keywords flag for/) + last = obj.bar(1, 2, a: "a") + Hash.ruby2_keywords_hash?(last).should == true + + last = obj.baz(1, 2, a: "a") + Hash.ruby2_keywords_hash?(last).should == true + + last = obj.bob(1, 2, a: "a") + Hash.ruby2_keywords_hash?(last).should == true + end + + it "returns nil" do + obj = Object.new + + obj.singleton_class.class_exec do + def foo(*a) end + + ruby2_keywords(:foo).should == nil end + end - it "prints warning when a method accepts keyword splat" do - obj = Object.new - def obj.foo(**a) end + it "raises NameError when passed not existing method name" do + obj = Object.new - -> { - obj.singleton_class.class_exec do - ruby2_keywords :foo - end - }.should complain(/Skipping set of ruby2_keywords flag for/) + -> { + obj.singleton_class.class_exec do + ruby2_keywords :not_existing + end + }.should raise_error(NameError, /undefined method [`']not_existing'/) + end + + it "accepts String as well" do + obj = Object.new + + obj.singleton_class.class_exec do + def foo(*a) a.last end + ruby2_keywords "foo" end + + last = obj.foo(1, 2, a: "a") + Hash.ruby2_keywords_hash?(last).should == true + end + + it "raises TypeError when passed not Symbol or String" do + obj = Object.new + + -> { + obj.singleton_class.class_exec do + ruby2_keywords Object.new + end + }.should raise_error(TypeError, /is not a symbol nor a string/) + end + + it "prints warning when a method does not accept argument splat" do + obj = Object.new + def obj.foo(a, b, c) end + + -> { + obj.singleton_class.class_exec do + ruby2_keywords :foo + end + }.should complain(/Skipping set of ruby2_keywords flag for/) + end + + it "prints warning when a method accepts keywords" do + obj = Object.new + def obj.foo(a:, b:) end + + -> { + obj.singleton_class.class_exec do + ruby2_keywords :foo + end + }.should complain(/Skipping set of ruby2_keywords flag for/) + end + + it "prints warning when a method accepts keyword splat" do + obj = Object.new + def obj.foo(**a) end + + -> { + obj.singleton_class.class_exec do + ruby2_keywords :foo + end + }.should complain(/Skipping set of ruby2_keywords flag for/) end end |