Skip to content

Commit

Permalink
Validate autoloaded explicit namespaces are classes or modules
Browse files Browse the repository at this point in the history
  • Loading branch information
fxn committed Oct 13, 2024
1 parent 1289989 commit 5ecdd9f
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 1 deletion.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,8 @@ class Hotel < ApplicationRecord
end
```

When autoloaded, Zeitwerk verifies the expected constant (`Hotel` in the example) stores a class or module object. If it doesn't, `Zeitwerk::Error` is raised.

An explicit namespace must be managed by one single loader. Loaders that reopen namespaces owned by other projects are responsible for loading their constants before setup.

<a id="markdown-collapsing-directories" name="collapsing-directories"></a>
Expand Down
9 changes: 8 additions & 1 deletion lib/zeitwerk/core_ext/module.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@
module Zeitwerk::ConstAdded
def const_added(cname)
if loader = Zeitwerk::ExplicitNamespace.__loader_for(self, cname)
loader.on_namespace_loaded(const_get(cname, false))
namespace = const_get(cname, false)

unless namespace.is_a?(Module)
cref = Zeitwerk::Cref.new(self, cname)
raise Zeitwerk::Error, "#{cref.path} is expected to be a namespace, should be a class or module (got #{namespace.class})"
end

loader.on_namespace_loaded(namespace)
end
super
end
Expand Down
13 changes: 13 additions & 0 deletions test/lib/zeitwerk/test_explicit_namespace.rb
Original file line number Diff line number Diff line change
Expand Up @@ -166,4 +166,17 @@ def self.hash(_)
assert M::X
end
end

test "if the expected constant does not define a class or module object, we raise a controlled error" do
on_teardown { remove_const :Hotel }

files = [
["hotel.rb", "Hotel = 1"],
["hotel/pricing.rb", "class Hotel::Pricing; end"]
]
with_setup(files) do
error = assert_raises(Zeitwerk::Error) { Hotel }
assert_equal "Hotel is expected to be a namespace, should be a class or module (got Integer)", error.message
end
end
end

0 comments on commit 5ecdd9f

Please sign in to comment.