00:00:18.439
Um, okay. Hi and welcome! My name is Arela, and I'm from the program committee.
00:00:24.680
It’s my honor to present our next speaker. Tim Riley is a core team member of Hanami, Dry-RB, and ROM-RB.
00:00:30.000
He’s a principal engineer at Buildkite. Tim has been writing Ruby for over 20 years and still loves it.
00:00:37.480
He works to bring the joy of Ruby to writing real maintainable applications of all shapes and sizes.
00:00:44.840
So please join me in giving a warm welcome to Tim!
00:00:58.920
Riley.
00:01:04.760
I like that logo a bit.
00:01:20.799
We’re into applications, fast tests, and method calls. I've got a premonition this session is going to stun us, so let's go!
00:01:48.240
Thank you! Yes, we’re doing this. I'm Tim.
00:01:54.640
It is really exciting to be here at RubyConf and it is the last session in the schedule, so I hope we can have some fun together this afternoon.
00:02:01.079
This is, in fact, my first time at RubyConf and my first time in San Diego.
00:02:08.200
So, I did a bit of research in anticipation of speaking here, and as you can imagine, I looked into the musical background of the city.
00:02:14.280
There's a lot of it, but being a child of the '90s, one thing stood out to me very quickly.
00:02:20.560
I learned that this crew here, Blink-182, is a San Diego band, born and bred. Super cool!
00:02:29.959
You might remember this setting; that's the clip of their breakthrough hit 'All the Small Things'.
00:02:35.440
From back in 1999, which came out a mere few months after another song that we're going to become familiar with today.
00:02:41.959
Through which we'll be making our own extremely niche contribution to San Diego music history.
00:02:49.640
Because yes, today we are going to explore a Ruby framework under the guidance of the iconic Ricky Martin.
00:02:57.360
In fact, speaking of San Diego, I learned that Ricky Martin himself was here on this day in 1999 as part of his "Livin' La Vida Loca" tour.
00:03:05.360
So we are going to bring it back with a loving RubyConf reinterpretation of his hit song.
00:03:12.799
At this point, I will share a bit more about me.
00:03:18.319
I'm Tim, say hi to me on Mastodon, it's a fun place to chat. I work at Buildkite, we make fast, flexible, and secure CI/CD systems and more.
00:03:25.799
And we do all of that with Ruby, which is super cool! If you're a Buildkite user or fan, or what have you, I have some stickers, so come find me afterwards.
00:03:32.920
In the Ruby open-source world, I work on a few other things, like Hanami, Dry-RB, and ROM-RB, and we'll touch on all of these as we go.
00:03:39.439
But we will focus mostly on Hanami.
00:03:44.799
So what is Hanami? Well, it's an app framework for Ruby and now is the perfect time to share it, because as of last year, we are now in the 2.0 era of Hanami.
00:03:52.360
And I’m really excited for what it might bring to us all. Not only for unlocking different possibilities for our Ruby apps...
00:03:59.840
But also for what it might bring to each of us as Ruby developers.
00:04:05.519
Now, this project has been around since 2014. Back when we released it, we described it as a fast and lightweight web framework.
00:04:12.079
Bringing a strong focus on testability and maintainability.
00:04:17.359
Then a year later, the Dry-RB project began, and that's where I was personally involved at the time.
00:04:24.840
Through this project, we were releasing a range of Ruby gems focused on solving common tasks in Ruby.
00:04:32.199
But each one was motivated by helping us all write more maintainable applications.
00:04:37.360
By 2018, both of our teams realized we were largely traveling in the same direction, trying to reach the same goals.
00:04:44.080
So we teamed up, and a few years later, Hanami 2.0 came out around this time last year.
00:04:50.520
It brings all the power and flexibility of Dry-RB along with a friendly and streamlined out-of-the-box experience.
00:04:56.800
In just a couple of weeks, version 2.1 will be coming out. We worked really hard to get it ready around this time for RubyConf.
00:05:03.560
We have a release candidate that you can already try out, so I'm really excited to share what we’re bringing to you in 2.1.
00:05:10.680
As we go through our journey today, getting to this point was a true team effort.
00:05:17.240
I might be the one sharing it right here, but I’d like to give a shout-out to all these legends, including my friends Luca and Peter.
00:05:24.600
They are my teammates on the core team. I also have a few extra people to thank courtesy of the wonderful Community Day that kicked off this conference.
00:05:30.440
So I'd like to say thanks to Dan and Roman, who are already writing their first PRs for Hanami, which is fantastic.
00:05:37.240
Ariel helped me debug a tricky issue with our front-end asset preparation.
00:05:43.639
So thanks to RubyConf and these three people for pitching in.
00:05:49.440
So that's the team.
00:05:55.360
But as for today, we're all on Team Ricky!
00:06:01.000
This means that from here, we'll be progressing through his song.
00:06:06.759
As we go from verse all the way through to chorus, we will learn and progress our understanding of Hanami.
00:06:13.360
We'll see what it's like to get started with the framework and then learn more about its features.
00:06:19.400
Lastly, we'll step back and see what this means for all of us living that life with Hanami.
00:06:28.400
So let's get started building! This is how we'll get started with the framework.
00:06:35.400
Like any gem, the first thing we can do is install it using the command: gem install Hanami.
00:06:41.280
From there, we can create a new app with the command: hanami new, and let's call our app 'Bookshop'.
00:06:47.639
That's enough for us to hop into our terminal, run the Hanami Dev servers, and from there, we can open our web browser.
00:06:53.440
We'll see this lovely welcome screen, which gives us everything we need to know to learn more about Hanami.
00:07:00.400
We developed this just for version 2.1, so you'll see this in the release candidate.
00:07:05.879
It'll be the first thing people experience when they try the 2.1.0 version in a few weeks' time.
00:07:11.800
Luckily, however, we don't need to click these links because we're all here together.
00:07:18.039
So let's continue our tour directly. We can start by looking at the app class, and this is nice and simple.
00:07:24.440
We won't need to spend too long here, but there's one thing to point out, even at this stage: in Hanami, all our app code lives together.
00:07:30.319
In a single namespace matching our app's name. So here we have module Bookshop.
00:07:36.440
This is the module where all of our code will live.
00:07:43.479
Now, from the app, we can take our tour across to the routes, and what we see here is a blank slate.
00:07:50.199
We need to get started, so the first thing we'll do to make our app real is to create an action.
00:07:57.199
Hanami gives us a generator we can use, so for our Bookshop app, let's go ahead and make a products index action.
00:08:03.080
Something that can return a list of products. If we come back to our routes, we'll see that the generator has made one for us.
00:08:10.560
This looks pretty straightforward: a GET request to /products will take us through to that products index action.
00:08:16.759
Here's the action the generator made for us.
00:08:22.199
This is a basic starter action. The thing to know about actions in Hanami is that they handle all our app's HTTP logic.
00:08:28.159
We have exactly one action class per endpoint, and it's the handle method that’s the most important part of each action.
00:08:33.640
That's where we tell it how to behave. We can see that we're given separate request and response objects.
00:08:39.640
We can use these for inspecting the request params or setting the response body.
00:08:46.120
Now, speaking of those params, Hanami gives us flexible param validation schemas.
00:08:51.640
This can help ensure those incoming params meet our expectations both in terms of their structure and types.
00:08:57.720
Since this is an index action, let's go ahead and add a couple of optional params: one for a page number and another for a per-page value.
00:09:04.320
In both these cases, if they're provided, we want to make sure these are integers and are greater than zero.
00:09:10.880
Now with this done, that's enough for us to go ahead and return an error response if any of those params are not valid.
00:09:17.040
Hanami actions give us a number of different features that are helpful for managing that HTTP behavior.
00:09:23.079
What about our business logic? This is our index action. Where will we do the job of fetching the products to give back to the user?
00:09:30.600
Well, to do this, let's get to know one of Hanami's most important features, and that is: in Hanami, our app is also a container.
00:09:37.440
A container here works as a central organizing object responsible for loading and providing access to all the components in our app.
00:09:42.640
Now, what is a component here? Well, it's just our Ruby objects. Our action is already a component.
00:09:48.000
We can access it by using a key that matches its name, just like this.
00:09:55.279
What we get back is an instance of our action ready for us to work with.
00:10:01.480
In fact, any class we put into app will become available as a component.
00:10:06.800
So in this case, let's go and make a component for a product repository.
00:10:11.920
This repository will return a list of products for our action and could look something like this.
00:10:19.279
Since we put this in the app/repos directory, we give it a matching namespace inside Bookshop.
00:10:24.640
We then can add a method to find and return the latest products from our database.
00:10:31.440
This uses the full query API from the ROM-RB collection of gems.
00:10:37.720
These are standalone Ruby gems that have been in use for many years, tested and refined in real production environments.
00:10:44.080
Our plan for Hanami is to make these available to you in an easy-to-use way.
00:10:50.279
So now our repo's done. We have two components: our action and our repository.
00:10:56.800
But how do we get them to know about each other? What we really want is to use this repository inside our action.
00:11:02.839
To do this, we can use Hanami's deps mixin. We can include this mixin inside any class in a Hanami app.
00:11:08.880
Then we can pass it a list of components we want to make available as dependencies.
00:11:14.880
'Deps' here is our shorthand for dependencies, and those components become available as instance methods.
00:11:20.399
This means we can use the latest products from our product repository and return a JSON response from this action.
00:11:27.360
Now with this done, we can go back to our terminal and this time make a request.
00:11:35.040
We see here are our products returned to us as JSON, so that's a simple API.
00:11:42.040
We’re already making progress, but what if we wanted to build a more full-stack kind of application?
00:11:48.480
Something that can return our products as HTML? Well, for this, we can introduce a view.
00:11:53.519
Just like actions, every view has its own class in Hanami. This one here is for our products index.
00:11:59.920
Like everywhere else in a Hanami app, we can include dependencies inside our view.
00:12:06.040
Here we’ll bring in that product repo, so we can get access to our products where we need them.
00:12:12.639
Now, from here, the main job of views is to prepare exposures. These are the values we want to pass over to our template.
00:12:19.240
Let's pass over those latest products, and then in our template, we can prepare the HTML however we need.
00:12:27.079
To get the response that we want to give our users. Since our views are classes in Hanami apps, they are components as well.
00:12:34.160
So we can bring it in as a dependency to the action we created earlier.
00:12:39.720
In our handle method, we can render the view onto the response.
00:12:44.920
This will now return our products as HTML. Rendering a view from an action is such a common task.
00:12:51.040
Hanami will take care of finding that matching view for us, so we don’t even need the boilerplate.
00:12:57.040
After this, we can run our server again and see our products come back as HTML.
00:13:04.279
Well done, everybody, we've just made our first working feature with Hanami!
00:13:13.720
Now, you'll see because we’re onto the next verse, we’re into new sensations, new code broken up by slices.
00:13:35.120
By the way, no ChatGPT was used or harmed in the preparation of this presentation.
00:13:41.480
So now we've given Hanami a go, and we're in a great position to learn more of its features.
00:13:47.079
There's no better place to start than with the testing experience.
00:13:53.440
How might we test this view that we just created? Well, views like every part of Hanami are designed to be directly testable.
00:14:01.720
We can call new directly on our view like this, even passing a test double for that product repo dependency.
00:14:11.720
This will let us control its behavior for the purposes of this test.
00:14:18.040
Now from here, that's enough for us to go ahead and call our view and assert that its output meets our expectations.
00:14:25.079
What we see here is a very low-level isolated unit test.
00:14:30.759
For things like views or actions, we won’t typically want to go this far.
00:14:35.959
A more appropriate path would be to make an integrated end-to-end test, like a request spec or a Capybara-driven feature spec.
00:14:42.639
These are available in every new Hanami app.
00:14:49.519
The thing to take away is that because all the objects in a Hanami app are designed to be directly tested, it means we as the app authors get.
00:14:54.680
To make the best testing choice for each situation.
00:15:00.680
Now we've interacted with our code through tests.
00:15:06.639
We can also do this through the console. In Hanami, you can load the console via the Hanami console command.
00:15:13.480
And when you're in, you can use this app shortcut to refer to our app.
00:15:18.759
From there, we can access our components that come back as instances ready for us to use and interact with.
00:15:24.440
This console always starts quickly, no matter the size of our app, because we provide a lightweight boot mode across all aspects of Hanami development.
00:15:31.800
We use this for the console to running our tests, which is great for a responsive TDD flow.
00:15:38.720
Even when starting the dev servers, no matter how big our apps grow, all our development interactions will remain snappy.
00:15:45.199
Now, another thing that Hanami does when it boots our app is load our settings.
00:15:51.120
These are the values we give our apps to help them run correctly in each environment.
00:15:57.199
Things like keys, secrets, and flags. We can define our settings.
00:16:04.279
Say we want to extend our app now to send emails when new products are created.
00:16:09.360
To do this, we want to use some kind of third-party email API.
00:16:14.680
Here we can add a setting for its API key and specify we want this to be a string.
00:16:20.519
Hanami will then load these settings from matching environment variables by their names.
00:16:26.600
When we're working locally, we can pull these in from files as well.
00:16:33.399
Now, once that's done, we can access our loaded settings as another component in our app called settings.
00:16:40.160
It comes with methods defined for every one of the settings that we prepared.
00:16:46.639
Remember how we declared the string type for this setting? This allows Hanami to give us early feedback if we ever boot our app into an environment where the settings don't meet our expectations.
00:16:52.600
Which is really good for safety, because it means that we never get into an unexpected state as we work within our app.
00:16:59.079
The other benefit of having this settings object available as a component is that we can bring it into any class in our app thanks to the deps mixin.
00:17:06.679
So we can confidently access our settings wherever we need them.
00:17:12.640
No direct accesses to end sprinkled throughout our app!
00:17:18.160
Aside from our settings, so far we've only seen those components loaded from the classes we create in our app directory.
00:17:26.400
What if we wanted a component that needed some kind of special handling as part of its setup?
00:17:32.679
This is where providers help and we need to make one now for our email service.
00:17:39.280
We've already gone and created the setting for its API key. Now we need to go and use it.
00:17:46.679
Providers have their own folder in config, and here we're registering a provider for our email service.
00:17:53.039
Inside each provider, we have a couple of lifecycle steps that we can work with.
00:17:59.039
In this case, the first thing we'll do is require the gem that will let us interact with this third-party email service.
00:18:05.760
Then we’ll grab our API key from the settings we just prepared, pass it in to configure an instance of this email client.
00:18:12.840
Then we can register this instance directly in our app using a key called email_service.
00:18:18.000
With this done, we can now use email_service as a dependency inside any class in our app.
00:18:25.520
We can be confident that it’s the properly set up object that we've just managed here.
00:18:33.360
By this point, we've covered nearly everything we need to know about Hanami apps.
00:18:40.760
But there's one more important thing for us to discover, and that's slices.
00:18:48.000
Slices help us organize our app into separate domains or technical concerns.
00:18:53.840
They're a great way for introducing modularity and clearer internal boundaries to our apps as they grow.
00:19:01.679
If at this point you're thinking of Packwerk packs or Phoenix umbrella apps, then you're on the right track.
00:19:07.200
In a new Hanami app, you don’t see slices up front.
00:19:12.360
That's because we want the apps to be as approachable and simple as possible in that earlier state.
00:19:18.679
But they are built in and will appear as soon as we start putting code into the slices directory.
00:19:25.240
Hanami gives us a friendly generator we can use.
00:19:31.120
So for today, let's go and make an admin slice, which will prepare an admin slice directory for us.
00:19:38.400
Into this directory, we can create a products create action.
00:19:44.840
Just like our app, every slice maps to a single Ruby namespace.
00:19:51.120
So here we have an admin namespace, and Hanami gives an admin slice object that we can interact with.
00:19:58.800
This works exactly like our app, offering access to our components with the only restriction being that it only loads components from inside the slice directory.
00:20:05.440
Also, like the app, slices come with their own deps mixin, but it only loads components from inside the slice.
00:20:12.679
Now we’ve seen that both our app and slices act as containers for arranging and organizing our components.
00:20:20.120
Thanks to our deps mixin, we can clearly recognize the dependencies and the relationships between those components.
00:20:26.679
This starts to help us visualize our app as a big directed graph.
00:20:32.920
From here, we can take things one step further because slices can import components from other slices.
00:20:39.679
This means we can now envision an equally clear graph of all of our app’s high-level concerns.
00:20:46.960
This is a powerful approach; we have the tools built into the framework to help us organize our code at every level.
00:20:53.440
For easier understanding and maintainability. Slices also help us with operational flexibility.
00:21:01.680
Because we can choose which slices we want to load for each deployment of our app.
00:21:07.440
One thing we want to do with Hanami is extend this power to apps of all kinds, not just web apps.
00:21:14.160
Let's take a look at the Gemfile of a new Hanami app.
00:21:20.760
What we're seeing here is a web app because it includes the router, view, and controller gems.
00:21:27.360
But these are not fixed dependencies of the framework; this is why they’re here in the Gemfile.
00:21:34.000
Say we wanted to build something different than a web app; all we need to do is remove these extra lines.
00:21:40.760
With the one core Hanami gem that remains, we have all the quality-of-life features we've just learned.
00:21:46.680
We have app containers, components, deps, settings, providers, the console, even slices.
00:21:54.080
And we can put all these features to use for any kind of app.
00:22:00.760
This means we get to keep everything we need and nothing we don't.
00:22:07.360
Hanami 2 is no longer just a web framework; it's an everything framework.
00:22:15.360
Say we want to build a CLI tool, a serverless function, a stream consumer, or a chatbot.
00:22:22.520
For each of these, we still want all the conveniences that a full framework might give us.
00:22:29.680
Now we can do it with Hanami. I'm not sure if Ruby has ever had something quite like this.
00:22:36.280
I’m really excited to see where people might take it.
00:22:42.760
Now we’ve seen every part of Hanami.
00:22:49.440
Everything you see on the screen is either released or imminently releasing in the next few weeks.
00:22:55.680
This means you have enough to build an API or even now a full-stack web app that goes all the way through to HTML views.
00:23:03.360
Along with fully integrated and fast-to-compile frontend assets.
00:23:12.000
Now we’re a small team who’ve been building this.
00:23:20.240
We have been looking to maintain our quality and momentum by releasing things progressively.
00:23:26.640
This means the database layer is coming next year, but by virtue of us using ROM, which is an already released Ruby gem...