EdvardM
3/31/2015 - 10:15 AM

Refactored Gilded rose kata, in Ruby

Refactored Gilded rose kata, in Ruby

class QualityUpdater

  attr_reader :updater, :item
  def initialize(item)
    @item = item
    @updater = ItemUpdater.build(@item)
  end

  ### Main code
  def update_cycle
    updater.adjust_quality
    updater.decrement_sell_in

    updater.update_expired_quality if expired_item?
  end

  private

  def expired_item?
    @item.sell_in < 0
  end
end

class ItemUpdater
  MAX_QUALITY = 50

  class << self
    def build(item)
      select_class(item.name).new(item)
    end

    def select_class(name)
      case name
      when 'Sulfuras, Hand of Ragnaros'
        SulfurasUpdater
      when 'Aged Brie'
        AgedBrieUpdater
      when 'Backstage passes to a TAFKAL80ETC concert'
        TAFKALUpdater
      when 'Conjured Mana Cake'
        ConjuredManaCakeUpdater
      else
        self
      end
    end
  end

  def initialize(item)
    @item = item
  end

  def adjust_quality
    adjust_quality_by(-1)
  end

  def update_expired_quality
    adjust_quality
  end

  def adjust_quality_by(amount)
    @item.quality = enforced_between(0..MAX_QUALITY, @item.quality + amount)
  end

  def decrement_sell_in
    @item.sell_in -= 1
  end

  private

  def enforced_between(range, value)
    [[value, range.min].max, range.max].min
  end
end

class SulfurasUpdater < ItemUpdater
  def adjust_quality; end
  def decrement_sell_in; end
  def update_expired_quality; end
end

class AgedBrieUpdater < ItemUpdater
  def adjust_quality
    adjust_quality_by(1)
  end

  def update_expired_quality
    adjust_quality_by(1)
  end
end

class TAFKALUpdater < ItemUpdater
  def adjust_quality
    adjust_quality_by(1)
    adjust_quality_by(1) if @item.sell_in < 11
    adjust_quality_by(1) if @item.sell_in < 6
  end

  def update_expired_quality
    zero_quality!
  end

  def zero_quality!
    @item.quality = 0
  end
end

class ConjuredManaCakeUpdater < ItemUpdater
  def adjust_quality
    adjust_quality_by(-2)
  end
end

def update_quality(items)
  items.each { |item| QualityUpdater.new(item).update_cycle }
end

# DO NOT CHANGE THINGS BELOW -----------------------------------------

Item = Struct.new(:name, :sell_in, :quality)