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
196: Building a Simple App with Hanami
You've probably spent some time with Rails or other "magic" frameworks where you just set an instance variable in a controller and it magically appears in your HTML template. When you first dive into Hanami, you'll likely try to do the exact same thing, only to find that your view is stubbornly empty.
# app/actions/movies/show.rb
module MovieTracker
module Actions
module Movies
class Show < MovieTracker::Action
def handle(request, response)
# I'm fetching the movie, so it should be available in the view, right?
@movie = MovieRepository.new.find(request.params[:id])
end
end
end
end
end
You refresh the page, and instead of seeing your movie title, you get a nil error or just a blank spot where the data should be. It's frustrating because the code looks logically sound. You've fetched the data, assigned it to a variable, and the route is working. So why isn't the view seeing it?
The Invisible Instance Variable
The problem here is that Hanami is designed with a strict separation of concerns. Unlike Rails, Hanami doesn't automatically leak the internal state of your Action class into your View. I actually love this design choice once you get used to it, because it prevents that "blob" of state that makes large Rails apps so hard to debug. In Hanami, if you want a piece of data to leave the Action and enter the View, you have to be explicit about it.
Using expose to Bridge the Gap
To fix this, we use the expose method. This tells Hanami, "I am intentionally making this specific piece of data available to the outside world."
# app/actions/movies/show.rb
module MovieTracker
module Actions
module Movies
class Show < MovieTracker::Action
# This is the missing piece!
expose :movie
def handle(request, response)
movie_repo = MovieRepository.new
response[:movie] = movie_repo.find(request.params[:id])
end
end
end
end
end
Notice two things here. First, expose :movie declares the output. Second, instead of using @movie, I'm assigning the value to response[:movie]. While Hanami can handle the instance variable if exposed, using the response hash is the idiomatic way to pass data along. It makes it very clear that this data is part of the HTTP response being sent back to the user.
Connecting the Repository to the Template
Now that the Action is explicitly exposing the movie, the View can pick it up. In Hanami, the View is actually a class that prepares data for the template. If you've exposed :movie in your action, it becomes available as a local variable in your template file.
# app/templates/movies/show.html.erb
<h1><%= movie.title %></h1>
<p>Released in: <%= movie.release_year %></p>
If you had forgotten the expose call in the action, this template would throw a NameError: undefined local variable or method 'movie'. By forcing us to define our "exports," Hanami ensures that our Actions remain lean and our Views don't accidentally rely on hidden state.
Structuring the Simple App
When building a simple app, remember that Hanami splits everything into these distinct buckets: Actions (handling the request), Repositories (talking to the database), and Views/Templates (rendering the UI). It feels like more files at first, but as your app grows, you'll be thankful you aren't scrolling through a 500-line controller file trying to find where a variable was defined.
📋 Practical Task
Build a "Developer Snippet" Library
Your task is to build a basic snippet library using Hanami. You will need to implement a feature that displays a specific code snippet based on an ID passed in the URL.
- Create a
SnippetRepositorywith afind(id)method that returns a mock object (or a real database record if you have the DB configured) containing atitleand thecode_content. - Create a
Snippets::Showaction. - Crucially: Ensure you use
exposeso the snippet data reaches the view. - Create the corresponding template to render the snippet's title in an
<h2>and the code content inside<pre>tags.
Test your implementation by visiting /snippets/1 and ensuring the specific snippet details appear on the screen without any NameError exceptions.
There are no comments for now.