Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
fxn committed Apr 14, 2024
1 parent 949faba commit 33fe4a2
Show file tree
Hide file tree
Showing 7 changed files with 49 additions and 28 deletions.
7 changes: 5 additions & 2 deletions lib/zeitwerk/gem_inflector.rb
Expand Up @@ -9,8 +9,11 @@ def initialize(root_file)
@version_file = File.join(root_dir, namespace, "version.rb")
end

# @sig (String, String) -> String
def camelize(basename, abspath)
# See the rationale for the third optional argument in
# Zeitwerk::Inflector#camelize.
#
# @sig (String, String, String?) -> String
def camelize(basename, abspath, _namespace_name = nil)
abspath == @version_file ? "VERSION" : super
end
end
Expand Down
4 changes: 3 additions & 1 deletion lib/zeitwerk/gem_loader.rb
Expand Up @@ -19,6 +19,8 @@ def self.__new(root_file, namespace:, warn_on_extra_files:)
def initialize(root_file, namespace:, warn_on_extra_files:)
super()

@namespace = namespace

@tag = File.basename(root_file, ".rb")
@tag = real_mod_name(namespace) + "-" + @tag unless namespace.equal?(Object)

Expand Down Expand Up @@ -47,7 +49,7 @@ def warn_on_extra_files
next if abspath == expected_namespace_dir

basename_without_ext = basename.delete_suffix(".rb")
cname = inflector.camelize(basename_without_ext, abspath).to_sym
cname = cname_for(basename_without_ext, abspath, real_mod_name(@namespace))
ftype = dir?(abspath) ? "directory" : "file"

warn(<<~EOS)
Expand Down
22 changes: 14 additions & 8 deletions lib/zeitwerk/inflector.rb
Expand Up @@ -5,14 +5,20 @@ class Inflector
# Very basic snake case -> camel case conversion.
#
# inflector = Zeitwerk::Inflector.new
# inflector.camelize("post", ...) # => "Post"
# inflector.camelize("users_controller", ...) # => "UsersController"
# inflector.camelize("api", ...) # => "Api"
# inflector.camelize("post", abspath, "Object") # => "Post"
# inflector.camelize("users_controller", abspath, "Admin") # => "UsersController"
# inflector.camelize("api", abspath, "Object") # => "Api"
#
# Takes into account hard-coded mappings configured with `inflect`.
#
# @sig (String, String) -> String
def camelize(basename, _abspath)
# The third argument was added in 2.6.14. It is optional because existing
# subclasses using the previous signature with two arguments may be calling
# super(basename, abspath) or just super. We need these to work as they are.
# At the same time, super for new subclasses defining the new signature is
# going to work as well.
#
# @sig (String, String, String?) -> String
def camelize(basename, _abspath, _namespace_name = nil)
overrides[basename] || basename.split('_').each(&:capitalize!).join
end

Expand All @@ -24,9 +30,9 @@ def camelize(basename, _abspath)
# "mysql_adapter" => "MySQLAdapter"
# )
#
# inflector.camelize("html_parser", abspath) # => "HTMLParser"
# inflector.camelize("mysql_adapter", abspath) # => "MySQLAdapter"
# inflector.camelize("users_controller", abspath) # => "UsersController"
# inflector.camelize("html_parser", abspath, "MyGem") # => "HTMLParser"
# inflector.camelize("users_controller", abspath, "Admin") # => "UsersController"
# inflector.camelize("mysql_adapter", abspath, "Object") # => "MySQLAdapter"
#
# @sig (Hash[String, String]) -> void
def inflect(inflections)
Expand Down
23 changes: 12 additions & 11 deletions lib/zeitwerk/loader.rb
Expand Up @@ -265,17 +265,18 @@ def cpath_expected_at(path)

return unless root_namespace

if paths.empty?
real_mod_name(root_namespace)
else
cnames = paths.reverse_each.map { |b, a| cname_for(b, a) }
root_namespace_name = real_mod_name(root_namespace)
return root_namespace_name if paths.empty?

if root_namespace == Object
cnames.join("::")
else
"#{real_mod_name(root_namespace)}::#{cnames.join("::")}"
end
basename, abspath = paths.pop
cpath = cpath(root_namespace, cname_for(basename, abspath, root_namespace_name))
cpath = cpath.dup if cpath.frozen? # Symbol#name returns a frozen string.

paths.reverse_each do |b, a|
cpath << "::#{cname_for(b, a, cpath)}"
end

cpath
end

# Says if the given constant path would be unloaded on reload. This
Expand Down Expand Up @@ -411,12 +412,12 @@ def all_dirs
ls(dir) do |basename, abspath|
if ruby?(basename)
basename.delete_suffix!(".rb")
autoload_file(parent, cname_for(basename, abspath), abspath)
autoload_file(parent, cname_for(basename, abspath, real_mod_name(parent)), abspath)
else
if collapse?(abspath)
define_autoloads_for_dir(abspath, parent)
else
autoload_subdir(parent, cname_for(basename, abspath), abspath)
autoload_subdir(parent, cname_for(basename, abspath, real_mod_name(parent)), abspath)
end
end
end
Expand Down
7 changes: 4 additions & 3 deletions lib/zeitwerk/loader/eager_load.rb
Expand Up @@ -36,7 +36,7 @@ def eager_load_dir(path)

raise Zeitwerk::Error.new("#{abspath} is not a directory") unless dir?(abspath)

cnames = []
paths = []

root_namespace = nil
walk_up(abspath) do |dir|
Expand All @@ -49,7 +49,7 @@ def eager_load_dir(path)
return if hidden?(basename)

unless collapse?(dir)
cnames << inflector.camelize(basename, dir).to_sym
paths << [basename, dir]
end
end

Expand All @@ -58,9 +58,10 @@ def eager_load_dir(path)
return if @eager_loaded

namespace = root_namespace
cnames.reverse_each do |cname|
paths.reverse_each do |basename, abspath|
# Can happen if there are no Ruby files. This is not an error condition,
# the directory is actually managed. Could have Ruby files later.
cname = cname_for(basename, abspath, real_mod_name(namespace))
return unless cdef?(namespace, cname)
namespace = cget(namespace, cname)
end
Expand Down
8 changes: 6 additions & 2 deletions lib/zeitwerk/loader/helpers.rb
Expand Up @@ -146,8 +146,12 @@ module Zeitwerk::Loader::Helpers

# @raise [Zeitwerk::NameError]
# @sig (String, String) -> Symbol
private def cname_for(basename, abspath)
cname = inflector.camelize(basename, abspath)
private def cname_for(basename, abspath, namespace)
cname = if inflector.method(:camelize).arity == 3
inflector.camelize(basename, abspath, namespace)
else
inflector.camelize(basename, abspath)
end

unless cname.is_a?(String)
raise TypeError, "#{inflector.class}#camelize must return a String, received #{cname.inspect}"
Expand Down
6 changes: 5 additions & 1 deletion lib/zeitwerk/null_inflector.rb
@@ -1,5 +1,9 @@
class Zeitwerk::NullInflector
def camelize(basename, _abspath)
# See the rationale for the third optional argument in
# Zeitwerk::Inflector#camelize.
#
# @sig (String, String, String?) -> String
def camelize(basename, _abspath, _namespace = nil)
basename
end
end

0 comments on commit 33fe4a2

Please sign in to comment.