String Inflectors: bring a bit of Rails into JavaScript

The code from this article was taken from the book JavaScript for Rails Developers. Get your copy today! 🧑‍💻


Ruby developers working with JavaScript often miss the convenience of Ruby’s string manipulation methods. While Ruby (Rails) spoils us with elegant transformations like "user_name".camelize, JavaScript requires you to roll our own helpers or reach for external dependencies.

This article explores creating a lightweight collection of JavaScript string helpers, inspired by Rails’ ActiveSupport inflectors, instead of adding yet another package to your project. Let’s look at how to add these helpers in JavaScript and use them in your Rails app.

From snake_case to camelCase

In Rails, you can easily convert strings with String#camelize. Let’s implement a JavaScript equivalent:

// app/javascript/helpers/stringInflectors.js
export const camelize = (text) => {
  return text
    .replace(/[-_\s]+(.)?/g, (_, character) => character ? character.toUpperCase() : "")
    .replace(/^[A-Z]/, character => character.toLowerCase())
}

This function transforms underscored or hyphenated strings into camelCase format. The first regex replaces any dash, underscore, or space followed by a character with the uppercase version of that character. The second regex ensures the first character is lowercase, giving us proper camelCase.

Small aside: The _ in (_, character) denotes an unused parameter. This is a common JavaScript convention for parameters that must be included in the function signature but aren’t used in the implementation.

Now how to use it?

// app/javascript/controllers/form_controller.js
import { Controller } from "@hotwired/stimulus"
import { camelize } from "../helpers/stringInflectors"

export default class extends Controller {
  connect() {
    const propertyName = camelize("user_name") // => "userName"

    // Use the camelized property name
    this.element.dataset[propertyName] = "value"
  }
}

ordinalize: human-friendly numbers

Let’s look at a more specialized helper: ordinalize, which adds the appropriate suffix to numbers (1st, 2nd, 3rd, etc.).

In Rails, this functionality is provided by ActiveSupport::Inflector.ordinalize or the Integer#ordinalize method. The implementation logic in JavaScript is nearly identical:

export const ordinalize = (number) => {
  const mod100 = number % 100

  if (mod100 >= 11 && mod100 <= 13) return `${number}th`

  switch (number % 10) {
    case 1: return `${number}st`
    case 2: return `${number}nd`
    case 3: return `${number}rd`
    default: return `${number}th`
  }
}

This function handles the special cases for numbers ending in 11, 12, and 13 (which all use th), then applies the appropriate suffix based on the last digit.

Another small aside: The % (modulo) operator returns the remainder after division. Using number % 100 extracts just the last two digits of any number, which is perfect for handling ordinal suffix rules.

For a practical example using this helper in a Stimulus controller:

// app/javascript/controllers/leaderboard_controller.js
import { Controller } from "@hotwired/stimulus"
import { ordinalize } from "../helpers/stringInflectors"

export default class extends Controller {
  static targets = ["rank"]

  displayRank(position) {
    this.rankTarget.textContent = ordinalize(position)
  }
}

Add even more helpers

This is the complete module, that comes with the Pro package of the book. It includes several other useful transformations:

  • underscore: Converts camelCase to snake_case (Rails: String#underscore)
  • dasherize: Converts underscores to dashes (Rails: String#dasherize)
  • humanize: Capitalizes first word and converts underscores to spaces (Rails: String#humanize)
  • titleize: Capitalizes all words (Rails: String#titleize)
  • downcase and upcase: Simple case converters (Rails: String#downcase, String#upcase)
  • parameterize: Makes strings URL-friendly (Rails: String#parameterize)
// app/javascript/helpers/stringInflectors.js
export const camelize = (text) => {
  return text
    .replace(/[-_\s]+(.)?/g, (_, character) => character ? character.toUpperCase() : "")
    .replace(/^[A-Z]/, character => character.toLowerCase())
}

export const underscore = (text) => {
  return text
    .replace(/([A-Z])/g, "_$1")
    .replace(/[-\s]+/g, "_")
    .toLowerCase()
    .replace(/^_/, "")
}

export const dasherize = (text) => {
  return text
    .replace(/([A-Z])/g, "-$1")
    .replace(/[_\s]+/g, "-")
    .toLowerCase()
    .replace(/^-/, "")
}

export const humanize = (text) => {
  return underscore(text)
    .replace(/_id$/, "")
    .replace(/_/g, " ")
    .replace(/^[a-z]/, character => character.toUpperCase())
}

export const titleize = (text) => {
  return humanize(text).replace(/\b[a-z]/g, character => character.toUpperCase())
}

export const downcase = (text) => text.toLowerCase()
export const upcase = (text) => text.toUpperCase()

export const parameterize = (text) => {
  return text
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, "-")
    .replace(/^-|-$/g, "")
}

export const ordinalize = (number) => {
  const mod100 = number % 100

  if (mod100 >= 11 && mod100 <= 13) return `${number}th`

  switch (number % 10) {
    case 1: return `${number}st`
    case 2: return `${number}nd`
    case 3: return `${number}rd`
    default: return `${number}th`
  }
}

export default {
  camelize,
  underscore,
  dasherize,
  humanize,
  titleize,
  downcase,
  upcase,
  parameterize,
  ordinalize
}

To use these helpers in your Rails application, you can import specific helpers:

import { camelize, ordinalize } from "../helpers/stringInflectors"
// Usage: camelize("user_name")

Or import everything as a namespace:

import * as inflector from "../helpers/stringInflectors"
// Usage: inflector.camelize("user_name")

If you’re using importmap-rails, add this to your config/importmap.rb:

pin_all_from "app/javascript/helpers", under: "helpers"

Interested in writing and understanding code like this yourself? I recently wrote the book JavaScript for Rails Developers. It is already read by hundreds of (Rails) developers like you. 😊 This code snippet was included with it along with many other practical, code examples and a practical step by step explanation of writing beautiful, modern JavaScript code.

Published at . Have suggestions or improvements on this content? Do reach out.

More articles like this on modern Rails & frontend? Get them first in your inbox.
JavaScript for Rails Developers
Out now

UI components Library for Ruby on Rails apps

$ 99 one-time
payment

View components