Class: ConstantResolver
- Inherits:
-
Object
- Object
- ConstantResolver
- Defined in:
- lib/constant_resolver.rb,
lib/constant_resolver/version.rb
Overview
Get information about (partially qualified) constants without loading the application code. We infer the fully qualified name and the filepath.
The implementation makes a few assumptions about the code base:
-
‘Something::SomeOtherThing` is defined in a path of either `something/some_other_thing.rb` or `something.rb`, relative to the load path. Constants that have their own file do not have all-uppercase names like MAGIC_NUMBER or all-uppercase parts like SomeID. Rails’ ‘zeitwerk` autoloader makes the same assumption.
-
It is OK to not always infer the exact file defining the constant. For example, when a constant is inherited, we have no way of inferring the file it is defined in. You could argue though that inheritance means that another constant with the same name exists in the inheriting class, and this view is sufficient for all our use cases.
Defined Under Namespace
Classes: ConstantContext, Error
Constant Summary collapse
- VERSION =
"0.2.0"
Instance Method Summary collapse
- #config ⇒ Object private
-
#file_map ⇒ Hash<String, String>
Maps constant names to file paths.
-
#initialize(root_path:, load_paths:, inflector: DefaultInflector.new) ⇒ ConstantResolver
constructor
A new instance of ConstantResolver.
-
#resolve(const_name, current_namespace_path: []) ⇒ ConstantResolver::ConstantContext
Resolve a constant via its name.
Constructor Details
#initialize(root_path:, load_paths:, inflector: DefaultInflector.new) ⇒ ConstantResolver
Returns a new instance of ConstantResolver.
42 43 44 45 46 47 48 49 |
# File 'lib/constant_resolver.rb', line 42 def initialize(root_path:, load_paths:, inflector: DefaultInflector.new) root_path += "/" unless root_path.end_with?("/") @root_path = root_path @load_paths = coerce_load_paths(load_paths) @file_map = nil @inflector = inflector end |
Instance Method Details
#config ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
113 114 115 116 117 118 |
# File 'lib/constant_resolver.rb', line 113 def config { root_path: @root_path, load_paths: @load_paths, } end |
#file_map ⇒ Hash<String, String>
Maps constant names to file paths.
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/constant_resolver.rb', line 73 def file_map return @file_map if @file_map @file_map = {} duplicate_files = {} @load_paths.each_pair do |load_path, default_ns| Dir[glob_path(load_path)].each do |file_path| root_relative_path = file_path.delete_prefix!(@root_path) const_name = @inflector.camelize(root_relative_path.delete_prefix(load_path).delete_suffix!(".rb")) const_name = "#{default_ns}::#{const_name}" unless default_ns == "Object" existing_entry = @file_map[const_name] if existing_entry duplicate_files[const_name] ||= [existing_entry] duplicate_files[const_name] << root_relative_path end @file_map[const_name] = root_relative_path end end if duplicate_files.any? raise(Error, <<~MSG) Ambiguous constant definition: #{duplicate_files.map { |const_name, paths| (const_name, paths) }.join("\n")} MSG end if @file_map.empty? raise(Error, <<~MSG) Could not find any ruby files. Searched in: - #{@load_paths.keys.map { |load_path| glob_path(load_path) }.join("\n- ")} MSG end @file_map end |
#resolve(const_name, current_namespace_path: []) ⇒ ConstantResolver::ConstantContext
Resolve a constant via its name. If the name is partially qualified, we need the current namespace path to correctly infer its full name
58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/constant_resolver.rb', line 58 def resolve(const_name, current_namespace_path: []) current_namespace_path = [] if const_name.start_with?("::") inferred_name, location = resolve_constant(const_name.sub(/^::/, ""), current_namespace_path) return unless inferred_name ConstantContext.new( inferred_name, location, ) end |