summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/irb/cmd/ls.rb27
-rw-r--r--test/irb/test_cmd.rb60
2 files changed, 80 insertions, 7 deletions
diff --git a/lib/irb/cmd/ls.rb b/lib/irb/cmd/ls.rb
index b65fae2bf1..d5a371a4d6 100644
--- a/lib/irb/cmd/ls.rb
+++ b/lib/irb/cmd/ls.rb
@@ -38,21 +38,34 @@ module IRB
def dump_methods(o, klass, obj)
singleton_class = begin obj.singleton_class; rescue TypeError; nil end
- maps = class_method_map((singleton_class || klass).ancestors)
+ dumped_mods = Array.new
+ # singleton_class' ancestors should be at the front
+ maps = class_method_map(singleton_class&.ancestors || [], dumped_mods) + class_method_map(klass.ancestors, dumped_mods)
maps.each do |mod, methods|
name = mod == singleton_class ? "#{klass}.methods" : "#{mod}#methods"
o.dump(name, methods)
end
end
- def class_method_map(classes)
- dumped = Array.new
- classes.reject { |mod| mod >= Object }.map do |mod|
- methods = mod.public_instance_methods(false).select do |m|
- dumped.push(m) unless dumped.include?(m)
+ def class_method_map(classes, dumped_mods)
+ dumped_methods = Array.new
+ classes = classes.reject { |mod| mod >= Object }
+ classes.map do |mod|
+ next if dumped_mods.include? mod
+
+ dumped_mods << mod
+
+ methods = mod.public_instance_methods(false).select do |method|
+ if dumped_methods.include? method
+ false
+ else
+ dumped_methods << method
+ true
+ end
end
+
[mod, methods]
- end.reverse
+ end.compact
end
class Output
diff --git a/test/irb/test_cmd.rb b/test/irb/test_cmd.rb
index c2d952cc44..dc71c784e9 100644
--- a/test/irb/test_cmd.rb
+++ b/test/irb/test_cmd.rb
@@ -672,6 +672,66 @@ module TestIRB
assert_match(/C.methods:\s+m5\n/m, out)
end
+ def test_ls_class
+ out, err = execute_lines(
+ "module M1\n",
+ " def m2; end\n",
+ " def m3; end\n",
+ "end\n",
+
+ "class C1\n",
+ " def m1; end\n",
+ " def m2; end\n",
+ "end\n",
+
+ "class C2 < C1\n",
+ " include M1\n",
+ " def m3; end\n",
+ " def m4; end\n",
+ " def self.m3; end\n",
+ " def self.m5; end\n",
+ "end\n",
+ "ls C2"
+ )
+
+ assert_empty err
+ assert_match(/C2.methods:\s+m3\s+m5\n/, out)
+ assert_match(/C2#methods:\s+m3\s+m4\n.*M1#methods:\s+m2\n.*C1#methods:\s+m1\n/, out)
+ end
+
+ def test_ls_module
+ out, err = execute_lines(
+ "module M1\n",
+ " def m1; end\n",
+ " def m2; end\n",
+ "end\n",
+
+ "module M2\n",
+ " include M1\n",
+ " def m1; end\n",
+ " def m3; end\n",
+ " def self.m4; end\n",
+ "end\n",
+ "ls M2"
+ )
+
+ assert_empty err
+ assert_match(/M2\.methods:\s+m4\nModule#methods:/, out)
+ assert_match(/M2#methods:\s+m1\s+m3\n.*M1#methods:\s+m2\n/, out)
+ end
+
+ def test_ls_instance
+ out, err = execute_lines(
+ "class Foo; def bar; end; end\n",
+ "ls Foo.new"
+ )
+
+ assert_empty err
+ assert_match(/Foo#methods:\s+bar/, out)
+ # don't duplicate
+ assert_not_match(/Foo#methods:\s+bar\n.*Foo#methods/, out)
+ end
+
def test_ls_grep
pend if RUBY_ENGINE == 'truffleruby'
out, err = execute_lines("ls 42\n")