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
- 651