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

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

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

Резултати

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

Код

require 'bigdecimal'
require 'bigdecimal/util'
class Inventory
def initialize
@articles = {}
@coupons = {}
end
def register(name, price, promotion = {})
if (name.length > 40 || 0.01 > price.to_f || price.to_f > 999.99 ||
@articles.has_key?(name))
raise "Invalid parameters passed."
end
@articles[name] = Article.new(name, price.to_d, promotion)
end
def register_coupon(name, coupon_data)
if (@coupons.has_key? name)
raise "A coupon with the same name already exists."
end
@coupons[name] = Coupon.create(name, coupon_data)
end
def new_cart
Cart.new(@articles, @coupons)
end
end
class Cart
def initialize(articles, coupons)
@articles, @coupons = articles, coupons
@articles_in_cart, @coupon_in_use = Hash.new(0), nil
end
def add(item_name, amount = 1)
if (!@articles.has_key?(item_name) || amount < 0 || amount > 99)
raise "Invalid parameters passed."
end
@articles_in_cart[item_name] += amount
end
def use(coupon_name)
if (@coupons.has_key? coupon_name)
@coupon_in_use = @coupons[coupon_name]
else
raise "A coupon with the specified name doesn't exist."
end
end
def total
total = @articles_in_cart.inject('0'.to_d) do |sum, (k, v)|
sum + @articles[k].calculate_total(v)
end
@coupon_in_use ? @coupon_in_use.calculate_total(total) : total
end
def invoice
InvoiceCreator.create_invoice(@articles, @articles_in_cart, total, @coupon_in_use)
end
end
class Article
attr_reader :name
def initialize(name, price, promotion)
@name, @price, @quantity = name, price
if !promotion.empty?
@promotion = Promotion.create(promotion)
else
@promotion = nil
end
end
def calculate_total(number_of_items)
if @promotion.nil?
number_of_items * @price
else
@promotion.calculate_total(number_of_items, @price)
end
end
def get_invoice_info(number_of_items)
invoice_info = [{:name => "#{@name}", :quantity => number_of_items,
:price => (@price * number_of_items)}]
if @promotion
invoice_info.push({:name => @promotion.to_s_representation,
:price => -@promotion.calculate_discount(number_of_items, @price)})
end
invoice_info
end
end
class InvoiceCreator
DelimiterLine = "+#{"-" * 48}+#{"-" * 10}+\n"
def InvoiceCreator.create_invoice(items, items_in_cart, total, coupon = nil)
invoice = ""
invoice += InvoiceCreator.create_header
items_in_cart.each do |item_name, quantity|
items[item_name].get_invoice_info(quantity).each do |invoice_info|
invoice += print_product_line(invoice_info)
end
end
if (coupon)
invoice += InvoiceCreator.print_product_line :name => coupon.to_s_representation,
:price => -coupon.calculate_discount(total)
end
invoice += InvoiceCreator.create_footer(total)
end
private
def InvoiceCreator.create_header
DelimiterLine + (InvoiceCreator.print_title_line :name => "Name",
:quantity => "qty", :price => "price") + DelimiterLine
end
def InvoiceCreator.create_footer(total)
DelimiterLine + format("| %s | %8.2f |\n", "TOTAL".ljust(46), total.to_f) +
DelimiterLine
end
def InvoiceCreator.print_title_line(line_info)
format("| %s %3s | %8s |\n", line_info[:name].ljust(42), line_info[:quantity],
line_info[:price])
end
def InvoiceCreator.print_product_line(line_info)
if (line_info[:quantity])
format("| %s %3d | %8.2f |\n", line_info[:name].ljust(42), line_info[:quantity],
line_info[:price].to_f)
else
format("| %s | %8.2f |\n", line_info[:name].ljust(46), line_info[:price].to_f)
end
end
end
class Promotion
def Promotion.create(promotion_data)
promotion_type, promotion_attrs = promotion_data.flatten
{:get_one_free => GetOneFreePromotion, :package => PackagePromotion,
:threshold => ThresholdPromotion}[promotion_type].new(promotion_attrs)
end
end
class GetOneFreePromotion
def initialize(free_item_frequency)
@free_item_frequency = free_item_frequency
end
def calculate_total(number_of_items, item_price)
(number_of_items - number_of_items / @free_item_frequency) * item_price
end
def calculate_discount(number_of_items, item_price)
(number_of_items / @free_item_frequency) * item_price
end
def to_s_representation
" (buy #{@free_item_frequency - 1}, get 1 free)"
end
end
class PackagePromotion
def initialize(promotion_attrs)
@package_size, discount_percent = promotion_attrs.flatten
@discount_percent = discount_percent / '100'.to_d
end
def calculate_total(number_of_items, item_price)
items_at_regular_price = number_of_items % @package_size
items_at_regular_price * item_price +
(number_of_items - items_at_regular_price) * item_price * (1 - @discount_percent)
end
def calculate_discount(number_of_items, item_price)
(number_of_items - number_of_items % @package_size) * @discount_percent * item_price
end
def to_s_representation
" (get #{(@discount_percent * 100).to_i}% off for every #{@package_size})"
end
end
class ThresholdPromotion
def initialize(promotion_attrs)
@threshold, discount_percent = promotion_attrs.flatten
@discount_percent = discount_percent / '100'.to_d
end
def calculate_total(number_of_items, item_price)
if number_of_items > @threshold
discounted_items = number_of_items - @threshold
(discounted_items * (1 - @discount_percent) + @threshold) * item_price
else
number_of_items * item_price
end
end
def calculate_discount(number_of_items, item_price)
if number_of_items > @threshold
(number_of_items - @threshold) * item_price * @discount_percent
else
'0'.to_d
end
end
def to_s_representation
ordinalized_threshold = Utils::Conversions.ordinalize(@threshold)
discount_percent = (@discount_percent * 100).to_i
" (#{discount_percent}% off of every after the #{ordinalized_threshold})"
end
end
#I wish I could have used the active_support's ordinalize.
module Utils
class Conversions
def Conversions.ordinalize(number)
if (11..13).cover?(number % 100)
return "#{number}th"
else
case number % 10
when 1 then return "#{number}st"
when 2 then return "#{number}nd"
when 3 then return "#{number}rd"
else return "#{number}th"
end
end
end
end
end
class Coupon
def Coupon.create(name, coupon_data)
coupon_type, coupon_attrs = coupon_data.flatten
{:percent => PercentCoupon,
:amount => FlatAmountCoupon}[coupon_type].new(name, coupon_attrs)
end
def initialize(name)
@name = name
end
end
class PercentCoupon < Coupon
def initialize(name, discount_percent)
super(name)
@discount_percent = discount_percent / '100'.to_d
end
def calculate_total(total)
@total = total
total * (1 - @discount_percent)
end
def calculate_discount(total_after_discount)
@total * @discount_percent
end
def to_s_representation
"Coupon #{@name} - #{(@discount_percent * 100).to_i}% off"
end
end
class FlatAmountCoupon < Coupon
def initialize(name, discount_amount)
super(name)
@discount_amount = BigDecimal(discount_amount.to_s)
end
def calculate_total(total)
@total = total
total_after_discount = total - @discount_amount
total_after_discount < 0 ? 0 : total_after_discount
end
def calculate_discount(total_after_discount)
[@total, @discount_amount].min
end
def to_s_representation
format("Coupon %s - %.2f off", @name, @discount_amount.to_f)
end
end

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

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

Finished in 0.56454 seconds
19 examples, 0 failures

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

Веселин обнови решението на 04.11.2011 00:47 (преди около 13 години)

+require 'bigdecimal'
+require 'bigdecimal/util'
+
+class Inventory
+ def initialize
+ @articles = {}
+ @coupons = {}
+ end
+
+ def register(name, price, promotion = {})
+ if (name.length > 40 || 0.01 > price.to_f || price.to_f > 999.99 ||
+ @articles.has_key?(name))
+ raise "Invalid parameters passed."
+ end
+ @articles[name] = Article.new(name, price.to_d, promotion)
+ end
+
+ def register_coupon(name, coupon_data)
+ if (@coupons.has_key? name)
+ raise "A coupon with the same name already exists."
+ end
+ @coupons[name] = Coupon.create(name, coupon_data)
+ end
+
+ def new_cart
+ Cart.new(@articles, @coupons)
+ end
+end
+
+class Cart
+ def initialize(articles, coupons)
+ @articles, @coupons = articles, coupons
+ @articles_in_cart, @coupon_in_use = Hash.new(0), nil
+ end
+
+ def add(item_name, amount = 1)
+ if (!@articles.has_key?(item_name) || amount < 0 || amount > 99)
+ raise "Invalid parameters passed."
+ end
+ @articles_in_cart[item_name] += amount
+ end
+
+ def use(coupon_name)
+ if (@coupons.has_key? coupon_name)
+ @coupon_in_use = @coupons[coupon_name]
+ else
+ raise "A coupon with the specified name doesn't exist."
+ end
+ end
+
+ def total
+ total = @articles_in_cart.inject('0'.to_d) do |sum, (k, v)|
+ sum + @articles[k].calculate_total(v)
+ end
+
+ @coupon_in_use ? @coupon_in_use.calculate_total(total) : total
+ end
+
+ def invoice
+ InvoiceCreator.create_invoice(@articles, @articles_in_cart, total, @coupon_in_use)
+ end
+end
+
+class Article
+ attr_reader :name
+
+ def initialize(name, price, promotion)
+ @name, @price, @quantity = name, price
+ if !promotion.empty?
+ @promotion = Promotion.create(promotion)
+ else
+ @promotion = nil
+ end
+ end
+
+ def calculate_total(number_of_items)
+ if @promotion.nil?
+ number_of_items * @price
+ else
+ @promotion.calculate_total(number_of_items, @price)
+ end
+ end
+
+ def get_invoice_info(number_of_items)
+ invoice_info = [{:name => "#{@name}", :quantity => number_of_items,
+ :price => (@price * number_of_items)}]
+ if @promotion
+ invoice_info.push({:name => @promotion.to_s_representation,
+ :price => -@promotion.calculate_discount(number_of_items, @price)})
+ end
+
+ invoice_info
+ end
+end
+
+class InvoiceCreator
+ DelimiterLine = "+#{"-" * 48}+#{"-" * 10}+\n"
+
+ def InvoiceCreator.create_invoice(items, items_in_cart, total, coupon = nil)
+ invoice = ""
+ invoice += InvoiceCreator.create_header
+
+ items_in_cart.each do |item_name, quantity|
+ items[item_name].get_invoice_info(quantity).each do |invoice_info|
+ invoice += print_product_line(invoice_info)
+ end
+ end
+ if (coupon)
+ invoice += InvoiceCreator.print_product_line :name => coupon.to_s_representation,
+ :price => -coupon.calculate_discount(total)
+ end
+
+ invoice += InvoiceCreator.create_footer(total)
+ end
+
+ private
+
+ def InvoiceCreator.create_header
+ DelimiterLine + (InvoiceCreator.print_title_line :name => "Name",
+ :quantity => "qty", :price => "price") + DelimiterLine
+ end
+
+ def InvoiceCreator.create_footer(total)
+ DelimiterLine + format("| %s | %8.2f |\n", "TOTAL".ljust(46), total.to_f) +
+ DelimiterLine
+ end
+
+ def InvoiceCreator.print_title_line(line_info)
+ format("| %s %3s | %8s |\n", line_info[:name].ljust(42), line_info[:quantity],
+ line_info[:price])
+ end
+
+ def InvoiceCreator.print_product_line(line_info)
+ if (line_info[:quantity])
+ format("| %s %3d | %8.2f |\n", line_info[:name].ljust(42), line_info[:quantity],
+ line_info[:price].to_f)
+ else
+ format("| %s | %8.2f |\n", line_info[:name].ljust(46), line_info[:price].to_f)
+ end
+ end
+end
+
+class Promotion
+ def Promotion.create(promotion_data)
+ promotion_type, promotion_attrs = promotion_data.flatten
+ {:get_one_free => GetOneFreePromotion, :package => PackagePromotion,
+ :threshold => ThresholdPromotion}[promotion_type].new(promotion_attrs)
+ end
+end
+
+class GetOneFreePromotion
+ def initialize(free_item_frequency)
+ @free_item_frequency = free_item_frequency
+ end
+
+ def calculate_total(number_of_items, item_price)
+ (number_of_items - number_of_items / @free_item_frequency) * item_price
+ end
+
+ def calculate_discount(number_of_items, item_price)
+ (number_of_items / @free_item_frequency) * item_price
+ end
+
+ def to_s_representation
+ " (buy #{@free_item_frequency - 1}, get 1 free)"
+ end
+end
+
+class PackagePromotion
+ def initialize(promotion_attrs)
+ @package_size, discount_percent = promotion_attrs.flatten
+ @discount_percent = discount_percent / '100'.to_d
+ end
+
+ def calculate_total(number_of_items, item_price)
+ items_at_regular_price = number_of_items % @package_size
+
+ items_at_regular_price * item_price +
+ (number_of_items - items_at_regular_price) * item_price * (1 - @discount_percent)
+ end
+
+ def calculate_discount(number_of_items, item_price)
+ (number_of_items - number_of_items % @package_size) * @discount_percent * item_price
+ end
+
+ def to_s_representation
+ " (get #{(@discount_percent * 100).to_i}% off for every #{@package_size})"
+ end
+end
+
+class ThresholdPromotion
+ def initialize(promotion_attrs)
+ @threshold, discount_percent = promotion_attrs.flatten
+ @discount_percent = discount_percent / '100'.to_d
+ end
+
+ def calculate_total(number_of_items, item_price)
+ if number_of_items > @threshold
+ discounted_items = number_of_items - @threshold
+ (discounted_items * (1 - @discount_percent) + @threshold) * item_price
+ else
+ number_of_items * item_price
+ end
+ end
+
+ def calculate_discount(number_of_items, item_price)
+ if number_of_items > @threshold
+ (number_of_items - @threshold) * item_price * @discount_percent
+ else
+ '0'.to_d
+ end
+ end
+
+ def to_s_representation
+ ordinalized_threshold = Utils::Conversions.ordinalize(@threshold)
+ discount_percent = (@discount_percent * 100).to_i
+ " (#{discount_percent}% off of every after the #{ordinalized_threshold})"
+ end
+end
+
+#I wish I could have used the active_support's ordinalize.
+module Utils
+ class Conversions
+ def Conversions.ordinalize(number)
+ if (11..13).cover?(number % 100)
+ return "#{number}th"
+ else
+ case number % 10
+ when 1 then return "#{number}st"
+ when 2 then return "#{number}nd"
+ when 3 then return "#{number}rd"
+ else return "#{number}th"
+ end
+ end
+ end
+ end
+end
+
+class Coupon
+ def Coupon.create(name, coupon_data)
+ coupon_type, coupon_attrs = coupon_data.flatten
+ {:percent => PercentCoupon,
+ :amount => FlatAmountCoupon}[coupon_type].new(name, coupon_attrs)
+ end
+
+ def initialize(name)
+ @name = name
+ end
+end
+
+class PercentCoupon < Coupon
+ def initialize(name, discount_percent)
+ super(name)
+ @discount_percent = discount_percent / '100'.to_d
+ end
+
+ def calculate_total(total)
+ @total = total
+
+ total * (1 - @discount_percent)
+ end
+
+ def calculate_discount(total_after_discount)
+ @total * @discount_percent
+ end
+
+ def to_s_representation
+ "Coupon #{@name} - #{(@discount_percent * 100).to_i}% off"
+ end
+end
+
+class FlatAmountCoupon < Coupon
+ def initialize(name, discount_amount)
+ super(name)
+ @discount_amount = BigDecimal(discount_amount.to_s)
+ end
+
+ def calculate_total(total)
+ @total = total
+ total_after_discount = total - @discount_amount
+
+ total_after_discount < 0 ? 0 : total_after_discount
+ end
+
+ def calculate_discount(total_after_discount)
+ [@total, @discount_amount].min
+ end
+
+ def to_s_representation
+ format("Coupon %s - %.2f off", @name, @discount_amount.to_f)
+ end
+end