Skip to content

Commit 9b5401f

Browse files
committed
depend on Zeitwerk 2.1.0
1 parent 496e8ee commit 9b5401f

File tree

6 files changed

+83
-25
lines changed

6 files changed

+83
-25
lines changed

Gemfile.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ PATH
7171
i18n (>= 0.7, < 2)
7272
minitest (~> 5.1)
7373
tzinfo (~> 1.1)
74-
zeitwerk (~> 2.0)
74+
zeitwerk (~> 2.1)
7575
rails (6.0.0.beta3)
7676
actioncable (= 6.0.0.beta3)
7777
actionmailbox (= 6.0.0.beta3)
@@ -526,7 +526,7 @@ GEM
526526
websocket-extensions (0.1.3)
527527
xpath (3.2.0)
528528
nokogiri (~> 1.8)
529-
zeitwerk (2.0.0)
529+
zeitwerk (2.1.0)
530530

531531
PLATFORMS
532532
java

activesupport/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
* The Zeitwerk compatibility interface for `ActiveSupport::Dependencies` no
2+
longer implements `autoloaded_constants` or `autoloaded?` (undocumented,
3+
anyway). Experience shows introspection does not have many use cases, and
4+
troubleshooting is done by logging. With this design trade-off we are able
5+
to use even less memory in all environments.
6+
7+
*Xavier Noria*
8+
19
* Depends on Zeitwerk 2, which stores less metadata if reloading is disabled
210
and hence uses less memory when `config.cache_classes` is `true`, a standard
311
setup in production.

activesupport/activesupport.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,5 @@ Gem::Specification.new do |s|
3434
s.add_dependency "tzinfo", "~> 1.1"
3535
s.add_dependency "minitest", "~> 5.1"
3636
s.add_dependency "concurrent-ruby", "~> 1.0", ">= 1.0.2"
37-
s.add_dependency "zeitwerk", "~> 2.0"
37+
s.add_dependency "zeitwerk", "~> 2.1"
3838
end

activesupport/lib/active_support/dependencies/zeitwerk_integration.rb

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,8 @@ def safe_constantize(cpath)
2121
ActiveSupport::Inflector.safe_constantize(cpath)
2222
end
2323

24-
def autoloaded_constants
25-
cpaths = []
26-
Rails.autoloaders.each do |autoloader|
27-
cpaths.concat(autoloader.loaded_cpaths.to_a)
28-
end
29-
cpaths
30-
end
31-
32-
def autoloaded?(object)
33-
cpath = object.is_a?(Module) ? object.name : object.to_s
34-
Rails.autoloaders.any? { |autoloader| autoloader.loaded?(cpath) }
24+
def to_unload?(cpath)
25+
Rails.autoloaders.main.to_unload?(cpath)
3526
end
3627

3728
def verbose=(verbose)

activesupport/lib/active_support/descendants_tracker.rb

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,18 @@ def descendants(klass)
2222

2323
def clear
2424
if defined? ActiveSupport::Dependencies
25+
# to_unload? is only defined in Zeitwerk mode.
26+
to_unload = if Dependencies.respond_to?(:to_unload?)
27+
->(klass) { Dependencies.to_unload?(klass.name) }
28+
else
29+
->(klass) { Dependencies.autoloaded?(klass) }
30+
end
31+
2532
@@direct_descendants.each do |klass, descendants|
26-
if ActiveSupport::Dependencies.autoloaded?(klass)
33+
if to_unload[klass]
2734
@@direct_descendants.delete(klass)
2835
else
29-
descendants.reject! { |v| ActiveSupport::Dependencies.autoloaded?(v) }
36+
descendants.reject! { |v| to_unload[v] }
3037
end
3138
end
3239
else

railties/test/application/zeitwerk_integration_test.rb

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -98,24 +98,35 @@ class RESTfulController < ApplicationController
9898
assert_nil deps.safe_constantize("Admin")
9999
end
100100

101-
test "autoloaded_constants returns autoloaded constant paths" do
102-
app_file "app/models/admin/user.rb", "class Admin::User; end"
101+
test "to_unload? says if a constant would be unloaded (main)" do
102+
app_file "app/models/user.rb", "class User; end"
103103
app_file "app/models/post.rb", "class Post; end"
104104
boot
105105

106-
assert Admin::User
107-
assert_equal ["Admin", "Admin::User"], deps.autoloaded_constants
106+
assert Post
107+
assert deps.to_unload?("Post")
108+
assert_not deps.to_unload?("User")
109+
end
110+
111+
test "to_unload? says if a constant would be unloaded (once)" do
112+
add_to_config 'config.autoload_once_paths << "#{Rails.root}/extras"'
113+
app_file "extras/foo.rb", "class Foo; end"
114+
app_file "extras/bar.rb", "class Bar; end"
115+
boot
116+
117+
assert Foo
118+
assert_not deps.to_unload?("Foo")
119+
assert_not deps.to_unload?("Bar")
108120
end
109121

110-
test "autoloaded? says if a constant has been autoloaded" do
122+
test "to_unload? says if a constant would be unloaded (reloading disabled)" do
111123
app_file "app/models/user.rb", "class User; end"
112124
app_file "app/models/post.rb", "class Post; end"
113-
boot
125+
boot("production")
114126

115127
assert Post
116-
assert deps.autoloaded?("Post")
117-
assert deps.autoloaded?(Post)
118-
assert_not deps.autoloaded?("User")
128+
assert_not deps.to_unload?("Post")
129+
assert_not deps.to_unload?("User")
119130
end
120131

121132
test "eager loading loads the application code" do
@@ -315,4 +326,45 @@ def once_autoloader.reload
315326
assert_nil autoloader.logger
316327
end
317328
end
329+
330+
# This is here because to guarantee classic mode works as always, Zeitwerk
331+
# integration does not touch anything in classic. The descendants tracker is a
332+
# very small one-liner exception. We leave its main test suite untouched, and
333+
# add some minimal safety net here.
334+
#
335+
# When time passes, things are going to be reorganized (famous last words).
336+
test "descendants tracker" do
337+
class ::ZeitwerkDTIntegrationTestRoot
338+
extend ActiveSupport::DescendantsTracker
339+
end
340+
class ::ZeitwerkDTIntegrationTestChild < ::ZeitwerkDTIntegrationTestRoot; end
341+
class ::ZeitwerkDTIntegrationTestGrandchild < ::ZeitwerkDTIntegrationTestChild; end
342+
343+
begin
344+
app_file "app/models/user.rb", "class User < ZeitwerkDTIntegrationTestRoot; end"
345+
app_file "app/models/post.rb", "class Post < ZeitwerkDTIntegrationTestRoot; end"
346+
app_file "app/models/tutorial.rb", "class Tutorial < Post; end"
347+
boot
348+
349+
assert User
350+
assert Tutorial
351+
352+
direct_descendants = [ZeitwerkDTIntegrationTestChild, User, Post].to_set
353+
assert_equal direct_descendants, ZeitwerkDTIntegrationTestRoot.direct_descendants.to_set
354+
355+
descendants = direct_descendants.merge([ZeitwerkDTIntegrationTestGrandchild, Tutorial])
356+
assert_equal descendants, ZeitwerkDTIntegrationTestRoot.descendants.to_set
357+
358+
ActiveSupport::DescendantsTracker.clear
359+
360+
direct_descendants = [ZeitwerkDTIntegrationTestChild].to_set
361+
assert_equal direct_descendants, ZeitwerkDTIntegrationTestRoot.direct_descendants.to_set
362+
363+
descendants = direct_descendants.merge([ZeitwerkDTIntegrationTestGrandchild])
364+
assert_equal descendants, ZeitwerkDTIntegrationTestRoot.descendants.to_set
365+
ensure
366+
Object.send(:remove_const, :ZeitwerkDTIntegrationTestRoot)
367+
Object.send(:remove_const, :ZeitwerkDTIntegrationTestChild)
368+
end
369+
end
318370
end

0 commit comments

Comments
 (0)