Clean up HTML class attributes in Ruby on Rails

I keep on discovering handy view helpers in Ruby on Rails while developing my SaaS. A few weeks back I remembed cycle and last week I discovered a new helper that was introduced in Ruby on Rails 6.1: class_names.

Beautify your views

Most of us will probably recognize the class name spaghetti that starts to arise when your project grows. Things like this will become littered throughout your views:

<li class="item <%= 'active' if @active %>
    <%= 'disabled' if @unpaid_user %>">

Not exactly readable anymore, right? Well, thats what class_names is trying to fix. You just pass it a hash with the class names as keys and conditions as values. Look how nice!

<li class="<%= classs_name item: 1, active: @active, disabled: @unpaid_user ">

You sometimes even need an inline ternary operator:

<li class="<%= @verified_user ? 'verified' : 'unverified' %>">

This can be cleaned up as well:

<li class="<%= class_names verified: @verified, unverified: !@verified %>">

A final improvement might be done by using the tag helper:

tag.li class: class_names(verified: @verified, unverified: !@verified) do
  # content
end

What if your project uses an older Ruby on Rails version?

The great thing about these helpers is that they are so simple that you can easly backport them to older releases yourself. Something like this in app/helpers/class_names_helper.rb should work on older releases:

module ClassNamesHelper
  def build_tag_values(*args)
    tag_values = []

    args.each do |tag_value|
      case tag_value
      when Hash
        tag_value.each do |key, val|
          tag_values << key.to_s if val && key.present?
        end
      when Array
        tag_values.concat build_tag_values(*tag_value)
      else
        tag_values << tag_value.to_s if tag_value.present?
      end
    end

    tag_values
  end

  def class_names(*args)
    tokens = build_tag_values(*args).flat_map { |value| value.to_s.split(/\s+/) }.uniq

    safe_join(tokens, " ")
  end
end

Discussion

Let me know if you found this post useful!