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 < ApplicationRecord include XpGainable end class Bulbasaur < 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 < ApplicationController include Authenticatable REQUIRED_BADGE = 'Rainbow Badge' # Rest of your code end class EliteFourController < 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.