Question
I want to implement an enum-style pattern in Ruby.
What is the best way to represent a fixed set of named values in Ruby so that I can use them similarly to Java or C# enums?
I am looking for an approach that is idiomatic in Ruby while still providing the clarity and safety of enums from statically typed languages.
Short Answer
By the end of this page, you will understand how Ruby handles enum-like values, why Ruby does not have Java-style enums built into the language in the same way, and which Ruby patterns are commonly used instead. You will learn when to use symbols, constants, custom classes, and framework features such as Rails enums, along with the trade-offs of each approach.
Concept
Ruby does not have a built-in enum feature that works exactly like Java or C# enums. Instead, Ruby developers usually model a fixed set of named values using simpler language features.
The core idea of an enum is this:
- You have a limited list of allowed values.
- Each value has a meaningful name.
- You want your code to be readable and less error-prone.
For example, an order status might only allow:
:pending:paid:shipped:cancelled
In Ruby, the most common enum-like choices are:
- Symbols for lightweight named values
- Constants for shared named values
- Arrays or hashes to define allowed options
- Custom classes/modules when you need richer behavior
- Rails
enumif you are using Active Record
Why this matters in real programming:
- It prevents invalid values from being scattered through your code.
- It makes conditions easier to read.
- It centralizes business rules.
- It reduces bugs caused by typos like
"pendng"instead of"pending".
Ruby favors simple, flexible patterns over strict language-level enum syntax. That means the “best” enum approach depends on what you need:
- If you only need named values, use symbols.
- If you need validation, keep a list of allowed values.
- If you need methods on each value, use a class-based approach.
- If you are in Rails models, use
enum.
So the Ruby way is usually not to imitate Java exactly, but to choose the lightest pattern that clearly expresses a fixed set of values.
Mental Model
Think of an enum as a menu with a fixed list of choices.
In Java or C#, the language gives you a special printed menu automatically. In Ruby, you usually create the menu yourself.
For example, imagine a coffee shop where drink sizes can only be:
- small
- medium
- large
You could represent that menu in Ruby as:
- symbols:
:small,:medium,:large - constants:
SMALL,MEDIUM,LARGE - a class if each size has extra behavior, like pricing rules
The important part is not the syntax. The important part is that your program treats the list as fixed and known in advance.
So an enum in Ruby is less like a special language feature and more like a well-organized set of allowed labels.
Syntax and Examples
1. Simple enum-like values with symbols
This is the most idiomatic Ruby approach for many cases.
STATUSES = [:pending, :paid, :shipped, :cancelled].freeze
status = :paid
if STATUSES.include?(status)
puts "Valid status"
else
puts "Invalid status"
end
Explanation:
:paidis a symbol.STATUSESstores the allowed values..freezeprevents accidental modification.include?checks whether the value is allowed.
2. Using constants for named values
module Status
PENDING = :pending
PAID = :paid
SHIPPED = :shipped
CANCELLED = :cancelled
ALL = [, , , ].freeze
status =
puts .?(status)
Step by Step Execution
Consider this example:
module Status
PENDING = :pending
PAID = :paid
SHIPPED = :shipped
ALL = [PENDING, PAID, SHIPPED].freeze
end
status = :paid
if Status::ALL.include?(status)
puts "Accepted: #{status}"
else
puts "Rejected"
end
Step by step:
- Ruby defines a module named
Status. - Inside the module, three constants are created:
Status::PENDINGStatus::PAIDStatus::SHIPPED
ALLstores every valid status in a frozen array.- The variable
statusis assigned the symbol .
Real World Use Cases
Enum-style patterns are common whenever a value must come from a small predefined set.
Common examples
- Order status:
:pending,:paid,:shipped - User roles:
:admin,:editor,:viewer - Payment methods:
:card,:bank_transfer,:cash - Job states:
:queued,:running,:failed,:completed - Environment names:
:development,:test,:production
In scripts
A command-line script might only accept certain modes:
MODES = [:fast, , ].freeze
Real Codebase Usage
In real Ruby codebases, developers often use enum-like patterns in a few practical ways.
1. Constants plus an ALL list
This is a common pattern for validation and clarity.
module Role
ADMIN = :admin
EDITOR = :editor
VIEWER = :viewer
ALL = [ADMIN, EDITOR, VIEWER].freeze
end
Why it is useful:
- avoids magic values spread across files
- gives one source of truth
- works well in validations and forms
2. Guard clauses for validation
def set_status(status)
raise ArgumentError, "Invalid status" unless Status::ALL.include?(status)
@status = status
end
This is a common Ruby style: reject invalid input early.
Common Mistakes
1. Using plain strings everywhere
Broken example:
status = "Paid"
if status == "paid"
puts "OK"
end
Problem:
- string comparisons are easy to break because of case or spelling differences
Better:
status = :paid
2. Not centralizing allowed values
Broken example:
if status == :pending
# ...
elsif status == :paid
# ...
elsif status == :shipped
# ...
end
Problem:
- values are repeated throughout the codebase
- adding a new value becomes harder
Better:
module Status
PENDING = :pending
PAID = :paid
=
Comparisons
| Approach | Best for | Pros | Cons |
|---|---|---|---|
| Symbols in an array | Simple fixed values | Idiomatic, short, easy to use | No extra behavior by default |
| Constants in a module | Named access like Status::PAID | Clear namespacing, readable | Slightly more setup |
| Custom class objects | Values with methods/data | Closest to Java-style enums | More code, more complexity |
| Strings | External input or serialization | Easy to print/store | More typo-prone in internal logic |
Rails enum | Active Record models | Built-in query and helper methods | Rails-specific |
Symbols vs strings
Cheat Sheet
Quick reference
Simple enum-like list
STATUSES = [:pending, :paid, :shipped].freeze
Namespaced constants
module Status
PENDING = :pending
PAID = :paid
SHIPPED = :shipped
ALL = [PENDING, PAID, SHIPPED].freeze
end
Validate a value
Status::ALL.include?(status)
Raise on invalid input
raise ArgumentError, "Invalid status" unless Status::ALL.include?(status)
Class-based enum style
FAQ
Does Ruby have built-in enums like Java or C#?
Not in the same way. Ruby usually uses symbols, constants, arrays, hashes, or framework helpers such as Rails enum.
What is the most idiomatic enum-like approach in Ruby?
For simple cases, symbols are the most idiomatic choice, often combined with a frozen list of allowed values.
Should I use symbols or strings for enum values in Ruby?
Use symbols for internal logic in most cases. Use strings mainly when receiving or sending external data, such as JSON or HTTP parameters.
How can I validate enum values in Ruby?
Store all allowed values in a constant like ALL and use include? to check whether a value is valid.
How do I make Ruby enums look more like Java enums?
Use a module with constants or create a custom class where each enum value is a singleton-like object.
What is the best enum option in Rails?
If the enum is a model attribute stored in the database, use Active Record enum.
Can Ruby enum values have methods?
Yes, but you usually need a custom class or objects instead of plain symbols.
Why should I freeze enum arrays or hashes?
Freezing prevents accidental modification of the shared list of allowed values.
Mini Project
Description
Build a small order status helper for an e-commerce application. The project demonstrates how to represent a fixed set of statuses in idiomatic Ruby, validate inputs, and convert those statuses into user-friendly labels. This is a practical example of how enum-like values are used in real code without needing Java-style language support.
Goal
Create a Ruby module and class that safely manage a fixed list of order statuses and reject invalid values.
Requirements
- Define a fixed set of order statuses using Ruby constants and symbols.
- Store all allowed statuses in one frozen collection.
- Create an
Orderclass that validates the status during initialization. - Add a method that returns a human-readable label for each status.
- Show example usage with both valid and invalid statuses.
Keep learning
Related questions
Calling a Class Method from an Instance in Ruby
Learn how to call a class method from an instance in Ruby using self.class, with examples, pitfalls, and practical usage patterns.
Calling an Overridden Monkey-Patched Method in Ruby
Learn how to call the original method when monkey patching in Ruby, including alias_method patterns, examples, pitfalls, and practical usage.
Convert a Unix Timestamp to Ruby DateTime
Learn how to convert Unix timestamps to Ruby DateTime and Time objects, with examples, differences, pitfalls, and practical Ruby usage.