Class: Rigor::Environment::RbsLoader
- Inherits:
-
Object
- Object
- Rigor::Environment::RbsLoader
- Defined in:
- lib/rigor/environment/rbs_loader.rb
Overview
Loads RBS class declarations and method definitions from disk and exposes them to the inference engine in a small, stable surface.
Slice 4 phase 1 only enabled the RBS core signatures shipped with the ‘rbs` gem (`Object`, `Integer`, `String`, `Array`, …). Phase 2a adds opt-in stdlib library loading (`pathname`, `json`, `tempfile`, …) and arbitrary-directory signature loading (typically the project’s local ‘sig/` tree). Both are off by default on `RbsLoader.default` so the core-only fast path stays cheap; project-aware loading is opted into through for_project or by constructing a custom loader.
The default instance is shared across the process: building the core RBS environment costs hundreds of milliseconds and the data is read-only. The shared instance is frozen, but holds a mutable state hash for lazy memoization of the heavy ‘RBS::Environment` and `RBS::DefinitionBuilder` – the user-visible API stays purely functional.
See docs/internal-spec/inference-engine.md for the binding contract. rubocop:disable Metrics/ClassLength
Instance Attribute Summary collapse
-
#cache_store ⇒ Object
readonly
Returns the value of attribute cache_store.
-
#libraries ⇒ Object
readonly
Returns the value of attribute libraries.
-
#signature_paths ⇒ Object
readonly
Returns the value of attribute signature_paths.
Class Method Summary collapse
-
.build_env_for(libraries:, signature_paths:) ⇒ Object
Builds an ‘RBS::Environment` from explicit `libraries` and `signature_paths`.
- .default ⇒ Object
-
.reset_default! ⇒ Object
Used by tests to discard the cached default loader; production code MUST NOT call this.
Instance Method Summary collapse
-
#class_known?(name) ⇒ Boolean
Returns true when an RBS class or module declaration with the given name is loaded.
- #class_ordering(lhs, rhs) ⇒ Object
-
#class_type_param_names(class_name) ⇒ Object
Slice 4 phase 2d.
-
#constant_names ⇒ Array<String>
Every RBS-declared constant name (top-level prefixed, e.g., ‘“::Math::PI”`) currently loaded into the environment.
-
#constant_type(name) ⇒ Object
Slice A constant-value lookup.
-
#each_constant_decl ⇒ Object
Yields ‘(name, entry)` for every RBS constant declaration currently loaded into the environment.
-
#each_known_class_name ⇒ Object
Yields every known class / module / alias name (top-level prefixed) currently loaded into the environment.
-
#initialize(libraries: [], signature_paths: [], cache_store: nil) ⇒ RbsLoader
constructor
A new instance of RbsLoader.
-
#instance_definition(class_name) ⇒ RBS::Definition?
The resolved instance definition for ‘class_name`, or nil when the class is unknown or its definition cannot be built (RBS may raise on broken hierarchies; we fail-soft and return nil so the caller can fall back).
- #instance_method(class_name:, method_name:) ⇒ RBS::Definition::Method?
-
#singleton_definition(class_name) ⇒ RBS::Definition?
The resolved singleton (class object) definition for ‘class_name`.
-
#singleton_method(class_name:, method_name:) ⇒ RBS::Definition::Method?
The class method on ‘class_name`.
Constructor Details
#initialize(libraries: [], signature_paths: [], cache_store: nil) ⇒ RbsLoader
Returns a new instance of RbsLoader.
86 87 88 89 90 91 92 93 94 95 |
# File 'lib/rigor/environment/rbs_loader.rb', line 86 def initialize(libraries: [], signature_paths: [], cache_store: nil) @libraries = libraries.map(&:to_s).freeze @signature_paths = signature_paths.map { |p| Pathname(p) }.freeze @cache_store = cache_store @state = { env: nil, builder: nil } @instance_definition_cache = {} @singleton_definition_cache = {} @class_known_cache = {} @hierarchy = RbsHierarchy.new(self) end |
Instance Attribute Details
#cache_store ⇒ Object (readonly)
Returns the value of attribute cache_store.
67 68 69 |
# File 'lib/rigor/environment/rbs_loader.rb', line 67 def cache_store @cache_store end |
#libraries ⇒ Object (readonly)
Returns the value of attribute libraries.
67 68 69 |
# File 'lib/rigor/environment/rbs_loader.rb', line 67 def libraries @libraries end |
#signature_paths ⇒ Object (readonly)
Returns the value of attribute signature_paths.
67 68 69 |
# File 'lib/rigor/environment/rbs_loader.rb', line 67 def signature_paths @signature_paths end |
Class Method Details
.build_env_for(libraries:, signature_paths:) ⇒ Object
Builds an ‘RBS::Environment` from explicit `libraries` and `signature_paths`. Stateless surface so the v0.0.9 Cache::RbsEnvironment producer can build an env on cache miss without holding a loader instance, and the instance-side #build_env delegates here so the implementation stays single-rooted.
52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/rigor/environment/rbs_loader.rb', line 52 def build_env_for(libraries:, signature_paths:) rbs_loader = RBS::EnvironmentLoader.new libraries.each do |library| next unless rbs_loader.has_library?(library: library, version: nil) rbs_loader.add(library: library, version: nil) end signature_paths.each do |path| path = Pathname(path) unless path.is_a?(Pathname) rbs_loader.add(path: path) if path.directory? end RBS::Environment.from_loader(rbs_loader).resolve_type_names end |
.default ⇒ Object
34 35 36 |
# File 'lib/rigor/environment/rbs_loader.rb', line 34 def default @default ||= new.freeze end |
.reset_default! ⇒ Object
Used by tests to discard the cached default loader; production code MUST NOT call this. The shared loader holds a several-MB RBS::Environment, so dropping it during a normal run wastes the cost of rebuilding it.
42 43 44 |
# File 'lib/rigor/environment/rbs_loader.rb', line 42 def reset_default! @default = nil end |
Instance Method Details
#class_known?(name) ⇒ Boolean
Returns true when an RBS class or module declaration with the given name is loaded. Accepts unprefixed or top-level-prefixed names (“Integer” or “::Integer”). Memoized per-name (positive and negative results both cache).
When ‘cache_store` is set, the loader fetches the entire set of known class / module / alias names once (per process) through Cache::RbsKnownClassNames.fetch and answers `class_known?` from the in-memory Set. Cold runs pay a single env walk and persist the result; warm runs (and a separate loader sharing the same Store) skip the env walk entirely.
108 109 110 111 112 113 114 115 116 117 |
# File 'lib/rigor/environment/rbs_loader.rb', line 108 def class_known?(name) key = name.to_s return @class_known_cache[key] if @class_known_cache.key?(key) @class_known_cache[key] = if cache_store cached_class_known(name) else compute_class_known(name) end end |
#class_ordering(lhs, rhs) ⇒ Object
206 207 208 |
# File 'lib/rigor/environment/rbs_loader.rb', line 206 def class_ordering(lhs, rhs) @hierarchy.class_ordering(lhs, rhs) end |
#class_type_param_names(class_name) ⇒ Object
Slice 4 phase 2d. Returns the class’s declared type-parameter names as Symbols (e.g., ‘[:Elem]` for `Array`, `[:K, :V]` for `Hash`). Used by the dispatcher to build the substitution map from receiver `type_args` into the method’s return type. The instance definition is the canonical source because singleton methods (e.g., ‘Array.new`) parameterize over the same `Elem` as instance methods.
Returns an empty array for non-generic classes and for unknown names (the loader stays fail-soft). NOTE: in the ‘rbs` gem, `RBS::Definition#type_params` returns `Array<Symbol>` directly, not the AST `TypeParam` object (those live on the AST level).
When ‘cache_store` is set, the loader fetches the entire type-parameter-name table once (per process) through Cache::RbsClassTypeParamNames.fetch and answers point lookups from it. Cold runs build the table once and persist it; warm runs (and a separate loader sharing the same Store) skip the env walk entirely.
194 195 196 197 198 199 200 201 202 203 204 |
# File 'lib/rigor/environment/rbs_loader.rb', line 194 def class_type_param_names(class_name) if cache_store key = class_name.to_s.delete_prefix("::") return type_param_names_table.fetch(key, []).dup end definition = instance_definition(class_name) return [] unless definition definition.type_params.dup end |
#constant_names ⇒ Array<String>
Returns every RBS-declared constant name (top-level prefixed, e.g., ‘“::Math::PI”`) currently loaded into the environment. Used by the cache producer that materialises the constant-type table; ordinary callers should keep using #constant_type for point lookups.
215 216 217 218 219 |
# File 'lib/rigor/environment/rbs_loader.rb', line 215 def constant_names env.constant_decls.keys.map(&:to_s) rescue StandardError [] end |
#constant_type(name) ⇒ Object
Slice A constant-value lookup. Returns the translated ‘Rigor::Type` for a non-class constant declaration (`BUCKETS: Array`, `DEFAULT_PATH: String`, …) or `nil` when no constant entry exists for `name` in the loaded RBS environment. Callers MUST treat the return value as authoritative when present and as “unknown” when nil; the loader does NOT consult the class declarations here — class objects are still resolved through #class_known? and `Environment#singleton_for_name`.
When ‘cache_store` is set, the loader fetches the entire translated constant table once (per process) through Cache::RbsConstantTable.fetch and answers point lookups from it. Cold runs pay the translation cost up-front and write the result to disk; warm runs skip the translation entirely and pay only a `Marshal.load` of the table.
252 253 254 255 256 257 258 259 260 261 262 263 |
# File 'lib/rigor/environment/rbs_loader.rb', line 252 def constant_type(name) rbs_name = parse_type_name(name) return nil unless rbs_name if cache_store constant_type_table[rbs_name.to_s] else translate_constant_decl(rbs_name) end rescue StandardError nil end |
#each_constant_decl ⇒ Object
Yields ‘(name, entry)` for every RBS constant declaration currently loaded into the environment. The cache producer uses this to materialise the constant-type table without going back through #constant_type (which would recurse back into the cache when `cache_store` is set).
226 227 228 229 230 231 232 233 234 |
# File 'lib/rigor/environment/rbs_loader.rb', line 226 def each_constant_decl return enum_for(:each_constant_decl) unless block_given? env.constant_decls.each do |rbs_name, entry| yield rbs_name.to_s, entry end rescue StandardError # fail-soft: a broken environment yields no entries. end |
#each_known_class_name ⇒ Object
Yields every known class / module / alias name (top-level prefixed) currently loaded into the environment. The cache producer that materialises the known-name set uses this so it never recurses back through #class_known?.
123 124 125 126 127 128 129 130 |
# File 'lib/rigor/environment/rbs_loader.rb', line 123 def each_known_class_name return enum_for(:each_known_class_name) unless block_given? env.class_decls.each_key { |rbs_name| yield rbs_name.to_s } env.class_alias_decls.each_key { |rbs_name| yield rbs_name.to_s } rescue StandardError # fail-soft: a broken environment yields no names. end |
#instance_definition(class_name) ⇒ RBS::Definition?
Returns the resolved instance definition for ‘class_name`, or nil when the class is unknown or its definition cannot be built (RBS may raise on broken hierarchies; we fail-soft and return nil so the caller can fall back).
136 137 138 139 140 141 |
# File 'lib/rigor/environment/rbs_loader.rb', line 136 def instance_definition(class_name) key = class_name.to_s return @instance_definition_cache[key] if @instance_definition_cache.key?(key) @instance_definition_cache[key] = build_instance_definition(class_name) end |
#instance_method(class_name:, method_name:) ⇒ RBS::Definition::Method?
144 145 146 147 148 149 |
# File 'lib/rigor/environment/rbs_loader.rb', line 144 def instance_method(class_name:, method_name:) definition = instance_definition(class_name) return nil unless definition definition.methods[method_name.to_sym] end |
#singleton_definition(class_name) ⇒ RBS::Definition?
The resolved singleton (class object) definition for ‘class_name`. The methods on this definition are the *class methods* of `class_name`, including those inherited from `Class` and `Module` for class types. Returns nil for unknown names and on RBS build errors (fail-soft).
156 157 158 159 160 161 |
# File 'lib/rigor/environment/rbs_loader.rb', line 156 def singleton_definition(class_name) key = class_name.to_s return @singleton_definition_cache[key] if @singleton_definition_cache.key?(key) @singleton_definition_cache[key] = build_singleton_definition(class_name) end |
#singleton_method(class_name:, method_name:) ⇒ RBS::Definition::Method?
Returns the class method on ‘class_name`. For example, `singleton_method(class_name: “Integer”, method_name: :sqrt)` returns the definition for `Integer.sqrt`, while `singleton_method(class_name: “Foo”, method_name: :new)` returns Class#new for any class type.
168 169 170 171 172 173 |
# File 'lib/rigor/environment/rbs_loader.rb', line 168 def singleton_method(class_name:, method_name:) definition = singleton_definition(class_name) return nil unless definition definition.methods[method_name.to_sym] end |