Решение на Трета задача от Ангел Лазаров

Обратно към всички решения

Към профила на Ангел Лазаров

Резултати

  • 6 точки от тестове
  • 0 бонус точки
  • 6 точки общо
  • 19 успешни тест(а)
  • 0 неуспешни тест(а)

Код

require 'bigdecimal'
require 'bigdecimal/util'
class Item
attr_reader :price
def initialize(name, price, promotions=nil)
@name = name
@price = price
@promotions = promotions
end
def price_for_quantity(quantity)
total_price = quantity * price
total_price -= discount quantity
end
def discount(quantity)
if @promotions[:get_one_free]
price * (quantity / @promotions[:get_one_free])
elsif @promotions[:package]
price * (quantity / package[:qty]) * package[:qty] * package[:percent] / 100.0
elsif @promotions[:threshold] and quantity - threshold[:qty] > 0
price * (quantity - threshold[:qty]) * threshold[:percent] / 100.0
else
'0'.to_d
end
end
def promotion_type
@promotions.keys[0] if @promotions
end
def promotion_params
type = promotion_type
if type == :get_one_free
[@promotions[:get_one_free]-1]
else
@promotions[type].flatten
end
end
private
def package
Hash[*(([:qty, :percent].zip(@promotions[:package].flatten)).flatten)]
end
def threshold
Hash[*(([:qty, :percent].zip(@promotions[:threshold].flatten)).flatten)]
end
end
class Inventory
attr_reader :items, :coupons
def initialize()
@items = {}
@coupons = {}
end
def register(item_name, item_price, promotions={})
item_price = item_price.to_d
if @items[item_name] or item_name.size > 40
raise "Item \"#{item_name}\" is already in inventory"
elsif !((0.01..999.99) === item_price)
raise "Price \"#{item_price.to_digits}\" is not in the range (0.01, 999.99)"
end
@items[item_name] = Item.new item_name, item_price, promotions
end
def register_coupon(coupon_name, type)
if @coupons[coupon_name]
raise "Cannot register more than one coupon of name #{coupon_name}"
end
@coupons[coupon_name] = type
end
def new_cart
Cart.new self
end
end
class Cart
attr_reader :items, :coupon
def initialize(inventory)
@inventory = inventory
@items = Hash.new 0
@coupon = nil
end
def add(item_name, quantity=1)
if !@inventory.items[item_name]
raise "Item \"#{item_name}\" is not in the inventory"
elsif quantity <= 0
raise "Invalid quantitiy: \"#{quantity}\""
elsif @items[item_name] + quantity > 99
raise "Cannot add have more than 99 of item \"#{item_name}\" "
end
@items[item_name] += quantity
end
def use(coupon)
raise "Cannot use more than one coupon" if @coupon
raise "Coupon #{coupon} not in inventory" if !@inventory.coupons[coupon]
@coupon = coupon
end
def total
total_price = total_price_items
total_price -= coupon_discount if @coupon
total_price
end
def total_price_items
@items.inject('0'.to_d) do |total, (name, quantity)|
total += @inventory.items[name].price_for_quantity quantity
end
end
def coupon_discount
total = total_price_items
coupon = @inventory.coupons[@coupon]
if coupon_type == :percent
total * coupon[:percent] / 100.0
elsif coupon_type == :amount
coupon[:amount].to_d > total ? total : coupon[:amount].to_d
end
end
def coupon_type
@inventory.coupons[@coupon].keys[0]
end
def coupon_params
[@coupon, @inventory.coupons[coupon].values[0]]
end
def invoice
Invoice.new(self, @inventory).invoice_string
end
end
class Invoice
def initialize(cart, inventory)
@cart = cart
@inventory = inventory
@separator = "+#{'-'*48}+#{'-'*10}+\n"
@promotion_formats = promotion_formats
@coupon_formats = { :percent => "Coupon %s - %d%% off",
:amount => "Coupon %s - %.2f off" }
end
def invoice_string
result = @separator + make_line('Name', 'qty', 'price') + @separator
@cart.items.each do |name, qty|
item = @inventory.items[name]
result << make_line(name, qty, "%.2f" % (item.price * qty))
result << make_discount_lines(item, qty) if item.promotion_type
end
result << make_coupon_lines if @cart.coupon
result << @separator + make_line('TOTAL', '', "%.2f" % @cart.total) + @separator
end
private
def to_ordinal(number)
labels = %w{th st nd rd th th th th th th}
(10...20) === number ? "#{number}th" : "#{number}#{labels[number % 10]}"
end
def promotion_formats
{:get_one_free => "(buy %d, get 1 free)",
:package => "(get %d%% off for every %d)",
:threshold => "(%d%% off of every after the %s)",
}
end
def make_coupon_lines
coupon_format = @coupon_formats[@cart.coupon_type]
coupon_params = @cart.coupon_params
title = coupon_format % coupon_params
make_line(title, '', "-%.2f" % @cart.coupon_discount)
end
def make_discount_lines(item, quantity)
promotion_format = @promotion_formats[item.promotion_type]
promotion_params = item.promotion_params.reverse
if item.promotion_type == :threshold
promotion_params[-1] = to_ordinal(promotion_params[-1])
end
title = promotion_format % promotion_params
make_line(title, '', "-%.2f" % item.discount(quantity), 3)
end
def make_line(title, quantity, price, title_left_offset=1)
quantity = quantity.to_s
title_offset_str = " " * title_left_offset
quantity_offset_str = " " * (47 - title_left_offset - title.size - quantity.size)
result = "|" + title_offset_str + title
result << quantity_offset_str + quantity
result << " |" + ' ' * (9 - price.size) + price + " |\n"
end
end

Лог от изпълнението

...................

Finished in 0.5674 seconds
19 examples, 0 failures

История (1 версия и 0 коментара)

Ангел обнови решението на 05.11.2011 20:48 (преди около 13 години)

+require 'bigdecimal'
+require 'bigdecimal/util'
+
+class Item
+ attr_reader :price
+
+ def initialize(name, price, promotions=nil)
+ @name = name
+ @price = price
+ @promotions = promotions
+ end
+
+ def price_for_quantity(quantity)
+ total_price = quantity * price
+ total_price -= discount quantity
+ end
+
+ def discount(quantity)
+ if @promotions[:get_one_free]
+ price * (quantity / @promotions[:get_one_free])
+ elsif @promotions[:package]
+ price * (quantity / package[:qty]) * package[:qty] * package[:percent] / 100.0
+ elsif @promotions[:threshold] and quantity - threshold[:qty] > 0
+ price * (quantity - threshold[:qty]) * threshold[:percent] / 100.0
+ else
+ '0'.to_d
+ end
+ end
+
+ def promotion_type
+ @promotions.keys[0] if @promotions
+ end
+
+ def promotion_params
+ type = promotion_type
+ if type == :get_one_free
+ [@promotions[:get_one_free]-1]
+ else
+ @promotions[type].flatten
+ end
+ end
+
+ private
+
+ def package
+ Hash[*(([:qty, :percent].zip(@promotions[:package].flatten)).flatten)]
+ end
+
+ def threshold
+ Hash[*(([:qty, :percent].zip(@promotions[:threshold].flatten)).flatten)]
+ end
+end
+
+class Inventory
+ attr_reader :items, :coupons
+
+ def initialize()
+ @items = {}
+ @coupons = {}
+ end
+
+ def register(item_name, item_price, promotions={})
+ item_price = item_price.to_d
+ if @items[item_name] or item_name.size > 40
+ raise "Item \"#{item_name}\" is already in inventory"
+ elsif !((0.01..999.99) === item_price)
+ raise "Price \"#{item_price.to_digits}\" is not in the range (0.01, 999.99)"
+ end
+ @items[item_name] = Item.new item_name, item_price, promotions
+ end
+
+ def register_coupon(coupon_name, type)
+ if @coupons[coupon_name]
+ raise "Cannot register more than one coupon of name #{coupon_name}"
+ end
+ @coupons[coupon_name] = type
+ end
+
+ def new_cart
+ Cart.new self
+ end
+end
+
+class Cart
+ attr_reader :items, :coupon
+ def initialize(inventory)
+ @inventory = inventory
+ @items = Hash.new 0
+ @coupon = nil
+ end
+
+ def add(item_name, quantity=1)
+ if !@inventory.items[item_name]
+ raise "Item \"#{item_name}\" is not in the inventory"
+ elsif quantity <= 0
+ raise "Invalid quantitiy: \"#{quantity}\""
+ elsif @items[item_name] + quantity > 99
+ raise "Cannot add have more than 99 of item \"#{item_name}\" "
+ end
+ @items[item_name] += quantity
+ end
+
+ def use(coupon)
+ raise "Cannot use more than one coupon" if @coupon
+ raise "Coupon #{coupon} not in inventory" if !@inventory.coupons[coupon]
+ @coupon = coupon
+ end
+
+ def total
+ total_price = total_price_items
+ total_price -= coupon_discount if @coupon
+ total_price
+ end
+
+ def total_price_items
+ @items.inject('0'.to_d) do |total, (name, quantity)|
+ total += @inventory.items[name].price_for_quantity quantity
+ end
+ end
+
+ def coupon_discount
+ total = total_price_items
+ coupon = @inventory.coupons[@coupon]
+ if coupon_type == :percent
+ total * coupon[:percent] / 100.0
+ elsif coupon_type == :amount
+ coupon[:amount].to_d > total ? total : coupon[:amount].to_d
+ end
+ end
+
+ def coupon_type
+ @inventory.coupons[@coupon].keys[0]
+ end
+
+ def coupon_params
+ [@coupon, @inventory.coupons[coupon].values[0]]
+ end
+
+ def invoice
+ Invoice.new(self, @inventory).invoice_string
+ end
+end
+
+class Invoice
+ def initialize(cart, inventory)
+ @cart = cart
+ @inventory = inventory
+ @separator = "+#{'-'*48}+#{'-'*10}+\n"
+ @promotion_formats = promotion_formats
+ @coupon_formats = { :percent => "Coupon %s - %d%% off",
+ :amount => "Coupon %s - %.2f off" }
+ end
+
+ def invoice_string
+ result = @separator + make_line('Name', 'qty', 'price') + @separator
+ @cart.items.each do |name, qty|
+ item = @inventory.items[name]
+ result << make_line(name, qty, "%.2f" % (item.price * qty))
+ result << make_discount_lines(item, qty) if item.promotion_type
+ end
+ result << make_coupon_lines if @cart.coupon
+ result << @separator + make_line('TOTAL', '', "%.2f" % @cart.total) + @separator
+ end
+
+ private
+
+ def to_ordinal(number)
+ labels = %w{th st nd rd th th th th th th}
+ (10...20) === number ? "#{number}th" : "#{number}#{labels[number % 10]}"
+ end
+
+ def promotion_formats
+ {:get_one_free => "(buy %d, get 1 free)",
+ :package => "(get %d%% off for every %d)",
+ :threshold => "(%d%% off of every after the %s)",
+ }
+ end
+
+ def make_coupon_lines
+ coupon_format = @coupon_formats[@cart.coupon_type]
+ coupon_params = @cart.coupon_params
+ title = coupon_format % coupon_params
+ make_line(title, '', "-%.2f" % @cart.coupon_discount)
+ end
+
+ def make_discount_lines(item, quantity)
+ promotion_format = @promotion_formats[item.promotion_type]
+ promotion_params = item.promotion_params.reverse
+ if item.promotion_type == :threshold
+ promotion_params[-1] = to_ordinal(promotion_params[-1])
+ end
+ title = promotion_format % promotion_params
+ make_line(title, '', "-%.2f" % item.discount(quantity), 3)
+ end
+
+ def make_line(title, quantity, price, title_left_offset=1)
+ quantity = quantity.to_s
+ title_offset_str = " " * title_left_offset
+ quantity_offset_str = " " * (47 - title_left_offset - title.size - quantity.size)
+ result = "|" + title_offset_str + title
+ result << quantity_offset_str + quantity
+ result << " |" + ' ' * (9 - price.size) + price + " |\n"
+ end
+end