# Avoid coupling the sender of a request to its receiver by giving more than one
# object a chance to handle the request. Chain the receiving objects and pass
# the request along the chain until an object handles it.

# Implements the chain of responsibility pattern. Does not know anything
# about the approval process, merely whether the current handler can approve
# the request, or must pass it to a successor.
class PurchaseApprover
  attr_reader :successor

  def initialize(successor)
    @successor = successor
  end

  def process_request(request)
    if approve_request request
      nil
    elsif successor
      successor.process_request request
    else
      deny_request request
    end
  end

  # This may be overridden by a handler if it wants to provide a custom action
  # when it is the last member of the chain
  def deny_request(request)
    # Implementation
  end
end

# Parts of approving chain
# Base class for approvers who only consider whether the request amount is allowable
class AmountApprover < PurchaseApprover
  BASE = 500

  def approve_request(request)
    if request.amount < self.class::ALLOWABLE
      print_approval request
      true
    else
      false
    end
  end
end

class Manager < AmountApprover
  ALLOWABLE = 10 * BASE

  def print_approval(request)
    puts "Manager will approve $#{request.amount}"
  end
end

class Director < AmountApprover
  ALLOWABLE = 20 * BASE

  def print_approval(request)
    puts "Director will approve $#{request.amount}"
  end
end

class VicePresident < AmountApprover
  ALLOWABLE = 40 * BASE

  def print_approval(request)
    puts "VicePresident will approve $#{request.amount}"
  end
end

class President < AmountApprover
  ALLOWABLE = 60 * BASE

  def print_approval(request)
    puts "President will approve $#{request.amount}"
  end
end

class ChiefFinancialOperations < PurchaseApprover
  # An example of a handler that does not inherit from AmountApprover
  def approve_request(request)
    if within_annual_budget? request
      puts "CFO will approve $#{request.amount}"
      true
    else
      false
    end
  end

  def within_annual_budget?(request)
    # Implementation
  end
end

class PurchaseRequest
  attr_reader :amount, :amount, :purpose

  def initialize(number, amount, purpose)
    @number = number
    @amount = amount
    @purpose = purpose
  end
end

# Realization of pattern class
class CLP
  def initialize(*approvers)
    @authority = build_approvers(approvers).first
  end

  def process_request(request)
    @authority.process_request request
  end

  private

  def build_approvers(approver_classes)
    [].tap do |approvers|
      approver_classes.reverse.inject(nil) do |successor, approver|
        approver.new(successor).tap { |approver| approvers.unshift approver }
      end
    end
  end
end

# Usage
approvers = CLP.new(Manager, Director, VicePresident, President)
approvers.process_request PurchaseRequest.new(0, amount, 'General')
