ruby-changes:71454
From: Jeremy <ko1@a...>
Date: Fri, 18 Mar 2022 03:52:14 +0900 (JST)
Subject: [ruby-changes:71454] b6804d62f8 (master): Make Proc#parameters support lambda keyword for returning parameters as if lambda
https://git.ruby-lang.org/ruby.git/commit/?id=b6804d62f8 From b6804d62f822237e136e698e006c913df9990ec2 Mon Sep 17 00:00:00 2001 From: Jeremy Evans <code@j...> Date: Tue, 27 Aug 2019 18:38:48 -0700 Subject: Make Proc#parameters support lambda keyword for returning parameters as if lambda This makes it easier to use Proc#parameters to build wrappers. Implements [Feature #15357] --- NEWS.md | 2 ++ proc.c | 39 ++++++++++++++++++++++++++++------ spec/ruby/core/proc/parameters_spec.rb | 15 +++++++++++++ test/ruby/test_proc.rb | 22 +++++++++++++++++++ 4 files changed, 72 insertions(+), 6 deletions(-) diff --git a/NEWS.md b/NEWS.md index ca16d44b17..ffdbc2b812 100644 --- a/NEWS.md +++ b/NEWS.md @@ -69,6 +69,7 @@ Note: We're only listing outstanding class updates. https://github.com/ruby/ruby/blob/trunk/NEWS.md#L69 * Proc * Proc#dup returns an instance of subclass. [[Bug #17545]] + * Proc#parameters now accepts lambda keyword. [[Feature #15357]] * Refinement * Refinement#refined_class has been added. [[Feature #12737]] @@ -170,6 +171,7 @@ The following deprecated APIs are removed. https://github.com/ruby/ruby/blob/trunk/NEWS.md#L171 [Feature #13110]: https://bugs.ruby-lang.org/issues/13110 [Feature #14332]: https://bugs.ruby-lang.org/issues/14332 [Feature #15231]: https://bugs.ruby-lang.org/issues/15231 +[Feature #15357]: https://bugs.ruby-lang.org/issues/15357 [Bug #15928]: https://bugs.ruby-lang.org/issues/15928 [Feature #16131]: https://bugs.ruby-lang.org/issues/16131 [Feature #16806]: https://bugs.ruby-lang.org/issues/16806 diff --git a/proc.c b/proc.c index aa6f31f294..4a4ba70c87 100644 --- a/proc.c +++ b/proc.c @@ -1415,19 +1415,46 @@ rb_unnamed_parameters(int arity) https://github.com/ruby/ruby/blob/trunk/proc.c#L1415 /* * call-seq: - * prc.parameters -> array + * prc.parameters(lambda: nil) -> array * - * Returns the parameter information of this proc. + * Returns the parameter information of this proc. If the lambda + * keyword is provided and not nil, treats the proc as a lambda if + * true and as a non-lambda if false. * + * prc = proc{|x, y=42, *other|} + * prc.parameters #=> [[:opt, :x], [:opt, :y], [:rest, :other]] * prc = lambda{|x, y=42, *other|} * prc.parameters #=> [[:req, :x], [:opt, :y], [:rest, :other]] + * prc = proc{|x, y=42, *other|} + * prc.parameters(lambda: true) #=> [[:req, :x], [:opt, :y], [:rest, :other]] + * prc = lambda{|x, y=42, *other|} + * prc.parameters(lamdba: false) #=> [[:opt, :x], [:opt, :y], [:rest, :other]] */ static VALUE -rb_proc_parameters(VALUE self) +rb_proc_parameters(int argc, VALUE *argv, VALUE self) { - int is_proc; - const rb_iseq_t *iseq = rb_proc_get_iseq(self, &is_proc); + static ID keyword_ids[1]; + VALUE opt, lambda; + VALUE kwargs[1]; + int is_proc ; + const rb_iseq_t *iseq; + + iseq = rb_proc_get_iseq(self, &is_proc); + + if (!keyword_ids[0]) { + CONST_ID(keyword_ids[0], "lambda"); + } + + rb_scan_args(argc, argv, "0:", &opt); + if (!NIL_P(opt)) { + rb_get_kwargs(opt, keyword_ids, 0, 1, kwargs); + lambda = kwargs[0]; + if (!NIL_P(lambda)) { + is_proc = !RTEST(lambda); + } + } + if (!iseq) { return rb_unnamed_parameters(rb_proc_arity(self)); } @@ -4248,7 +4275,7 @@ Init_Proc(void) https://github.com/ruby/ruby/blob/trunk/proc.c#L4275 rb_define_method(rb_cProc, "==", proc_eq, 1); rb_define_method(rb_cProc, "eql?", proc_eq, 1); rb_define_method(rb_cProc, "source_location", rb_proc_location, 0); - rb_define_method(rb_cProc, "parameters", rb_proc_parameters, 0); + rb_define_method(rb_cProc, "parameters", rb_proc_parameters, -1); rb_define_method(rb_cProc, "ruby2_keywords", proc_ruby2_keywords, 0); // rb_define_method(rb_cProc, "isolate", rb_proc_isolate, 0); is not accepted. diff --git a/spec/ruby/core/proc/parameters_spec.rb b/spec/ruby/core/proc/parameters_spec.rb index 11a38b66e3..3ced7b22ab 100644 --- a/spec/ruby/core/proc/parameters_spec.rb +++ b/spec/ruby/core/proc/parameters_spec.rb @@ -20,6 +20,21 @@ describe "Proc#parameters" do https://github.com/ruby/ruby/blob/trunk/spec/ruby/core/proc/parameters_spec.rb#L20 proc {|x| }.parameters.first.first.should == :opt end + ruby_version_is "3.2" do + it "sets the first element of each sub-Array to :req if argument would be required if a lambda if lambda keyword used" do + proc {|x| }.parameters(lambda: true).first.first.should == :req + proc {|y,*x| }.parameters(lambda: true).first.first.should == :req + end + + it "regards named parameters in procs as required if lambda keyword used" do + proc {|x| }.parameters(lambda: true).first.first.should == :req + end + + it "regards named parameters in lambda as optional if lambda: false keyword used" do + lambda {|x| }.parameters(lambda: false).first.first.should == :opt + end + end + it "regards optional keyword parameters in procs as optional" do proc {|x: :y| }.parameters.first.first.should == :key end diff --git a/test/ruby/test_proc.rb b/test/ruby/test_proc.rb index 392b6be665..e81dfec874 100644 --- a/test/ruby/test_proc.rb +++ b/test/ruby/test_proc.rb @@ -1234,6 +1234,28 @@ class TestProc < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_proc.rb#L1234 assert_empty(pr.parameters.map{|_,n|n}.compact) end + def test_parameters_lambda + assert_equal([], proc {}.parameters(lambda: true)) + assert_equal([], proc {||}.parameters(lambda: true)) + assert_equal([[:req, :a]], proc {|a|}.parameters(lambda: true)) + assert_equal([[:req, :a], [:req, :b]], proc {|a, b|}.parameters(lambda: true)) + assert_equal([[:opt, :a], [:block, :b]], proc {|a=:a, &b|}.parameters(lambda: true)) + assert_equal([[:req, :a], [:opt, :b]], proc {|a, b=:b|}.parameters(lambda: true)) + assert_equal([[:rest, :a]], proc {|*a|}.parameters(lambda: true)) + assert_equal([[:req, :a], [:rest, :b], [:block, :c]], proc {|a, *b, &c|}.parameters(lambda: true)) + assert_equal([[:req, :a], [:rest, :b], [:req, :c]], proc {|a, *b, c|}.parameters(lambda: true)) + assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], proc {|a, *b, c, &d|}.parameters(lambda: true)) + assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:block, :e]], proc {|a, b=:b, *c, d, &e|}.parameters(lambda: true)) + assert_equal([[:req], [:block, :b]], proc {|(a), &b|a}.parameters(lambda: true)) + assert_equal([[:req, :a], [:req, :b], [:opt, :c], [:opt, :d], [:rest, :e], [:req, :f], [:req, :g], [:block, :h]], proc {|a,b,c=:c,d=:d,*e,f,g,&h|}.parameters(lambda: true)) + + pr = eval("proc{|"+"(_),"*30+"|}") + assert_empty(pr.parameters(lambda: true).map{|_,n|n}.compact) + + assert_equal([[:opt, :a]], lambda {|a|}.parameters(lambda: false)) + assert_equal([[:opt, :a], [:opt, :b], [:opt, :c], [:opt, :d], [:rest, :e], [:opt, :f], [:opt, :g], [:block, :h]], lambda {|a,b,c=:c,d=:d,*e,f,g,&h|}.parameters(lambda: false)) + end + def pm0() end def pm1(a) end def pm2(a, b) end -- cgit v1.2.1 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/