Question

Convert CamelCase to snake_case in Ruby with underscore

rubyruby-on-railsstringformattingcase-conversion

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.

Take Quiz

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:

  1. input stores the string "OrderItem".
  2. input.underscore examines the capital-letter word boundary between Order and Item.
  3. It inserts _ at that boundary and lowercases the text.
  4. result becomes "order_item".
  5. puts result prints:
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::InvoiceGenerator conventionally lives at billing/invoice_generator.rb.
  • Database naming: A model-related name such as UserProfile corresponds naturally to user_profile naming.
  • 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_case strings.

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
Take Quiz

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

ApproachBest forExampleNotes
String#underscore from ActiveSupportRails applications and projects already using ActiveSupport"UserProfile".underscoreHandles common Rails naming conventions, namespaces, and many acronym cases.
Plain Ruby helper methodSmall scripts or gems without ActiveSupportsnake_case("UserProfile")You choose and maintain the conversion rules.
Manual gsub at each call siteOne-off experiments onlytext.gsub(...)Repetition makes behavior inconsistent; extract a helper instead.
camelizeThe reverse direction"user_profile".camelizeConverts 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.

Take Quiz

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, not to_underscore.
  • underscore comes from ActiveSupport, not Ruby's standard String API.
  • :: 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"
Take Quiz

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.

Take Quiz

Keep learning

Related questions

View more