summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRadosław Bułat <radek.bulat@gmail.com>2020-12-18 19:17:57 +0100
committerYusuke Endoh <mame@ruby-lang.org>2020-12-19 18:19:49 +0900
commiteb8ea336d33af7e1dec4c17964c671c33cf75ce1 (patch)
tree0d093c71f75825dbc5ebfcb3b74bb6fad6250bf9
parent8148f88b92a6b8d850b991afe74fb0d0f9c11887 (diff)
Feature 17314: allow to pass array to public, protected and private methods
-rw-r--r--NEWS.md3
-rw-r--r--spec/ruby/core/main/fixtures/classes.rb8
-rw-r--r--spec/ruby/core/main/private_spec.rb31
-rw-r--r--spec/ruby/core/main/public_spec.rb27
-rw-r--r--spec/ruby/core/module/shared/set_visibility.rb34
-rw-r--r--vm_method.c31
6 files changed, 120 insertions, 14 deletions
diff --git a/NEWS.md b/NEWS.md
index f72e7f028c..3e42459fc0 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -249,6 +249,9 @@ Outstanding ones only.
p C.ancestors #=> [C, M1, M2, Object, Kernel, BasicObject]
```
+ * Module#public, Module#protected and Module#private methods now accept single
+ array argument with a list of method names. [[Feature #17314]]
+
* Module#attr_accessor, Module#attr_reader, Module#attr_writer and Module#attr
methods now return array of defined methods names as symbols.
[[Feature #17314]]
diff --git a/spec/ruby/core/main/fixtures/classes.rb b/spec/ruby/core/main/fixtures/classes.rb
index 6aba948ce0..757cee4e4a 100644
--- a/spec/ruby/core/main/fixtures/classes.rb
+++ b/spec/ruby/core/main/fixtures/classes.rb
@@ -13,6 +13,14 @@ def main_public_method
end
public :main_public_method
+def main_public_method2
+end
+public :main_public_method2
+
def main_private_method
end
private :main_private_method
+
+def main_private_method2
+end
+private :main_private_method2
diff --git a/spec/ruby/core/main/private_spec.rb b/spec/ruby/core/main/private_spec.rb
index e34e0c7b7b..78c5d287d4 100644
--- a/spec/ruby/core/main/private_spec.rb
+++ b/spec/ruby/core/main/private_spec.rb
@@ -4,20 +4,41 @@ require_relative 'fixtures/classes'
describe "main#private" do
after :each do
Object.send(:public, :main_public_method)
+ Object.send(:public, :main_public_method2)
end
- it "sets the visibility of the given method to private" do
- eval "private :main_public_method", TOPLEVEL_BINDING
- Object.should have_private_method(:main_public_method)
+ context "when single argument is passed and it is not an array" do
+ it "sets the visibility of the given methods to private" do
+ eval "private :main_public_method", TOPLEVEL_BINDING
+ Object.should have_private_method(:main_public_method)
+ end
+ end
+
+ context "when multiple arguments are passed" do
+ it "sets the visibility of the given methods to private" do
+ eval "private :main_public_method, :main_public_method2", TOPLEVEL_BINDING
+ Object.should have_private_method(:main_public_method)
+ Object.should have_private_method(:main_public_method2)
+ end
+ end
+
+ ruby_version_is "3.0" do
+ context "when single argument is passed and is an array" do
+ it "sets the visibility of the given methods to private" do
+ eval "private [:main_public_method, :main_public_method2]", TOPLEVEL_BINDING
+ Object.should have_private_method(:main_public_method)
+ Object.should have_private_method(:main_public_method2)
+ end
+ end
end
it "returns Object" do
eval("private :main_public_method", TOPLEVEL_BINDING).should equal(Object)
end
- it "raises a NameError when given an undefined name" do
+ it "raises a NameError when at least one of given method names is undefined" do
-> do
- eval "private :main_undefined_method", TOPLEVEL_BINDING
+ eval "private :main_public_method, :main_undefined_method", TOPLEVEL_BINDING
end.should raise_error(NameError)
end
end
diff --git a/spec/ruby/core/main/public_spec.rb b/spec/ruby/core/main/public_spec.rb
index afe25c705a..bfc27a9e80 100644
--- a/spec/ruby/core/main/public_spec.rb
+++ b/spec/ruby/core/main/public_spec.rb
@@ -4,11 +4,32 @@ require_relative 'fixtures/classes'
describe "main#public" do
after :each do
Object.send(:private, :main_private_method)
+ Object.send(:private, :main_private_method2)
end
- it "sets the visibility of the given method to public" do
- eval "public :main_private_method", TOPLEVEL_BINDING
- Object.should_not have_private_method(:main_private_method)
+ context "when single argument is passed and it is not an array" do
+ it "sets the visibility of the given methods to public" do
+ eval "public :main_private_method", TOPLEVEL_BINDING
+ Object.should_not have_private_method(:main_private_method)
+ end
+ end
+
+ context "when multiple arguments are passed" do
+ it "sets the visibility of the given methods to public" do
+ eval "public :main_private_method, :main_private_method2", TOPLEVEL_BINDING
+ Object.should_not have_private_method(:main_private_method)
+ Object.should_not have_private_method(:main_private_method2)
+ end
+ end
+
+ ruby_version_is "3.0" do
+ context "when single argument is passed and is an array" do
+ it "sets the visibility of the given methods to public" do
+ eval "public [:main_private_method, :main_private_method2]", TOPLEVEL_BINDING
+ Object.should_not have_private_method(:main_private_method)
+ Object.should_not have_private_method(:main_private_method2)
+ end
+ end
end
it "returns Object" do
diff --git a/spec/ruby/core/module/shared/set_visibility.rb b/spec/ruby/core/module/shared/set_visibility.rb
index a04b1a54a0..9f31e230ca 100644
--- a/spec/ruby/core/module/shared/set_visibility.rb
+++ b/spec/ruby/core/module/shared/set_visibility.rb
@@ -6,6 +6,40 @@ describe :set_visibility, shared: true do
end
describe "with argument" do
+ describe "one or more arguments" do
+ it "sets visibility of given method names" do
+ visibility = @method
+ old_visibility = [:protected, :private].find {|vis| vis != visibility }
+
+ mod = Module.new {
+ send old_visibility
+ def test1() end
+ def test2() end
+ send visibility, :test1, :test2
+ }
+ mod.should send(:"have_#{visibility}_instance_method", :test1, false)
+ mod.should send(:"have_#{visibility}_instance_method", :test2, false)
+ end
+ end
+
+ ruby_version_is "3.0" do
+ describe "array as a single argument" do
+ it "sets visibility of given method names" do
+ visibility = @method
+ old_visibility = [:protected, :private].find {|vis| vis != visibility }
+
+ mod = Module.new {
+ send old_visibility
+ def test1() end
+ def test2() end
+ send visibility, [:test1, :test2]
+ }
+ mod.should send(:"have_#{visibility}_instance_method", :test1, false)
+ mod.should send(:"have_#{visibility}_instance_method", :test2, false)
+ end
+ end
+ end
+
it "does not clone method from the ancestor when setting to the same visibility in a child" do
visibility = @method
parent = Module.new {
diff --git a/vm_method.c b/vm_method.c
index 1546722b14..1fbb481b50 100644
--- a/vm_method.c
+++ b/vm_method.c
@@ -1974,6 +1974,16 @@ rb_mod_alias_method(VALUE mod, VALUE newname, VALUE oldname)
}
static void
+check_and_export_method(VALUE self, VALUE name, rb_method_visibility_t visi)
+{
+ ID id = rb_check_id(&name);
+ if (!id) {
+ rb_print_undef_str(self, name);
+ }
+ rb_export_method(self, id, visi);
+}
+
+static void
set_method_visibility(VALUE self, int argc, const VALUE *argv, rb_method_visibility_t visi)
{
int i;
@@ -1985,13 +1995,19 @@ set_method_visibility(VALUE self, int argc, const VALUE *argv, rb_method_visibil
return;
}
- for (i = 0; i < argc; i++) {
- VALUE v = argv[i];
- ID id = rb_check_id(&v);
- if (!id) {
- rb_print_undef_str(self, v);
+
+ VALUE v;
+
+ if (argc == 1 && (v = rb_check_array_type(argv[0])) != Qnil) {
+ long j;
+
+ for (j = 0; j < RARRAY_LEN(v); j++) {
+ check_and_export_method(self, RARRAY_AREF(v, j), visi);
}
- rb_export_method(self, id, visi);
+ } else {
+ for (i = 0; i < argc; i++) {
+ check_and_export_method(self, argv[i], visi);
+ }
}
}
@@ -2013,6 +2029,7 @@ set_visibility(int argc, const VALUE *argv, VALUE module, rb_method_visibility_t
* public -> self
* public(symbol, ...) -> self
* public(string, ...) -> self
+ * public(array) -> self
*
* With no arguments, sets the default visibility for subsequently
* defined methods to public. With arguments, sets the named methods to
@@ -2031,6 +2048,7 @@ rb_mod_public(int argc, VALUE *argv, VALUE module)
* protected -> self
* protected(symbol, ...) -> self
* protected(string, ...) -> self
+ * protected(array) -> self
*
* With no arguments, sets the default visibility for subsequently
* defined methods to protected. With arguments, sets the named methods
@@ -2058,6 +2076,7 @@ rb_mod_protected(int argc, VALUE *argv, VALUE module)
* private -> self
* private(symbol, ...) -> self
* private(string, ...) -> self
+ * private(array) -> self
*
* With no arguments, sets the default visibility for subsequently
* defined methods to private. With arguments, sets the named methods