The code from this article, originally published on Rails Designer, 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
andupcase
: 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.
Top comments (0)