How Rails inflections work their magic, turning 'UI' into 'Ui'

Written on Oct 24, 2023

Years ago, Phil Karlton mentioned once, during my dad's time at Netscape, he did indeed throw a quote around, on more than one occasion:
There are only two hard things in Computer Science: cache invalidation and naming things

In every developer's journey, there comes a point where we have to grapple with names. Whether it's naming a new startup, a pet project, your kid πŸ˜‰ or simply figuring out what to call that new variable, naming is an art. But what if I told you that Rails has a special magic trick up its sleeve to help with naming? Welcome to the world of Rails inflections.

1. Setting the Stage, Why Inflections?

Imagine working on an app, and suddenly you find yourself fumbling around with pluralizations and singularizations. You might think, Okay, I'll just add an 's' at the end, and call it a day but then irregular words like person, wolf, loaf, tooth or even regular words in the same case NASA throw a wrench in your plans. That's where Rails' inflections come in.

Inflections in Rails are all about rules. Rules that define how words change their form, especially in the context of pluralization and singularization. And as with all things Rails, there's a convention for that. You've got to appreciate and love convention ❀️ πŸ’Ž

Picture this, I was on a quest to create a namespaced view component, something like:

rails generate component UI::Container

Expecting Rails to generate a neat UI::ContainerComponent, I was left scratching my head when I saw this instead:

module Ui
  class ContainerComponent < ViewComponent::Base
    ...
  end
end

Ui? I thought. I explicitly said 'UI'!

2. Rails' Inflection Shenanigans

For those new to the term, inflection in Rails refers to the ways our beloved framework handles singulars, plurals, acronyms, and other linguistic peculiarities. It's what magically gives us person -> people or mouse -> mice. But sometimes, this magic can feel more like a trick!

By default, Rails doesn't treat UI as an acronym but as any other word. So, in its quest to be helpful, it transformed UI to camel-cased Ui!"

3. Taking Back Control with Custom Inflections

Determined to have my component named UI::ContainerComponent, I ventured into Rails' inflection rules. And voila! A simple addition to the inflections initializers config/initializers/inflections.rb did the trick:

ActiveSupport::Inflector.inflections(:en) do |inflect|
  inflect.acronym "UI"
end

With this in place, Rails recognized UI as an all-uppercase acronym. Problem solved!

4. Delving into ActiveSupport::Inflector

Rails provides a multitude of tools under the hood to make our lives easier, and one of those tools is the ActiveSupport::Inflector module. If you've ever wondered how Rails magically handles word transformations, pluralizations, singularizations, and the like, this module is the sorcerer behind the curtain.

01 - A Peek into Inflections

Inflections, in the linguistic sense, refer to the modifications of a word to express different grammatical categories. In Rails, inflections help in handling words that don't follow the typical pluralization or singularization rules and other word-based transformations.

module ActiveSupport
  module Inflector
    extend self
    # ...
  end
end

02 - The Power of the Inflector Class

At the core of the Inflector module is the Inflections class. It's a rule book that Rails consults every time it needs to transform words. This class holds rules for pluralizations, singularizations, acronyms, and more. Developers can extend or modify the default behaviour to cater to specific needs, ensuring Rails can handle a vast array of word transformations.

class Inflections
  @__instance__ = Concurrent::Map.new
  # ... (the rest of the class)
end

Here's a quick glance at some of the key methods in the Inflections class:

  • acronym(word): It identifies terms that should remain uppercase.

    ActiveSupport::Inflector.inflections do |inflect|
      inflect.acronym 'API'
    end
    

    Now, camelize('api_controller') will result in 'APIController'.

  • plural(rule, replacement): Guides Rails in pluralizing words.

    ActiveSupport::Inflector.inflections do |inflect|
      inflect.plural /^(ox)$/i, '\1en'
    end
    

    This will pluralize ox to oxen.

  • singular(rule, replacement): Assists Rails in singularizing words.

    ActiveSupport::Inflector.inflections do |inflect|
      inflect.singular /^(ox)en/i, '\1'
    end
    

    This transforms oxen back to ox.

  • irregular(singular, plural): Handles words that defy typical pluralization patterns.

    ActiveSupport::Inflector.inflections do |inflect|
      inflect.irregular 'person', 'people'
    end
    

    This ensures person pluralizes to people and not persons.

  • uncountable(words): Informs Rails about words that don't have a plural form.

    ActiveSupport::Inflector.inflections do |inflect|
      inflect.uncountable 'information'
    end
    

    This ensures information remains information even when pluralized.

By understanding and using these methods, you can ensure that Rails handles word transformations in the way you intend.

03- Extending the Inflector's Reach

The inflections method in the ActiveSupport::Inflector module allows developers to add or modify inflection rules based on their needs, even for different locales.

def inflections(locale = :en)
    if block_given?
        yield Inflections.instance(locale)
    else
        Inflections.instance_or_fallback(locale)
    end
end

5. Customizing Inflection Rules

Not all words follow the standard English rules for pluralization or singularization. And here's where the true power of Rails inflections shines.

- Irregular Inflections:

"foot".pluralize  # => "foots"  # Oops!

# Let's fix this in config/initializers/inflections.rb
ActiveSupport::Inflector.inflections(:en) do |inflect|
  inflect.irregular "foot", "feet"
end

"foot".pluralize  # => "feet"  # That's better!

- Uncountable Inflections:
Some words just don't like to be counted. For instance, milk. Using inflections, you can specify such words.

"milk".pluralize  # => "milks"  # Wait, what?

ActiveSupport::Inflector.inflections(:en) do |inflect|
  inflect.uncountable "milk"
end

"milk".pluralize  # => "milk"  # Perfect!

- Acronym Inflections:
Remember our little UI vs. Ui debacle? Acronym inflections come to the rescue here.

"html5".camelize  # => "Html5"

ActiveSupport::Inflector.inflections(:en) do |inflect|
  inflect.acronym "HTML5"
end

"html5".camelize  # => "HTML5"

6. Further Reading and Exploration

For those keen on diving deeper into Rails inflections, the Rails documentation and Inflector API are a goldmine. Additionally, I recommend exploring the Rails source code to understand the intricacies of the ActiveSupport::Inflector module. It's not just educational; it's a testament to the elegant design of Rails.

7. Pro Tips for the Avid Rails Developer

  1. Explore Before You Define: Before you rush to define a new inflection, ensure it's not already handled by Rails.
  2. Comment Your Custom Inflections: If you're adding a custom rule, a brief comment explaining the rationale can be invaluable for your future self and fellow developers.
  3. Less is More: While the power to define any word behavior is tempting, over-customization can lead to confusing code. Stick to the necessary.

8. Conclusion

Rails, in its essence, is designed to make developers' lives easier. Its convention-over-configuration philosophy ensures that you're up and running in no time. However, with every convention, there are occasional outliers. Inflections, a seemingly minor feature, play a pivotal role in addressing these nuances.

From guiding Rails on how to handle irregular words to ensuring that acronyms are treated right, inflections become an indispensable tool in a developer's arsenal. The ActiveSupport::Inflector module is like a Swiss Army knife, offering a plethora of methods to juggle words in ways you never imagined possible.

But as with all powerful tools, it's essential to wield them with care. Over-reliance or unnecessary customization can muddy the waters, making it harder for fellow developers to navigate your codebase. It's always a balance between convention and customization.

Until next time, Happy Coding πŸ§‘πŸ½β€πŸ’» == Happy days 😁

Subscribe to Design and Develpment ideas

Enter your email to subscribe to a once-monthly newsletter curating the latest content on Rails, Hotwire, and other things you might find interesting.

Get in touch