What makes a constant

Free preview 4 min to read

An eight, spelled in capital letters

This app's Auth::PasswordPolicy opens with three lines that decide how every password in the app is validated:

module Auth
  class PasswordPolicy
    MIN_LENGTH = 8
    MAX_LENGTH = 128
    FORMAT = /\A(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^a-zA-Z\d\s]).+\z/
  end
end

MIN_LENGTH, MAX_LENGTH and FORMAT are constants. Nothing about the syntax is special: they're assigned with the same = as any local variable. Ruby treats them differently because of one rule: a name that starts with an uppercase letter is a constant. min_length would be a local variable and MIN_LENGTH is a constant, and the interpreter decides that when it parses the name, with no separate declaration.

Why not write 8?

Every place that checks a password's length reads Auth::PasswordPolicy::MIN_LENGTH instead of the literal 8. That's the point: one name, defined once, read everywhere. If the policy moves to a ten-character minimum next quarter, one line changes. Every validation, error message and test that refers to MIN_LENGTH picks up the new value. A bare 8 scattered across a dozen files would need a dozen edits, and someone would miss one.

The naming convention: SCREAMING_SNAKE_CASE

Ruby only requires the first letter to be uppercase. The community convention for constants that hold a fixed value is SCREAMING_SNAKE_CASE, all caps with words separated by underscores: MIN_LENGTH, not MinLength or Minlength. Class and module names are also constants in Ruby (more on that later in this course), and they follow a different convention, CamelCase, like PasswordPolicy itself. Both are constants; the casing tells you which kind you're looking at.

Reassigning a constant warns; it doesn't raise

Run this and Ruby doesn't stop you:

X = 1
X = 2
# warning: already initialized constant X
# warning: previous definition of X was here

Both warnings print, X ends up 2, and the program keeps running. That surprises people coming from languages where reassigning a constant is a compile error. Ruby's constants are a naming convention backed by a warning, not a rule the interpreter enforces. The warning goes to stderr even without Ruby's -w flag, so it's easy to miss in a busy log. It's there to catch the mistake of redefining something meant to stay fixed.

Constants can live at any level

MIN_LENGTH lives inside Auth::PasswordPolicy, scoped to that class. A constant can also live at the top level of a file, outside any class or module. For anything beyond a one-off script, though, that risks colliding with another top-level constant somewhere in a large app. Namespacing constants inside a module or class, as PasswordPolicy does, is the usual way to avoid the collision. A later lesson comes back to this when it looks at how Ruby finds a constant.

What this course covers

The lesson "Modules as namespaces", in Ruby modules: include/extend, showed Catalog::Course using :: to group one business area's classes under a shared name. This course builds on that and goes where that lesson didn't. It covers what a constant is, why reassigning one only warns, and why a constant can still be mutated despite its name. Then come the order Ruby searches when it looks a constant up, and const_get/const_set for reaching a constant by a name computed at runtime. The last lessons tie it back to a file in this app, app/services/auth.rb, and to a surprise about how Rails autoloading treats it.

Key takeaways

  • A constant is any name that starts with an uppercase letter; Ruby decides this when it parses the code, with no separate declaration.
  • SCREAMING_SNAKE_CASE is the convention for value-holding constants; CamelCase is the convention for classes and modules, which are constants too.
  • Reassigning a constant prints warning: already initialized constant X and keeps running; it doesn't raise.
  • Auth::PasswordPolicy::MIN_LENGTH is this app's single source for password length, read everywhere instead of a repeated magic number.
Lesson 1 of 7 · free

Keep going with lesson 2

Constants can still be mutated

Changing the object a constant points to gives no warning, so Array and Hash constants need .freeze.

A free account also gives you the quizzes, your progress and notes, and the discussion.

Type to start searching...