In Ruby, how does Object.const_get("Billing::Invoice") resolve a namespaced name, and what does Module#name return for a class built with Class.new?
answer
- walks each :: segment
- inherit flag applies per step
- NameError: wrong constant name
- anonymous class name is nil
- set_temporary_name since 3.3
basics
~20 sconst_get splits the string on :: and looks up each segment in the module found so far, raising NameError if one is missing or malformed. Module#name returns nil for an anonymous class until it is assigned to a constant.
solid answer
~40 s`Object.const_get("Billing::Invoice")` looks up `Billing` in `Object`, then `Invoice` inside `Billing`, and returns the class. A leading `::` starts from `Object`. The optional second argument, `inherit` (default `true`), controls whether each step also searches ancestors; with `false`, `const_get("Billing::Sub::TAX", false)` fails if `TAX` is only inherited. A missing constant raises `NameError` (uninitialized constant) after `const_missing` has had its chance, and a malformed one such as `"billing"` raises `NameError` for a wrong constant name. `Module#constants` lists names reachable in a module, as symbols, with no guaranteed order. `Class.new` creates an anonymous class whose `name` is `nil`; assigning it to a constant gives it that permanent name, and since Ruby 3.3 `set_temporary_name` can label it without a constant.
code
ruby · 20 linesmodule Billing
class Invoice
TAX = 0.2
end
class Sub < Invoice; end
end
Object.const_get("Billing::Invoice") # => Billing::Invoice
Object.const_get("::Billing::Sub::TAX") # => 0.2 (inherited)
Object.const_get("Billing::Sub::TAX", false) # NameError: TAX is not in Sub itself
Object.const_get("billing") # NameError: wrong constant name billing
Billing.constants.sort # => [:Invoice, :Sub]
klass = Class.new
klass.name # => nil
klass.set_temporary_name("report(draft)")
klass.name # => "report(draft)"
Report = klass
klass.name # => "Report"go deeper
Recall that const_get turns a string like "Billing::Invoice" into the class, raising NameError if it does not exist.
Explain the per-segment walk, the inherit flag, the two NameError cases, and why an anonymous class has a nil name until it is assigned to a constant.
Build class-name registries that sort constants, handle nil names, allowlist inputs to const_get, and use set_temporary_name for generated classes.
Choose explicit registries over name-based lookup where class names cross a trust or persistence boundary.
## Constants are looked up by name at run time Class and module names in Ruby are **constants**, and `Module#const_get` looks one up from a name held in a `Symbol` or `String`. It is how plugin systems, serializers and job runners turn a stored class name back into a class. ## How a namespaced string resolves Given `Object.const_get("Billing::Invoice")`, Ruby: 1. splits the path on `::`; 2. looks up `Billing` in the receiver, `Object`; 3. looks up `Invoice` inside the module it just found; 4. returns the value of the last segment. A leading `::`, as in `"::Billing::Invoice"`, starts the walk from `Object` regardless of the receiver. Each intermediate value must be a class or module; if a segment names something else, Ruby raises `TypeError` saying it does not refer to a class or module. ## The inherit flag The second argument, `inherit`, defaults to `true`. When true, each lookup also searches the ancestors of the module being searched (and `Object` when that is a module). The documentation stresses that the flag is **respected on each lookup** along the path: - `Object.const_get("Billing::Sub::TAX")` finds `TAX` defined in `Sub`'s superclass; - `Object.const_get("Billing::Sub::TAX", false)` raises `NameError`, because `TAX` is not defined in `Sub` itself. ## Failure modes | Input | Result | |---|---| | `"Billing::Invoice"` | the class | | `"Billing::Nope"` | `NameError`: uninitialized constant (after `const_missing`) | | `"billing"` | `NameError`: wrong constant name | | `"Billing::VERSION::X"` where `VERSION` is a string | `TypeError` | Because `const_get` performs a full constant lookup, registered autoloads fire and `const_missing` runs on a miss, which is how lazy loaders plug in. Taking the name from request data is a separate security concern and should always go through an allowlist. ## Listing constants `Module#constants` returns the names of constants reachable in a module as symbols, including those from included modules, unless you pass `false`. Its documentation makes **no guarantee about order**, so sort it before comparing or displaying. `Object.constants` is a quick way to see every top-level constant defined so far, classes and modules included. ## Names of anonymous classes `Module#name` returns a class or module's name as a `String`, or **`nil` for anonymous modules**. A class built with `Class.new` is anonymous until it is assigned to a constant: - `klass = Class.new` — `klass.name` is `nil`, and `inspect` shows something like `#<Class:0x...>`; - `Report = klass` — `klass.name` becomes `"Report"`, a permanent name. Since Ruby 3.3, `Module#set_temporary_name` gives an anonymous module a label for introspection and `inspect` without assigning a constant. The name must not be a valid constant path, and it is discarded once the module gets a permanent name. Code that uses `name` as a key must handle `nil` for anonymous classes. ## Round-tripping a class through its name Job queues and serializers often store a class as its name and restore it later: 1. At enqueue time, store `job.class.name`, for example `"Billing::InvoiceJob"`. 2. At run time, call `Object.const_get(stored_name)` to get the class back. The round trip works only for classes with a **permanent name**. For an anonymous class, step 1 stores `nil`. A temporary name does not help either, because it is by design not a valid constant path, so `const_get` rejects it with `NameError`. Generated classes that must survive such a round trip need to be assigned to a constant. ## Mistakes to avoid - Splitting on `::` and calling `const_get` segment by segment by hand; one call handles the path. - Assuming `inherit: false` applies only to the last segment. - Using `klass.name` as a registry key without handling `nil`. - Relying on the order of `constants`. - Passing untrusted strings to `const_get` without an allowlist.
- Why can Object.const_get("Billing::Invoice") load code that has not been required yet?When a lookup misses, `const_get` calls `const_missing` on the module before raising `NameError`, and autoload entries are resolved as part of the lookup. A loader that hooks either mechanism can load the file and return the constant, so `const_get` behaves like a direct reference to `Billing::Invoice`.
- What does set_temporary_name refuse, and what replaces a temporary name?It rejects a name that is a valid constant path, such as `"Report"`, so temporary and permanent names cannot be confused, and it cannot rename a module that already has a permanent name. Assigning the module to a constant later replaces the temporary name with the permanent one.
saying these in an interview costs you the question
- const_get cannot resolve a string containing ::
- The inherit flag applies only to the last segment of the path
- A class created with Class.new is named after the variable holding it
- const_get returns nil when the constant is missing
- Module#constants returns names in definition order