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.3.0"
Instance Method Summary collapse
- #config ⇒ Object private
-
#file_map ⇒ Hash<String, String>
Maps constant names to file paths.
-
#initialize(root_path:, load_paths:, exclude: [], 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:, exclude: [], inflector: DefaultInflector.new) ⇒ ConstantResolver
Returns a new instance of ConstantResolver.
46 47 48 49 50 51 52 53 54 |
# File 'lib/constant_resolver.rb', line 46 def initialize(root_path:, load_paths:, exclude: [], 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 @exclude = exclude 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.
122 123 124 125 126 127 |
# File 'lib/constant_resolver.rb', line 122 def config { root_path: @root_path, load_paths: @load_paths, } end |
#file_map ⇒ Hash<String, String>
Maps constant names to file paths.
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 111 112 113 114 115 116 117 118 119 |
# File 'lib/constant_resolver.rb', line 78 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 if allowed?(root_relative_path) @file_map[const_name] = root_relative_path end 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
63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/constant_resolver.rb', line 63 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 |