-
-
Save sarvsav/1332140b5ba2a2a3a381 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require_relative "rules" | |
# An object which represents the checkout process or cash register. | |
# It scans items, which are added to an internal list of items, | |
# which are then used to return the #total at any given moment. | |
# This total tries to use the rules which lead to the cheapest price. | |
class CheckOut < Struct.new(:rules) | |
attr_accessor :rules # list of available pricing rules (passed on initialization) | |
attr_accessor :items # hash of scanned items and quantities | |
# { "A" => 0, "B" => 1 } means we have scanned one "A" and zero "B"s | |
def initialize(*args) | |
super | |
@items = Hash.new { 0 } # if an item isn't found, it is assumed the quantity is 0. | |
end | |
# increment item counter in items hash. | |
def scan(item) | |
@items[item] += 1 | |
puts "scanned 1 #{item}, totaling #{@items[item]}" | |
end | |
# calculates grand total by summing each item's total | |
def total | |
puts "Calculating grand total..." | |
# sum the totals for each item | |
result = @items.inject(0) { |sum, key_val| sum + item_total(key_val.first, key_val.last) } | |
puts "Grand total is #{result}!" | |
result | |
end | |
# calculate item's total by using the best rules available for the quantity. | |
def item_total(item, quantity) | |
item_total = 0 # counter of running total | |
while quantity != 0 | |
# find the best rule for this item and quantity | |
rule = Rule.for(item, quantity) | |
# apply that rule for that quantity | |
item_total += rule.quantity_required * rule.unit_price | |
# reduce the quantity, and find the next best rule again, until no more quantity | |
# assumes we always have a Rule for quantity 1, which is 'normal' price. | |
quantity -= rule.quantity_required | |
end | |
puts "#{item} total: #{item_total}" | |
item_total | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# An object which represents a Rule, for pricing items at checkout. | |
# Rules are made up of: | |
# product_name (in this case "A", "B", "C", "D") | |
# description (just useful for debugging, and UX) | |
# unit_price (used to determine the best rule, since this will give the best price) | |
# quantity_required (the quantity required for the rule to be applicable) | |
class Rule < Struct.new(:product_name, :description, :unit_price, :quantity_required) | |
# stores ALL the rules, used for searching which is the best rule. | |
def self.all | |
@rules ||= [] | |
end | |
# add a new rule to the list of rules | |
def self.add(product_name, description, unit_price, quantity_required) | |
@rules ||= [] | |
@rules << new(product_name, description, unit_price, quantity_required) | |
end | |
# find the best rule from all rules, given a specific item, and a quantity | |
def self.for(item, quantity) | |
# filter rules down to ones relevant to the item being purchased | |
item_rules = all.select { |rule| rule.product.to_s == item.to_s } | |
puts "found #{item_rules.size} rules for #{item}..." | |
# filter rules down to ones meeting the quantity required | |
item_rules = item_rules.select { |rule| rule.quantity_required <= quantity } | |
puts "#{item_rules.size} rules are applicable..." | |
# find the rule which provides the best unit price | |
rule = item_rules.min_by { |rule| rule.unit_price } | |
puts "Applying rule: #{rule.description}" | |
rule | |
end | |
end | |
# Initialize the rules according to the kata data | |
Rule.add "A", "1 for 50", 50/1.0, 1 | |
Rule.add "A", "3 for 130", 130/3.0, 3 | |
Rule.add "B", "1 for 30", 30/1.0, 1 | |
Rule.add "B", "2 for 45", 45/2.0, 2 | |
Rule.add "C", "1 for 20", 20/1.0, 1 | |
Rule.add "D", "1 for 15", 15/1.0, 1 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment