Skip to content

Commit

Permalink
Feature 17314: allow to pass array to public, protected and private m…
Browse files Browse the repository at this point in the history
…ethods
  • Loading branch information
radarek authored and mame committed Dec 19, 2020
1 parent 8148f88 commit eb8ea33
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 14 deletions.
3 changes: 3 additions & 0 deletions NEWS.md
Expand Up @@ -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]]
Expand Down
8 changes: 8 additions & 0 deletions spec/ruby/core/main/fixtures/classes.rb
Expand Up @@ -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
31 changes: 26 additions & 5 deletions spec/ruby/core/main/private_spec.rb
Expand Up @@ -4,20 +4,41 @@
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
27 changes: 24 additions & 3 deletions spec/ruby/core/main/public_spec.rb
Expand Up @@ -4,11 +4,32 @@
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
Expand Down
34 changes: 34 additions & 0 deletions spec/ruby/core/module/shared/set_visibility.rb
Expand Up @@ -6,6 +6,40 @@
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 {
Expand Down
31 changes: 25 additions & 6 deletions vm_method.c
Expand Up @@ -1973,6 +1973,16 @@ rb_mod_alias_method(VALUE mod, VALUE newname, VALUE oldname)
return ID2SYM(id);
}

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)
{
Expand All @@ -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);
}
}
}

Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit eb8ea33

Please sign in to comment.