Skip to content

Commit d08adb6

Browse files
committed
Implement exit_program (!!!) command
Pry includes an `exit-program` command, also aliased as `quit-program` and `!!!` which calls `Kernel.exit`. This is useful when `binding.pry` has been used in a loop. This implements a similar `exit_program` command, also aliased as `quit_program` and `!!!`.
1 parent a14fc6c commit d08adb6

File tree

4 files changed

+96
-2
lines changed

4 files changed

+96
-2
lines changed

lib/irb/cmd/exit_program.rb

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# frozen_string_literal: true
2+
3+
require_relative "nop"
4+
5+
module IRB
6+
module ExtendCommand
7+
class ExitProgram < Nop
8+
category "Misc"
9+
description "End the current program, optionally with a status to give to `Kernel.exit`"
10+
11+
def execute(arg = true)
12+
Kernel.exit(arg)
13+
end
14+
end
15+
end
16+
end

lib/irb/extend-command.rb

+6
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,12 @@ def irb_context
197197
:irb_history, :History, "cmd/history",
198198
[:history, NO_OVERRIDE],
199199
[:hist, NO_OVERRIDE],
200+
],
201+
202+
[
203+
:irb_exit_program, :ExitProgram, 'cmd/exit_program',
204+
[:exit_program, NO_OVERRIDE],
205+
[:quit_program, NO_OVERRIDE],
200206
]
201207
]
202208

lib/irb/init.rb

+3-2
Original file line numberDiff line numberDiff line change
@@ -187,8 +187,9 @@ def IRB.init_config(ap_path)
187187

188188
@CONF[:COMMAND_ALIASES] = {
189189
# Symbol aliases
190-
:'$' => :show_source,
191-
:'@' => :whereami,
190+
:'$' => :show_source,
191+
:'@' => :whereami,
192+
:'!!!' => :exit_program,
192193
}
193194
end
194195

test/irb/cmd/test_exit_program.rb

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# frozen_string_literal: true
2+
require 'irb'
3+
4+
require_relative "../helper"
5+
6+
module TestIRB
7+
class ExitProgramTest < IntegrationTestCase
8+
def test_irb_exit_program
9+
assert_exits_program(with_status: 0) do
10+
type "irb_exit_program"
11+
end
12+
end
13+
14+
def test_exit_program
15+
assert_exits_program(with_status: 0) do
16+
type "exit_program"
17+
end
18+
end
19+
20+
def test_quit_program
21+
assert_exits_program(with_status: 0) do
22+
type "quit_program"
23+
end
24+
end
25+
26+
def test_triple_bang
27+
assert_exits_program(with_status: 0) do
28+
type "!!!"
29+
end
30+
end
31+
32+
def test_exit_code_zero
33+
assert_exits_program(with_status: 0) do
34+
type "!!! 0"
35+
end
36+
end
37+
38+
def test_exit_code_one
39+
assert_exits_program(with_status: 1) do
40+
type "!!! 1"
41+
end
42+
end
43+
44+
def test_exit_code_expression
45+
assert_exits_program(with_status: 2) do
46+
type "n = 1"
47+
type "!!! n + 1"
48+
end
49+
end
50+
51+
private
52+
53+
def assert_exits_program(with_status:, &block)
54+
write_ruby <<~'RUBY'
55+
begin
56+
binding.irb
57+
puts "Did not raise #{SystemExit}!" # Interpolate so we don't match whereami context
58+
rescue SystemExit => e
59+
puts "Raised SystemExit with status #{e.status.inspect}"
60+
end
61+
RUBY
62+
63+
output = run_ruby_file(&block)
64+
65+
refute_includes(output, "Did not raise SystemExit!", "AN ERROR MESSAGE")
66+
matching_status = output[/(?<=Raised SystemExit with status )(\d+)/]
67+
refute_nil matching_status, "Did not find exit status in output: \n#{output}"
68+
assert_equal with_status, matching_status.to_i, "Exited with wrong status code"
69+
end
70+
end
71+
end

0 commit comments

Comments
 (0)