LaunchKit
All quizzes
Ruby 0 views

A class definition behind if false

Question

What's the return value of the following Ruby code?

class MyClass
  def self.to_s
    "MyClass is cool"
  end
end if false

MyClass.to_s # => ???

The correct answer is

Explanation

TL;DR

A class ... end definition is ordinary runtime code, and the if false modifier applies to the whole definition. It never executes, so the constant MyClass is never created, and MyClass.to_s raises NameError: uninitialized constant MyClass.

Step by step

class MyClass
  def self.to_s
    "MyClass is cool"
  end
end if false

defined?(MyClass) # => nil

begin
  MyClass.to_s
rescue NameError => e
  e.message # => "uninitialized constant MyClass"
end
  1. class MyClass ... end if false is one statement: the class definition is the body of a statement modifier, exactly like puts "hi" if false.
  2. Unlike languages where class declarations are hoisted at compile time, Ruby defines a class only when the class keyword executes. It assigns a new Class object to the constant and runs the body.
  3. The condition is false, so none of that happens. defined?(MyClass) returns nil.
  4. Referencing the missing constant raises NameError. (Had the class existed, MyClass.to_s would have returned "MyClass is cool" since the singleton method overrides the default Module#to_s.)

The parse-time contrast

The if false modifier famously does leave a trace when the body is a local-variable assignment: the parser registers the variable even though the assignment never runs. Constants get no such treatment:

x = 1 if false
x # => nil (local variable exists, unassigned)

class Ghost; end if false
defined?(Ghost) # => nil (constant does not exist at all)

Local variables are created at parse time; classes, modules, methods, and constants are created at run time.

A legitimate use

The pattern is real, with defined? as the guard. Ruby's resolv-replace library reopens SOCKSSocket only when the interpreter was compiled with SOCKS support:

class SOCKSSocket < TCPSocket
  # patch DNS resolution...
end if defined? SOCKSSocket

When the constant is absent, the whole reopening is skipped and nothing breaks. Use this sparingly: conditionally defined classes make it hard to know what exists at runtime, and a NameError far from the guard is the usual symptom.

Share this quiz

Comments

No comments yet. Be the first.

Only used to confirm and publish your comment. Never shown publicly, never shared.

Markdown: **bold**, `code`, ```fenced blocks```, > quotes, [links](url). HTML and images are not rendered.