If you’ve made it this far, you should know about routes, controllers, and views in Hanami. In the last Learn Hanami lesson, we’ll be looking at Hanami models.
If you have a Rails (or MVC) background, you’re likely familiar with models. Models represent data your application will use. However, Hanami may utilize models differently than you’re used to.
At this point in the blog application, you’ve been writing; you’ve created an action for displaying posts. This lesson will teach you how to build and display posts using a
Generating Hanami Models
Hanami has a generator for creating models like the generator you utilized in the last lesson to create an action. To create the model, run the following command from your terminal:
bundle exec hanami generate model post
Being that the generator will create several files, I want you to only look for two:
I would like to point your attention to where these files live. The model created two files inside the lib directory. The lib folder is where the functionality of Ruby Gems typically live. This structure is intentionally mirroring that.
The idea is to develop our application like a Ruby gem.
With your models inside the lib directory, Hanami models are also available across multiple applications in the apps directory.
So, entities. What are they?
If you come from a Rails background, you can think of an entity as a model without database interactivity.
An entity is domain object that is defined by its identity.
Similarly, the entity is responsible for business logic. However, unlike Rails Hanami models don’t concern themselves with validation or persistence. This concept can be very confusing, at first. I’m going to skip examples to answer the other question you may have: “How does it work with the database?”
Being that entities don’t persist data, might have you wondering how persistence happens.
So, repositories are responsible for “persisting” models.
An object that mediates between entities and the persistence layer.
In fact, a repository has several jobs:
- take the entity and attempt to persist it to a database
- retrieve an entity from a database
- query entities from a database
- update entities in a database
At this point, it’s best that I show you how to tie it all together with an example.
While you may be familiar with migrations, let’s look at Hanami’s definition of a migration:
Migrations are a feature that allows to manage database schema via Ruby.
To put it another way, using a migration will allow you to create database tables, add/remove columns, etc. Luckily, when you ran the generator to create a model, a migration was created for you.
This command creates a migration file inside db/migrations/ with the name
…_create_posts.rb. Open that file, and you should see the following:
This file contains an empty migration that doesn’t do anything right now. You’re about to change that.
In order to show posts, you likely need a posts table in your database. At this point, change your migration file to look like this:
This new code uses a DSL to create a database table named posts with five columns.
- ID (Primary Key)
- Post Title
- Post Content
- Created At
- Updated At
By default, Hanami uses an SQLite database. This database default allows us not to have to do any setup of our database. I wouldn’t recommend it for production.
At this point, you’re ready to run the migration.
bundle exec hanami db migrate
Once you’ve run the migration, a file named blog_development.sqlite inside the db folder exists.
Instantiating Hanami Models
Hanami can open up a console that loads your Hanami application. To illustrate this in action, open the console by running
bundle exec hanami console
Once you have the console running, I’d like you to instantiate a post using
Without having to provide any information to the Post entity class, it’s aware of
content. In fact, this functionality comes from automatic schemas. Automatic schemas allow you to instantiate an object with attributes. As a result, you’ll notice in the example you can reference the fields as well.
At this point you might be thinking, “can I call save on this object and it persist to the database?” However, the answer is no. Remember, entities don’t persist data, repositories do.
Saving to a Database
In order to save your entity to the database, I’ll have you pass your newly instantiated model to a repository.
At this point, you might be wondering what the benefit to having an object with business logic separated from an object responsible for persistence. As a matter of fact, it’s quite convenient to have one object responsible for both. Hanami has an excellent set of reasons why you would want to separate the two:
- Applications depend on a standard API, instead of low-level details (Dependency Inversion principle)
- Applications depend on a stable API, that doesn’t change if the storage changes
- Developers can postpone storage decisions
- Confines persistence logic to a low level
- Multiple data sources can easily coexist in an application
Making it All Work
Given everything you’ve learned thus far, you’re now ready to tie it all together.
For the most part, your application should be ready to go. However, add just a small bit of code to complete the feature we’ve been working towards.
To start, change the route in apps/web/config/routes.rb from
get ‘/posts’, to: ‘posts#index’ to
get ‘/’, to: ‘posts#index’. As a result of this, the homepage of your application will the list of posts.
Next, you’ll need to setup data to get from your controller to your view and subsequently your template. In order for the data to be available, you’ll need to define an Exposure.
Specifically, you’ll need to create a posts exposure in apps/web/controllers/posts/index.rb
By using an exposure, data isn’t “shared” between the controller and view.
Additionally, you’ll see your newly created exposure being set as an instance variable. The right-hand side of the instance variable is using the repository to grab all the posts from the database.
Using the Data
At this point, we’re just wanting to see the title of all the posts on the home page. Since you won’t be manipulating data for the view, you can go ahead and access this exposure from the template. Now, open up apps/web/templates/posts/index.html.erb and replace it with the following code:
In order to verify everything is working, fire up the server and go to http://localhost:2300. At this point, if everything is working correctly you should see:
Hooray, this is what you wanted to see: At this point you have a post retrieved from the database.
Additionally, what would happen if you added more posts to the database?
Challenge: Open the console and create more records.
All in all, how did it go? Were you able to create records? If not, refer back to the repository.
If you were able to add new records, you should now see those records appearing on the home page as you refresh it.
Finally, you’ve made it to the end. To summarize, at this point you should be able to create:
- Actions (Controllers, Views, and Templates)
- Models (Entities and Repositories)
As previously stated, I believe Hanami is important to the Ruby community. At this point, I hope you’ve been able to understand the basics enough to afterward dig into the Hanami documentation to continue learning.
In conclusion, I’d like to thank you for taking the time to go through this mini-lesson series with me. Because of all that Hanami offers and my limited amount of time, the mini-lesson series was limited to the basics. However, Hanami is a full-featured framework ready for you to use.
Learn From My Mistakes
I love to share what I'm learning.