This is part 3 (the last) in this series.
Introduction:
In part 2, this was achieved;
- Scanning of each of the items, A, B, C, and D was done
- The corresponding test was written for each
In part 3, this is going to be handled;
- Some house cleaning
- Changing
CheckOut
Spec - Adding
PricingRules
Spec - Adding
PricingRules
Class - Adding pricing rules
Some House Cleaning:
The code in the ChecKOut
class needs to be included in a separate file under lib/checkout.rb
Below is the checkout.rb
file:
class CheckOut
def initialize(pricing_rules)
@total = 0
end
def total
@total
end
def scan(item)
if item == 'A'
@total = 50
elsif item == 'B'
@total = 30
elsif item == 'C'
@total = 20
else
@total = 15
end
end
end
The checkout
spec needs to included in a separate file under spec/checkout_spec.rb
Below is the checkout_spec.rb
file:
require 'checkout'
RSpec.describe 'checkout' do
context 'when nothing has been scanned' do
it 'shows a total of zero' do
pricing_rules = :Rule
checkout = CheckOut.new(pricing_rules)
total = checkout.total
expect(total).to eq 0
end
end
context 'when A has been scanned' do
it 'shows the price of A' do
pricing_rules = :Rule
checkout = CheckOut.new(pricing_rules)
checkout.scan('A')
total = checkout.total
expect(total).to eq 50
end
end
context 'when B has been scanned' do
it 'shows the price of B' do
pricing_rules = :Rule
checkout = CheckOut.new(pricing_rules)
checkout.scan('B')
total = checkout.total
expect(total).to eq 30
end
end
context 'when C has been scanned' do
it 'shows the price of C' do
pricing_rules = :Rule
checkout = CheckOut.new(pricing_rules)
checkout.scan('C')
total = checkout.total
expect(total).to eq 20
end
end
context 'when D has been scanned' do
it 'shows the price of D' do
pricing_rules = :Rule
checkout = CheckOut.new(pricing_rules)
checkout.scan('D')
total = checkout.total
expect(total).to eq 15
end
end
end
Changing CheckOut
Spec:
This is the new checkout_spec.rb
file:
require 'checkout'
require 'pricing_rules'
RSpec.describe 'checkout' do
context 'when nothing has been scanned' do
it 'shows total of zero' do
pricing_rules = :Rule
checkout = CheckOut.new(pricing_rules)
total = checkout.total
expect(total).to eq 0
end
end
context 'when A has been scanned' do·
it 'shows the price of A' do
pricing_rules = PricingRules.new(
"A" => {unit_price: 50, discount_price: 130, discount_count: 3})
checkout = CheckOut.new(pricing_rules)
checkout.scan('A')
total = checkout.total
expect(total).to eq 50
end
end
context 'when B has been scanned' do
it 'shows the price of B' do
pricing_rules = PricingRules.new(
"B" => {unit_price: 30, discount_price: 45, discount_count: 2})
checkout = CheckOut.new(pricing_rules)
checkout.scan('B')
total = checkout.total
expect(total).to eq 30
end
end
context 'when C has been scanned' do·
it 'shows the price of C' do
pricing_rules = PricingRules.new("C" => {unit_price: 20})
checkout = CheckOut.new(pricing_rules)
checkout.scan('C')
total = checkout.total
expect(total).to eq 20
end
end
context 'when D has been scanned' do·
it 'shows the price of D' do
pricing_rules = PricingRules.new("D" => {unit_price: 15})
checkout = CheckOut.new(pricing_rules)
checkout.scan('D')
total = checkout.total
expect(total).to eq 15
end
end
end
This is the new checkout.rb
file:
class CheckOut
def initialize(pricing_rules)
@pricing_rules = pricing_rules
@items_count = Hash.new(0)·
end
def scan(item)
@items_count[item] += 1
end
def total
@items_count.inject(0) do |total, (item, count)|
if item == 'B' || item == 'A'
total += @pricing_rules.price_for(item, count)
else
total += count*@pricing_rules.unit_price(item)
end
end
end
end
Adding PricingRules
Spec:
This is the pricing_rules_spec.rb
file;
require "pricing_rules"
RSpec.describe PricingRules do
describe "#unit_price" do
it "fetches the unit price for the item" do
pricing_rules = PricingRules.new("A" => {unit_price: 50})
unit_price = pricing_rules.unit_price("A")
expect(unit_price).to eq 50
end
end
describe "#discount_price" do
it "fetches the discount price for the item" do
pricing_rules = PricingRules.new("A" => {discount_price: 130})
discount_price = pricing_rules.discount_price("A")
expect(discount_price).to eq 130
end
end
describe "#discount_count" do
it "fetches the discount count of the item" do
pricing_rules = PricingRules.new("A" => {discount_count: 3})
discount_count = pricing_rules.discount_count("A")
expect(discount_count).to eq 3
end
end
describe "#price_for" do
it "calculates the price for a number of items" do
pricing_rules = PricingRules.new(
"A" => {unit_price: 50, discount_price: 130, discount_count: 3})
price = pricing_rules.price_for("A", 3)
expect(price).to eq 130
end
end
describe "#has_dicount_price" do
it "checks if an item has a discount price" do
pricing_rules = PricingRules.new("A" => {discount_price: 130})
has_discount = pricing_rules.has_discount_price("A")
expect(has_discount).to be(true)
end
end
end
Adding PricingRules
Class:
This is the pricing_rules.rb
file:
class PricingRules·
def initialize(rules)
@rules = rules
end
def unit_price(item)
@rules[item][:unit_price]
end
def discount_price(item)
@rules[item][:discount_price]
end
def discount_count(item)
@rules[item][:discount_count]
end
def price_for(item, count)
count/discount_count(item) * discount_price(item) +·
count% discount_count(item) * unit_price(item)·
end
def has_discount_price(item)
@rules[item].has_key?(:discount_price)
end
end
When the pricing_rules_spec
tests are run, they pass successfully;
./bin/rspec spec/pricing_rules_spec.rb:3
Run options: include {:locations=>{"./spec/pricing_rules_spec.rb"=>[3]}}
Randomized with seed 46467
PricingRules
#has_dicount_price
checks if an item has a discount price
#price_for
calculates the price for a number of items
#discount_price
fetches the discount price for the item
#discount_count
fetches the discount count of the item
#unit_price
fetches the unit price for the item
Finished in 0.0013 seconds (files took 0.05617 seconds to load)
5 examples, 0 failures
Randomized with seed 46467
Press ENTER or type command to continue
Adding Pricing Rules:
Edit the checkout.rb
file to include pricing_rules
in the initialize
method.
The checkout.rb
file changes to:
class CheckOut
def initialize(pricing_rules)
@pricing_rules = pricing_rules
@total = 0
end
def total
@total
end
def scan(item)
if item == 'A'
@total = 50
elsif item == 'B'
@total = 30
elsif item == 'C'
@total = 20
else·
@total = 15
end
end
end
Make certain that written tests still pass and they do as shown below:
./bin/rspec spec/check_out_spec.rb
Randomized with seed 13654
.....
Finished in 0.00245 seconds (files took 0.05355 seconds to load)
5 examples, 0 failures
Randomized with seed 13654
Press ENTER or type command to continue
Edit the chekout.rb
file further to this;
class CheckOut
def initialize(pricing_rules)
@pricing_rules = pricing_rules
@items_count = Hash.new(0)·
end
··
def scan(item)
@items_count[item] += 1
end
def total
@items_count.inject(0) do |total, (item, count)|
if item == 'B' || item == 'A'
total += @pricing_rules.price_for(item, count)
else
total += count*@pricing_rules.unit_price(item)
end
end
end
end
This was done to take into account the pricing_rules
and items_count
Edit the checkout_spec.rb
file to gradually include pricing rules information
Adding Pricing Rules To A
Include pricing_rules
for the context 'when A has been scanned'
:
context 'when A has been scanned' do
it 'shows the price of A' do
pricing_rules = PricingRules.new(
"A" => {unit_price: 50, discount_price: 130, discount_count: 3})
checkout = CheckOut.new(pricing_rules)
checkout.scan('A')
total = checkout.total
expect(total).to eq 50
end
end
When the test is run, it passes successfully:
./bin/rspec spec/checkout_spec.rb:16
Run options: include {:locations=>{"./spec/checkout_spec.rb"=>[16]}}
Randomized with seed 64567
checkout
when A has been scanned
shows the price of A
Finished in 0.00064 seconds (files took 0.04095 seconds to load)
1 example, 0 failures
Randomized with seed 64567
Press ENTER or type command to continue
Adding Pricing Rules to the rest of the items:
context 'when B has been scanned' do
it 'shows the price of B' do
pricing_rules = PricingRules.new(
"B" => {unit_price: 30, discount_price: 45, discount_count: 2})
checkout = CheckOut.new(pricing_rules)
checkout.scan('B')
total = checkout.total
expect(total).to eq 30
end
end
context 'when C has been scanned' do·
it 'shows the price of C' do
pricing_rules = PricingRules.new("C" => {unit_price: 20})
checkout = CheckOut.new(pricing_rules)
checkout.scan('C')
total = checkout.total
expect(total).to eq 20
end
end
context 'when B, A and then D have been scanned' do
it 'shows the total price of B, A and D' do
pricing_rules = PricingRules.new(
"B" => {unit_price: 30, discount_price: 45, discount_count: 2},
"A" => {unit_price: 50, discount_price: 130, discount_count: 3},
"D" => {unit_price: 15}
)
checkout = CheckOut.new(pricing_rules)
checkout.scan('B')
checkout.scan('A')
checkout.scan('D')
total = checkout.total
expect(total).to eq 95
end
end
context 'when D has been scanned' do·
it 'shows the price of D' do
pricing_rules = PricingRules.new("D" => {unit_price: 15})
checkout = CheckOut.new(pricing_rules)
checkout.scan('D')
total = checkout.total
expect(total).to eq 15
end
end
context 'when B, A and another B has been scanned' do
it 'shows the total discounted price of B plus price of A' do
pricing_rules = PricingRules.new(
"B" => {unit_price: 30, discount_price: 45, discount_count: 2},
"A" => {unit_price: 50, discount_price: 130, discount_count: 3},
"D" => {unit_price: 15}
)
checkout = CheckOut.new(pricing_rules)
checkout.scan('B')
checkout.scan('A')
checkout.scan('B')
total = checkout.total
expect(total).to eq 95
end
end
context 'when A, A, and another A have been scanned' do
it 'shows the total discounted price of three A items' do
pricing_rules = PricingRules.new(
"A" => {unit_price: 50, discount_price: 130, discount_count: 3})
checkout = CheckOut.new(pricing_rules)
checkout.scan('A')
checkout.scan('A')
checkout.scan('A')
total = checkout.total
expect(total).to eq 130
end
end
Notice that some new contexts have been added;
- 'when B, A and then D have been scanned'
- 'when B, A and then D have been scanned'
- 'when A, A, and another A have been scanned'
These new contexts are meant to handle the situations for discounts.
These tests run successfully;
./bin/rspec spec/checkout_spec.rb:4
Run options: include {:locations=>{"./spec/checkout_spec.rb"=>[4]}}
Randomized with seed 1369
checkout
when B, A and another B has been scanned
shows the total discounted price of B plus price of A
when nothing has been scanned
shows total of zero
when A has been scanned
shows the price of A
when D has been scanned
shows the price of D
when B has been scanned
shows the price of B
when A, A, and another A have been scanned
shows the total discounted price of three A items
when C has been scanned
shows the price of C
when B, A and then D have been scanned
shows the total price of B, A and D
Finished in 0.00125 seconds (files took 0.05214 seconds to load)
8 examples, 0 failures
Randomized with seed 1369
Press ENTER or type command to continue
Conclusion:
This concludes the series.
Shout out to Rob for his immense assistance on this and making it easy to comprehend.
The final project can be found here.