未验证 提交 5ffccfad 编写于 作者: X Xavier Noria 提交者: GitHub

Merge pull request #39140 from Shopify/missing-name-corner-case

Make NameError#missing_name work even for real Ruby NameError
......@@ -88,7 +88,11 @@ class HelpersTypoControllerTest < ActiveSupport::TestCase
def test_helper_typo_error_message
e = assert_raise(NameError) { HelpersTypoController.helper "admin/users" }
# This message is better if autoloading.
assert_equal "uninitialized constant Admin::UsersHelper", e.message
if RUBY_VERSION >= "2.6"
assert_equal "uninitialized constant Admin::UsersHelper\nDid you mean? Admin::UsersHelpeR", e.message
else
assert_equal "uninitialized constant Admin::UsersHelper", e.message
end
end
end
......
......@@ -14,9 +14,22 @@ def missing_name
# It extends NameError#message with spell corrections which are SLOW.
# We should use original_message message instead.
message = respond_to?(:original_message) ? original_message : self.message
return unless message.start_with?("uninitialized constant ")
unless /undefined local variable or method/.match?(message)
$1 if /((::)?([A-Z]\w*)(::[A-Z]\w*)*)$/ =~ message
receiver = begin
self.receiver
rescue ArgumentError
nil
end
if receiver == Object
name.to_s
elsif receiver
"#{real_mod_name(receiver)}::#{self.name}"
else
if match = message.match(/((::)?([A-Z]\w*)(::[A-Z]\w*)*)$/)
match[1]
end
end
end
......@@ -35,4 +48,18 @@ def missing_name?(name)
missing_name == name.to_s
end
end
private
UNBOUND_METHOD_MODULE_NAME = Module.instance_method(:name)
private_constant :UNBOUND_METHOD_MODULE_NAME
if UnboundMethod.method_defined?(:bind_call)
def real_mod_name(mod)
UNBOUND_METHOD_MODULE_NAME.bind_call(mod)
end
else
def real_mod_name(mod)
UNBOUND_METHOD_MODULE_NAME.bind(mod).call
end
end
end
......@@ -592,8 +592,8 @@ def load_missing_constant(from_mod, const_name)
end
end
name_error = NameError.new("uninitialized constant #{qualified_name}", const_name)
name_error.set_backtrace(caller.reject { |l| l.start_with?(__FILE__) })
name_error = uninitialized_constant(qualified_name, const_name, receiver: from_mod)
name_error.set_backtrace(caller.reject { |l| l.start_with? __FILE__ })
raise name_error
end
......@@ -801,6 +801,16 @@ def log(message)
end
private
if RUBY_VERSION < "2.6"
def uninitialized_constant(qualified_name, const_name, receiver:)
NameError.new("uninitialized constant #{qualified_name}", const_name)
end
else
def uninitialized_constant(qualified_name, const_name, receiver:)
NameError.new("uninitialized constant #{qualified_name}", const_name, receiver: receiver)
end
end
# Returns the original name of a class or module even if `name` has been
# overridden.
def real_mod_name(mod)
......
......@@ -11,6 +11,9 @@ def test_name_error_should_set_missing_name
assert_equal "NameErrorTest::SomeNameThatNobodyWillUse____Really", exc.missing_name
assert exc.missing_name?(:SomeNameThatNobodyWillUse____Really)
assert exc.missing_name?("NameErrorTest::SomeNameThatNobodyWillUse____Really")
if RUBY_VERSION >= "2.6"
assert_equal NameErrorTest, exc.receiver
end
end
def test_missing_method_should_ignore_missing_name
......@@ -19,5 +22,6 @@ def test_missing_method_should_ignore_missing_name
end
assert_not exc.missing_name?(:Foo)
assert_nil exc.missing_name
assert_equal self, exc.receiver
end
end
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册