Решение на Трета задача от Аксения Пенкова

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

Към профила на Аксения Пенкова

Резултати

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

Код

require 'bigdecimal'
require 'bigdecimal/util'
class Product
PRODUCT_NAME_LIMIT = 40
MIN_PRICE = 0.01
MAX_PRICE = 999.99
attr_reader :name, :price, :promotion
def initialize(name, price, promotion={})
price = BigDecimal price
validate_length_of name
validate_belonging_to_interval price
@name = name
@price = price
@promotion = initialize_promotion promotion
end
def initialize_promotion(hash)
promotion = nil
promotion = GetOneFree.new(hash[:get_one_free]) unless hash[:get_one_free].nil?
promotion = Package.new(*hash[:package].to_a[0]) unless hash[:package].nil?
promotion = Threshold.new(*hash[:threshold].to_a[0]) unless hash[:threshold].nil?
promotion
end
def promo?
!promotion.nil?
end
private
def validate_length_of(product)
error_message = "Name should be at most #{PRODUCT_NAME_LIMIT} symbols"
raise error_message if product.size > PRODUCT_NAME_LIMIT
end
def validate_belonging_to_interval(price)
error_message = "Price should be in [#{MIN_PRICE}, #{MAX_PRICE}]"
raise error_message if price < MIN_PRICE or price > MAX_PRICE
end
end
class Inventory
attr_reader :stock, :coupons
def initialize
@stock = []
@coupons = []
end
def register(name, price, promotion={})
validate_uniqueness_of name, stock
product = Product.new name, price, promotion
stock << product
end
def register_coupon(name, options)
validate_uniqueness_of name, coupons
type, value = options.to_a.first
coupon = PercentCoupon.new(name, value) if type == :percent
coupon = AmountCoupon.new(name, value) if type == :amount
coupons << coupon
end
def find(item)
type, name = item.to_a.first
type == :product ? container = stock : container = coupons
container.select { |item| item.name == name }.first
end
def new_cart
cart = ShoppingCart.new self
end
private
def validate_uniqueness_of(item, container)
error_message = "#{item} is already registered"
registered = container.map(&:name).map(&:downcase)
raise error_message if registered.include? item.downcase
end
end
class CartItem
QUANTITY_LIMIT = 99
attr_reader :product
attr_accessor :quantity
def initialize(product, quantity)
validate_value_of quantity
@product = product
@quantity = quantity
end
def increase_quantity(quantity)
validate_value_of self.quantity + quantity
self.quantity += quantity
end
def price
product.price * quantity
end
def discount
return '0'.to_d unless product.promo?
product.promotion.item_discount product.price, quantity
end
private
def validate_value_of(quantity)
error_message = "Quantity should be positive integer less than 100"
raise error_message if quantity <= 0 or quantity > QUANTITY_LIMIT
end
end
class ShoppingCart
attr_reader :goods, :inventory
attr_accessor :coupon, :invoice
def initialize(inventory, invoice='')
@inventory = inventory
@goods = []
@coupon = nil
@invoice = invoice
end
def add(name, quantity=1)
validate_registration_of name, inventory.stock
item = goods.select { |item| item.product.name == name }
if item.empty?
product = inventory.find product: name
goods << CartItem.new(product, quantity)
else
item.first.increase_quantity quantity
end
end
def use(name)
validate_registration_of name, inventory.coupons
validate_claimed_coupon
self.coupon = inventory.find coupon: name
end
def products_price
goods.inject(0) { |sum, cart_item| sum += cart_item.price }
end
def products_discount
goods.inject(0) { |sum, cart_item| sum += cart_item.discount }
end
def coupon_discount
return '0'.to_d if coupon.nil?
price = products_price
discount = products_discount
coupon.discount(price - discount)
end
def total
products_price - products_discount - coupon_discount
end
def invoice
self.invoice = (Invoice.new self).invoice
end
private
def validate_registration_of(item, container)
error_message = "#{item} is not registered"
registered = container.map(&:name).map(&:downcase)
raise error_message unless registered.include? item.downcase
end
def validate_claimed_coupon
error_message = "You alredy claimed to use #{coupon}"
raise error_message unless self.coupon.nil?
end
end
class GetOneFree
attr_reader :n_th_free
def initialize(n_th_free)
validate_numericallity_of n_th_free
@n_th_free = n_th_free
end
def free_items(quantity)
quantity / n_th_free
end
def paid_items(quantity)
quantity - free_items(quantity)
end
def item_discount(price, quantity)
price * free_items(quantity)
end
def invoice
"(buy #{n_th_free - 1}, get 1 free)"
end
private
def validate_numericallity_of(n_th_free)
error_message = "The value should be positive integer more than 1"
raise error_message if n_th_free <= 1 or !n_th_free.kind_of? Integer
end
end
class Package
attr_reader :size, :discount
def initialize(size, discount)
@size = size
@discount = discount
end
def bought_packages(quantity)
quantity / size
end
def item_discount(price, quantity)
packs = bought_packages quantity
packs * size * price * discount / 100
end
def invoice
"(get #{discount}% off for every #{size})"
end
end
class Threshold
attr_reader :size, :discount
def initialize(size, discount)
@size = size
@discount = discount
end
def discounted_items(quantity)
quantity - size < 0 ? 0 : quantity - size
end
def item_discount(price, quantity)
discounted = discounted_items quantity
discounted * price * discount / 100
end
def number_suffix
suffixes = {1 => 'st', 2 => 'nd', 3 => 'rd'}
suffix = suffixes[size % 10] || 'th'
suffix = 'th' if [11, 12, 13].include? size
suffix
end
def invoice
"(#{discount}% off of every after the #{size}#{number_suffix})"
end
end
class PercentCoupon
attr_reader :name, :percent
def initialize(name, percent)
@name = name
@percent = percent
end
def discount(price)
price * percent / 100
end
def invoice
"Coupon #{name} - #{percent}% off"
end
end
class AmountCoupon
attr_reader :name, :amount
def initialize(name, amount)
@name = name
@amount = amount
end
def discount(price)
price - amount < 0 ? price : amount
end
def invoice
"Coupon #{name} - #{sprintf '%.2f', amount.to_f} off"
end
end
class Invoice
SEPARATOR = "+------------------------------------------------+----------+\n"
HEADER = "| Name qty | price |\n"
TOTAL = "| TOTAL |"
attr_reader :cart, :invoice
def initialize(cart)
@cart = cart
@invoice = create_invoice
end
def print(price)
sprintf '%.2f', price.to_f
end
def invoice_header
SEPARATOR + HEADER + SEPARATOR
end
def invoice_body
body = ''
cart.goods.each do |cart_item|
body += "| #{cart_item.product.name.ljust 40} #{cart_item.quantity.to_s.rjust 5}" +
" | #{print(cart_item.price).rjust 8} |\n"
body += "| #{cart_item.product.promotion.invoice.ljust 44} | " +
"#{print(0 - cart_item.discount).rjust 8} |\n" if cart_item.product.promo?
end
body
end
def invoice_coupon
coupon = ''
coupon += "| #{cart.coupon.invoice.ljust 46} | " +
"#{print(0 - cart.coupon_discount).rjust 8} |\n" unless cart.coupon.nil?
coupon
end
def invoice_total
SEPARATOR + TOTAL + "#{sprintf('%.2f', cart.total.to_f).to_s.rjust 9} |\n" + SEPARATOR
end
def create_invoice
invoice_header + invoice_body + invoice_coupon + invoice_total
end
end

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

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

Finished in 0.5855 seconds
19 examples, 0 failures

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

Аксения обнови решението на 06.11.2011 20:19 (преди около 13 години)

+require 'bigdecimal'
+require 'bigdecimal/util'
+
+class Product
+ PRODUCT_NAME_LIMIT = 40
+
+ MIN_PRICE = 0.01
+ MAX_PRICE = 999.99
+
+ attr_reader :name, :price, :promotion
+
+ def initialize(name, price, promotion={})
+ price = BigDecimal price
+
+ validate_length_of name
+ validate_belonging_to_interval price
+
+ @name = name
+ @price = price
+ @promotion = initialize_promotion promotion
+ end
+
+ def initialize_promotion(hash)
+ promotion = nil
+ promotion = GetOneFree.new(hash[:get_one_free]) unless hash[:get_one_free].nil?
+ promotion = Package.new(*hash[:package].to_a[0]) unless hash[:package].nil?
+ promotion = Threshold.new(*hash[:threshold].to_a[0]) unless hash[:threshold].nil?
+
+ promotion
+ end
+
+ def promo?
+ !promotion.nil?
+ end
+
+ private
+ def validate_length_of(product)
+ error_message = "Name should be at most #{PRODUCT_NAME_LIMIT} symbols"
+
+ raise error_message if product.size > PRODUCT_NAME_LIMIT
+ end
+
+ def validate_belonging_to_interval(price)
+ error_message = "Price should be in [#{MIN_PRICE}, #{MAX_PRICE}]"
+
+ raise error_message if price < MIN_PRICE or price > MAX_PRICE
+ end
+end
+
+class Inventory
+ attr_reader :stock, :coupons
+
+ def initialize
+ @stock = []
+ @coupons = []
+ end
+
+ def register(name, price, promotion={})
+ validate_uniqueness_of name, stock
+
+ product = Product.new name, price, promotion
+ stock << product
+ end
+
+ def register_coupon(name, options)
+ validate_uniqueness_of name, coupons
+
+ type, value = options.to_a.first
+
+ coupon = PercentCoupon.new(name, value) if type == :percent
+ coupon = AmountCoupon.new(name, value) if type == :amount
+
+ coupons << coupon
+ end
+
+ def find(item)
+ type, name = item.to_a.first
+
+ type == :product ? container = stock : container = coupons
+
+ container.select { |item| item.name == name }.first
+ end
+
+ def new_cart
+ cart = ShoppingCart.new self
+ end
+
+ private
+ def validate_uniqueness_of(item, container)
+ error_message = "#{item} is already registered"
+ registered = container.map(&:name).map(&:downcase)
+
+ raise error_message if registered.include? item.downcase
+ end
+end
+
+class CartItem
+ QUANTITY_LIMIT = 99
+
+ attr_reader :product
+ attr_accessor :quantity
+
+ def initialize(product, quantity)
+ validate_value_of quantity
+
+ @product = product
+ @quantity = quantity
+ end
+
+ def increase_quantity(quantity)
+ validate_value_of self.quantity + quantity
+
+ self.quantity += quantity
+ end
+
+ def price
+ product.price * quantity
+ end
+
+ def discount
+ return '0'.to_d unless product.promo?
+
+ product.promotion.item_discount product.price, quantity
+ end
+
+ private
+ def validate_value_of(quantity)
+ error_message = "Quantity should be positive integer less than 100"
+ raise error_message if quantity <= 0 or quantity > QUANTITY_LIMIT
+ end
+end
+
+class ShoppingCart
+ attr_reader :goods, :inventory
+ attr_accessor :coupon, :invoice
+
+ def initialize(inventory, invoice='')
+ @inventory = inventory
+ @goods = []
+ @coupon = nil
+ @invoice = invoice
+ end
+
+ def add(name, quantity=1)
+ validate_registration_of name, inventory.stock
+
+ item = goods.select { |item| item.product.name == name }
+ if item.empty?
+ product = inventory.find product: name
+
+ goods << CartItem.new(product, quantity)
+ else
+ item.first.increase_quantity quantity
+ end
+ end
+
+ def use(name)
+ validate_registration_of name, inventory.coupons
+ validate_claimed_coupon
+
+ self.coupon = inventory.find coupon: name
+ end
+
+ def products_price
+ goods.inject(0) { |sum, cart_item| sum += cart_item.price }
+ end
+
+ def products_discount
+ goods.inject(0) { |sum, cart_item| sum += cart_item.discount }
+ end
+
+ def coupon_discount
+ return '0'.to_d if coupon.nil?
+
+ price = products_price
+ discount = products_discount
+
+ coupon.discount(price - discount)
+ end
+
+ def total
+ products_price - products_discount - coupon_discount
+ end
+
+ def invoice
+ self.invoice = (Invoice.new self).invoice
+ end
+
+ private
+ def validate_registration_of(item, container)
+ error_message = "#{item} is not registered"
+ registered = container.map(&:name).map(&:downcase)
+
+ raise error_message unless registered.include? item.downcase
+ end
+
+ def validate_claimed_coupon
+ error_message = "You alredy claimed to use #{coupon}"
+
+ raise error_message unless self.coupon.nil?
+ end
+end
+
+class GetOneFree
+ attr_reader :n_th_free
+
+ def initialize(n_th_free)
+ validate_numericallity_of n_th_free
+
+ @n_th_free = n_th_free
+ end
+
+ def free_items(quantity)
+ quantity / n_th_free
+ end
+
+ def paid_items(quantity)
+ quantity - free_items(quantity)
+ end
+
+ def item_discount(price, quantity)
+ price * free_items(quantity)
+ end
+
+ def invoice
+ "(buy #{n_th_free - 1}, get 1 free)"
+ end
+
+ private
+ def validate_numericallity_of(n_th_free)
+ error_message = "The value should be positive integer more than 1"
+ raise error_message if n_th_free <= 1 or !n_th_free.kind_of? Integer
+ end
+end
+
+class Package
+ attr_reader :size, :discount
+
+ def initialize(size, discount)
+ @size = size
+ @discount = discount
+ end
+
+ def bought_packages(quantity)
+ quantity / size
+ end
+
+ def item_discount(price, quantity)
+ packs = bought_packages quantity
+
+ packs * size * price * discount / 100
+ end
+
+ def invoice
+ "(get #{discount}% off for every #{size})"
+ end
+end
+
+class Threshold
+ attr_reader :size, :discount
+
+ def initialize(size, discount)
+ @size = size
+ @discount = discount
+ end
+
+ def discounted_items(quantity)
+ quantity - size < 0 ? 0 : quantity - size
+ end
+
+ def item_discount(price, quantity)
+ discounted = discounted_items quantity
+
+ discounted * price * discount / 100
+ end
+
+ def number_suffix
+ suffixes = {1 => 'st', 2 => 'nd', 3 => 'rd'}
+
+ suffix = suffixes[size % 10] || 'th'
+ suffix = 'th' if [11, 12, 13].include? size
+
+ suffix
+ end
+
+ def invoice
+ "(#{discount}% off of every after the #{size}#{number_suffix})"
+ end
+end
+
+class PercentCoupon
+ attr_reader :name, :percent
+
+ def initialize(name, percent)
+ @name = name
+ @percent = percent
+ end
+
+ def discount(price)
+ price * percent / 100
+ end
+
+ def invoice
+ "Coupon #{name} - #{percent}% off"
+ end
+end
+
+class AmountCoupon
+ attr_reader :name, :amount
+
+ def initialize(name, amount)
+ @name = name
+ @amount = amount
+ end
+
+ def discount(price)
+ price - amount < 0 ? price : amount
+ end
+
+ def invoice
+ "Coupon #{name} - #{sprintf '%.2f', amount.to_f} off"
+ end
+end
+
+class Invoice
+ SEPARATOR = "+------------------------------------------------+----------+\n"
+ HEADER = "| Name qty | price |\n"
+ TOTAL = "| TOTAL |"
+
+ attr_reader :cart, :invoice
+
+ def initialize(cart)
+ @cart = cart
+ @invoice = create_invoice
+ end
+
+ def print(price)
+ sprintf '%.2f', price.to_f
+ end
+
+ def invoice_header
+ SEPARATOR + HEADER + SEPARATOR
+ end
+
+ def invoice_body
+ body = ''
+
+ cart.goods.each do |cart_item|
+ body += "| #{cart_item.product.name.ljust 40} #{cart_item.quantity.to_s.rjust 5}" +
+ " | #{print(cart_item.price).rjust 8} |\n"
+ body += "| #{cart_item.product.promotion.invoice.ljust 44} | " +
+ "#{print(0 - cart_item.discount).rjust 8} |\n" if cart_item.product.promo?
+ end
+
+ body
+ end
+
+ def invoice_coupon
+ coupon = ''
+
+ coupon += "| #{cart.coupon.invoice.ljust 46} | " +
+ "#{print(0 - cart.coupon_discount).rjust 8} |\n" unless cart.coupon.nil?
+
+ coupon
+ end
+
+ def invoice_total
+ SEPARATOR + TOTAL + "#{sprintf('%.2f', cart.total.to_f).to_s.rjust 9} |\n" + SEPARATOR
+ end
+
+ def create_invoice
+ invoice_header + invoice_body + invoice_coupon + invoice_total
+ end
+end