Talks

Livin’ La Vida Hanami

Livin’ La Vida Hanami

by Tim Riley

The video titled 'Livin’ La Vida Hanami' presented by Tim Riley at RubyConf 2023 focuses on the Hanami web framework for Ruby, particularly its 2.0 version and the upcoming 2.1 release. In this session, Tim introduces Hanami as an application framework designed to enhance the development experience by facilitating maintainability and testability.

Key points discussed include:
- Introduction to Hanami: A Ruby framework originally launched in 2014 that has evolved into a robust solution for creating maintainable applications.
- Hanami 2.0 Release: The framework has integrated Dry-RB functionalities, offering a combination of power and simplicity tailored for Ruby developers.
- Hands-on Development: Tim guides the audience through building a sample application named 'Bookshop', illustrating key components such as actions, repositories, and views.
- App Structure: Emphasis is placed on how all application code is organized and how dependencies are managed using Hanami's design patterns, including a central container concept.
- Testing and Modularization: The session highlights the testing capabilities of Hanami, including low-level and integrated tests, and introduces the concept of 'slices' for app modularization, allowing better organization as applications scale.
- Multiple Application Types: Tim reveals that Hanami is no longer just limited to web apps, demonstrating its flexibility by discussing potential use cases for CLI tools, serverless functions, and other types of applications.

In conclusion, Tim underscores Hanami's commitment to maintainability, testability, and developer joy, noting that Hanami 2 and beyond are expected to pave new pathways for Ruby application development, making it relevant across various domains. Overall, attendees are encouraged to explore and adopt Hanami for their future Ruby projects, promising a fulfilling development experience.

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...