본문 바로가기

소프트웨어 이야기/장고와 루비온레일즈

레일즈에 Service/Decorator Layer 적용하기 (7) - Helper 대신에 Decorator 사용하기

이 포스팅은 Build Sleek Rails Components With Plain Old Ruby Objects을 정리한 글입니다 :)


저자는 drafer + decorator + POLO를 사용해서, 데코레이터 레이어를 사용했다고 합니다. 

헬퍼 메서드는 모든 뷰에서 호출할 수 있다보니,

각 도메인별로 데코레이터 클래스를 만들고, 이를 호출하게 하자는 컨셉으로 보입니다.  


  # app/decorators/entry_decorator.rb
    class EntryDecorator < BaseDecorator
      decorates :entry


      def readable_time_period
        mins = entry.time_period
        return Time.at(60 * mins).utc.strftime('%M <small>Mins</small>').html_safe if mins < 60
        Time.at(60 * mins).utc.strftime('%H <small>Hour</small> %M <small>Mins</small>').html_safe
      end


      def readable_speed
        "#{sprintf('%0.2f', entry.speed)} <small>Km/H</small>".html_safe
      end
    end


 # app/views/entries/_entry.html.erb
    -  <td><%= readable_speed(entry) %> </td>
    +  <td><%= decorate(entry).readable_speed %> </td>

    -  <td><%= readable_time_period(entry) %></td>
    +  <td><%= decorate(entry).readable_time_period %></td>


위에 처럼 클래스를 만들어 사용하려면 사전작업이 필요합니다.

BaseDecorator 클래스를 커스텀하게 만들어줘야하는데요. 그런데 이런 기본 클래스가 어떤 이유로 만들어졌고, 어떤 원리인지는 저도 잘 이해가 안되네요 :(


데코레이터와 헬퍼 메서드를 수정하는건  Build Sleek Rails Components With Plain Old Ruby Objects를 읽어보시고, 이해하시는게 더 좋을 것 같아요. ㅎㅎㅎ 

    % app/decorators/base_decorator.rb
    require 'delegate'


    class BaseDecorator < SimpleDelegator
      def initialize(base, view_context)
        super(base)
        @object = base
        @view_context = view_context
      end


      private


      def self.decorates(name)
        define_method(name) do
          @object
        end
      end


      def _h
        @view_context
      end
    end


    module ApplicationHelper


      # .....


      def decorate(object, klass = nil)
        klass ||= "#{object.class}Decorator".constantize
        decorator = klass.new(object, self)
        yield decorator if block_given?
        decorator
      end


      # .....


    end