Решение на Трета задача от Ивайло Петров

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

Към профила на Ивайло Петров

Резултати

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

Код

require 'bigdecimal'
require 'bigdecimal/util'
class Inventory
def initialize
@products, @coupons = {}, {}
end
def new_cart
Cart.new self
end
def register_coupon name, type
raise "Invalid parameters passed." if type.size > 1 or @coupons[name]
key, coupon_args = type.flatten
coupon_class = {percent: PercentCoupon, amount: AmountCoupon}[key]
@coupons[name] = coupon_class.new name, coupon_args
end
def register name, price, promotion = {}
price = price.to_d
if @products[name] or name.size > 40 or price < '0.01'.to_d or
price > '999.99'.to_d
raise "Invalid parameters passed."
end
@products[name] = Product.new name, price, promotion
end
def get_product name
raise "Invalid parameters passed" unless @products[name]
@products[name]
end
def get_coupon name
raise "Invalid parameters passed" unless @coupons[name]
@coupons[name]
end
end
class Cart
def initialize inventory
@inventory, @products = inventory, Hash.new('0'.to_d)
end
def orders
@products.map { |product, amount| Order.new product, amount }
end
def add name, amount = 1
raise "Invalid parameters passed" unless amount > 0 and amount < 100
@products[@inventory.get_product(name)] += amount
end
def use coupon_name
raise "Invalid parameters passed" unless @inventory.get_coupon coupon_name
@coupon_name = coupon_name
end
def total
products_total - coupon_discount(products_total)
end
def invoice
Printer.new.print orders, coupon, -coupon_discount(products_total), total
end
def coupon_discount value
@coupon_name ? @inventory.get_coupon(@coupon_name).get_discount(value) : 0.0
end
def products_total
@products.inject(0) do |accumulated, product|
accumulated + product[0].total_price_for_amount(product[1])
end
end
def coupon
@inventory.get_coupon @coupon_name if @coupon_name
end
end
class Order
attr_reader :product, :amount
def initialize product, amount
@product, @amount = product, amount
end
def to_s
@product.invoice_s @amount
end
def invoice_s amount_s, discount_s
amount_s.call(@product.name, "%.0f" % @amount, @product.price_for_amount(@amount)) +
discount_s.call(@product.promotion, -@product.discount_for_amount(@amount))
end
end
class Product
attr_reader :name, :promotion
def initialize name, price, promotion
@name, @price= name, price
promotions = {
get_one_free: GetOneFreePromotion,
package: PackagePromotion,
threshold: ThresholdPromotion
}
key, args = promotion.flatten
unless promotion.empty?
@promotion = promotions[key].new self, *[*args].flatten
end
end
def price_for_amount amount
amount * @price
end
def discount_for_amount amount
@promotion ? @promotion.calculate_discount(amount.to_i) : '0'.to_d
end
def total_price_for_amount amount
price_for_amount(amount) - discount_for_amount(amount)
end
def eql? other
@name.eql? other.name
end
def hash
@name.hash
end
end
class GetOneFreePromotion
def initialize product, count
raise "Invalid parameters passed" unless count > 0
@product, @count = product, count
end
def calculate_discount amount
@product.price_for_amount(amount / @count)
end
def to_s
"(buy #{@count-1}, get 1 free)"
end
end
class PackagePromotion
def initialize product, count, discount_percent
raise "Invalid parameters passed" unless count > 0 && discount_percent > 0
@product, @count, @discount_percent = product, count, discount_percent
end
def calculate_discount amount
@product.price_for_amount((amount / @count ) * @count) * @discount_percent / 100.0
end
def to_s
"(get #{@discount_percent.to_i}% off for every #{@count})"
end
end
class ThresholdPromotion
def initialize product, amount, percent
raise "Invalid parameters passed" unless amount > 0 and percent > 0 and percent < 100
@product, @amount, @percent = product, amount, percent
end
def calculate_discount amount
if amount > @amount
@product.price_for_amount(amount - @amount) * @percent / 100.0
else
'0.00'.to_d
end
end
def to_s
suffixes = Hash.new('th').merge({1 => 'st', 2 => 'nd', 3 => 'rd'})
"(#{@percent.to_i}% off of every after the #{@amount}#{suffixes[@amount]})"
end
end
class PercentCoupon
def initialize name, percent
raise "Invalid parameters passed" unless percent > 0 and percent < 100
@name, @percent = name, percent
end
def get_discount sum
@percent * sum / 100
end
def to_s
"Coupon #{@name} - #{@percent.to_i}% off"
end
end
class AmountCoupon
def initialize name, amount
@name, @amount = name, amount
end
def get_discount sum
@amount > sum ? sum : @amount
end
def to_s
"Coupon #{@name} - " + "%.2f off" % @amount
end
end
class Printer
VerticalSeparator = "+------------------------------------------------+----------+\n"
ProductToS = ->(name, amount, price, format = '.2f') do
"| #{name.ljust(43)}#{amount.rjust(3)} |" + " %8#{format} |\n" % price
end
TotalToS = ->(str, price) do
str and str != '' ? "| #{str.ljust(46)} |" + " %8.2f |\n" % price : ''
end
DiscountToS = ->(promotion, price) do
promotion ? TotalToS.call(' ' + promotion.to_s, price) : ''
end
def print orders, coupon, coupon_discount, total
orders_s = orders.map { |order| order.invoice_s(ProductToS, DiscountToS)}
coupon_s = TotalToS.call(coupon.to_s, coupon_discount)
orders_s.inject(make_header, &:+) + coupon_s + total_s(total)
end
def make_header
VerticalSeparator + ProductToS.call('Name', 'qty', 'price', 's') + VerticalSeparator
end
def total_s total
total = " %8.2f " % total
VerticalSeparator + TotalToS.call('TOTAL', total) + VerticalSeparator
end
end

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

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

Finished in 0.57675 seconds
19 examples, 0 failures

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

Ивайло обнови решението на 03.11.2011 16:28 (преди около 13 години)

+require 'bigdecimal'
+require 'bigdecimal/util'
+
+class Inventory
+ def initialize
+ @products, @coupons = {}, {}
+ end
+
+ def new_cart
+ Cart.new self
+ end
+
+ def register_coupon name, type
+ raise "Invalid parameters passed." if type.size > 1 or @coupons[name]
+
+ key, coupon_args = type.flatten
+ coupon_class = {percent: PercentCoupon, amount: AmountCoupon}[key]
+
+ @coupons[name] = coupon_class.new name, coupon_args
+ end
+
+ def register name, price, promotion = {}
+ price = price.to_d
+ if @products[name] or name.size > 40 or price < '0.01'.to_d or
+ price > '999.99'.to_d
+ raise "Invalid parameters passed."
+ end
+ @products[name] = Product.new name, price, promotion
+ end
+
+ def get_product name
+ raise "Invalid parameters passed" unless @products[name]
+ @products[name]
+ end
+
+ def get_coupon name
+ raise "Invalid parameters passed" unless @coupons[name]
+ @coupons[name]
+ end
+end
+
+class Cart
+ def initialize inventory
+ @inventory, @products = inventory, Hash.new('0'.to_d)
+ end
+
+ def orders
+ @products.map { |product, amount| Order.new product, amount }
+ end
+
+ def add name, amount = 1
+ raise "Invalid parameters passed" unless amount > 0 and amount < 100
+ @products[@inventory.get_product(name)] += amount
+ end
+
+ def use coupon_name
+ raise "Invalid parameters passed" unless @inventory.get_coupon coupon_name
+ @coupon_name = coupon_name
+ end
+
+ def total
+ products_total - coupon_discount(products_total)
+ end
+
+ def invoice
+ Printer.new.print orders, coupon, -coupon_discount(products_total), total
+ end
+
+ def coupon_discount value
+ @coupon_name ? @inventory.get_coupon(@coupon_name).get_discount(value) : 0.0
+ end
+
+ def products_total
+ @products.inject(0) do |accumulated, product|
+ accumulated + product[0].total_price_for_amount(product[1])
+ end
+ end
+
+ def coupon
+ @inventory.get_coupon @coupon_name if @coupon_name
+ end
+end
+
+class Order
+ attr_reader :product, :amount
+
+ def initialize product, amount
+ @product, @amount = product, amount
+ end
+
+ def to_s
+ @product.invoice_s @amount
+ end
+
+ def invoice_s amount_s, discount_s
+ amount_s.call(@product.name, "%.0f" % @amount, @product.price_for_amount(@amount)) +
+ discount_s.call(@product.promotion, -@product.discount_for_amount(@amount))
+ end
+end
+
+class Product
+ attr_reader :name, :promotion
+
+ def initialize name, price, promotion
+ @name, @price= name, price
+ promotions = {
+ get_one_free: GetOneFreePromotion,
+ package: PackagePromotion,
+ threshold: ThresholdPromotion
+ }
+
+ key, args = promotion.flatten
+ unless promotion.empty?
+ @promotion = promotions[key].new self, *[*args].flatten
+ end
+ end
+
+ def price_for_amount amount
+ amount * @price
+ end
+
+ def discount_for_amount amount
+ @promotion ? @promotion.calculate_discount(amount.to_i) : '0'.to_d
+ end
+
+ def total_price_for_amount amount
+ price_for_amount(amount) - discount_for_amount(amount)
+ end
+
+ def eql? other
+ @name.eql? other.name
+ end
+
+ def hash
+ @name.hash
+ end
+end
+
+class GetOneFreePromotion
+ def initialize product, count
+ raise "Invalid parameters passed" unless count > 0
+ @product, @count = product, count
+ end
+
+ def calculate_discount amount
+ @product.price_for_amount(amount / @count)
+ end
+
+ def to_s
+ "(buy #{@count-1}, get 1 free)"
+ end
+end
+
+class PackagePromotion
+ def initialize product, count, discount_percent
+ raise "Invalid parameters passed" unless count > 0 && discount_percent > 0
+ @product, @count, @discount_percent = product, count, discount_percent
+ end
+
+ def calculate_discount amount
+ @product.price_for_amount((amount / @count ) * @count) * @discount_percent / 100.0
+ end
+
+ def to_s
+ "(get #{@discount_percent.to_i}% off for every #{@count})"
+ end
+end
+
+class ThresholdPromotion
+ def initialize product, amount, percent
+ raise "Invalid parameters passed" unless amount > 0 and percent > 0 and percent < 100
+ @product, @amount, @percent = product, amount, percent
+ end
+
+ def calculate_discount amount
+ if amount > @amount
+ @product.price_for_amount(amount - @amount) * @percent / 100.0
+ else
+ '0.00'.to_d
+ end
+ end
+
+ def to_s
+ suffixes = Hash.new('th').merge({1 => 'st', 2 => 'nd', 3 => 'rd'})
+ "(#{@percent.to_i}% off of every after the #{@amount}#{suffixes[@amount]})"
+ end
+end
+
+class PercentCoupon
+ def initialize name, percent
+ raise "Invalid parameters passed" unless percent > 0 and percent < 100
+ @name, @percent = name, percent
+ end
+
+ def get_discount sum
+ @percent * sum / 100
+ end
+
+ def to_s
+ "Coupon #{@name} - #{@percent.to_i}% off"
+ end
+end
+
+class AmountCoupon
+ def initialize name, amount
+ @name, @amount = name, amount
+ end
+
+ def get_discount sum
+ @amount > sum ? sum : @amount
+ end
+
+ def to_s
+ "Coupon #{@name} - " + "%.2f off" % @amount
+ end
+end
+
+class Printer
+ VerticalSeparator = "+------------------------------------------------+----------+\n"
+
+ ProductToS = ->(name, amount, price, format = '.2f') do
+ "| #{name.ljust(43)}#{amount.rjust(3)} |" + " %8#{format} |\n" % price
+ end
+
+ TotalToS = ->(str, price) do
+ str and str != '' ? "| #{str.ljust(46)} |" + " %8.2f |\n" % price : ''
+ end
+
+ DiscountToS = ->(promotion, price) do
+ promotion ? TotalToS.call(' ' + promotion.to_s, price) : ''
+ end
+
+ def print orders, coupon, coupon_discount, total
+ orders_s = orders.map { |order| order.invoice_s(ProductToS, DiscountToS)}
+ coupon_s = TotalToS.call(coupon.to_s, coupon_discount)
+ orders_s.inject(make_header, &:+) + coupon_s + total_s(total)
+ end
+
+ def make_header
+ VerticalSeparator + ProductToS.call('Name', 'qty', 'price', 's') + VerticalSeparator
+ end
+
+ def total_s total
+ total = " %8.2f " % total
+ VerticalSeparator + TotalToS.call('TOTAL', total) + VerticalSeparator
+ end
+end