Posted at

Gem を使わずにデコレータを作る

More than 1 year has passed since last update.


はじめに

Rails にはデコレータを作るための代表的な Gem として DraperActiveDecorator があります。これらの Gem は Rails のモデルオブジェクトを View に特化したオブジェクトにデコレートすることを目的としています。これらは便利ですが、Rails の View 以外、あるいは Rails 以外で使うには、より汎用的な仕組みがほしいです。

実は Ruby の標準ライブラリにそのような仕組みが存在します。それが SimpleDelegator です。今回はこの SimpleDelegator を使ってデコレータを作ってみます。


SimpleDelegator を使ってデコレータクラスを作る方法は、SimpleDelegator を継承したクラスを用意するだけです。

class GirlDecorator < SimpleDelegator

def full_name
"#{last_name} #{first_name}"
end
end

class Girl

attr_reader :last_name, :first_name

def initialize(last_name, first_name)
@last_name = last_name
@first_name = first_name
end
end

girl = Girl.new('中川', '夏紀')

decorated_girl = GirlDecorator.new(girl)
decorated_girl.full_name #=> "中川 夏紀"


おまけ

次のようなモジュールを定義すると便利かもしれません (クラス間の結合度は上がりますが) 。

# クラス名 + Decorator という名前のデコレータクラスを定義していることが前提。

module Decoratable
# Draper の API を真似て
# デコレートされたオブジェクトを返すインスタンスメソッドを定義する。
def decorate
# Active Support を使っている場合は
# Object.const_get の代わりに "#{self.class}Decorator".constantize で OK。
decorator_class = Object.const_get("#{self.class}Decorator")
decorator_class.new(self)
end
end

class Girl

include Decoratable

attr_reader :last_name, :first_name

def initialize(last_name, first_name)
@last_name = last_name
@first_name = first_name
end
end

girl = Girl.new('中川', '夏紀')

decorated_girl = girl.decorate
decorated_girl.full_name #=> "中川 夏紀"