Решение на Трета задача от Христо Банчев

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

Към профила на Христо Банчев

Резултати

  • 5 точки от тестове
  • 0 бонус точки
  • 5 точки общо
  • 16 успешни тест(а)
  • 3 неуспешни тест(а)

Код

require 'bigdecimal'
require 'bigdecimal/util'
class Product
attr_accessor :name, :price, :promotion
def initialize(name, price, promotion)
@name = name
@price = price.to_d
@promotion = promotion
end
end
class Inventory
attr_accessor :products, :coupons
def initialize
@products = {}
@coupons = {}
end
def register(name, price, promotion = nil)
raise "The name must have less than 40 characters" if name.size > 40
raise "There is another product with that name" if @products.key? name
raise "Invalid price value" if not price.to_d.between? 0.001, 999.99
raise "Only one promotion per product" if not promotion.nil? and promotion.size > 1
@products[name] = Product.new(name,price,promotion)
end
def register_coupon(name, discount)
@coupons[name] = discount
end
def new_cart
Cart.new(self)
end
def get_product (name)
@products[name]
end
end
class Cart
attr_accessor :products, :coupon_name, :calculator, :inventory
def initialize(inventory)
@inventory = inventory
@products = {}
@coupon_name = nil
@calculator = ProductCalculator.new(@inventory, self)
end
def add (name, quantity = 1)
raise "The given product does not exist!" if @inventory.products[name].nil?
new_quantity = (@products.key?(name) ? @products[name] : 0) + quantity
raise "Invalid quantity" if not new_quantity.between? 1, 99
@products[name] = new_quantity
end
def total
@calculator.total
end
def invoice
Invoice.new(self).text
end
def use (coupon_name)
raise "Only one coupon is allowed" if not @coupon_name.nil?
raise "The coupon is not registered" if not @inventory.coupons.key? coupon_name
@coupon_name = coupon_name
end
def coupon
@inventory.coupons[@coupon_name]
end
end
class ProductCalculator
def initialize(inventory, cart)
@inventory = inventory
@cart = cart
end
def total
return "0.00".to_d if @cart.products.size == 0
sum = sum_for_coupon
sum = apply_coupon(sum)
sum
end
def product_total(name, quantity)
@inventory.products[name].price * quantity
end
def sum_for_coupon
decimals = @cart.products.map do |name, quantity|
product_total(name, quantity) + apply_promotion(name, quantity)
end
decimals.inject &:+
end
def apply_promotion(name, quantity)
product = @inventory.products[name]
promotion = product.promotion.to_a.first
return 0 if promotion.nil?
case promotion[0]
when :get_one_free then get_one_free(product, quantity, promotion[1])
when :package then package(product, quantity, promotion[1].to_a.first)
when :threshold then threshold(product, quantity, promotion[1].to_a.first)
else 0
end
end
def apply_coupon(total)
return total if @cart.coupon_name.nil?
coupon = @inventory.coupons[@cart.coupon_name].to_a.first
case coupon[0]
when :percent then total * (100 - coupon[1]) / "100.00".to_d
when :amount then total > coupon[1].to_d ? total - coupon[1].to_d : 0
end
end
private
def get_one_free(product, quantity, every)
0 - quantity / every * product.price
end
def package(product, quantity, promotion_body)
count = promotion_body[0]
cents = promotion_body[1]
0 - (quantity - quantity % count) * cents / "100.00".to_d * product.price
end
def threshold(product, quantity, promotion_body)
count = quantity - promotion_body[0]
cents = promotion_body[1]
count <= 0 ? 0 : 0 - count * cents / "100.00".to_d * product.price
end
end
class Invoice
attr_accessor :text
LineSeparator = "+------------------------------------------------+----------+\n"
Header = "| Name qty | price |\n"
Template = "| %-43s%3s | %8s |\n"
def initialize(cart)
@text = LineSeparator + Header + LineSeparator
@calc = cart.calculator
cart.products.each do |name, quantity|
product = cart.inventory.products[name]
add_line(name, quantity, @calc.product_total(name, quantity))
add_promotion(product.promotion, product.name, quantity)
end
add_coupon cart.coupon_name, cart.coupon
add_footer
end
private
def add_line(name, quantity = "", price)
@text = @text + Template % [name, quantity, "%0.2f" % price.to_s('F')]
end
def add_footer
@text += LineSeparator
add_line("TOTAL", @calc.total)
@text += LineSeparator
end
def add_promotion(promo, product_name, quantity)
return if promo.nil?
promo_text = case promo.keys.first
when :get_one_free then " (buy %d, get 1 free)" % [promo.values.first-1]
when :package then package_text(promo.values.last.to_a.first)
when :threshold then threshold_text(promo.values.last.to_a.first)
end
add_line(promo_text, @calc.apply_promotion(product_name, quantity))
end
def package_text(values)
" (get %s off for every %d)" % [values.last.to_s + "%", values.first]
end
def threshold_text(values)
" (%s off of every after the %dth)" % [values.last.to_s + "%", values.first]
end
def add_coupon(coupon_name, coupon)
return if coupon.nil?
coupon_text = case coupon.keys.first
when :percent then percent_text(coupon_name, coupon.values.first)
when :amount then amount_text(coupon_name, coupon.values.first)
end
add_line(coupon_text, @calc.apply_coupon(@calc.sum_for_coupon) - @calc.total)
end
def percent_text(name, percent)
"Coupon %s - %s off" % [name, percent.to_s + "%"]
end
def amount_text(name, amount)
"Coupon %s - %s off" % [name, amount]
end
end

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

..............F..FF

Failures:

  1) Inventory with a '% off' coupon shows the discount in the invoice
     Failure/Error: cart.invoice.should eq <<INVOICE
       
       expected: "+------------------------------------------------+----------+\n| Name                                       qty |    price |\n+------------------------------------------------+----------+\n| Green Tea                                   10 |    10.00 |\n| Coupon TEA-TIME - 20% off                      |    -2.00 |\n+------------------------------------------------+----------+\n| TOTAL                                          |     8.00 |\n+------------------------------------------------+----------+\n"
            got: "+------------------------------------------------+----------+\n| Name                                       qty |    price |\n+------------------------------------------------+----------+\n| Green Tea                                   10 |    10.00 |\n| Coupon TEA-TIME - 20% off                      |     0.00 |\n+------------------------------------------------+----------+\n| TOTAL                                          |     8.00 |\n+------------------------------------------------+----------+\n"
       
       (compared using ==)
       
       Diff:
       @@ -2,7 +2,7 @@
        | Name                                       qty |    price |
        +------------------------------------------------+----------+
        | Green Tea                                   10 |    10.00 |
       -| Coupon TEA-TIME - 20% off                      |    -2.00 |
       +| Coupon TEA-TIME - 20% off                      |     0.00 |
        +------------------------------------------------+----------+
        | TOTAL                                          |     8.00 |
        +------------------------------------------------+----------+
     # /tmp/d20111115-5847-qa99a7/spec.rb:240:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  2) Inventory with an 'amount off' coupon shows the discount in the invoice
     Failure/Error: cart.invoice.should eq <<INVOICE
     TypeError:
       can't convert String into Integer
     # /tmp/d20111115-5847-qa99a7/solution.rb:174:in `to_s'
     # /tmp/d20111115-5847-qa99a7/solution.rb:174:in `add_line'
     # /tmp/d20111115-5847-qa99a7/solution.rb:208:in `add_coupon'
     # /tmp/d20111115-5847-qa99a7/solution.rb:167:in `initialize'
     # /tmp/d20111115-5847-qa99a7/solution.rb:69:in `new'
     # /tmp/d20111115-5847-qa99a7/solution.rb:69:in `invoice'
     # /tmp/d20111115-5847-qa99a7/spec.rb:281:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  3) Inventory with multiple discounts can print an invoice
     Failure/Error: cart.invoice.should eq <<INVOICE
       
       expected: "+------------------------------------------------+----------+\n| Name                                       qty |    price |\n+------------------------------------------------+----------+\n| Green Tea                                    8 |    22.32 |\n|   (buy 1, get 1 free)                          |   -11.16 |\n| Black Coffee                                 5 |    14.95 |\n|   (get 20% off for every 2)                    |    -2.39 |\n| Milk                                         5 |     8.95 |\n|   (30% off of every after the 3rd)             |    -1.07 |\n| Cereal                                       3 |     7.47 |\n| Coupon BREAKFAST - 10% off                     |    -3.91 |\n+------------------------------------------------+----------+\n| TOTAL                                          |    35.16 |\n+------------------------------------------------+----------+\n"
            got: "+------------------------------------------------+----------+\n| Name                                       qty |    price |\n+------------------------------------------------+----------+\n| Green Tea                                    8 |    22.32 |\n|   (buy 1, get 1 free)                          |   -11.16 |\n| Black Coffee                                 5 |    14.95 |\n|   (get 20% off for every 2)                    |    -2.39 |\n| Milk                                         5 |     8.95 |\n|   (30% off of every after the 3th)             |    -1.07 |\n| Cereal                                       3 |     7.47 |\n| Coupon BREAKFAST - 10% off                     |     0.00 |\n+------------------------------------------------+----------+\n| TOTAL                                          |    35.16 |\n+------------------------------------------------+----------+\n"
       
       (compared using ==)
       
       Diff:
       
       @@ -6,9 +6,9 @@
        | Black Coffee                                 5 |    14.95 |
        |   (get 20% off for every 2)                    |    -2.39 |
        | Milk                                         5 |     8.95 |
       -|   (30% off of every after the 3rd)             |    -1.07 |
       +|   (30% off of every after the 3th)             |    -1.07 |
        | Cereal                                       3 |     7.47 |
       -| Coupon BREAKFAST - 10% off                     |    -3.91 |
       +| Coupon BREAKFAST - 10% off                     |     0.00 |
        +------------------------------------------------+----------+
        | TOTAL                                          |    35.16 |
        +------------------------------------------------+----------+
     # /tmp/d20111115-5847-qa99a7/spec.rb:309:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

Finished in 0.56382 seconds
19 examples, 3 failures

Failed examples:

rspec /tmp/d20111115-5847-qa99a7/spec.rb:233 # Inventory with a '% off' coupon shows the discount in the invoice
rspec /tmp/d20111115-5847-qa99a7/spec.rb:274 # Inventory with an 'amount off' coupon shows the discount in the invoice
rspec /tmp/d20111115-5847-qa99a7/spec.rb:295 # Inventory with multiple discounts can print an invoice

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

Христо обнови решението на 07.11.2011 16:55 (преди около 13 години)

+require 'bigdecimal'
+require 'bigdecimal/util'
+
+class Product
+ attr_accessor :name, :price, :promotion
+
+ def initialize(name, price, promotion)
+ @name = name
+ @price = price.to_d
+ @promotion = promotion
+ end
+end
+
+class Inventory
+ attr_accessor :products, :coupons
+
+ def initialize
+ @products = {}
+ @coupons = {}
+ end
+
+ def register(name, price, promotion = nil)
+ raise "The name must have less than 40 characters" if name.size > 40
+ raise "There is another product with that name" if @products.key? name
+ raise "Invalid price value" if not price.to_d.between? 0.001, 999.99
+ raise "Only one promotion per product" if not promotion.nil? and promotion.size > 1
+
+ @products[name] = Product.new(name,price,promotion)
+ end
+
+ def register_coupon(name, discount)
+ @coupons[name] = discount
+ end
+
+ def new_cart
+ Cart.new(self)
+ end
+
+ def get_product (name)
+ @products[name]
+ end
+end
+
+class Cart
+ attr_accessor :products, :coupon_name, :calculator, :inventory
+
+ def initialize(inventory)
+ @inventory = inventory
+ @products = {}
+ @coupon_name = nil
+ @calculator = ProductCalculator.new(@inventory, self)
+ end
+
+ def add (name, quantity = 1)
+ raise "The given product does not exist!" if @inventory.products[name].nil?
+
+ new_quantity = (@products.key?(name) ? @products[name] : 0) + quantity
+
+ raise "Invalid quantity" if not new_quantity.between? 1, 99
+
+ @products[name] = new_quantity
+ end
+
+ def total
+ @calculator.total
+ end
+
+ def invoice
+ Invoice.new(self).text
+ end
+
+ def use (coupon_name)
+ raise "Only one coupon is allowed" if not @coupon_name.nil?
+ raise "The coupon is not registered" if not @inventory.coupons.key? coupon_name
+ @coupon_name = coupon_name
+ end
+
+ def coupon
+ @inventory.coupons[@coupon_name]
+ end
+end
+
+class ProductCalculator
+ def initialize(inventory, cart)
+ @inventory = inventory
+ @cart = cart
+ end
+
+ def total
+ return "0.00".to_d if @cart.products.size == 0
+
+ sum = sum_for_coupon
+ sum = apply_coupon(sum)
+ sum
+ end
+
+ def product_total(name, quantity)
+ @inventory.products[name].price * quantity
+ end
+
+ def sum_for_coupon
+ decimals = @cart.products.map do |name, quantity|
+ product_total(name, quantity) + apply_promotion(name, quantity)
+ end
+
+ decimals.inject &:+
+ end
+
+ def apply_promotion(name, quantity)
+ product = @inventory.products[name]
+ promotion = product.promotion.to_a.first
+ return 0 if promotion.nil?
+
+ case promotion[0]
+ when :get_one_free then get_one_free(product, quantity, promotion[1])
+ when :package then package(product, quantity, promotion[1].to_a.first)
+ when :threshold then threshold(product, quantity, promotion[1].to_a.first)
+ else 0
+ end
+ end
+
+ def apply_coupon(total)
+ return total if @cart.coupon_name.nil?
+
+ coupon = @inventory.coupons[@cart.coupon_name].to_a.first
+
+ case coupon[0]
+ when :percent then total * (100 - coupon[1]) / "100.00".to_d
+ when :amount then total > coupon[1].to_d ? total - coupon[1].to_d : 0
+ end
+ end
+
+ private
+
+ def get_one_free(product, quantity, every)
+ 0 - quantity / every * product.price
+ end
+
+ def package(product, quantity, promotion_body)
+ count = promotion_body[0]
+ cents = promotion_body[1]
+ 0 - (quantity - quantity % count) * cents / "100.00".to_d * product.price
+ end
+
+ def threshold(product, quantity, promotion_body)
+ count = quantity - promotion_body[0]
+ cents = promotion_body[1]
+ count <= 0 ? 0 : 0 - count * cents / "100.00".to_d * product.price
+ end
+end
+
+class Invoice
+ attr_accessor :text
+ LineSeparator = "+------------------------------------------------+----------+\n"
+ Header = "| Name qty | price |\n"
+ Template = "| %-43s%3s | %8s |\n"
+
+
+ def initialize(cart)
+ @text = LineSeparator + Header + LineSeparator
+ @calc = cart.calculator
+ cart.products.each do |name, quantity|
+ product = cart.inventory.products[name]
+ add_line(name, quantity, @calc.product_total(name, quantity))
+ add_promotion(product.promotion, product.name, quantity)
+ end
+ add_coupon cart.coupon_name, cart.coupon
+ add_footer
+ end
+
+ private
+
+ def add_line(name, quantity = "", price)
+ @text = @text + Template % [name, quantity, "%0.2f" % price.to_s('F')]
+ end
+
+ def add_footer
+ @text += LineSeparator
+ add_line("TOTAL", @calc.total)
+ @text += LineSeparator
+ end
+
+ def add_promotion(promo, product_name, quantity)
+ return if promo.nil?
+
+ promo_text = case promo.keys.first
+ when :get_one_free then " (buy %d, get 1 free)" % [promo.values.first-1]
+ when :package then package_text(promo.values.last.to_a.first)
+ when :threshold then threshold_text(promo.values.last.to_a.first)
+ end
+ add_line(promo_text, @calc.apply_promotion(product_name, quantity))
+ end
+
+ def package_text(values)
+ " (get %s off for every %d)" % [values.last.to_s + "%", values.first]
+ end
+
+ def threshold_text(values)
+ " (%s off of every after the %dth)" % [values.last.to_s + "%", values.first]
+ end
+
+ def add_coupon(coupon_name, coupon)
+ return if coupon.nil?
+ coupon_text = case coupon.keys.first
+ when :percent then percent_text(coupon_name, coupon.values.first)
+ when :amount then amount_text(coupon_name, coupon.values.first)
+ end
+ add_line(coupon_text, @calc.apply_coupon(@calc.sum_for_coupon) - @calc.total)
+ end
+
+ def percent_text(name, percent)
+ "Coupon %s - %s off" % [name, percent.to_s + "%"]
+ end
+
+ def amount_text(name, amount)
+ "Coupon %s - %s off" % [name, amount]
+ end
+end