Getting started with Ruby on RailsRoutingActiveRecordViewsActiveRecord MigrationsRails Best PracticesNaming ConventionsActionCableActiveModelUser Authentication in RailsActiveRecord AssociationsActiveRecord ValidationsActiveRecord Query InterfaceActionMailerRails generate commandsConfigurationI18n - InternationalizationUsing GoogleMaps with RailsFile UploadsCachingActionControllerConfigurationSafe ConstantizeRails 5Authorization with CanCanMongoidGemsChange default timezoneAsset PipelineUpgrading RailsActiveRecord LockingDebuggingConfigure Angular with RailsRails loggerPrawn PDFRails APIDeploying a Rails app on HerokuActiveSupportForm HelpersActiveRecord TransactionsRSpec and Ruby on RailsDecorator patternElasticsearchReact with Rails using react-rails gemRails Cookbook - Advanced rails recipes/learnings and coding techniquesMultipurpose ActiveRecord columnsClass OrganizationShallow RoutingModel states: AASMRails 5 API AutheticationTesting Rails ApplicationsActive JobsRails frameworks over the yearsAdd Admin PanelNested form in Ruby on RailsFactory GirlImport whole CSV files from specific folderTools for Ruby on Rails code optimization and cleanupActiveJobActive Model SerializersRails Engine - Modular RailsSingle Table InheritanceActiveRecord TransactionsTurbolinksFriendly IDSecurely storing authentication keysAuthenticate Api using DeviseIntegrating React.js with Rails Using HyperloopChange a default Rails application enviornmentReserved WordsRails -EnginesAdding an Amazon RDS to your rails applicationPayment feature in railsRails on docker

Decorator pattern

Other topics

Remarks:

The Decorator pattern allows you to add or modify behavior of objects in a situational way without affecting the base object.

This can be achieved though plain Ruby using the stdlib, or via popular gems such as Draper.

Decorating a Model using SimpleDelegator

Most Rails developers start by modifying their model information within the template itself:

<h1><%= "#{ @user.first_name } #{ @user.last_name }" %></h1>
<h3>joined: <%= @user.created_at.in_time_zone(current_user.timezone).strftime("%A, %d %b %Y %l:%M %p") %></h3>

For models with a lot of data, this can quickly become cumbersome and lead to copy-pasting logic from one template to another.

This example uses SimpleDelegator from the stdlib.

All requests to a SimpleDelegator object are passed to the parent object by default. You can override any method with presentation logic, or you can add new methods that are specific to this view.

SimpleDelegator provides two methods: __setobj__ to set what object is being delegated to, and __getobj__ to get that object.

class UserDecorator < SimpleDelegator
  attr_reader :view
  def initialize(user, view)
    __setobj__ @user
    @view = view
  end

  # new methods can call methods on the parent implicitly
  def full_name
    "#{ first_name } #{ last_name }"
  end

  # however, if you're overriding an existing method you need
  # to use __getobj__
  def created_at
    Time.use_zone(view.current_user.timezone) do
      __getobj__.created_at.strftime("%A, %d %b %Y %l:%M %p")
    end
  end
end

Some decorators rely on magic to wire-up this behavior, but you can make it more obvious where the presentation logic is coming from by initializing the object on the page.

<% user = UserDecorator.new(@user, self) %>
<h1><%= user.full_name %></h1>
<h3>joined: <%= user.created_at %></h3>

By passing a reference to the view object into the decorator, we can still access all of the rest of the view helpers while building the presentation logic without having to include it.

Now the view template is only concerned with inserting data into the page, and it is much more clear.

Decorating a Model using Draper

Draper automatically matches up models with their decorators by convention.

# app/decorators/user_decorator.rb
class UserDecorator < Draper::Decorator
  def full_name
    "#{object.first_name} #{object.last_name}"
  end

  def created_at
    Time.use_zone(h.current_user.timezone) do
      object.created_at.strftime("%A, %d %b %Y %l:%M %p")
    end
  end
end

Given a @user variable containing an ActiveRecord object, you can access your decorator by calling #decorate on the @user, or by specifying the Draper class if you want to be specific.

<% user = @user.decorate %><!-- OR -->
<% user = UserDecorator.decorate(@user) %>
<h1><%= user.full_name %></h1>
<h3>joined: <%= user.created_at %></h3>

Contributors

Topic Id: 5694

Example Ids: 20195,20484

This site is not affiliated with any of the contributors.