Решение на Трета задача от Славена Василева

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

Към профила на Славена Василева

Резултати

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

Код

require 'bigdecimal'
require 'bigdecimal/util'
class Integer
def ordinal
sufix = %w{ th st nd rd th th th th th th }
self.to_s + (self / 10 == 1 ? 'th' : sufix[self%10])
end
end
class Product
attr_reader :name, :promotion
def initialize name, price_to_string, promotion = nil
@name = name
@price = price_to_string.to_d
validate
@promotion = Promotion.get_promotion promotion if promotion
end
def discount quantity
@promotion ? @promotion.discount(@price, quantity) : 0
end
def price quantity
@price * quantity
end
def discounted_price quantity
price(quantity) - discount(quantity)
end
def validate
raise "Product name is too long" if @name.length > 40
raise "Product price is not valid" if not @price.between?(0.01, 999.99)
end
end
class GetOneFree
def initialize promotion_params
@product_count = promotion_params
end
def discount price, quantity
(quantity / @product_count) * price
end
def to_s
"buy %d, get 1 free" % (@product_count - 1)
end
end
class Package
def initialize promotion_params
@product_count, @percents_discount = promotion_params.flatten
end
def discount price, quantity
discount_per_package = price * @product_count * @percents_discount / 100
(quantity / @product_count) * discount_per_package
end
def to_s
"get %d%% off for every %d" % [@percents_discount, @product_count]
end
end
class Threshold
def initialize promotion_params
@product_count, @percents_discount = promotion_params.flatten
end
def discount price, quantity
discounted_quantity = [quantity - @product_count, '0'.to_d].max
discounted_quantity * price * @percents_discount / 100
end
def to_s
"%d%% off of every after the %s" % [@percents_discount, @product_count.ordinal]
end
end
class Promotion
PROMOTION_TYPES = {
get_one_free: ->(x) { GetOneFree.new x } ,
package: ->(x) { Package.new x } ,
threshold: ->(x) { Threshold.new x }
}
def Promotion.get_promotion promotion_map
promotion_type, promotion_args = promotion_map.flatten
PROMOTION_TYPES[promotion_type].(promotion_args)
end
end
class PercentCoupon
def initialize name, percent
@name = name
@percent = percent
end
def discount total
@percent * total / 100
end
def to_s
"Coupon %s - %d%% off" % [@name, @percent]
end
end
class AmountCoupon
def initialize name, amount
@name = name
@amount = amount.to_d
end
def discount total
[total, @amount].min
end
def to_s
"Coupon %s - %.2f off" % [@name, @amount]
end
end
class Coupon
COUPON_TYPES = {
percent: ->(x, y) { PercentCoupon.new x, y },
amount: ->(x, y) { AmountCoupon.new x, y }
}
def Coupon.get_coupon name, coupon_map
coupon_type, coupon_param = coupon_map.flatten
COUPON_TYPES[coupon_type].(name, coupon_param)
end
end
class Inventory
attr_reader :products, :coupons
def initialize
@products = {}
@coupons = {}
end
def register name, price, promotion = nil
raise "This product already exists in inventory" if @products[name]
@products[name] = Product.new name, price, promotion
end
def register_coupon name, props
@coupons[name] = Coupon.get_coupon name, props
end
def new_cart
Cart.new self
end
end
class Cart
attr_reader :products, :quantity, :coupon
def initialize inventory
@inventory = inventory
@products = {}
@quantity = Hash.new 0
end
def add product_name, quantity = 1
@products[product_name] = @inventory.products.fetch(product_name) do
raise "No such product in inventory."
end
unless quantity.between? 1, 99
raise "Invalid product count."
end
@quantity[product_name] += quantity
end
def use coupon_name
@coupon = @inventory.coupons.fetch(coupon_name) do
raise "This coupon does not exist."
end
end
def discount
@coupon ? @coupon.discount(clear_total) : 0
end
def clear_total
@products.inject(0) do |total, product|
total += product[1].discounted_price @quantity[product[0]]
end
end
def total
total_no_discount = clear_total
total_no_discount - discount
end
def invoice
BillFormatter.new(self).bill
end
end
class Templates
ROW = "| %s | %8.2f |\n"
PRODUCT = "%-43s%3d"
PROMOTION = " (%s)"
DESCRIPTION = "%-46s"
TOTAL = DESCRIPTION % "TOTAL"
BORDER = "+%s+%s+\n" % ["-" * 48, "-" * 10]
TITLE = "| %-43s%3s | %8s |\n" % ["Name", "qty", "price"]
HEADER = [BORDER, TITLE, BORDER].join
FOOTER = [BORDER, "%s", BORDER].join
end
class BillFormatter
def initialize cart
@cart = cart
end
def bill
footer = Templates::FOOTER % format_total(@cart.total)
products_list = []
@cart.products.each do |name, product|
quantity = @cart.quantity[name]
products_list << format_product(product, quantity)
products_list << format_promotion(product, quantity) if product.promotion
end
products_list << format_coupon if @cart.coupon
[Templates::HEADER, products_list.join, footer].join
end
def format_product product, quantity
descripton = Templates::PRODUCT % [product.name, quantity]
Templates::ROW % [descripton, product.price(quantity)]
end
def format_promotion product, quantity
promotion_string = Templates::PROMOTION % product.promotion.to_s
descripton = Templates::DESCRIPTION % promotion_string
Templates::ROW % [descripton, -1 * product.discount(quantity)]
end
def format_coupon
descripton = Templates::DESCRIPTION % @cart.coupon.to_s
Templates::ROW % [descripton, -1 * @cart.discount]
end
def format_total total
Templates::ROW % [Templates::TOTAL, total]
end
end

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

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

Finished in 0.56671 seconds
19 examples, 0 failures

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

Славена обнови решението на 07.11.2011 10:59 (преди над 12 години)

+require 'bigdecimal'
+require 'bigdecimal/util'
+
+class Integer
+ def ordinal
+ sufix = %w{ th st nd rd th th th th th th }
+ self.to_s + (self / 10 == 1 ? 'th' : sufix[self%10])
+ end
+end
+
+class Product
+ attr_reader :name, :promotion
+
+ def initialize name, price_to_string, promotion = nil
+ @name = name
+ @price = price_to_string.to_d
+ validate
+ @promotion = Promotion.get_promotion promotion if promotion
+ end
+
+ def discount quantity
+ @promotion ? @promotion.discount(@price, quantity) : 0
+ end
+
+ def price quantity
+ @price * quantity
+ end
+
+ def discounted_price quantity
+ price(quantity) - discount(quantity)
+ end
+
+ def validate
+ raise "Product name is too long" if @name.length > 40
+ raise "Product price is not valid" if not @price.between?(0.01, 999.99)
+ end
+end
+
+class GetOneFree
+ def initialize promotion_params
+ @product_count = promotion_params
+ end
+
+ def discount price, quantity
+ (quantity / @product_count) * price
+ end
+
+ def to_s
+ "buy %d, get 1 free" % (@product_count - 1)
+ end
+end
+
+class Package
+ def initialize promotion_params
+ @product_count, @percents_discount = promotion_params.flatten
+ end
+
+ def discount price, quantity
+ discount_per_package = price * @product_count * @percents_discount / 100
+ (quantity / @product_count) * discount_per_package
+ end
+
+ def to_s
+ "get %d%% off for every %d" % [@percents_discount, @product_count]
+ end
+end
+
+class Threshold
+ def initialize promotion_params
+ @product_count, @percents_discount = promotion_params.flatten
+ end
+
+ def discount price, quantity
+ discounted_quantity = [quantity - @product_count, '0'.to_d].max
+ discounted_quantity * price * @percents_discount / 100
+ end
+
+ def to_s
+ "%d%% off of every after the %s" % [@percents_discount, @product_count.ordinal]
+ end
+end
+
+class Promotion
+ PROMOTION_TYPES = {
+ get_one_free: ->(x) { GetOneFree.new x } ,
+ package: ->(x) { Package.new x } ,
+ threshold: ->(x) { Threshold.new x }
+ }
+
+ def Promotion.get_promotion promotion_map
+ promotion_type, promotion_args = promotion_map.flatten
+ PROMOTION_TYPES[promotion_type].(promotion_args)
+ end
+end
+
+class PercentCoupon
+ def initialize name, percent
+ @name = name
+ @percent = percent
+ end
+
+ def discount total
+ @percent * total / 100
+ end
+
+ def to_s
+ "Coupon %s - %d%% off" % [@name, @percent]
+ end
+end
+
+class AmountCoupon
+ def initialize name, amount
+ @name = name
+ @amount = amount.to_d
+ end
+
+ def discount total
+ [total, @amount].min
+ end
+
+ def to_s
+ "Coupon %s - %.2f off" % [@name, @amount]
+ end
+end
+
+class Coupon
+ COUPON_TYPES = {
+ percent: ->(x, y) { PercentCoupon.new x, y },
+ amount: ->(x, y) { AmountCoupon.new x, y }
+ }
+
+ def Coupon.get_coupon name, coupon_map
+ coupon_type, coupon_param = coupon_map.flatten
+ COUPON_TYPES[coupon_type].(name, coupon_param)
+ end
+end
+
+class Inventory
+ attr_reader :products, :coupons
+
+ def initialize
+ @products = {}
+ @coupons = {}
+ end
+
+ def register name, price, promotion = nil
+ raise "This product already exists in inventory" if @products[name]
+ @products[name] = Product.new name, price, promotion
+ end
+
+ def register_coupon name, props
+ @coupons[name] = Coupon.get_coupon name, props
+ end
+
+ def new_cart
+ Cart.new self
+ end
+end
+
+class Cart
+ attr_reader :products, :quantity, :coupon
+
+ def initialize inventory
+ @inventory = inventory
+ @products = {}
+ @quantity = Hash.new 0
+ end
+
+ def add product_name, quantity = 1
+ @products[product_name] = @inventory.products.fetch(product_name) do
+ raise "No such product in inventory."
+ end
+ unless quantity.between? 1, 99
+ raise "Invalid product count."
+ end
+ @quantity[product_name] += quantity
+ end
+
+ def use coupon_name
+ @coupon = @inventory.coupons.fetch(coupon_name) do
+ raise "This coupon does not exist."
+ end
+ end
+
+ def discount
+ @coupon ? @coupon.discount(clear_total) : 0
+ end
+
+ def clear_total
+ @products.inject(0) do |total, product|
+ total += product[1].discounted_price @quantity[product[0]]
+ end
+ end
+
+ def total
+ total_no_discount = clear_total
+ total_no_discount - discount
+ end
+
+ def invoice
+ BillFormatter.new(self).bill
+ end
+end
+
+class Templates
+ ROW = "| %s | %8.2f |\n"
+ PRODUCT = "%-43s%3d"
+ PROMOTION = " (%s)"
+ DESCRIPTION = "%-46s"
+ TOTAL = DESCRIPTION % "TOTAL"
+ BORDER = "+%s+%s+\n" % ["-" * 48, "-" * 10]
+ TITLE = "| %-43s%3s | %8s |\n" % ["Name", "qty", "price"]
+ HEADER = [BORDER, TITLE, BORDER].join
+ FOOTER = [BORDER, "%s", BORDER].join
+end
+
+
+class BillFormatter
+ def initialize cart
+ @cart = cart
+ end
+
+ def bill
+ footer = Templates::FOOTER % format_total(@cart.total)
+ products_list = []
+ @cart.products.each do |name, product|
+ quantity = @cart.quantity[name]
+ products_list << format_product(product, quantity)
+ products_list << format_promotion(product, quantity) if product.promotion
+ end
+ products_list << format_coupon if @cart.coupon
+ [Templates::HEADER, products_list.join, footer].join
+ end
+
+ def format_product product, quantity
+ descripton = Templates::PRODUCT % [product.name, quantity]
+ Templates::ROW % [descripton, product.price(quantity)]
+ end
+
+ def format_promotion product, quantity
+ promotion_string = Templates::PROMOTION % product.promotion.to_s
+ descripton = Templates::DESCRIPTION % promotion_string
+ Templates::ROW % [descripton, -1 * product.discount(quantity)]
+ end
+
+ def format_coupon
+ descripton = Templates::DESCRIPTION % @cart.coupon.to_s
+ Templates::ROW % [descripton, -1 * @cart.discount]
+ end
+
+ def format_total total
+ Templates::ROW % [Templates::TOTAL, total]
+ end
+end