Decorators

What are decorators?

  • Design pattern
  • "[They] allow behavior to be added to an individual object, either statically or dynamically, without affecting the behavior of other objects from the same class."
  • In Rails, mostly used for abstracting presentation logic from the views and models

Why decorate?

Why decorate?


  class User < ActiveRecord::Base
    def full_name
      first_name + last_name
    end
  end
  • Remove presentation code from models

Why decorate?


  - if @user.email
    = @user.email
  - else
    | This user has no email address
  • Remove logic from views

Why decorate?


  # Helper:

  module PostsHelper
    def formatted_time(time)
      time.strftime("%A, %B %e")
    end
  end

  # View:

  = formatted_time(@user.created_at)
  • Move code from procedural helpers to object-oriented decorators

Plain old ruby object decorators

PORO decorators

  # User model
  class User < ActiveRecord::Base
  end

  # User decorator
  class UserDecorator
    def initialize(user)
      @user = user
    end

    def full_name
      @user.first_name + @user.last_name
    end

    def email
      @user.email ? @user.email : "This user has no email address"
    end
  end

PORO decorators

  # Controller
  class UsersController < ApplicationController
    def show
      @user = User.find(params[:id])
      @user_decorator = UserDecorator.new(@user)
    end
  end

  # View
  # Instead of writing this:
  = @user.first_name + @user.last_name

  # Or this, if there's a full_name method defined on the model:
  = @user.full_name 

  # We can now write this:
  = @user_decorator.full_name

Draper

What is Draper?

  • A gem created by Jeff Casimir
  • Simplifies creating decorators and adds some cool functionalities, e.g. automatic decoration of associated models

Draper

  class User < ActiveRecord::Base
    has_many :posts
  end

  class UserDecorator < Draper::Decorator
    delegate_all
    decorates_association :posts

    def full_name
      first_name + last_name
    end

    def email
      object.email ? object.email : "This user has no email"
    end
  end

  class PostDecorator < Draper::Decorator
    def formatted_time
      created_at.strftime("%A, %B %e")
    end
  end

Draper

  class UsersController < ApplicationController
    def show
      @user = User.find(params[:id]).decorate
      @first_post = @user.posts.first
    end
  end

  # In views

  = @user.full_name

  = @first_post.formatted_time

Draper

  class UsersController < ApplicationController
    decorates_assigned :user

    def show
      @user = User.find(params[:id])
    end
  end

  # What decorates_assigned does in the background:

  def user
    @decorated_user ||= @user.decorate
  end
  helper_method :user

  # In views

  = user.full_name

Draper

  # Inheritance
  class UserDecorator < Draper::Decorator
  end

  class AdminUserDecorator < UserDecorator
  end

  @admin_user_decorator = AdminUserDecorator.new(@admin_user)

  # Mixins
  module TimeFormatDecorator
  end

  class PostDecorator < Draper::Decorator
    include TimerFormatDecorator
  end

Thanks :)

Decorators & Draper

By mivanek

Decorators & Draper

A presentation about decorators and Draper gem for internal Ruby Talks

  • 615