Kata: Conference tickets - purchases
In the previous log, we have started a tickets reservation system.
At this point, we have only supported the ticket part, ensuring there were enough tickets.
Let's handle next rules!
Rule 2: Tickets have a price
We could edit one more time our data structure, but the current one is simple enough to focus on its responsibility.
We can add an extra layer to handle extra attributes, as follows:
-- describe "Purchases" $ do
-- describe "toTickets" $ do
-- it "with Alice has one ticket" $
-- toTickets (Purchase $ Map.fromList [("Alice", Map.singleton 1 50)])
-- `shouldBe` [Attendee $ Set.singleton 1]
-- it "with Alice has one ticket and Bob booked two days" $
-- toTickets (Purchase $ Map.fromList [("Alice", Map.singleton 1 50), ("Bob", Map.fromList [(1, 50), (3, 80)])])
-- `shouldBe` [Attendee $ Set.singleton 1, Attendee $ Set.fromList [1, 3]]
newtype Purchase
= Purchase {unPurchase :: Map.Map AttendeeName (Map.Map Int TicketPrice)}
newtype AttendeeName
= AttendeeName Text
deriving newtype (IsString, Eq, Ord)
newtype TicketPrice
= TicketPrice {eurTicketPrice :: Int}
deriving newtype (Num)
toTickets = map (Attendee . Map.keysSet) . Map.elems . (.unPurchase)
Rule 3: There could be two tickets, with different prices, for the same day
This one is a bit ambiguous, it could suggest that we have a dedicated data-type for tickets, but, in the mean time, our code is handling different ticket prices, regardless the days.
Rule 4: There could be multiple days-tickets
This one is more explicit, tickets are now more complex, having a price and a set of days, we can parameterize (both parametrically in Purchase and ad-hoc in toTickets), as follows:
-- describe "Purchases" $ do
-- describe "toTickets" $ do
-- it "with Alice has one ticket" $
-- toTickets (Purchase $ Map.fromList [("Alice", Set.singleton Conference)])
-- `shouldBe` [Attendee $ Set.singleton 1]
-- it "with Alice has one ticket and Bob booked two days" $
-- toTickets (Purchase $ Map.fromList [("Alice", Set.singleton Conference), ("Bob", Set.singleton TwoDays)])
-- `shouldBe` [Attendee $ Set.singleton 1, Attendee $ Set.fromList [1, 2]]
newtype Purchase ticket
= Purchase {unPurchase :: Map.Map AttendeeName (Set.Set ticket)}
toTickets = map (Attendee . foldMap ticketDays) . Map.elems . (.unPurchase)
data LyonCraft = Conference | Unconference | TwoDays
deriving stock (Eq, Ord, Show)
price =
\case
Conference -> 50
Unconference -> 80
TwoDays -> 120
ticketDays =
\case
Conference -> Set.singleton 1
Unconference -> Set.singleton 2
TwoDays -> Set.fromList [1, 2]
It both represents the user-facing tickets and gives extensibility for any upcoming ticket rules.
Next time, we will have a look at how we can prevent not coherent configuration (e.g. a daily limit of 10 with a total limit of 1)