Skip to content

Instantly share code, notes, and snippets.

@jenya239
Last active May 14, 2019 17:01
Show Gist options
  • Save jenya239/6df153af6ae4abba262f0a50517506ed to your computer and use it in GitHub Desktop.
Save jenya239/6df153af6ae4abba262f0a50517506ed to your computer and use it in GitHub Desktop.
instat test
require 'awesome_print'
class Item
attr_reader :code, :name
def initialize( code, name )
@code, @name = code, name
end
def to_s
"#{ @code } #{ @name }"
end
end
class Rule
def process( records )
records .each do |rec|
price = get_price( rec .item .code .to_sym )
rec .price = price if price != nil
end
end
protected
def get_price( code )
throw :abstract
end
end
class Base < Rule
def initialize( prices )
@prices = prices
end
protected
def get_price( code )
@prices[ code ]
end
end
class BuyOneGetOneFree < Rule
def initialize( code )
@code = code
end
def process( records )
@free = false
super( records )
end
protected
def get_price( code )
return nil unless code == @code
res = @free ? 0 : nil
@free = ! @free
res
end
end
class BulkDiscount < Rule
def initialize( code, quantity, price )
@code, @quantity, @price = code, quantity, price
end
def process( records )
count = records .inject( 0 )do |sum, rec|
rec .item .code .to_sym == @code ? sum + 1 : sum
end
return if count < @quantity
super( records )
end
protected
def get_price( code )
return nil unless code == @code
@price
end
end
class Record
attr_accessor :price
attr_reader :item
def initialize( item )
@item = item
@price = 0
end
end
class Checkout
attr_reader :records
def initialize( pricing_rules )
@pricing_rules = pricing_rules
@records = []
end
def scan( item )
@records << Record .new( item )
@pricing_rules .each{ |rule| rule .process( @records ) }
end
def total
@records .inject( 0 ){ |sum, rec| sum + rec .price }
end
end
class Test
def initialize
i1 = Item .new 'FR1', 'Fruit tea'
i2 = Item .new 'AP1', 'Apple'
i3 = Item .new 'CF1', 'Coffee'
@items = [ i1, i2, i3 ] .inject( {} ){ |hash, item| hash[ item .code .to_sym ] = item; hash }
#print i1, ' ', i2, ' ', i3, "\n"
@pricing_rules = [ Base .new( { FR1: 3.11, AP1: 5.0, CF1: 11.23 } ) ]
@pricing_rules << BuyOneGetOneFree .new( :FR1 )
@pricing_rules << BulkDiscount .new( :AP1, 3, 4.50 )
self .class .instance_methods(false) .each do |m|
matched = /^test(.+)$/ .match( m .to_s )
if matched
print "\n\ntest #{ matched[ 1 ] }\n"
setup
send m
end
end
end
def assert( value, expected, msg = '' )
res = value == expected ? 'PASSED' : 'FAILED'
print "#{ msg } #{ expected } #{ value } #{ res }"
end
def setup
@co = Checkout .new( @pricing_rules )
end
def test1
%i[ FR1 AP1 FR1 CF1 ] .each{ |code| @co .scan( @items[ code ] ) }
assert @co .total, 22.25, 'FR1 AP1 FR1 CF1'
end
def test2
%i[ FR1 FR1 ] .each{ |code| @co .scan( @items[ code ] ) }
assert @co .total, 3.11, 'FR1 FR1'
end
def test3
%i[ AP1 AP1 FR1 AP1 ] .each{ |code| @co .scan( @items[ code ] ) }
assert @co .total, 16.61, 'AP1 AP1 FR1 AP1'
end
end
Test .new
=begin
InStat's quest for global domination has prompted us to open a supermarket - we sell only three products:
+--------------|--------------|---------+
| Product Code | Name | Price |
+--------------|--------------|---------+
| FR1 | Fruit tea | $3.11 |
| AP1 | Apple | $5.00 |
| CF1 | Coffee | $11.23 |
+--------------|--------------|---------+
Our CEO is a big fan of buy-one-get-one-free offers and of fruit tea. He wants us to add a rule to do this.
The COO, though, likes low prices and wants people buying apple to get a price discount for bulk purchases.
If you buy 3 or more apple, the price should drop to $4.50. Our check-out can scan items in any order, and because the CEO and COO change their minds often, it needs to be flexible regarding our pricing rules.
The interface to our checkout looks like this (shown in Ruby):
co = Checkout.new(pricing_rules)
co.scan(item)
co.scan(item)
price = co.total
Implement a checkout system that fulfils these requirements in Ruby.
Here are some test data:
Basket: FR1, AP1, FR1, CF1
Total price expected: $22.25
Basket: FR1, FR1
Total price expected: $3.11
Basket: AP1, AP1, FR1, AP1
Total price expected: $16.61
PS: Here at InStat, we love specs ;)
=end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment