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
checks if an item has a discount price
calculates the price for a number of items
fetches the discount price for the item
fetches the discount count of the item
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.