🚀 Understanding Rails Folders & Files: Concerns, Services, Helpers, Initializers, and Lib

Lakhveer Singh Rajput
CodeX
Published in
3 min readAug 29, 2024

Ruby on Rails, often celebrated for its simplicity and convention over configuration, organizes code into specific folders to maintain structure and clarity. Among these, folders like concerns, services, helpers, lib, and config/intializerserve distinct purposes. Knowing when and how to use them is crucial for building clean, maintainable applications. Let's dive deep into each of these folders, their use cases, and how they differ from one another.

🛠️ Concerns

Purpose:
The concerns folder in Rails is used to share reusable code across models or controllers(Both can have separate concernsfolders). If you find yourself repeating the same logic across multiple models or controllers, that's a good indication that the code might belong in a concern.

Example:
Suppose you have a Taggable module that you want to use in both your Article and Comment models. Instead of duplicating the code, you can place it in a concern. Here we had extended ActiveSupport::Concern a module that will be helpful to include the associations, validations, etc by providing the method accessible in Model.

# app/models/concerns/taggable.rb
module Taggable
extend ActiveSupport::Concern

included do
has_many :tags
end

def tag_list
tags.map(&:name).join(", ")
end
end

# app/models/article.rb
class Article < ApplicationRecord
include Taggable
end

# app/models/comment.rb
class Comment < ApplicationRecord
include Taggable
end

Key Points:

  • Concerns are meant for shared behavior across models or controllers.
  • They promote DRY (Don’t Repeat Yourself) principles by encapsulating common logic.

⚙️ Services

Purpose:
The services folder is where you place service objects. A service object is responsible for a single unit of work or business logic that doesn’t fit neatly into your models or controllers. This can include complex operations like payment processing, sending emails, or interacting with third-party APIs.

Example:
Imagine you need to handle user registrations with multiple steps, such as creating a user, sending a welcome email, and adding them to a mailing list.

# app/services/user_registration_service.rb
class UserRegistrationService
def initialize(user_params)
@user_params = user_params
end

def call
user = create_user
send_welcome_email(user)
add_to_mailing_list(user)
end

private

def create_user
User.create(@user_params)
end

def send_welcome_email(user)
UserMailer.welcome_email(user).deliver_now
end

def add_to_mailing_list(user)
MailingList.add(user.email)
end
end

# Usage in a controller
UserRegistrationService.new(params[:user]).call

Key Points:

  • Service objects are used to encapsulate business logic or workflows.
  • They help keep models and controllers lean and focused on their primary responsibilities.

🧰 Helpers

Purpose:
Helpers in Rails are used to extract complex view logic from your templates. They allow you to create methods that can be used across views to format data, generate HTML, or handle repetitive tasks.

Example:
Suppose you have a method that generates a formatted date string for various views.

# app/helpers/application_helper.rb
module ApplicationHelper
def formatted_date(date)
date.strftime("%B %d, %Y")
end
end

# In your view
<%= formatted_date(@article.created_at) %>

Key Points:

  • Helpers are intended for view-related logic.
  • They keep your views clean and readable by moving complex logic into helper methods.

📚 Lib

Purpose:
The lib folder in Rails is a general-purpose directory for placing any code that doesn't fit into the standard Rails MVC structure. This can include custom libraries, modules, or third-party integrations.

Example:
Let’s say you have a custom utility class for generating unique identifiers across your application.

# lib/unique_identifier_generator.rb
class UniqueIdentifierGenerator
def self.generate
SecureRandom.hex(10)
end
end

# Usage in a model
class Order < ApplicationRecord
before_create :set_unique_identifier

private

def set_unique_identifier
self.identifier = UniqueIdentifierGenerator.generate
end
end

Key Points:

  • The lib folder is a catch-all for custom libraries or classes.
  • It’s useful for organizing code that doesn’t belong in the MVC layers.

🔄 Initializers

Purpose:
The config/initializers directory is where you place code that needs to be run when your application starts. This includes configuring libraries, setting global settings, or extending built-in Rails classes.

Example:
If you want to set up a custom time zone or configure a gem like Devise, you will do so in an initializer.

# config/initializers/time_zone.rb
Rails.application.config.time_zone = 'Central Time (US & Canada)'

# config/initializers/devise.rb
Devise.setup do |config|
config.mailer_sender = 'please-change-me-at-config-initializers-devise@example.com'
end

Key Points:

  • Initializers are run at startup and are crucial for setting up your application’s configuration.
  • They are often used for third-party gem configurations and global settings.

🧩 Concerns vs. Services vs. Helpers vs. Lib

  • Concerns are for shared model/controller logic.
  • Services encapsulate business logic or workflows outside the MVC layers.
  • Helpers focus on simplifying view logic.
  • Lib is a general-purpose directory for custom code that doesn’t fit into the standard Rails structure.

🚀 Conclusion

Rails provides these directories to help you organize your code in a way that promotes clarity, maintainability, and adherence to the MVC pattern. By understanding the use cases and differences between concerns, services, helpers, and the lib folder, you can structure your application more effectively, making it easier to maintain and scale.

Happy coding! 😄

--

--

Lakhveer Singh Rajput
CodeX
Writer for

Ruby on Rails enthusiast, book lover and DevOps explorer. Follow me for insights on coding, book recommendations, and bridging development with operations.🚀📚