The secret of Decorator pattern

The secret of Decorator pattern

Introduction

In Ruby on Rails, clean and modular code is crucial for building scalable, maintainable applications. One powerful tool that Rails developers often overlook is the Decorator Pattern. This design pattern helps separate view-related logic from core business logic, making your code cleaner, more reusable, and easier to maintain.

In this guide, we’ll explore how to implement the Decorator Pattern in Ruby on Rails, highlight practical use cases, and demonstrate how decorators can streamline complex applications.

What is the Decorator Pattern?

The Decorator Pattern is a structural design pattern that allows you to extend the functionality of an object at runtime. In Rails, decorators are often used to add or modify methods on a model object for display purposes, without changing the model’s core logic. By using decorators, you can ensure your model remains focused on business logic while view-related methods are handled elsewhere.

Why Use the Decorator Pattern in Rails?

Using the Decorator Pattern in Rails offers several advantages:

  • Keeps Models Slim: By isolating display-related methods, the model remains focused on core business responsibilities.
  • Increases Reusability: You can apply different decorators to the same model based on context, reducing code duplication.
  • Simplifies Views: Decorators make view templates cleaner by encapsulating complex formatting or conditional display logic.

Use Cases for the Decorator Pattern in Rails

Let’s look at specific scenarios where the decorator pattern shines in Rails applications.

1. Handling Complex Display Logic in Views

For models like Product or User, it’s common to have complex display requirements, such as showing formatted prices or user statuses. Instead of adding these methods directly to the model, decorators can handle this formatting.

Example:

class ProductDecorator < Draper::Decorator
  delegate_all

  def formatted_price
    h.number_to_currency(object.price, unit: "USD")
  end
end

2. Role-Based Display for User Profiles

When you have multiple user roles (e.g., admin, customer, guest) and need to display role-specific information, decorators let you define role-based logic in a clean way.

Example:

class AdminUserDecorator < Draper::Decorator
  delegate_all

  def detailed_user_info
    "#{object.full_name} - Admin Access"
  end
end

3. Formatting Data for Export

When exporting data, such as to CSV or JSON, decorators can be used to format the data specifically for export purposes, making it easy to adjust display details without cluttering the model.

Example:

class UserDecorator < Draper::Decorator
  delegate_all

  def export_data
    {
      name: "#{object.first_name} #{object.last_name}",
      email: object.email,
      joined_on: object.created_at.strftime('%Y-%m-%d')
    }
  end
end

4. Customizing API Responses

Decorators can be used to control how data is presented in API responses, such as filtering attributes or altering the format. This approach is particularly useful for role-based APIs or multi-client applications.

5. Managing Date and Time Formatting

Dates and times often need to be formatted based on user preferences or locales. Decorators can centralize this formatting logic to make it easy to update across the application.

class OrderDecorator < Draper::Decorator
  delegate_all

  def formatted_date
    h.l(object.date, format: :long)
  end
end

How to Implement the Decorator Pattern in Rails

Using the Draper Gem

The Draper gem is a popular choice for decorators in Rails. Here’s a quick guide to setting it up:

  1. Install Draper:
    Add Draper to your Gemfile:
   gem 'draper'

Run bundle install.

  1. Generate a Decorator:
   rails generate decorator User

This creates a UserDecorator where you can add view-related methods.

  1. Define Methods in the Decorator:
   class UserDecorator < Draper::Decorator
     delegate_all

     def formatted_name
       "#{object.first_name} #{object.last_name}".titleize
     end
   end
  1. Apply the Decorator:
    In your controller, use the decorate method:
   @user = User.find(params[:id]).decorate

Manual Implementation Without Draper

If you prefer a custom decorator, you can create a simple decorator class:

class UserDecorator
  def initialize(user)
    @user = user
  end

  def formatted_name
    "#{@user.first_name} #{@user.last_name}".titleize
  end

  def method_missing(method, *args, &block)
    @user.send(method, *args, &block)
  end
end

Conclusion

The Decorator Pattern in Ruby on Rails is a powerful technique for building clean, modular code that improves both readability and maintainability. By separating view-related logic from models, decorators allow for flexible, reusable code that adapts easily to different display contexts.

Whether you’re creating role-specific views, formatting data exports, or customizing API responses, decorators help you keep your code organized and aligned with Rails best practices.

Embrace the decorator pattern in your Rails projects to take control of your display logic and make your application easier to scale and maintain!

Leave a Comment

Comments

No comments yet. Why don’t you start the discussion?

    Leave a Reply