Lesson 1
Implementing a Potluck Dinner System with Backward Compatibility in Ruby
Introduction

Welcome to today's lesson, where we will tackle a common challenge in software engineering: the introduction of complex features while preserving backward compatibility. Our focus will be on a Potluck Dinner organization system, where we will manage participants and their respective dishes for each round. Get ready for an exciting journey through Ruby programming, step-by-step analysis, and strategic thinking. Let's dive into our adventure!

Starter Task Review

Initially, our Potluck Dinner organization system allows us to add and remove participants and manage their respective dishes for each round. There are three essential methods:

  • add_participant(member_id): This method adds a participant. If a participant with the given member_id already exists, it won't create a new one but will return false. Otherwise, it will add the member and return true.
  • remove_participant(member_id): This method removes a participant with the given member_id. If the participant exists, the system will remove them and return true. Otherwise, it will return false. When removing a participant, you need to remove their dish if they brought one.
  • add_dish(member_id, dish_name): This method enables each participant to add their dishes for every round. If a participant has already added a dish for this round OR if the member_id isn't valid, the method will return false. Otherwise, it will add the dish for the respective participant's round and return true.

Let's write our Ruby code, which implements the functions as per our initial state:

Ruby
1class Potluck 2 def initialize 3 @participants = {} 4 @dishes = {} 5 end 6 7 def add_participant(member_id) 8 return false if @participants.key?(member_id) 9 10 @participants[member_id] = true 11 true 12 end 13 14 def remove_participant(member_id) 15 return false unless @participants.key?(member_id) 16 17 @participants.delete(member_id) 18 @dishes.delete(member_id) 19 true 20 end 21 22 def add_dish(member_id, dish_name) 23 return false unless @participants.key?(member_id) 24 return false if @dishes.key?(member_id) 25 26 @dishes[member_id] = dish_name 27 true 28 end 29end

In this code, we employed Ruby hashes to store unique participant IDs and their respective dish names. With this foundation laid, let's introduce some advanced functionalities.

Introducing Advanced Functionalities

Our Potluck Dinner organization system is currently simple but practical. To make it even more exciting, we're going to introduce a "Dish of the Day" feature. This feature will enable participants to vote, and the dish receiving the most votes will be declared the "Dish of the Day."

To add this feature, we will define two new methods:

  • vote(member_id, vote_id): This method will allow a participant to cast a vote for a dish. Each participant can cast a vote only once per round. If a participant tries to vote again or if the member_id isn't valid, it should return false.
  • dish_of_the_day: This method will calculate and return the "Dish of the Day" based on the votes received. If multiple dishes tie for the highest number of votes, the dish brought by the participant who joined first acquires precedence. If there are no votes, the function returns nil.
Step 1: Constructor and Updating Existing Methods

First, we update our Potluck class to store the participant's joining time directly in the participants hash, and ensure compatibility across other methods.

Constructor Update:

Ruby
1class Potluck 2 def initialize 3 @participants = {} 4 @dishes = {} 5 @votes = {} 6 end 7end

Updating Methods to Incorporate Join Time and Consistent Removal:

Ruby
1def add_participant(member_id) 2 return false if @participants.key?(member_id) 3 4 @participants[member_id] = { join_time: Time.now.to_f } 5 true 6end 7 8def remove_participant(member_id) 9 return false unless @participants.key?(member_id) 10 11 @participants.delete(member_id) 12 @dishes.delete(member_id) 13 @votes.delete(member_id) 14 true 15end 16 17def add_dish(member_id, dish_name) 18 return false unless @participants.key?(member_id) 19 return false if @dishes.key?(member_id) 20 21 @dishes[member_id] = dish_name 22 true 23end

By storing a join_time in the participants hash, we keep track of who joined first. We also ensure removal from all related structures (@dishes and @votes) for consistency.

Step 2: Implementing the vote Method

Next, we add a method to allow participants to vote for the dishes:

Ruby
1def vote(member_id, vote_id) 2 if @participants.key?(member_id) && !@votes.key?(member_id) 3 @votes[member_id] = vote_id 4 true 5 else 6 false 7 end 8end

This ensures each participant can only vote once per round, returning false if the participant has already voted or if the participant doesn't exist.

Step 3: Implementing the dish_of_the_day Method

Finally, we implement the logic to determine the "Dish of the Day":

Ruby
1def dish_of_the_day 2 return nil if @votes.empty? 3 4 vote_count = Hash.new(0) 5 @votes.values.each { |vote| vote_count[vote] += 1 } 6 7 max_votes = vote_count.values.max 8 max_vote_dishes = vote_count.select { |_, count| count == max_votes }.keys 9 10 earliest_join_time = Float::INFINITY 11 dish_of_the_day_member = nil 12 max_vote_dishes.each do |member_id| 13 if @participants[member_id][:join_time] < earliest_join_time 14 earliest_join_time = @participants[member_id][:join_time] 15 dish_of_the_day_member = member_id 16 end 17 end 18 19 "Participant: '#{dish_of_the_day_member}', Dish: '#{@dishes[dish_of_the_day_member]}'" 20end

If there are no votes, the method returns nil. In case of a tie, the participant with the earliest join time wins.

Ensuring Backward Compatibility
  • Participants Data Structure Change: By using a hash for participant details, we ensure each participant remains uniquely identifiable while enabling additional functionality like join time.
  • Method Signature Consistency: All method signatures remain unchanged to ensure existing interactions remain unaffected.
Final Implementation

Combining all our steps, this is the final implementation of our Potluck class with all the required methods:

Ruby
1class Potluck 2 def initialize 3 @participants = {} 4 @dishes = {} 5 @votes = {} 6 end 7 8 def add_participant(member_id) 9 return false if @participants.key?(member_id) 10 11 @participants[member_id] = { join_time: Time.now.to_f } 12 true 13 end 14 15 def remove_participant(member_id) 16 return false unless @participants.key?(member_id) 17 18 @participants.delete(member_id) 19 @dishes.delete(member_id) 20 @votes.delete(member_id) 21 true 22 end 23 24 def add_dish(member_id, dish_name) 25 return false unless @participants.key?(member_id) 26 return false if @dishes.key?(member_id) 27 28 @dishes[member_id] = dish_name 29 true 30 end 31 32 def vote(member_id, vote_id) 33 if @participants.key?(member_id) && !@votes.key?(member_id) 34 @votes[member_id] = vote_id 35 true 36 else 37 false 38 end 39 end 40 41 def dish_of_the_day 42 return nil if @votes.empty? 43 44 vote_count = Hash.new(0) 45 @votes.values.each { |vote| vote_count[vote] += 1 } 46 47 max_votes = vote_count.values.max 48 max_vote_dishes = vote_count.select { |_, count| count == max_votes }.keys 49 50 earliest_join_time = Float::INFINITY 51 dish_of_the_day_member = nil 52 max_vote_dishes.each do |member_id| 53 if @participants[member_id][:join_time] < earliest_join_time 54 earliest_join_time = @participants[member_id][:join_time] 55 dish_of_the_day_member = member_id 56 end 57 end 58 59 "Participant: '#{dish_of_the_day_member}', Dish: '#{@dishes[dish_of_the_day_member]}'" 60 end 61end
Lesson Summary

Bravo! You've successfully completed the task of introducing complex features while preserving backward compatibility using Ruby. This skill is invaluable in real-life software engineering scenarios, where maintaining existing functionality while enhancing systems is crucial. Strengthen this ability with even more practice and explore similar challenges. I'll see you in the next lesson! Happy coding with Ruby!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.