Skip to Content
Course content

196: Building a Simple App with Hanami

Click on the "Edit" button in the top corner of the screen to edit your slide content.

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 SnippetRepository with a find(id) method that returns a mock object (or a real database record if you have the DB configured) containing a title and the code_content.
  • Create a Snippets::Show action.
  • Crucially: Ensure you use expose so 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.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.