Ruby/RailsでのBuilder Patternを実装できた

photo credit: Andrew* via photopin cc

前回

インスタンスメソッドで書くとどうなるか調べるか。

と書いた通り、それぞれの書き方を比較してみた。

クラスメソッドでinstance_evalの場合

def self.build_eval(&block)
  label = Label.new
  label.instance_eval(&block) if block_given?
  label
end

@label_build_eval = Api::Label.build_eval do
  self.category = 'bg-01'
  self.text = 'new'
end

クラスメソッドでblock.callの場合

def self.build_call(&block)
  label = Label.new
  block.call(label) if block_given?
  label
end

@label_build_call = Api::Label.build_call do | label |
  label.category = 'bg-01'
  label.text = 'new'
end

インスタンスメソッドでblock.callの場合

def self.builder(&block)
  block.call(self) if block_given?
  label
end

@link = Api::Link.new
@link.builder do | link |
  label.category = 'bg-01'
  label.text = 'new'
end

採用された実装

以前記載したように、

@api = Api::PanelApi.build do
  self.id = '999'
  (略)
  self.left_panel = build_panel do
    self.priority = 'normal'
    (略)
    add_link do
      self.id = 'left-sample1'
      (略)
      add_image do
        self.width = 200
        (略)
      end
    end
  end
  self.right_panel = build_panel do
    self.priority = 'normal'
    (略)
    add_link do
      self.id = 'right-sample1'
      (略)
      self.title = 'right-title-sample1'
      add_image do
        self.width = 200
        (略)
      end
    end
  end
end

これだけ階層が深いと確かに使い手にとってself.だと分かり易くないので、インスタンスメソッドでblock.callで実装することになった。その場合は以下のようになる。

@api = Api::PanelApi.builder do | panel_api |
  panel_api.id = '999'
  (略)
  panel_api.left_panel = build_panel do | panel |
    panel.priority = 'normal'
    (略)
    panel.add_link do | link |
      link.id = 'left-sample1'
      (略)
      link.add_image do | image |
        image.width = 200
        (略)
      end
    end
  end
end

こうして見ると、どのModelの変数に値を設定しているのか分かり易くなった。