Skip to content

Commit c68bb24

Browse files
st0012matzbot
authored andcommitted
[ruby/irb] Add a new initialization step to validate IRB.conf's
values (ruby/irb#953) Currently, users can only find out that they have set a wrong value for IRB configs when the value is used, with opaque error messages like "comparison of Integer with true failed (TypeError)". This commit adds a new initialization step to validate the values of some IRB configs, so that users can find out about the wrong values during the initialization of IRB. ruby/irb@af8ef2948b
1 parent 69d0a3b commit c68bb24

File tree

3 files changed

+123
-12
lines changed

3 files changed

+123
-12
lines changed

lib/irb/context.rb

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,12 @@ def initialize(irb, workspace = nil, input_method = nil)
7373

7474
self.prompt_mode = IRB.conf[:PROMPT_MODE]
7575

76-
if IRB.conf[:SINGLE_IRB] or !defined?(IRB::JobManager)
77-
@irb_name = IRB.conf[:IRB_NAME]
78-
else
79-
@irb_name = IRB.conf[:IRB_NAME]+"#"+IRB.JobManager.n_jobs.to_s
76+
@irb_name = IRB.conf[:IRB_NAME]
77+
78+
unless IRB.conf[:SINGLE_IRB] or !defined?(IRB::JobManager)
79+
@irb_name = @irb_name + "#" + IRB.JobManager.n_jobs.to_s
8080
end
81+
8182
self.irb_path = "(" + @irb_name + ")"
8283

8384
case input_method

lib/irb/init.rb

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ def IRB.setup(ap_path, argv: ::ARGV)
5252
IRB.init_error
5353
IRB.parse_opts(argv: argv)
5454
IRB.run_config
55+
IRB.validate_config
5556
IRB.load_modules
5657

5758
unless @CONF[:PROMPT][@CONF[:PROMPT_MODE]]
@@ -427,6 +428,40 @@ def IRB.irbrc_files
427428
@irbrc_files
428429
end
429430

431+
def IRB.validate_config
432+
conf[:IRB_NAME] = conf[:IRB_NAME].to_s
433+
434+
irb_rc = conf[:IRB_RC]
435+
unless irb_rc.nil? || irb_rc.respond_to?(:call)
436+
raise_validation_error "IRB.conf[:IRB_RC] should be a callable object. Got #{irb_rc.inspect}."
437+
end
438+
439+
back_trace_limit = conf[:BACK_TRACE_LIMIT]
440+
unless back_trace_limit.is_a?(Integer)
441+
raise_validation_error "IRB.conf[:BACK_TRACE_LIMIT] should be an integer. Got #{back_trace_limit.inspect}."
442+
end
443+
444+
prompt = conf[:PROMPT]
445+
unless prompt.is_a?(Hash)
446+
msg = "IRB.conf[:PROMPT] should be a Hash. Got #{prompt.inspect}."
447+
448+
if prompt.is_a?(Symbol)
449+
msg += " Did you mean to set `IRB.conf[:PROMPT_MODE]`?"
450+
end
451+
452+
raise_validation_error msg
453+
end
454+
455+
eval_history = conf[:EVAL_HISTORY]
456+
unless eval_history.nil? || eval_history.is_a?(Integer)
457+
raise_validation_error "IRB.conf[:EVAL_HISTORY] should be an integer. Got #{eval_history.inspect}."
458+
end
459+
end
460+
461+
def IRB.raise_validation_error(msg)
462+
raise TypeError, msg, @irbrc_files
463+
end
464+
430465
# loading modules
431466
def IRB.load_modules
432467
for m in @CONF[:LOAD_MODULES]

test/irb/test_init.rb

Lines changed: 83 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -268,15 +268,94 @@ def with_argv(argv)
268268
end
269269
end
270270

271+
class ConfigValidationTest < TestCase
272+
def setup
273+
@original_home = ENV["HOME"]
274+
@original_irbrc = ENV["IRBRC"]
275+
# To prevent the test from using the user's .irbrc file
276+
ENV["HOME"] = Dir.mktmpdir
277+
IRB.instance_variable_set(:@existing_rc_name_generators, nil)
278+
super
279+
end
280+
281+
def teardown
282+
super
283+
ENV["IRBRC"] = @original_irbrc
284+
ENV["HOME"] = @original_home
285+
File.unlink(@irbrc)
286+
end
287+
288+
def test_irb_name_converts_non_string_values_to_string
289+
assert_no_irb_validation_error(<<~'RUBY')
290+
IRB.conf[:IRB_NAME] = :foo
291+
RUBY
292+
293+
assert_equal "foo", IRB.conf[:IRB_NAME]
294+
end
295+
296+
def test_irb_rc_name_only_takes_callable_objects
297+
assert_irb_validation_error(<<~'RUBY', "IRB.conf[:IRB_RC] should be a callable object. Got :foo.")
298+
IRB.conf[:IRB_RC] = :foo
299+
RUBY
300+
end
301+
302+
def test_back_trace_limit_only_accepts_integers
303+
assert_irb_validation_error(<<~'RUBY', "IRB.conf[:BACK_TRACE_LIMIT] should be an integer. Got \"foo\".")
304+
IRB.conf[:BACK_TRACE_LIMIT] = "foo"
305+
RUBY
306+
end
307+
308+
def test_prompt_only_accepts_hash
309+
assert_irb_validation_error(<<~'RUBY', "IRB.conf[:PROMPT] should be a Hash. Got \"foo\".")
310+
IRB.conf[:PROMPT] = "foo"
311+
RUBY
312+
end
313+
314+
def test_eval_history_only_accepts_integers
315+
assert_irb_validation_error(<<~'RUBY', "IRB.conf[:EVAL_HISTORY] should be an integer. Got \"foo\".")
316+
IRB.conf[:EVAL_HISTORY] = "foo"
317+
RUBY
318+
end
319+
320+
private
321+
322+
def assert_irb_validation_error(rc_content, error_message)
323+
write_rc rc_content
324+
325+
assert_raise_with_message(TypeError, error_message) do
326+
IRB.setup(__FILE__)
327+
end
328+
end
329+
330+
def assert_no_irb_validation_error(rc_content)
331+
write_rc rc_content
332+
333+
assert_nothing_raised do
334+
IRB.setup(__FILE__)
335+
end
336+
end
337+
338+
def write_rc(content)
339+
@irbrc = Tempfile.new('irbrc')
340+
@irbrc.write(content)
341+
@irbrc.close
342+
ENV['IRBRC'] = @irbrc.path
343+
end
344+
end
345+
271346
class InitIntegrationTest < IntegrationTestCase
272-
def test_load_error_in_rc_file_is_warned
273-
write_rc <<~'IRBRC'
274-
require "file_that_does_not_exist"
275-
IRBRC
347+
def setup
348+
super
276349

277350
write_ruby <<~'RUBY'
278351
binding.irb
279352
RUBY
353+
end
354+
355+
def test_load_error_in_rc_file_is_warned
356+
write_rc <<~'IRBRC'
357+
require "file_that_does_not_exist"
358+
IRBRC
280359

281360
output = run_ruby_file do
282361
type "'foobar'"
@@ -293,10 +372,6 @@ def test_normal_errors_in_rc_file_is_warned
293372
raise "I'm an error"
294373
IRBRC
295374

296-
write_ruby <<~'RUBY'
297-
binding.irb
298-
RUBY
299-
300375
output = run_ruby_file do
301376
type "'foobar'"
302377
type "exit"

0 commit comments

Comments
 (0)