In Ruby, what do singleton_class, singleton_class? and Class#attached_object return, and when does attached_object raise TypeError?
answer
- created on first request
- nil, true, false share classes
- Integer and Symbol refuse
- attached_object since 3.2
- TypeError for ordinary classes
basics
~10 sobj.singleton_class returns the object's singleton class, creating it if needed. singleton_class? says whether a module is one. Class#attached_object, since Ruby 3.2, returns the owning object and raises TypeError on an ordinary class.
solid answer
~30 s`obj.singleton_class` returns the singleton class of `obj`, creating it on first use; it inspects as `#<Class:#<Gateway:0x...>>`, and for a class `Report` as `#<Class:Report>`. Integers, Floats and Symbols cannot have one, so the call raises `TypeError` ("can't define singleton"). `nil`, `true` and `false` return their shared classes, `NilClass`, `TrueClass` and `FalseClass`. `Module#singleton_class?` returns `true` only for a real singleton class, so `nil.singleton_class.singleton_class?` is `false`. `Class#attached_object`, added in Ruby 3.2, goes the other way: `Report.singleton_class.attached_object` returns `Report`. On any class that is not a singleton class — `Report`, `NilClass` — it raises `TypeError` saying the class is not a singleton class.
code
ruby · 13 linesclass Report; end
gateway = Object.new
Report.singleton_class # => #<Class:Report>
Report.singleton_class.singleton_class? # => true
Report.singleton_class? # => false
Report.singleton_class.attached_object # => Report
gateway.singleton_class.attached_object # => gateway
nil.singleton_class # => NilClass
nil.singleton_class.singleton_class? # => false
Report.attached_object # TypeError: 'Report' is not a singleton class
42.singleton_class # TypeError: can't define singletongo deeper
Recall that singleton_class returns an object's hidden class and that numbers and symbols cannot have one.
Explain the special cases (nil, true, false share classes; immediates raise), what singleton_class? distinguishes, and what attached_object returns.
Use attached_object and singleton_class? in tooling that prints method owners, and catch def nil.x as a hidden core patch in review.
Prefer the 3.2+ reflection methods over inspect parsing in internal tools, and retire such parsing once every supported Ruby has them.
## Three reflective questions Singleton classes are hidden from `obj.class`, so Ruby provides separate methods to reach and recognise them: 1. `Kernel#singleton_class` — give me this object's singleton class. 2. `Module#singleton_class?` — is this module a singleton class? 3. `Class#attached_object` — which object does this singleton class belong to? ## singleton_class `obj.singleton_class` returns the object's singleton class, **creating it if it does not exist yet**. The result is a real `Class` you can inspect, reopen with `class_eval`, or ask for `instance_methods(false)`: - for an ordinary object it inspects as `#<Class:#<Gateway:0x...>>`; - for a class `Report` it inspects as `#<Class:Report>` and holds `Report`'s class methods. Some values cannot have a singleton class: | Receiver | `singleton_class` result | |---|---| | an ordinary object | its own singleton class | | a class or module | its singleton class, holding its class methods | | `nil`, `true`, `false` | `NilClass`, `TrueClass`, `FalseClass` — shared, not singleton classes | | an Integer, Float or Symbol | `TypeError`: can't define singleton | | an interned frozen string, such as a frozen literal | `TypeError`: can't define singleton | The `nil` row matters: `def nil.blank? = true` does not give one value a private method, it adds `blank?` to `NilClass`, so every `nil` in the process answers it. ## singleton_class? `Module#singleton_class?` answers whether a module is a singleton class: - `Report.singleton_class?` is `false`; - `Report.singleton_class.singleton_class?` is `true`; - `nil.singleton_class.singleton_class?` is `false`, because `NilClass` is an ordinary class. Tools that walk classes use it to skip or label singleton classes, for example when printing a method's owner. ## attached_object `Class#attached_object` was added in **Ruby 3.2**. It returns the object a singleton class belongs to: - `Report.singleton_class.attached_object` returns `Report`; - `gateway.singleton_class.attached_object` returns `gateway`. Called on a class that is not a singleton class, it raises `TypeError` with a message such as `'Report' is not a singleton class`. That includes `NilClass`, `TrueClass` and `FalseClass`, which is consistent with `singleton_class?` returning `false` for them. Before 3.2 there was no direct call for this, so tools fell back on indirect tricks such as parsing `inspect` output. ## Turning a method owner into a readable name A documentation tool or debugger often holds a method's owner and wants to print it the way a person would write it. The two reflection predicates make that a small function: ```ruby def label(owner, name) if owner.singleton_class? "#{owner.attached_object.inspect}.#{name}" else "#{owner}##{name}" end end label(Report.singleton_class, :build) # => "Report.build" label(Report, :render) # => "Report#render" ``` Checking `singleton_class?` first matters, because `attached_object` raises `TypeError` for every class that is not a singleton class. ## Where these are used - **Debuggers and documentation tools** that receive a method's owner and must print `Report.build` rather than `#<Class:Report>#build`. - **Hooks and generators** that receive a singleton class and need the object it serves. - **Tests of metaprogramming code**, to assert that a method was added to the class object and not to its instances. ## Mistakes to avoid - Calling `singleton_class` on an Integer or Symbol and expecting a class back. - Assuming `def nil.x` affects only one value. - Calling `attached_object` on an ordinary class and expecting `nil` instead of `TypeError`. - Treating `NilClass` as a singleton class because `nil.singleton_class` returns it.
- What happens if you write def nil.blank? = true?`nil.singleton_class` is `NilClass`, so the method is added to `NilClass` and every `nil` in the process responds to `blank?`. It is effectively a patch to a core class, not a per-object method.
- Why does 42.singleton_class raise instead of returning a class?Small Integers, Floats and static Symbols are immediate values with no heap object to attach a class to, and Ruby applies the same refusal to every Integer, Float and Symbol, raising `TypeError` with "can't define singleton". The same error stops `def n.x` when `n` holds an Integer.
saying these in an interview costs you the question
- nil.singleton_class returns a singleton class unique to nil
- 42.singleton_class returns a new class for that integer
- attached_object returns nil for an ordinary class
- obj.class returns the singleton class once one exists
- Report.singleton_class? is true because Report has class methods