Skip to content

Instantly share code, notes, and snippets.

@lukemorton
Last active September 24, 2016 18:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lukemorton/260cc1535e67cd2a76c8463beaa64596 to your computer and use it in GitHub Desktop.
Save lukemorton/260cc1535e67cd2a76c8463beaa64596 to your computer and use it in GitHub Desktop.
module Billing
extend self
SENIOR_CITIZEN_DISCOUNT = 5
def billable_accounts
Account.where(free: false)
end
def monthly_bill(account)
Bill.new(
account: account,
amount: account.plan.amount - discounts(account),
)
end
def discounts(account)
if account.type == :senior_citizen
SENIOR_CITIZEN_DISCOUNT
else
0
end
end
end
FactoryGirl.create_list(:account, 2)
expect(Billing.billable_accounts.count).to eq(2)
expect(Billing.billable_accounts).to include(a_kind_of(Account))
expect(Account).to receive(:where).with(free: false).and_return([:account])
expect(Billing.billable_accounts).to contain_exactly(:account)
standard_account = double(type: :standard, plan: double(amount: 10))
bill = Billing.monthly_bill(standard_account)
expect(bill.account).to eq(standard_account)
expect(bill.amount).to eq(10)
senior_account = double(type: :senior_citizen, plan: double(amount: 10))
bill = Billing.monthly_bill(senior_account)
expect(bill.amount).to eq(5)
describe(Billing) do
context 'calculating discount' do
it 'should calculate discount correctly' do
account = Account.new(type: :senior_citizen)
discount = Billing.discount(account)
expect(discount).to eq(5)
end
end
context 'retrieving billable accounts' do
it 'should retrieve billable accounts' do
expect(Account).to receive(:where).with(free: false).and_return([:account])
expect(Billing.billable_accounts).to contain_exactly(:account)
end
end
context 'creating monthly bill' do
it 'should create £10 bill for standard account' do
standard_account = double(type: :standard, plan: double(amount: 10))
bill = Billing.monthly_bill(standard_account)
expect(bill.account).to eq(standard_account)
expect(bill.amount).to eq(10)
end
it 'should create £5 bill for senior citizen' do
senior_account = double(type: :senior_citizen, plan: double(amount: 10))
bill = Billing.monthly_bill(senior_account)
expect(bill.amount).to eq(5)
end
end
end
module Billing
# [..]
private
def discounts(account)
if account.type == :senior_citizen
SENIOR_CITIZEN_DISCOUNT
else
0
end
end
end
module Billing
module Accounts
extend self
def all
Account.where(free: false)
end
end
module MonthlyBill
extend self
SENIOR_CITIZEN_DISCOUNT = 5
def for_account(account)
Bill.new(
account: account,
amount: account.plan.amount - discounts(account),
)
end
private
def discounts(account)
if account.type == :senior_citizen
SENIOR_CITIZEN_DISCOUNT
else
0
end
end
end
end
describe(Billing::Accounts) do
context 'retrieving billable accounts' do
it 'should retrieve billable accounts' do
expect(Account).to receive(:where).with(free: false).and_return([:account])
expect(Billing::Accounts.all).to contain_exactly(:account)
end
end
end
describe(Billing::MonthlyBill)
context 'creating monthly bill' do
it 'should create £10 bill for standard account' do
standard_account = double(type: :standard, plan: double(amount: 10))
bill = Billing::MonthlyBill.for_account(standard_account)
expect(bill.account).to eq(standard_account)
expect(bill.amount).to eq(10)
end
it 'should create £5 bill for senior citizen' do
senior_account = double(type: :senior_citizen, plan: double(amount: 10))
bill = Billing::MonthlyBill.monthly_bill(senior_account)
expect(bill.amount).to eq(5)
end
end
end
class MonthlyBillingJob
def perform
Billing::Accounts.all.each do |account|
bill = Billing::MonthlyBill.for_account(account)
bill.save!
BillMailer.new_bill(bill).deliver_now
end
end
end
class MonthlyBillingJob
def perform(create_and_send_monthly_bill)
create_and_send_monthly_bill.to_all_accounts
end
end
describe(MonthlyBillingJob) do
it 'should send bills to all accounts' do
create_and_send_monthly_bill = double(to_all_accounts: nil)
MonthlyBillingJob.new.perform(create_and_send_monthly_bill)
expect(create_and_send_monthly_bill).to have_received(:to_all_accounts)
end
end
module Billing
class CreateAndSendMonthlyBill
def to_all_accounts
Billing::Accounts.all.each do |account|
bill = Billing::MonthlyBill.for_account(account)
bill.save!
BillMailer.new_bill(bill).deliver_now
end
end
end
end
describe(Billing::CreateAndSendMonthlyBill) do
it 'should create and send bills to all accounts' do
account = double(type: :standard, plan: double(amount: 10))
expect(Account).to receive(:where).with(free: false).and_return([account])
bill = double(save!: true)
expect(bill).to receive(:save!)
expect(Billing::MonthlyBill).to receive(:for_account).and_return(bill)
mail = double(deliver_now: nil)
expect(BillMailer).to receive(:new_bill).and_return(mail)
expect(mail).to receive(:deliver_now)
sender = Billing::CreateAndSendMonthlyBill.new
sender.to_all_accounts
end
end
describe(Billing::CreateAndSendMonthlyBill) do
it 'should create and send bills to all accounts' do
billing_accounts = double(all: [:account])
bill = double(save!: true)
expect(bill).to receive(:save!)
monthly_bill_initialiser = double(for_account: bill)
mail = double(deliver_now: nil)
expect(BillMailer).to receive(:new_bill).and_return(mail)
expect(mail).to receive(:deliver_now)
sender = Billing::CreateAndSendMonthlyBill.new(
billing_accounts: billing_accounts,
monthly_bill_initialiser: monthly_bill_initialiser,
)
sender.to_all_accounts
end
end
module Billing
class CreateAndSendMonthlyBill
def initialize(
billing_accounts: Billing::Accounts,
monthly_bill_initialiser: Billing::MonthlyBill
)
@billing_accounts = billing_accounts
@monthly_bill_initialiser = monthly_bill_initialiser
end
def to_all_accounts
@billing_accounts.all.each do |account|
bill = @monthly_bill_initialiser.for_account(account)
bill.save!
BillMailer.new_bill(bill).deliver_now
end
end
end
end
module Billing
class CreateAndSendMonthlyBill
def initialize(
billing_accounts: Accounts,
create_monthly_bill: CreateMonthlyBill.new,
send_monthly_bill: SendMonthlyBill.new
)
@billing_accounts = billing_accounts
@create_monthly_bill = create_monthly_bill
@send_monthly_bill = send_monthly_bill
end
def to_all_accounts
@billing_accounts.all.each do |account|
bill = @create_monthly_bill.for_account(account)
@send_monthly_bill.send(bill)
end
end
end
end
module Billing
class CreateMonthlyBill
def initialize(monthly_bill_initialiser: Billing::MonthlyBill)
@monthly_bill_initialiser = monthly_bill_initialiser
end
def for_account(account)
bill = @monthly_bill_initialiser.for_account(account)
bill.save!
bill
end
end
end
module Billing
class SendMonthlyBill
def send(bill)
BillMailer.new_bill(bill).deliver_now
end
end
end
describe(Billing::CreateMonthlyBill) do
it 'should create bill for account' do
bill = double(save!: true)
expect(bill).to receive(:save!)
monthly_bill_initialiser = double(for_account: bill)
create_monthly_bill = Billing::CreateMonthlyBill.new(
monthly_bill_initialiser: monthly_bill_initialiser,
)
create_monthly_bill.for_account(:account)
end
end
describe(Billing::SendMonthlyBill) do
it 'should send bill' do
mail = double(deliver_now: nil)
expect(BillMailer).to receive(:new_bill).and_return(mail)
expect(mail).to receive(:deliver_now)
send_monthly_bill = Billing::SendMonthlyBill.new
send_monthly_bill.send(:buil)
end
end
describe(Billing::CreateAndSendMonthlyBill) do
it 'should create and send bills to all accounts' do
create_monthly_bill = double(for_account: :bill)
send_monthly_bill = double(send: :bill)
sender = Billing::CreateAndSendMonthlyBill.new(
billing_accounts: double(all: [:account]),
create_monthly_bill: create_monthly_bill,
send_monthly_bill: send_monthly_bill,
)
sender.to_all_accounts
expect(create_monthly_bill).to have_received(:for_account)
expect(send_monthly_bill).to have_received(:send)
end
end
module Billing
class FindAccounts
def all
Account.where(free: false)
end
end
end
describe(Billing::FindAccounts) do
context 'retrieving billable accounts' do
it 'should retrieve billable accounts' do
expect(Account).to receive(:where).with(free: false).and_return([:account])
expect(Billing::FindAccounts.new.all).to contain_exactly(:account)
end
end
end
module Billing
class CreateMonthlyBill
SENIOR_CITIZEN_DISCOUNT = 5
def initialize(bill: Bill.new)
@bill = bill
end
def for_account(account)
@bill.account = account
@bill.amount = account.plan.amount - discounts(account)
@bill.save!
@bill
end
private
def discounts(account)
if account.type == :senior_citizen
SENIOR_CITIZEN_DISCOUNT
else
0
end
end
end
end
describe(Billing::CreateMonthlyBill) do
context 'creating monthly bill' do
let(:bill) do
double(
:account= => nil,
:amount= => nil,
:save! => true
)
end
it 'should create £10 bill for standard account' do
standard_account = double(type: :standard, plan: double(amount: 10))
Billing::CreateMonthlyBill.new(bill: bill).for_account(standard_account)
expect(bill).to have_received(:account=).with(standard_account)
expect(bill).to have_received(:amount=).with(10)
expect(bill).to have_received(:save!)
end
it 'should create £5 bill for senior citizen' do
senior_account = double(type: :senior_citizen, plan: double(amount: 10))
Billing::CreateMonthlyBill.new(bill: bill).for_account(senior_account)
expect(bill).to have_received(:amount=).with(5)
end
end
end
app
├── jobs
│   └── monthly_billing_job.rb
├── mailers
│   └── bill_mailer.rb
└── models
├── account.rb
└── bill.rb
lib
├── billing
│   ├── create_and_send_monthly_bill.rb
│   ├── create_monthly_bill.rb
│   ├── find_accounts.rb
│   └── send_monthly_bill.rb
└── dependencies.rb
spec/app
└── jobs
└── monthly_billing_job_spec.rb
spec/lib
└── billing
├── create_and_send_monthly_bill_spec.rb
├── create_monthly_bill_spec.rb
├── find_accounts_spec.rb
└── send_monthly_bill_spec.rb
lib/billing
├── create_and_send_monthly_bill.rb
├── create_monthly_bill.rb
└── send_monthly_bill.rb
spec/billing
├── create_and_send_monthly_bill_spec.rb
├── create_monthly_bill_spec.rb
└── send_monthly_bill_spec.rb
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment