Skip to content

Instantly share code, notes, and snippets.

@laktek
Created June 17, 2009 22:41
Show Gist options
  • Save laktek/131562 to your computer and use it in GitHub Desktop.
Save laktek/131562 to your computer and use it in GitHub Desktop.
Vending Machine with multiple beverages (using State Pattern and a Singleton class)
require 'singleton'
class State
attr_accessor :machine
STATES = {:ready => "Ready", :listen => "Listen", :processing => "Processing"}
def initialize(machine)
@machine = machine
end
def insert_coin; end
def return_coin; end
def select(beverage); end
def get_beverage; end
def transit_to(state, options={})
machine.state = eval(STATES[state]).new @machine
end
end
class Ready < State
def insert_coin
if machine.processor.beverages_available?
transit_to :listen
return "Select what do you like to have"
else
return "Sorry out of beverages!"
end
end
def return_coin
return "You haven't inserted a coin"
end
def select(beverage)
return "Insert a coin before selecting a beverage"
end
def get_beverage
return "First you need to insert a coin"
end
end
class Listen < State
def insert_coin
return "Sorry! please wait till I finish the current order"
end
def return_coin
transit_to :ready
return "Here is your coin."
end
def select(beverage)
if machine.processor.make(beverage)
transit_to :processing
return "Please wait! Your #{beverage} will be ready in couple of minutes."
else
return_coin
return "Sorry #{beverage} is not available today"
end
end
def get_beverage
return "You need to select a beverage"
end
end
class Processing < State
def insert_coin
return "Sorry! please wait till I finish the current order"
end
def return_coin
return "Order is already done. Cannot return the coin."
end
def select(beverage)
return "Sorry! I'm already processing one order" #vending machine dont have multiprocessors
end
def get_beverage
#make the selected beverage
output = machine.processor.dispatch
transit_to :ready
output
end
end
class Processor
include Singleton
attr_accessor :available_beverages
def beverages_available?
!(@available_beverages.nil? or @available_beverages.empty?)
end
# outputs the beverage after make
def dispatch
# decrement the amount of the beverage by one
@available_beverages[@beverage_processing] -= 1
@available_beverages.delete(@beverage_processing) if (@available_beverages[@beverage_processing] == 0)
return "Take your #{@beverage_processing}"
end
def make(beverage)
# processing of beverages goes here
# This should return true if the selected beverage can be processed
# For simplicity, we just check for the availability of the beverage
@available_beverages.include?(beverage) && (@beverage_processing = beverage)
end
end
class VendingMachine
attr_accessor :state
attr_reader :processor
def initialize(beverages={})
@processor = Processor.instance
@processor.available_beverages = beverages
@state = Ready.new(self)
end
def method_missing(method, *args)
@state.send(method, *args)
end
end
# Normal Process
# >> require "new_state_machine"
# => true
# >> vm = VendingMachine.new {"Tea" => 5, "Coffee" => 10}
# => #<VendingMachine:0xb794fff4 @processor=#<Processor:0xb794ffa4 @available_beverages={"Coffee"=>1, "Tea"=>1}, state#<Ready:0xb794ff2c @machine=#<VendingMachine:0xb794fff4 ...>
# >> vm.insert_coin
# => "Select what do you like to have"
# >> vm.select("Coffee")
# => "Please wait! Your Coffee will be ready in couple of minutes."
# >> vm.get_beverage
# => "Take your Coffee"
#
# Requesting Latte (not available)
# >> vm.insert_coin
# => "Select what do you like to have"
# >> vm.select("Latte")
# => "Sorry Latte is not available today"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment