Skip to content

Instantly share code, notes, and snippets.

@tooky
Created July 2, 2014 10:26
Show Gist options
  • Save tooky/2e976994cbe71748227b to your computer and use it in GitHub Desktop.
Save tooky/2e976994cbe71748227b to your computer and use it in GitHub Desktop.
class Bottles
def song
verses(99, 0)
end
def verses(upper_bound, lower_bound)
upper_bound.downto(lower_bound).map { |i| verse(i) }.join("\n")
end
def verse(verse_number)
current_bn = create_bottle_number(verse_number)
next_bn = create_bottle_number(current_bn.next)
"#{current_bn.quantity.capitalize} #{current_bn.container} of beer on the wall, " +
"#{current_bn.quantity} #{current_bn.container} of beer.\n" +
"#{current_bn.action}, " +
"#{next_bn.quantity} #{next_bn.container} of beer on the wall.\n"
end
def create_bottle_number(number)
BottleNumber.for(number)
end
end
class BottleNumber
attr_reader :number
def self.for(number)
bottle_number = available_bottle_numbers.find { |subclass| subclass.handles?(number) }
bottle_number.new(number)
end
def initialize(number)
@number = number
end
def container
'bottles'
end
def pronoun
'one'
end
def quantity
number.to_s
end
def action
"Take #{pronoun} down and pass it around"
end
def next
number - 1
end
private
def self.handles?(number)
true
end
def self.available_bottle_numbers
available_bottle_numbers = subclasses
available_bottle_numbers << self
end
end
class BottleNumber1 < BottleNumber
def self.handles?(number)
number == 1
end
def pronoun
'it'
end
def container
'bottle'
end
end
class BottleNumber0 < BottleNumber
def self.handles?(number)
number.zero?
end
def next
99
end
def action
"Go to the store and buy some more"
end
def quantity
'no more'
end
end
class SixPack < BottleNumber
def self.handles?(number)
number == 6
end
def container
'six-pack'
end
def quantity
'1'
end
end
module Subclasses
# return a list of the subclasses of a class
def subclasses(direct = false)
classes = []
if direct
ObjectSpace.each_object(Class) do |c|
next unless c.superclass == self
classes << c
end
else
ObjectSpace.each_object(Class) do |c|
next unless c.ancestors.include?(self) and (c != self)
classes << c
end
end
classes
end
end
Object.send(:include, Subclasses)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment