Ruby
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Methods and Blocks
-
Section 4: Object-Oriented Ruby
-
Section 5: Metaprogramming
-
Section 6: Working with Data and Files
-
Section 7: Ruby Frameworks Overview
-
Section 8: Ecosystem and Testing
-
Section 9: Practical Projects
-
Section 10: Interview Practice
-
Section 11: Data Structures and Algorithms in Ruby
-
Section 12: More Practice Exercises
-
Section 13: Enumerable and Functional Style
-
Section 14: More OOP Practice
-
Section 15: More Testing
-
Section 16: Enumerable and Comparable Modules In Depth
-
Section 17: Ruby Standard Library: Core Utilities
-
Section 18: Ruby Standard Library: Data and Security
-
Section 19: Ruby Standard Library: CLI and Text
-
Section 20: Ruby Networking
-
Section 21: Ruby on Rails Deep Dive
-
Section 22: Ruby Metaprogramming Deep Dive
-
Section 23: Ruby Design Patterns
-
Section 24: Ruby Concurrency
-
Section 25: Ruby Testing Deep Dive
-
Section 26: Ruby Gems and Packaging
-
Section 27: Ruby Performance
-
Section 28: More Practice Exercises
-
Section 29: More Interview Practice
-
Section 30: Rails API Development
-
Section 31: Rails Authentication and Authorization
-
Section 32: Rails Testing Deep Dive
-
Section 33: Rails Performance
-
Section 34: Rails Deployment
-
Section 35: Sinatra and Lightweight Ruby Web Apps
-
Section 36: More Ruby Language Deep Dive
-
Section 37: Ruby 3.x Modern Features
-
Section 38: More Data Structures in Ruby
-
Section 39: More Practical Projects
-
Section 40: Ruby Ecosystem Tools
-
Section 41: More Practice and Review
-
Section 42: Final Practice and Mastery
-
Section 43: Ruby Interview Deep Dive
-
Section 44: Ruby Background Processing Deep Dive
-
Section 45: Ruby GraphQL
-
Section 46: Ruby Object Model Deep Dive
-
Section 47: Ruby Hanami Framework Overview
-
Section 48: Ruby gRPC and Protocol Buffers
-
Section 49: Ruby Data Processing
-
Section 50: Ruby Search Integration
-
Section 51: Ruby File Upload and Media
-
Section 52: Ruby Email and Notifications
-
Section 53: Ruby Admin Panels
-
Section 54: Ruby Feature Flags and Experimentation
-
Section 55: Ruby Monitoring and Observability
-
Section 56: Ruby Docker and Deployment Deep Dive
-
Section 57: Ruby Security Deep Dive
-
Section 58: More Advanced Metaprogramming
-
Section 59: More Final Projects
40: Implementing a Stack and Queue
By now, you're comfortable with Arrays, but in the real world, we often need to restrict how we interact with data to ensure our logic stays predictable. That's where Stacks and Queues come in. They aren't "new" data structures in Ruby so much as they are specific ways of using an Array.
Wait, can't I just use an Array instead of building a Stack class?
Technically, yes. Ruby's Array class already has push and pop, which are the two primary operations of a Stack. If you just need to store a few things and pop them off, an Array is fine. But here is why I usually wrap it in a class: intentionality.
If you pass a raw Array to another developer, they might use insert or delete_at, which completely destroys the "Last-In, First-Out" (LIFO) guarantee of a stack. By wrapping it, you're telling the rest of your app, "You are only allowed to add to the top and take from the top."
class Stack
def initialize
@storage = []
end
def push(element)
@storage << element
end
def pop
@storage.pop
end
def peek
@storage.last
end
def empty?
@storage.empty?
end
end
# Think of this like a browser's back button history
history = Stack.new
history.push("google.com")
history.push("github.com")
history.push("stackoverflow.com")
puts history.pop # => "stackoverflow.com" (The last page you visited)
How is a Queue different, and does Ruby handle it efficiently?
While a Stack is LIFO, a Queue is FIFO: First-In, First-Out. Think of it like a line at a coffee shop. The first person to get in line is the first person to get their latte. To do this in Ruby, we push to the end of the array but shift from the front.
I've had students ask if shift is slow because it has to move every other element in the array over one spot. In many languages, that's a huge performance hit. In Ruby, the internal implementation of Array is actually quite optimized for this, so for most applications, using shift is perfectly acceptable.
class Queue
def initialize
@storage = []
end
def enqueue(element)
@storage << element
end
def dequeue
@storage.shift
end
def front
@storage.first
end
end
# Example: A print queue
printer = Queue.new
printer.enqueue("Tax_Return.pdf")
printer.enqueue("Vacation_Photo.jpg")
printer.enqueue("Resume.pdf")
puts printer.dequeue # => "Tax_Return.pdf" (The first document sent)
When would I actually use these in a real project?
You'll run into these more often than you think. I once worked on a system that processed background emails. If we just used a list, we might accidentally process the newest email first, leaving the oldest ones sitting there for hours. A Queue was the only way to ensure fairness.
Stacks are the gold standard for any "Undo" feature. Every time the user performs an action, you push that action onto a stack. When they hit Ctrl+Z, you pop the most recent action and reverse it. If you used a Queue there, you'd be undoing the first thing the user did ten minutes ago, which would be a nightmare for the UX.
📋 Practical Task
Build a Customer Support Ticket Processor
You need to build a system that manages incoming support tickets. Tickets must be handled in the exact order they arrive to ensure fairness to the customers.
- Create a
TicketQueueclass that wraps an array. - Implement an
add_ticket(customer_name)method. - Implement a
process_ticketmethod that removes the oldest ticket from the queue and returns a string:"Now helping: [customer_name]". - If the queue is empty,
process_ticketshould return"No tickets to process!".
Test your implementation by adding three different customers and processing them one by one to verify that the first person added is the first person helped.
There are no comments for now.