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
197: Comparing Hanami to Rails
I've spent a lot of time chatting with Ruby devs who are looking for an alternative to Rails, and there is one phrase I hear constantly: "I want to try Hanami because it's a lightweight version of Rails."
Here is the problem: if you go into Hanami thinking it's just "Rails Lite," you're going to be frustrated. You'll spend your first three days wondering why you can't just add a method to a model and have it magically work everywhere. Hanami isn't just Rails with fewer gems; it is a fundamentally different philosophy of how an application should be structured. Rails is built on the "Omakase" principle—the framework makes the big decisions for you. Hanami is built on the principle of decoupling.
"Hanami is just a smaller Rails" vs. "Hanami is an architectural shift"
In Rails, the Model is the center of the universe. Thanks to ActiveRecord, your model is a "God Object"—it handles database schema, validations, persistence, and business logic. It's incredibly fast to build, but as your app grows, those models become bloated messes of a thousand lines.
Hanami splits those responsibilities apart. Let's look at a simple "User" implementation. In Rails, you'd have one file:
# Rails: app/models/user.rb
class User < ApplicationRecord
validates :email, presence: true
def full_name
"#{first_name} #{last_name}"
end
def self.active
where(active: true)
end
end
In Hanami, that one file is split into at least two different concepts: an Entity and a Repository. The Entity is a simple data object (it doesn't know the database exists), and the Repository is the only place where SQL is actually executed.
# Hanami: lib/my_app/entities/user.rb
module MyApp
class User < Hanami::Entity
def full_name
"#{first_name} #{last_name}"
end
end
end
# Hanami: lib/my_app/repositories/user_repository.rb
module MyApp
class UserRepository < Hanami::Repository
def active
users.where(active: true).to_a
end
end
end
I'll be honest: this feels like more work upfront. You're writing more files. But the payoff happens six months later. Because the User entity is just a plain object, you can test your business logic (like full_name) without ever hitting the database. Your tests become lightning fast.
"Convention is always better" vs. "Explicitness prevents surprises"
Rails relies heavily on "magic." You define a route, and Rails magically finds the controller action based on a naming convention. You call a method, and it's mixed in from a hidden concern. I love this for prototyping, but it can be a nightmare when you're debugging a complex production bug and can't find where a method is actually defined.
Hanami 2.0 doubles down on Dependency Injection. Instead of relying on global state or magic constants, Hanami encourages you to "import" the tools you need into your actions. It uses a container system (via the dry-system gem) to manage these dependencies.
When you look at a Hanami action, you can see exactly what it relies on. There's no guessing. If an action needs the UserRepository, it asks for it explicitly. This makes your code significantly easier to mock during testing and prevents the "spaghetti" effect where changing one line in a Rails model unexpectedly breaks a view helper three folders away.
So, which one should you use? If you need to ship a MVP in two weeks and you're the only dev, Rails is hard to beat. But if you're building a long-term system where maintainability and test speed are more important than initial setup speed, Hanami's decoupled approach is a breath of fresh air.
📋 Practical Task
Refactoring a Rails-style Model into Hanami Components
You have been handed a legacy Rails-style Product model that has become too bloated. Your goal is to refactor this logic into a Hanami-style structure by separating the Data Entity (business logic) from the Repository (persistence logic).
The Legacy Code:
class Product < ApplicationRecord
# Database logic
def self.out_of_stock
where("inventory_count <= 0")
end
# Business logic
def discounted_price
price * 0.9
end
end
Your Task:
- Create a
Productentity class that inherits fromHanami::Entityand contains thediscounted_pricemethod. - Create a
ProductRepositoryclass that inherits fromHanami::Repositoryand contains theout_of_stockmethod. - Ensure the
out_of_stockmethod uses the repository's internal query interface (e.g.,products.where(...)) rather than calling a class method on the entity.
There are no comments for now.