Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions lib/mspec/utils/name_map.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,17 @@ def exception?(name)
end

def class_or_module(c)
const = Object.const_get(c, false)
begin
const = Object.const_get(c, false)
rescue NameError, RuntimeError
# Either the constant doesn't exist or it is
# explicitly raising an error, like `SortedSet`.
return nil
end
return nil unless Module === const

filtered = @filter && EXCLUDED.include?(const.name)
return const if Module === const and !filtered
rescue NameError
return const unless filtered
end

def namespace(mod, const)
Expand Down
1 change: 1 addition & 0 deletions spec/utils/fixtures/this_file_raises.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
raise "This is a BAD file"
7 changes: 7 additions & 0 deletions spec/utils/name_map_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ class Fixnum
def f; end
end

autoload :BadFile, "#{__dir__}/fixtures/this_file_raises.rb"

def self.n; end
def n; end
end
Expand Down Expand Up @@ -84,6 +86,11 @@ def n; end
expect(@map.class_or_module("Hell")).to eq(nil)
expect(@map.class_or_module("Bush::Brain")).to eq(nil)
end

it "returns nil if accessing the constant raises RuntimeError" do
expect { NameMapSpecs::BadFile }.to raise_error(RuntimeError)
Copy link
Member

@eregon eregon Nov 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you remove this line?
The issue is after triggering the autoload, given that it fails, the constant might be removed or be in a weird state, so it's better to only trigger the autoload once from class_or_module->const_get.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I completely understand the reservation but I'm reluctant to remove it. Without it the test can easily regress to any of the other cases which return nil.

Actually, there is a test for this exact situtation, so a broken constant would fail the spec.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I completely understand the reservation but I'm reluctant to remove it. Without it the test can easily regress to any of the other cases which return nil.

How could it? The autoload clearly raises the error.

Actually, there is a test for this exact situtation, so a broken constant would fail the spec.

Yes, but that has been the subject of discussions on the CRuby tracker and there have been changes in this area, there used to be "undefined constants" for example.

I suppose one way to meet both of our concerns is to just test both cases, I'll add a second test which doesn't trigger the autoload first.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How could it? The autoload clearly raises the error.

I'm worried that in the case someone changes the tests, it would be very easy to break this one accidentally. A typo, removing the autoload, or removing raise in the file would all cause the method to return nil, so without the sanity check the test would pass.

Yes, but that has been the subject of discussions on the CRuby tracker and there have been changes in this area, there used to be "undefined constants" for example.

Oh, that's concerning. To be fair, I also intuitively expected that the constant would be in a strange state, but current behavior is clearly superior.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

without the sanity check the test would pass.

We could have something like expect(NameMapSpecs.autoload?(:BadFile).to be_a(String) or so, that checks the autoload is registered at least and there is no typo in the constant name.

expect(@map.class_or_module("NameMapSpecs::BadFile")).to eq(nil)
end
end

RSpec.describe NameMap, "#dir_name" do
Expand Down