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
193: Duck Typing vs Type Checking in Ruby
What actually is "Duck Typing" and how does it differ from what I'm used to?
You've probably heard the phrase: "If it walks like a duck and it quacks like a duck, then it's a duck." In Ruby, this isn't just a cute saying; it's a core design philosophy. In many languages, if a function expects a PaymentGateway object, you have to pass it something that explicitly inherits from the PaymentGateway class.
Ruby doesn't care about the "family tree" of the object. It only cares about whether the object can perform the action you're asking for. If you call .process_payment on an object, Ruby doesn't ask, "Are you a PaymentGateway?" It simply asks, "Do you have a method called process_payment?" If the answer is yes, it runs. I love this because it makes your code incredibly flexible.
class Stripe
def process_payment(amount)
puts "Processing $#{amount} via Stripe..."
end
end
class PayPal
def process_payment(amount)
puts "Processing $#{amount} via PayPal..."
end
end
# This method doesn't care about the class, only the behavior
def checkout(gateway, amount)
gateway.process_payment(amount)
end
checkout(Stripe.new, 100) # Works!
checkout(PayPal.new, 100) # Also works!
Why shouldn't I just use is_a? to make sure I have the right object?
I see a lot of developers coming from Java or C# who instinctively reach for is_a? or kind_of?. They'll write something like if payment_method.is_a?(CreditCard). While that works, it creates a rigid coupling between your logic and your class names.
Imagine you've written a whole system that accepts CreditCard and PayPal. Six months from now, your boss tells you that you need to support ApplePay. If you used type checking with is_a?, you now have to hunt down every single if/else block in your codebase and add || payment_method.is_a?(ApplePay). That's a recipe for bugs.
With duck typing, you don't change a single line of the checkout logic. You just create an ApplePay class with a process_payment method, and the rest of the system accepts it immediately. You're coding to an interface (the method name) rather than an implementation (the class name).
If I stop checking types, won't my app crash constantly with NoMethodError?
This is the valid "scary" part of duck typing. If you pass a String into that checkout method, Ruby will blow up because String doesn't have a process_payment method. You have a few ways to handle this depending on how strict you want to be.
If you're in a situation where you aren't 100% sure what's coming in, I recommend using respond_to?. This is the "polite" way of duck typing. Instead of demanding the object be a certain class, you just ask if it's capable of the specific action.
def checkout(gateway, amount)
if gateway.respond_to?(:process_payment)
gateway.process_payment(amount)
else
raise "The provided gateway doesn't know how to process payments!"
end
end
Personally, I try to avoid respond_to? unless I'm building a library for other people. In my own application code, I usually prefer to "ask for forgiveness rather than permission." I'll just call the method and let the error happen during development—that's how I find out I'm passing the wrong object. It keeps the "happy path" of the code much cleaner.
📋 Practical Task
Build a Polyglot Notification Dispatcher
You are building a notification system that can send alerts via different channels. Instead of checking the type of the notifier, you will use duck typing to handle them uniformly.
- Create three classes:
EmailNotifier,SMSNotifier, andSlackNotifier. - Each class must implement a method called
send_alert(message)that prints a specific string (e.g., "Sending Email: [message]"). - Create a class called
NotificationDispatcherwith a methoddispatch(notifiers, message). - The
dispatchmethod should take an array of notifier objects and a message string, then loop through the array and callsend_alerton each one. - Test your dispatcher by passing it an array containing one of each notifier.
- Bonus: Try passing an object that doesn't implement
send_alertinto the array and userespond_to?inside thedispatchmethod to skip it gracefully without crashing the program.
There are no comments for now.