Question
Convert CamelCase to snake_case in Ruby with underscore
Question
Is there a built-in or ready-to-use Ruby function that converts a CamelCase string into an underscore-separated string?
For example:
"CamelCaseString".to_underscore
I want it to return:
"camel_case_string"
Short Answer
You will learn how Ruby on Rails converts CamelCase names to snake_case with ActiveSupport's underscore method, when that method is available, and how to implement a small plain Ruby alternative when Rails is not being used.
Concept
Ruby itself does not define a String#underscore method. In Rails applications, however, ActiveSupport adds inflection helpers to strings, including underscore.
"CamelCaseString".underscore
# => "camel_case_string"
This conversion is commonly called underscore case or snake_case. It changes word boundaries in a class-style name into underscores and makes the result lowercase.
"UserProfile".underscore
# => "user_profile"
The conversion matters because Ruby conventions use different naming styles for different things:
- Classes and modules usually use
CamelCase:UserProfile - Methods, variables, file names, and database columns usually use
snake_case:user_profile
Rails uses these conventions heavily when connecting class names, file paths, routes, table names, and other framework features.
Mental Model
Think of a CamelCase name as words joined together with capital letters acting as separators:
Camel Case String
underscore reads each capital-letter boundary, inserts an underscore, and then changes all letters to lowercase:
CamelCaseString
camel_case_string
It is like taking a title written without spaces and relabelling it for a filename or database column where underscores are easier to read.
Syntax and Examples
In a Rails application, or in a Ruby script that loads ActiveSupport inflections, call underscore directly on the string.
"CamelCaseString".underscore
# => "camel_case_string"
"UserProfile".underscore
# => "user_profile"
"Admin::UserProfile".underscore
# => "admin/user_profile"
If you are outside Rails, require the specific ActiveSupport feature first:
require "active_support/core_ext/string/inflections"
puts "CamelCaseString".underscore
# camel_case_string
The method is named underscore, not to_underscore:
"CamelCaseString".underscore
# => "camel_case_string"
For a small plain Ruby solution, define a method instead of modifying String:
def snake_case(text)
text
.gsub(/([a-z\d])([A-Z])/, "\\1_\\2")
.gsub(/([A-Z]+)([A-Z][a-z])/, "\\1_\\2")
.downcase
end
snake_case("CamelCaseString")
# => "camel_case_string"
snake_case("HTMLParser")
# => "html_parser"
Step by Step Execution
Consider this ActiveSupport example:
require "active_support/core_ext/string/inflections"
input = "OrderItem"
result = input.underscore
puts result
Execution proceeds as follows:
inputstores the string"OrderItem".input.underscoreexamines the capital-letter word boundary betweenOrderandItem.- It inserts
_at that boundary and lowercases the text. resultbecomes"order_item".puts resultprints:
order_item
With a namespace:
"Admin::OrderItem".underscore
# => "admin/order_item"
ActiveSupport converts Ruby's module separator (::) into /, which is useful when deriving a path from a constant name.
Real World Use Cases
- Rails file paths: A class named
Billing::InvoiceGeneratorconventionally lives atbilling/invoice_generator.rb. - Database naming: A model-related name such as
UserProfilecorresponds naturally touser_profilenaming. - API field normalization: Convert incoming class-like identifiers into predictable keys or labels.
- Code generators: Generate Ruby file names from class names entered by a developer.
- Logging and configuration: Store readable identifiers consistently as
snake_casestrings.
For example, a generator can derive a filename from a requested class name:
require "active_support/core_ext/string/inflections"
class_name = "PaymentReceipt"
file_name = "#{class_name.underscore}.rb"
puts file_name
# payment_receipt.rb
Real Codebase Usage
In Rails codebases, developers usually rely on ActiveSupport inflections rather than writing regular expressions repeatedly.
A common pattern is to convert a trusted class or resource name into a conventional file-oriented value:
require "active_support/core_ext/string/inflections"
def serializer_path(class_name)
"app/serializers/#{class_name.underscore}_serializer.rb"
end
serializer_path("Admin::User")
# => "app/serializers/admin/user_serializer.rb"
When accepting external input, validate it before using its converted form in a path, query, or command. underscore changes formatting; it does not make input safe.
ALLOWED_RESOURCES = ["UserProfile", "OrderItem"].freeze
def resource_key(name)
return nil unless ALLOWED_RESOURCES.include?(name)
name.underscore
end
In non-Rails libraries, avoid globally adding methods to core classes unless that is an intentional project-wide decision. A standalone method such as snake_case(text) is often clearer and has fewer side effects.
Common Mistakes
Calling to_underscore
ActiveSupport uses underscore, not to_underscore.
# Incorrect
"CamelCaseString".to_underscore
# NoMethodError
# Correct
"CamelCaseString".underscore
# => "camel_case_string"
Assuming Ruby core provides the method
This fails in plain Ruby unless ActiveSupport has been loaded:
"CamelCaseString".underscore
# NoMethodError: undefined method `underscore'
Load the extension when ActiveSupport is installed:
require "active_support/core_ext/string/inflections"
Requiring all of Rails for a small script
For a standalone script, prefer the specific ActiveSupport extension instead of loading an entire framework.
Using an overly simple regular expression
This version works for many basic cases but handles acronyms poorly:
def snake_case(text)
text.gsub(/([a-z])([A-Z])/, "\\1_\\2").downcase
end
snake_case("HTMLParser")
# => "htmlparser" # Missing underscore
Use ActiveSupport or a more complete implementation when inputs can contain acronyms such as HTMLParser or APIClient.
Expecting arbitrary text to become valid Ruby names
underscore formats a string, but it does not validate identifiers. For example, spaces and punctuation may still need separate handling depending on your goal.
Comparisons
| Approach | Best for | Example | Notes |
|---|---|---|---|
String#underscore from ActiveSupport | Rails applications and projects already using ActiveSupport | "UserProfile".underscore | Handles common Rails naming conventions, namespaces, and many acronym cases. |
| Plain Ruby helper method | Small scripts or gems without ActiveSupport | snake_case("UserProfile") | You choose and maintain the conversion rules. |
Manual gsub at each call site | One-off experiments only | text.gsub(...) | Repetition makes behavior inconsistent; extract a helper instead. |
camelize | The reverse direction | "user_profile".camelize | Converts snake_case to CamelCase; it is also an ActiveSupport method. |
underscore is not the same as replacing spaces with underscores. Its main job is to identify word boundaries in CamelCase names.
Cheat Sheet
# Rails / ActiveSupport
require "active_support/core_ext/string/inflections"
"CamelCaseString".underscore
# => "camel_case_string"
"UserProfile".underscore
# => "user_profile"
"HTMLParser".underscore
# => "html_parser"
"Admin::UserProfile".underscore
# => "admin/user_profile"
# Reverse conversion
"user_profile".camelize
# => "UserProfile"
Rules to remember:
- The method name is
underscore, notto_underscore. underscorecomes from ActiveSupport, not Ruby's standardStringAPI.::is converted to/by ActiveSupport.- Use a dedicated helper method if your project does not depend on ActiveSupport.
- Formatting text does not validate or sanitize untrusted input.
FAQ
Does Ruby have a built-in underscore method?
No. String#underscore is supplied by ActiveSupport, which is included with Rails but is not part of Ruby core.
What is the correct method: underscore or to_underscore?
The ActiveSupport method is underscore.
"CamelCaseString".underscore
How do I use underscore outside Rails?
Install ActiveSupport and require its string inflections extension:
require "active_support/core_ext/string/inflections"
Does underscore handle acronyms such as HTMLParser?
Yes, ActiveSupport is designed to handle common CamelCase and acronym boundaries:
"HTMLParser".underscore
# => "html_parser"
Why does Admin::User become admin/user?
ActiveSupport treats :: as a Ruby namespace separator and converts it to /, which matches conventional directory paths.
Can I convert snake_case back to CamelCase?
Yes, with ActiveSupport's camelize method:
"user_profile".camelize
# => "UserProfile"
Mini Project
Description
Build a small filename helper for a Ruby code generator. Given a class name, it should produce the conventional Ruby source filename. This demonstrates converting CamelCase names into snake_case and handling namespaced classes.
Goal
Create a method that converts a class name such as Admin::OrderItem into admin/order_item.rb.
Requirements
Use ActiveSupport's underscore method.
Require only the ActiveSupport string inflections extension.
Accept a class name as a method argument.
Return a path ending in .rb.
Demonstrate the method with both a normal class name and a namespaced class name.
Keep learning
Related questions
Add a Default Value to an Existing Column in a Rails Migration
Learn how to add or change a default value on an existing database column using Rails Active Record migrations safely.
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.