 Right. Hello everyone. Can you hear me all right? Okay. So I'm Tim. I'm visiting today from Canberra in Australia. I love living there. It's a great city, but it has been the tail end of the winter. So I've really enjoyed being here in Bangkok these last few days. The weather and the food, both being refreshingly hot. I'm really excited to be here to help pick off this conference. So thanks to all the organizers for making this happen and all of you for coming here today. And I do need to say one thanks to these people, my family. They gave me tons of time to make this little talk that we'll be sharing. So back home in Australia, I work at a company called Ice Lab. It's a place where I ride a lot of Ruby. And out in the open source world, I do the same in projects like these. And what these projects share in common is they offer a new, fresh approach to helping you build more maintainable Ruby application. And we'll be looking at some of these in detail today. But I want to point out that my work in places like these didn't just come out of the blue. It was a long time coming because I've been working with Ruby for a long time since 2001, in fact. And time really does fly. And if you hadn't thought about it, 2001 is a long time ago now. And just to remember, you know, what I was up to in those days, I firstly went back into my photo collection and pulled up a pic just to see what I looked like back then. And you believe how young I looked? And so 18 years have passed since that time. And given I'm 35 right now, what it also means is that I've been riding Ruby for over half my life. And just to continue this little trip down Emery Lane, I went and found the code to my very first Ruby project. Now, this was a time before Rails, before Ruby gems even. So this was just a little project, but it wasn't about the web at all. In fact, it was an IRC bot because back then I had a bunch of friends whose hobby was hanging out and writing these things in completely inappropriate languages. Now, I might have been young back in 2001, but I already knew that Bash wasn't for me. So I looked around a little bit more and I noticed a friend who'd started experimenting with this language called Ruby. And it really did look nice. So I gave it a go, and that's what I used to write my own little IRC bot. Now, looking through the code today, though, it really does seem like a strange relic from some foreign age, from the little things like my camel casing of the file names, to some perhaps more egregious things that a beginner might do like building a whole plugin system based on global variables, through to just the plain old strange things like my complete absence of empty lines. Yes, every single class like this was looking like this, with not a bit of white space to be seen. And that fixed me, we see there. It's old enough to drink now. Not yet fixed. So I have definitely come a long way from this 11 file application, that's for sure. But the amazing thing is some 18 years later, still writing Ruby. And I'm still loving it. So really, I couldn't have found a better language to accompany me through my journey as a programmer over all these years. And it's this that I want to share with you today. Some thoughts from a life long lived with this language called Ruby. And some other ideas about what's exciting me about Ruby in the present. And we'll be tying together a whole bunch of threads to do this today. So I think I found just the right way to do this. So let me set the stage for you. It is a golden age in the land of Rubinia. After enduring years of unrest, the compatibility crises of the 1.9 epoch, and the roiling dramas played out across many impassioned comment threads, the citizens of Rubinia have at last arrived at a time of peace and prosperity. Thanks to the dedicated oversight of Lord Matz and the Knights of the Core team, the villagers are thriving, and the harvest are well managed and bountiful. And with every passing cycle, new facilities are built and spread across the land for all to enjoy. Most of the ancient pockets of trouble have been destroyed, with the only remaining hazards for wayfarers being the occasional stray band of marauding bugs. Most of the lands have been mapped, though some unexplored regions still remain. And you, as a new arrival to Rubinia, are as yet a Rubyist in name only. Your quest is to find and master the virtues of the Rubyists, then wield them to foster a new standard, to create a shining vision to lead all of Rubinia further into this new age of light. Your quest will not be simple. It will not be easy, but it will be noble for it is needed. Your destiny is Rubinia's and Rubinia awaits you. So let's begin. The quest of the Rubyists. Yes, that's right. I think the perfect way to explore this age of Ruby is to enter a big open world computer role playing game from its very own golden age, which as you might tell is 1985. And because today will be set in this golden age, we have no big evil arch nemesis to defeat. Instead, our quest as the Rubyist is to exemplify the good life. To learn and master the virtues of the Rubyist and then help create a better future for everyone. And today we'll be going through this game together. And as we do so, we'll be doing two things. The first thing is finding out the steps we need to take as we go through this game. And then we'll see how as Rubyist in our world, how taking some of those steps can help create a better future for ourselves and those around us. Now, some of you might have noticed that I'll be heavily inspired today by Ultima 4, the 1985 role playing game by Richard Garriott. But if you're not familiar with this style or era of gaming, that's okay. We'll be talking a lot about Ruby. It's why we're here. This game is just going to help us connect the threads. So I hope you can sit back, relax and enjoy this adventure. So let's get started. The first thing we'll want to do in Rubinia is explore the world. To understand the landscapes and the townships and meet the people who are all really friendly and very happy to talk with us. In fact, engaging in dialogue is one of the most critical things that we can do in this world. Because through dialogue, we begin to learn how this world works and learn about things we get to discover for ourselves. And this is how we learn what role we have to play in this world, how we can be a part of this ecosystem. Now, what does this mean for us acting as Rubyists in our world today? Well, first, I think it means taking some effort to observe what's going on around us in the community. Observing the discourse, hearing the things that are being talked about and thought about as a community. The kinds of discussions that take place on blogs and chat rooms in and around conferences like this. Those things reveal not just what people are thinking about on the surface, but also the values and the priorities that underpin our community. For a personal example, here's a talk I watched in 2015 that completely changed the way I thought about Ruby and I only came across it because I'd chosen to follow some people online whose Ruby work I thought was interesting to me. And we can also observe the actual work being undertaken as well. But just going back to that talk, it was given by Piotr Sonica and I really liked what he had to say. And his ideas were clearly informed by the work he'd been doing. So from there, it's just one click away and you're seeing 2015 GitHub and I can look at all those ideas being played out in concrete code, which I could explore for myself. And yes, I think we should take plenty of time to actively observe code as well. In fact, reading code is some of the most instructive reading we can do as programming. It helps ground our ideas about different techniques and opens our eyes to different problem spaces. And doing this with Ruby really feels like a superpower because with commands like bundle open, it's just so easy to dive into any piece of code running our application. And after ating these with the language, this is something I still do every day. So if this is not part of your regular programming flow, I highly recommend it. So now after all that observation, that watching, there's just one thing left for us to do and that's to close the loop, to reach out and engage with the people we want to learn more from or work with. Because the smallest constructive steps make you a part of this community. And for me, that meant sending a single email to Piotra, which ended up leading to a long collaboration together. Because at the root of it, programming is a collaborative, social activity. For our work to make the biggest difference has to happen in teams and communities in a society. And that's why we're leading with these points today. Because the skills to work within any given society are the same skills that lay the foundation of a happy programming career. These are not soft skills, they're core skills and they're ones worth practicing. And in the Ruby community, we already have some guiding principles that can help guide our actions in our community. They're worth remembering every day. But back in Rubinia, we're starting to get some idea of how this society works and continuing our explorations. And then as we, what's this? We've been attacked by a nasty bug. But that's okay. Because as the Rubyist, it's our job, find, squish these things. But to do this successfully, however, we need to get to know our equipment. And this brings us to the first of several technical exercises for our Rubyist. And we're starting with one of the most important, understanding the purpose and the danger of one of Ruby's most fundamental pieces of equipment, state. So when we think about building an application with Ruby, we think about working with objects, right? With every object being an instance of a class, just like we're doing here with a new entry for our player's log. And since this is a brand new entry, we can go and assign some attributes and save the thing and away we go. We've already built the first part of a new application. Everything's pretty good, right? Well, I think there's one thing at play here that we should take some time to notice. And perhaps it's easy to gloss over because we can take it for granted as a feature of Ruby. And as what we did when we mutated this object state. Here, we assign those two attributes. And these methods are what we call attribute writers in Ruby. And behind the scenes, they'll be assigning or modifying the object's instance variables. And as those instance variables that make up the object's state. These are the values that it stores or manages internally. And when we modify these values after an object's already created, it means that object now has mutable state. Which means that it's possible to change the state, the nature of any one of these objects over time. Now, in a simple scenario, this will all be fine. But we all know that nothing stays simple for very long. Especially when we start building out this actual class. And this is just a few keystrokes from this. And then we get all of this to deal with. Now, these are convenient features on the face of it. And they'll get us a long way in a short period of time. But they all rest on a foundation of mutable state side effects. Which means they can go on to become a huge source of complexity, bugs, and even technical debt. And I think we can do better. And I think to do this, we have to start recognizing that some of these problems arise when we have classes whose objects try to play two distinctly different roles. That is, objects that be also behaving as objects that do. In our case here, our entry is one of those objects that be courtesy of the attributes that it holds. Objects like this exist to hold the data in our system. But our entry is also one of those objects that do, courtesy of the methods that it offers that enact change by virtue of mutating its own state or triggering side effects. Now, you could argue that, in fact, these roles combined are a foundational aspect of object-oriented programming. That is the combination of data with behavior. But even with the very best of intentions and encapsulations, this approach still conflicts our object's behavior with the notion of time. And that means that to truly know a system, we need to know every possible combination of objects and every possible permutation of all their states in the entire lead-up to any given moment, which is a lot. And I'm sure anyone who's dealt with a large Rails app can know some of these challenges. And it just goes to show how a seemingly small design decision, like relying on mutable state, can lead to much bigger problems when you play it out across an entire app. So let's see where we can go from it. How about if we took those two roles and unbundled them? To start with, we could make it so our player log entries are simply objects that beat. In fact, we could start by modeling them just as a plain data structure. And then to take the role of the object that does, we could create a new dedicated class just for creating entries. And in this role, we can see here that our class defines absolutely zero internal state. There's no instance variables at all. So now we have this new arrangement of the code, which we can go on and run and see we've achieved the same outcome as before. We've created our entry. But we've gained several benefits because this arrangement is much simpler. Each piece of code has less work to do and there's no mutation to worry about. So we'll definitely have fewer bugs because of this. This arrangement is also easier to test because we can follow that one basic approach given a certain input when we run it through our code to get the output that we expect. And in arranging our code like this, we've ended up satisfying one of the most helpful object-oriented design principles, single-responsibility principle. Because create entry now has only one reason to change. And if you want to add more behavior dealing with entries, it's clear that the best way to do this will be by adding other single-responsibility classes around the one we already have. So I think we're in a much better place now. And it all came from this idea to separate objects that be from objects that do. And just think about it, another good name for objects that be is values. And another good name for objects that do could be functional objects or functions for short. So we replace that one do everything entry class from the beginning and we've now wound up with separate values and functions. And in fact, we began by musing over some of the foundational aspects of object-oriented programming, but we've ended up with some of the fundamental building blocks of functional programming. But really, what we've arrived at here is a style of Ruby that blends both object-oriented and functional programming. And I think it's going to serve us well here in the land of Rabinia. And at the very least, it's given us the right equipment to go ahead and squish this bug. So with that first encounter handled, we can begin to roam the lands again and continue our explorations. But now that we know about the dangers lurking out there, I think it's best we don't travel alone. We'd be much stronger in a group. So we need to go ahead and form a party. So now, as we visit the towns and talk to people, we'll be on the lookout for those sorts of people who would be interested to join us and form this party. These are people who can complement our own capabilities and will most certainly help us become a better Rubyist. So what could a party look like for a Rubyist today? Well, as an example, I want to share some folks who have been part of mine at different points in my Ruby life. First, we have some connectors. Here's Hugh, who I met in 2007. And back in those days, there was a website called workingwithrails.com, where you could go in there and register yourself and list yourself as a Rails programmer. You could share your name, location, and a few other funny things like your favorite gems, access, whatever. And the funny thing about it was that this site was small enough in those days that anyone interested could just load the page and scroll through it and see basically everyone who's there. And this is how Hugh ended up reaching out to me because he noticed I had changed my location and moved into his hometown. And he ended up getting me my first Ruby job there. Then we have Matt, who came to some of our early meetups in this town, and he's a very social guy. And he went on to introduce me to the broader Australian community. Now, moving forward in time, we have some teachers. Like Piotra, whose talk I shared before, that was just so formative for me. And Nikita, and both of these people are my teammates in the Dry-Bee and Rom-I-Bee projects. And I won't hesitate to say they're some of the smartest developers I know. So I learned so much just by talking through problems with them and seeing how they go about solving them in code. So we'll need a party a little bit like this as we explore the land as the rebellious. And here's why. We actually got lucky when we squished that bug before because it was just one little creature coming all on its own. When more often than not, we'll encounter many of these things all at once. And this is where we need our party and for our party to work together strategically. And that brings us to our next exercise. So far, we've only been working on things in a very small scale. But what if we needed to express some more complex behavior, the kind that might involve several different participants working together just like our party needs to work together in combat? This is where we have to take things a step further and use composition. So looking at that code before, I called that a single responsibility class because it only has one reason to change. But I did kind of cheat because that's not fully working code. I cheated because I ignored that create-in-db method. What we want is to make this work and we need something to provide that behavior for us. But we want this something to be external so that create entry itself can retain its focus. So this is the job for a dependency. And to make this all work, we're going to rely on our good old friend's state, but it'll positry. And then it'll capture it in the state so we can use it later. And then now back in our call method, we can create the entry in the database using the create method that this entry repository gives us. So with this done, we can now make an instance of create entry, passing in that repository. And yes, while we did capture that in state, we make note to never change it later, which means the behavior of this object will never change over time. So it means we can call it multiple times over with different inputs and have it work how we expect every single time. And that's composition at work. Instead of adding new behavior directly into an existing class, what we did is we provided it through another object and then compose those objects together to ensure the behavior is available where we need it. And in fact, we accomplished this using dependency injection, which is a classical object-oriented programming technique that works really nicely with our functional style objects. And thanks to this composition, using this dependency, our class is now fully working while still preserving its focus and its single responsibility. So now we've leveled up and we have the power of composition at our command, which means our party can work effectively together to defeat even the most challenging of attackers. And this means we can continue to roam the land again and push the frontiers. And along the way, we might encounter the unexpected like these treasure boxes here. Of course, we'll want to open them up and see what's inside, but we've heard tell some of them may contain poisonous traps. So we need to be prepared for this. What we need to do is cast some spells. Now, spells in Rubinia can be prepared by combining reagents, which we can buy from apothecaries in certain towns. And then once we have the right combination of reagents of ingredients, we can combine them to prepare the spells we want that will get ourselves out of any tough situation. And you know, if you think about it, this is pretty similar to something that we do as Rubyists every day, right? Going to a marketplace, acquiring items, and then combining them to great effects. Yeah, I'm talking about Ruby gems. And what will help our Rubyist is becoming acquainted with the kinds of focused interoperable gems that express the same philosophy that we've been developing so far. And this does mean we're gonna be seeing some code here that might look a little bit different because it takes a different approach to solving common problems. But trust me, each one of these boils down to plain old Ruby and we can use them however we want. So let's take a look at a few gems. First one is dry system. And dry system will help us take the functional style classes that we've written so far and make it easy for us to build whole applications out of them. And it does this by providing a container and which will act as a central facilitator, giving us access to all the objects in our application. So given a class like this, which we've written out so far, we can now access it from our container like this using this identifier. And then we're given back an instance of that class so we can go ahead and start to use it. Now you might be thinking at this point, well, this is just Ruby. We all know how to call dot new in a class. What's the big deal here? Well, this thing really starts to matter once we need to bring together our dependencies. And we saw how we arranged this class before so that it now requires a dependency to be provided when it's initialized. And I know when I saw a class like this for the first time, the question jumped right out to me, which was, well, in a real application, how's a class like this actually gonna work? How are we gonna make sure that we bring in the right dependencies for us to use it? Well, now that we have a container in place with every object being accessible by an identifier, we can use dry systems auto-injector to declare the dependencies that we want by their names right there in the class definition. So now, when we get our object in the container, that injector resolves all the dependencies and supplies them for us automatically. So we get back an object that's fully populated, fully functional and ready for us to use. And with this facility in place, object composition becomes extremely low friction. So when it comes to adding new functionality to our applications, adding that via composable single responsibility classes becomes the easiest and most natural thing to do. So this will improve the code quality of each individual component in our apps because they'll be able to retain their singular focus. But it also improves the flexibility of our app as a whole because by default, we're building things in a way that makes them easy to reuse. So that's dry system. Next up is dry validation. Say we wanted to make sure we only save our entries if all the valid attributes were there. This is where dry validation helps because it lets us define standalone validation contracts. So for our entries, we wanna make sure that both the body and date are given to us and filled and we wanna make sure they're properly typed as well. So that's our contract. Now we can go back to our create entry class and include the contract in our list of dependencies. And then in our call method, we can then run the input data through the contract and only go ahead and save that entry if the contract validated successfully. So that's pretty good. Now that we're thinking though about what a valid entry might look like, it would be good if we could find a way to nicely model them as we pass it around our app because if you remember, we're just using a plain hash so far to represent these. And this is where dry struct and dry types can help. With dry struct, we can define classes to represent our player log entries and give them strictly typed attributes. And these classes give us a place to put whatever other methods we might want around our data. And we'll be able to rely on these methods working exactly as we expect because these entries can only be initialized if all those attributes and their type expectations are met. So now once they are initialized, those attributes can also never be changed. These are effectively immutable. So this becomes a really helpful way to enhance our values. We get the ability to add extra behavior and because we don't need to worry about mutational side effects being something that these carry, we can then pass them really freely all around our application. So we sorted that out. Let's go back to create entry one more time and take a look. So now that our validation can only succeed, now that we only can save the entry conditionally based on that validation, it would be nice to find a way to properly express both sides of that result because returning a nil just isn't good enough for the failure case. And this is where dry monads and dry matcha come in. By mixing these into our class, we can now wrap our created entry in a success object. And now we can cover the failed validation case too by wrapping that in a failure object. So now when we call create entry, we'll see its return value wrapped up in either one of those outcomes, either a success or a failure. And then when we use the object, call it, we can use this matcha block to act on either case, the success case or the failure case. And by taking this approach here, we've improved our applications robustness because we've elevated failure handling. We've made it a first class concept, just as important as the handling of the success path. But that's not all. Because success and failure are both instances of the result monad, we can start to do some really cool things with them, like forming whole pipelines of conditionally executing commands. And in fact, dry monads offers a special mode to help with this and it's called denotation. What it does is it makes it natural to express this kind of pipeline of operations in regular Ruby code. And then when we run it, if every step returns a success, the code executes all the way through to the bottom and returns the final value. But as soon as a failure is returned, that pipeline will short circuit, returning that failure immediately and all the subsequent steps just get skipped. And we get this amazing, safe pipeline style behavior all while still writing natural, expressive, flowing Ruby. It's truly magic. But this is not the kind of magic that we've learned to be wary of as Rubyists. It's a good kind of magic. It's the kind of magic that arises from the combined, harmonious use of small, focused, flexible on its own, but they're all also designed to be composable, just like our own good application code should be composable. So it means we can combine the gems in a way that suits us. And then when we leverage them together, we achieve something from them that is greater than the sum of its parts. That's the magic. These gems are the reagents that will let us cast all number of spells. They'll help us through whatever feature requests come our way and whatever unexpected circumstances we encounter in Rubinia. So what about creating our own gems then? Well, the good thing to know is that every gem has to start from somewhere. Every dry-hubby gem started off small, just trying to solve a common problem. And some of them stayed that way in the end, like Crymatcher, which only has 100 odd lines of code. So if you see some common pattern or behavior in your code that you think you could extract, publish, then I say go for it because every gem is an opportunity for ourselves to level up, to practice and get better at building systems made of reusable parts. And the Ruby ecosystem might feel established, but there is always the opportunity to take another look at things and offer a new and interesting take. Every one of us has a unique perspective on things and that's something worth sharing. And this is the only way our ecosystem can evolve and continue to remain relevant. So all this experience under our belt, I think there's just one more place for us to go. In our travels, we've heard that there's a dungeon and it's going to permit our entry only once we've mastered the virtues of the Ruby. And you know, I think we're ready. We've shown curiosity by paying attention to the goings on around us and finding opportunities to engage with our community. We've shown cooperation by forming a party, working together. And we've shown cognizance by developing a working knowledge of how best to use Ruby's fundamentals along with the collection of helpful gems to better structure our code. And that's enough for us to enter the dungeon. And here, final task awaits us to find the codecs of ultimate Ruby wisdom which will foretell the future of Ruby. But we've also heard that for the codecs to appear, we have to take our own steps towards building the future for ourselves here in this dungeon. And in our journey so far, I think we've definitely learned enough concepts and elements that we can start to put something together. But I hear one of our party members ask very fair question, in the heat of the moment in this dank, unfamiliar dungeon, how are we going to know exactly what combination of tools and techniques are right to apply at any given moment? Well, lucky for us, some work has already begun on solving this problem. And that is the work being done in the Hanami team which offer a modern complete web framework for Ruby. The project was established by Luca Guidi and in the two years that have now passed since its one point over release, our three teams have realized just how similar our goals are. So we've decided to join forces to form a party of our own and work together on delivering the next major release, Hanami 2.0. And I think Hanami 2.0 will give us exactly what we need to take our steps together here in this dungeon because it takes all the concepts that we've seen so far and then curates them and it will wrap them up into a cohesive, nice, easy to use framework experience. And we're rebuilding every aspect of the framework itself according to these principles. Hanami 2.0 will be fully oriented around immutable functional objects working together in concert. We're incorporating dry system into the core of the framework which will make it really easy for you to build your whole application around components like this as well. And we're continuing our push for a framework that helps you focus on your application's domain. And this comes as part of implementing a layered application architecture. And Hanami 2.0 is gonna help us take this to the next level. Here's how it's gonna work. First we'll start with our application's core. This is what makes your app what it is. It's the code that expresses its most essential functionality. And then we can add layers for how that application is delivered in various forms like a layer for a web interface which handles routing, HTTP, view rendering. And these layers will contain their own specific behavior and access the core that's required. And then over on the other side we can introduce other layers like this one here for dealing with database persistence. And we have touched on repository objects so far. Well Hanami incorporates ROM to give us really powerful, easy to build repository objects right out of the box. And then we can take things one step further because with Hanami 2.0 we'll be able to decompose our application's core into discrete subsystems with every one of these representing a specific area of concern. And then from within each system we'll be able to use and import the others as we require. And in doing this we end up forming a clear dependency graph of our application's high level features. And then if we go and zoom into any one of these systems thanks to the way we're building our functional objects those dependency relationships adjust as clear the object by object level too. And thanks to all of these explicit connections when a request enters any Hanami app we'll be able to really clearly trace it as it moves its way through the app and the final result is served. So this means as developers we'll be able to jump into any part of the app and recognize that clear flow of data at play. We'll see the explicit inputs and outputs will notice that there's zero unsafe mutation and we'll see all the explicitly declared dependencies. And what this means is that if we have to go in and make a change we can make the change and then be truly confident of the consequences. And that's what makes for applications that are a joy to understand and maintain both in the small and in the large. And then later as our apps grow up and evolve and if we sometimes need to move away from some of those standard framework conventions well this is fully supported too because Hanami too will embody the ideal of a framework built on top of solid abstractions. The framework itself will rest on clearly defined or abstractions which it then implements using the ROMRB and DryRB gems. And as we know, we've seen these at play they're already established independently useful very flexible gems. So then if we ever need absolute control at certain places we can swap them out for our own implementations or just work with those gems directly and use them to their fullest extent. And we can do all of this without having to feel like we're fighting the framework or distorting our application. And this is what makes Hanami too framework of the future. It's a framework of the future because it's using modern Ruby tools and techniques. Yes, but it's also a framework that respects the future of our applications. It's going to help us get started quickly like any good framework should but it will do this in a way that sees us writing well factored, well-designed code right from day one. That's code that will be more than happy to revisit as our apps grow past day 1,000 beyond. So I think we've established some good clear steps for the future here in this dungeon with Hanami too. And once again in doing this we've demonstrated the virtues of the Rubyist. We demonstrated curiosity by looking at the current Ruby framework story and the struggles that app developers face and asking surely there's another way. We demonstrated cooperation by recognizing the shared ground between all these Ruby open source groups and teaming up to build something great together. And we've demonstrated cognizance by refining all the good Ruby principles that we practiced so far and embedding them deeply into the soul of this framework. I think this is enough for the dungeon to reveal what we're seeking. Off this, an altar has appeared. Should we go and take a look? Okay, upon the altar lies the codex and we open it, turn it to the last page and on it we read. The most expressive, you never know what's going to happen when you set out on an adventure. We spent our time today roaming the wide open spaces of this land called Rubinia. And for the person in front of their DOS PC in 1985, this kind of world must have seemed like an endless possibilities. Well, that's the world we have today with Ruby. Those possibilities are endless. Ruby is a language that flourished and has then endured because it's a language that embraces possibility. And if we embrace it back, those possibilities are ours to create. We can create whatever future we want to see. And then as individuals, all of us together as Rubyists, I wanna reassure you that we can each take these steps at whatever time we're ready. Because I know for me, it took a good 15 years before I met the right people and arrived at the right moment to discover the way I truly wanted to work with Ruby. And Ruby was just there waiting for me all along. And now that the language has reached this golden age, we can be sure that Ruby will be waiting for a long time. The journey is long, so we should make it ours and enjoy the ride. Thank you all for taking this ride with me today. And I do need to say a thank you to Ancient Bards, a favorite band of mine, and they gave you permission to use the music to set the scene for us today. So really, it's a joy to be able to kick off this conference, but to also do it while sharing three of my greatest loves, Ruby, Retro Games and Epic Orchestral Metal was just even sweeter. And as for Ruby, I'm more excited about the future than I've ever been, and it's largely in part to the work going on in these groups here. As for Hanami 2, it's had one alpha release already, and we're working hard to get another one out the door next month. So other than that, I hope you all have a great conference. I look forward to chatting with many of you and hearing about your future with this wonderful, weird language of ours. Thank you so much.