diff --git a/app/models/bet.rb b/app/models/bet.rb index 1c46571..466f04d 100644 --- a/app/models/bet.rb +++ b/app/models/bet.rb @@ -1,4 +1,5 @@ class Bet < ApplicationRecord + include DebtFilter has_many :users, through: :user_bets has_many :user_bets has_many :options, class_name: 'BetOption' @@ -10,13 +11,13 @@ class Bet < ApplicationRecord def resolve(winning_option:) winning_option.update!(winner: true) debt_relationships = user_bets.winners.to_a.product(user_bets.losers.to_a) - debt_relationships.map do |(won_bet, lost_bet)| - Debt.create( + save_debts(debt_relationships.map do |(won_bet, lost_bet)| + Debt.new( creditor: won_bet.user, debtor: lost_bet.user, amount: amount_owed(lost_bet, won_bet) ) - end + end) end def winner_total diff --git a/app/models/debt_filter.rb b/app/models/debt_filter.rb new file mode 100644 index 0000000..3192f52 --- /dev/null +++ b/app/models/debt_filter.rb @@ -0,0 +1,55 @@ +module DebtFilter + def save_debts(debts) + filter_debts(debts).map do |debt| + debt.save + debt + end + end + + private + + def filter_debts(debts) + reverse_negatives(merge_debts(reverse_duplicates(debts))) + end + + def unique_participants(debts) + uniques = [] + debts.each do |debt| + next if uniques.include?([debt.creditor, debt.debtor]) || + uniques.include?([debt.debtor, debt.creditor]) + uniques << [debt.debtor, debt.creditor] + end + uniques + end + + def reverse_duplicates(debts) + uniques = unique_participants(debts) + debts.map do |debt| + if uniques.include?([debt.debtor, debt.creditor]) + debt + else + Debt.new(debtor: debt.creditor, + creditor: debt.debtor, amount: -debt.amount) + end + end + end + + def merge_debts(debts) + debts.group_by { |debt| [debt.debtor, debt.creditor] }.map do |_, group| + Debt.new(debtor: group.first.debtor, + creditor: group.first.creditor, + amount: group.sum(&:amount)) + end + end + + def reverse_negatives(debts) + debts.map do |debt| + if debt.amount < 0 + Debt.new(debtor: debt.creditor, + creditor: debt.debtor, amount: debt.amount.abs) + else + debt + end + end + end +end diff --git a/spec/modules/debt_filter_spec.rb b/spec/modules/debt_filter_spec.rb new file mode 100644 index 0000000..41e7b0c --- /dev/null +++ b/spec/modules/debt_filter_spec.rb @@ -0,0 +1,42 @@ +require 'rails_helper' + +RSpec.describe DebtFilter, type: :module do + let(:debt_filter) { Class.new { extend DebtFilter } } + let(:bob) do + User.create(username: 'Bob', email: 'bob@mail.com', password: '123') + end + let(:peter) do + User.create(username: 'Peter', email: 'peter@mail.com', password: 'pete1') + end + let(:jack) do + User.create(username: 'Jack', email: 'jack@mail.com', password: 'jeeek') + end + + let(:debt1) { Debt.new(debtor: bob, creditor: peter, amount: 20) } + let(:debt2) { Debt.new(debtor: peter, creditor: bob, amount: 100) } + let(:debt3) { Debt.new(debtor: bob, creditor: peter, amount: 60) } + let(:debt4) { Debt.new(debtor: jack, creditor: peter, amount: 55.5) } + let(:debts) { [debt1, debt2, debt3, debt4] } + + describe('#save_debts') do + subject(:saved_debts) { debt_filter.save_debts(debts) } + let(:expected_debt1) do + Debt.new(debtor: peter, creditor: bob, amount: 20) + end + let(:expected_debt2) { debt4 } + it 'saves debts after filtering them' do + aggregate_failures 'testing debts' do + expect(saved_debts.first).to( + have_attributes(debtor: expected_debt1.debtor, + creditor: expected_debt1.creditor, + amount: expected_debt1.amount) + ) + expect(saved_debts.second).to( + have_attributes(debtor: expected_debt2.debtor, + creditor: expected_debt2.creditor, + amount: expected_debt2.amount) + ) + end + end + end +end