class Machine

Attributes

available_change[RW]
products[RW]

Public Class Methods

new(products:, available_change:) click to toggle source
# File lib/machine/machine.rb, line 14
def initialize(products:, available_change:)
  @products = products
  @available_change = transform_keys_to_cash(available_change)
end

Public Instance Methods

refill_cash(cash) click to toggle source
# File lib/machine/machine.rb, line 31
def refill_cash(cash)
  reload_cash(cash_refills: cash)
end
refill_products(products) click to toggle source
# File lib/machine/machine.rb, line 27
def refill_products(products)
  reload_products(product_refills: products)
end
select_product(product:, coins:) click to toggle source
# File lib/machine/machine.rb, line 19
def select_product(product:, coins:)
  raise ArgumentError unless coins.is_a? Array
  raise NoMoneyError if coins.empty?
  product = products[product]
  raise OutOfSelectedProductError if product.nil? || product[:available].zero?
  find_product(product: product[:product], cash_values: Cash.cash_values(coins))
end

Private Instance Methods

calculate_change(product_price:, coins:) click to toggle source
# File lib/machine/machine.rb, line 66
        def calculate_change(product_price:, coins:)
  sorted_coins = coins.sort.reverse!
  sorted_coins.each do |coin|
    product_price -= coin
  end
  raise MoreMoneyError if product_price.positive?
  product_price.abs
end
calculate_coin_denominations(change:) click to toggle source
# File lib/machine/machine.rb, line 75
        def calculate_coin_denominations(change:)
  available_coins = remaining_change.keys.sort.reverse!
  change_array = []
  available_coins.each do |coin|
    next if (change - coin).negative?
    until (change - coin).negative?
      change -= coin
      change_array << coin
    end
  end
  decrement_available_change(change_array)
  change_array
end
change(product_price:, coins:) click to toggle source
# File lib/machine/machine.rb, line 62
        def change(product_price:, coins:)
  @change ||= calculate_change(product_price: product_price, coins: coins)
end
correct_change_given?(product_price:, coins: []) click to toggle source
# File lib/machine/machine.rb, line 57
        def correct_change_given?(product_price:, coins: [])
  raise ArgumentError unless coins.is_a? Array
  true if exact_change?(coins: coins) && (product_price - coins.first).zero?
end
decrement_available_change(change_array) click to toggle source
# File lib/machine/machine.rb, line 125
        def decrement_available_change(change_array)
  change_array.each { |change| available_change[change] -= 1 }
end
decrement_available_products(product) click to toggle source
# File lib/machine/machine.rb, line 133
        def decrement_available_products(product)
  products[product.name][:available] -= 1
end
exact_change?(coins:) click to toggle source
# File lib/machine/machine.rb, line 113
        def exact_change?(coins:)
  coins.length == 1
end
find_product(product:, cash_values:) click to toggle source
# File lib/machine/machine.rb, line 35
        def find_product(product:, cash_values:)
  product_price = product.cash_value
  if correct_change_given?(product_price: product_price, coins: cash_values)
    increment_available_change(cash_values)
    decrement_available_products(product)
    return product_and_change(product)
  end
  raise OutOfMoneyError if remaining_change.empty?
  increment_available_change(cash_values)
  change = change(product_price: product_price, coins: cash_values)
  coins = calculate_coin_denominations(change: change)
  decrement_available_products(product)
  product_and_change(product, Cash.string_values(coins))
end
increment_available_change(cash) click to toggle source
# File lib/machine/machine.rb, line 129
        def increment_available_change(cash)
  cash.each { |coin| available_change[coin] += 1 }
end
product_and_change(product, change = nil) click to toggle source
# File lib/machine/machine.rb, line 50
        def product_and_change(product, change = nil)
  {
    product: product.name,
    change: change.nil? ? ["0"] : change
  }
end
reload_cash(cash_refills:) click to toggle source
# File lib/machine/machine.rb, line 103
        def reload_cash(cash_refills:)
  raise ArgumentError if cash_refills.empty?
  raise InvalidCoinError unless (cash_refills.keys - Cash.valid_coins).empty?
  cash_value_refills = transform_keys_to_cash(cash_refills)
  cash_value_refills.each do |coin, amount|
    next if amount.negative?
    available_change[coin] += amount
  end
end
reload_products(product_refills:) click to toggle source
# File lib/machine/machine.rb, line 89
        def reload_products(product_refills:)
  raise ArgumentError unless product_refills.is_a? Array
  raise ArgumentError unless product_refills.all? { |product_refill| product_refill[:product].is_a? Product }
  raise ArgumentError if product_refills.empty?
  product_refills.each do |product_refill|
    product_name = product_refill[:product].name
    if products[product_name].nil?
      products[product_name] = product_refill
    else
      products[product_name][:available] += product_refill[:available]
    end
  end
end
remaining_change() click to toggle source
# File lib/machine/machine.rb, line 117
        def remaining_change
  available_change.select { |coin, remaining| remaining.positive? }
end
transform_keys_to_cash(coins) click to toggle source
# File lib/machine/machine.rb, line 121
        def transform_keys_to_cash(coins)
  coins.transform_keys! { |coin| Cash.cash_value(coin) }
end