Pallet Town: Rails Concerns

Sep 20, 2024 | Programming, Ruby

As you dive deeper into Ruby on Rails, you might notice your models and controllers starting to bloat with methods and logic. This not only makes your code harder to read but also tougher to maintain. Enter Rails Concerns—a powerful tool to keep your code a little more on the DRY (Don’t Repeat Yourself) side of life. As always, these Pallet Town posts are meant for developers starting out on their journey, so more experienced coders may find the samples a bit contrived and an overall general focus on simplicity and generalizations; that’s intentional to make the concepts easier for new folks to grasp.

What Are Concerns?

Rails Concerns are a way to extract shared code from models or controllers into re-usable modules. Think of them as mixins that encapsulate behavior which can be included in multiple classes. Mechanically Concerns are just Ruby modules. However, Rails provides some syntactic sugar to make them easier to work with, especially when dealing with ActiveRecord models or ActionController controllers. If you have any experience with languages like Java or C#, you might mistakenly think that Concerns are effectively the same as Interfaces. That’s not right. While Interfaces do allow a type of sharing between classes, they do not include the actual implementations for their methods. Concerns do. Classes using the same concern also have the same implementation; that’s actually the main point of concerns.

Why So Concerned?

As your application grows, you’ll often find that multiple models or controllers share similar methods or logic. Copying and pasting code isn’t ideal—it leads to duplication and makes maintenance a nightmare. Concerns solve this problem by allowing you to:

  • DRY Up Your Code: Reduce repetition by extracting common functionality.
  • Improve Readability: Keep your classes focused on their primary responsibilities.
  • Enhance Maintainability: Update shared behavior in one place which trust me will save you a lot of headaches as your client or you decided you want to ever so slightly tweak the implementation of some functionality that crosses multiple classes roughly two-hundred times.

Model Concerns in Action

Suppose you’re building a Pokédex in Rails and want to track XP gained by your various Pokemon as you stack those wins like Ash himself.

class Pikachu < ApplicationRecord def calculate_xp(battles_won) self.xp = battles_won * 50 end end class Bulbasaur < ApplicationRecord def calculate_xp(battles_won) self.xp = battles_won * 50 end end

Here, both Pikachu and Bulbasaur have the same calculate_xp method. This duplication can be eliminated using a Concern like so:

module XpGainable
  extend ActiveSupport::Concern

  def calculate_xp(battles_won)
    self.xp = battles_won * xp_multiplier
  end

  def xp_multiplier
    50
  end
end

This would be pathed like so: `app/models/concerns/xp_gainable.rb.

From there its a Magicarp-simple matter of just including the concern in the respective models and voila they both have the calculate_xp method.

class Pikachu &lt; ApplicationRecord
  include XpGainable
end

class Bulbasaur &lt; ApplicationRecord
  include XpGainable
end

This approach has allowed us to centralize our XP logic and avoid code duplication. While admittedly a simple example, it does illustrate the point.

It’s much the same for Controllers, but you get the added benefit of being able to take advantage of other built-in Rails-goodness such as before_action. Let’s make sure those filthy casuals actually have earned the appropriate gym badges before allowing them to challenge or pals The Elite Four:

module Authenticatable
  extend ActiveSupport::Concern

  included do
    before_action :authenticate_trainer
    before_action :check_badges
  end

  private

  def authenticate_trainer
    # Authentication logic here
    redirect_to login_path unless current_trainer
  end

  def check_badges
    required_badge = self.class::REQUIRED_BADGE
    unless current_trainer.badges.include?(required_badge)
      redirect_to badges_path, alert: "You need the #{required_badge} badge to access this area!"
    end
  end
end

As you can see, we have another concern but this time we are also hooking into the before_action Controller functionality. Here’s our Poke-Gatekeeping in practice:

class GymLeadersController &lt; ApplicationController
  include Authenticatable
  REQUIRED_BADGE = 'Rainbow Badge'
  # Rest of your code
end

class EliteFourController &lt; ApplicationController
  include Authenticatable
  REQUIRED_BADGE = 'Earth Badge'
  # Rest of your code
end

I hope this has helped you get a basic understanding of what Rails Concerns are. I also want to give you a word of caution. Concerns should not be overused and there are some experienced Rails developers who don’t approve of them at all. For my part, I think they are a useful tool just as cinnamon is a useful seasoning – when used sparingly and only when absolutely called for. If you have any questions, please feel free to reach out on LinkedIn and do checkout Alice for any automation or ETL needs your organization might have.

More from Mike:

About Me

Hi! I’m Mike! I’m a software engineer who codes at The Mad Botter INC. You might know me from Coder Radio or The Mike Dominick Show.  Drop me a line if you’re interested in having some custom mobile or web development done.

Follow Me

© 2024 Copyright Michael Dominick | All rights reserved