 Thanks for coming to Promises in Ruby. Super excited to be here, sharing what I think is some pretty cool code. My name's Dinshaw. I've been doing Ruby since about 2000, no, sorry, building websites since about 2000. Found Ruby via Rails in 2006, and I now work at Constant Contact in one of our New York offices in Tribeca. And we have been recently trying to automate the publishing of ads to various ad distribution services. And these services tend to have pretty complex domains, and they expose them via RESTful services. So what this means for us is that we've had to manage these really long sequential workflows. So we'll create one object on the remote service, and then we get an ID back, and we use that to create the next one. We do that like five times, and then we have to send all the IDs back to associate them with each other. And we started off coding this top-down, and it quickly just got ugly and coming from an object-oriented world. You don't really want to look at a bunch of just top-down methods. And then we tried to make it a little cooler, and we were using a state machine to try and manage the workflow. And if any of you have tried to use state machine to manage a workflow, it almost immediately gets unmanageable. And you find yourself bouncing around between the callbacks that you've defined in your state machine and the actual code you're running, and you don't know where to handle exceptions. And nothing really makes sense. So we started looking for something that was a little more along the lines of how we thought about the problem to express it in code. And a friend of mine suggested looking at JavaScript promises. So I took a look, and it looked really cool. And I did a very superficial implementation in Ruby, and we used it for a little while. And then this talk got accepted. So I went back to look at promises again and realized that I had completely missed the point of promises in Ruby. So took a little bit of a deeper dive, and it turns out that it's just a really cool pattern. And watching it happen in Ruby is really cool, too. So we're going to do a lot of code. We're going to go through the code that we're currently using. So the slides, if you want to follow on, there's some links to peripheral reading and stuff, github.com slash dinshaw. I also just tweeted both the slides and the code base. So that's atTallFriend. So if you, at some point, decide that it's interesting enough that you actually do want to see the code, feel free to find me and check a look. And the code is actually down there in the footer as well. So what are promises? Really? All right. Well, I'll fix that, but try the tweet. Try checking atTallFriend, and that should work. And if not, I apologize. I'll get it pretty afterwards. So what are promises? Where do they come from? I'm quoting here, promises are trying to provide direct correspondence between synchronous and asynchronous functions. And that's a quote by Dominic Denicola, who, you could argue, is the modern father of promises, at least in JavaScript. The first link here is a presentation that he gives called Callback's Promises and Co-Routines, where he talks about the origins being from Joule and E, which are languages for managing distributed systems. So they are heavily into concurrency. And then he talks about just the application and the value and where it came from. You're missing the point of promises is a blog post that he wrote, because in the beginning when languages were starting to adopt the promise pattern, there were some disparate implementations, because people were kind of taking part of the spec and implementing that. And there were a lot of differences in the implementations. So he tries to, in this blog post, he really does a great job of clarifying the truly core concepts. And then a lot of that work and a lot of community contributions culminated in the Promises A-plus specification. And it's a quick paper to read, and you'll see, if you look at the contributors, you'll see a lot of names that you're probably pretty familiar with from the Ruby community and the JavaScript community. So what is a promise? We'll start with the problem that promises was trying to address. So we've all, if you've written any JavaScript, you've probably done something like this. But we're assuming a couple things here that GetCat is a function that is going out over the wire. It's making an HTTP call, getting some representation of a cat. And then, because we're not sure how long that's going to take, but we want to be able to decide what to do afterwards, we pass it in a function. And we have access to an error or the cat, depending on what happens. We check for the error. If we get the error, we handle the GetCat error. Otherwise, we proceed. And then, we do this again for the cat pics. And it's a pretty manageable, it's a pretty readable solution anyway. Like all of us get what's going on here when we look at this, but it doesn't look anything like the way we think about this problem in our heads. So wouldn't it be nice if we could have some way to express what we're actually thinking here, which is like, I want to get a cat. I want to get the cat pics. And I'm going to show you the cat pic. And the rest of it could be argued as sort of implementation details. So Promise's attempts to solve this problem. So this is a working promise code if you had an implementation. There's a couple of assumptions being made here that I'll get into. But we're saying GetCat. And GetCat should return a promise. One of the contractual obligations of a promise is that the return value will respond to them. So in this case, it's probably returning a promise that responds to this then method. And a promise is either in one of three states. It begins its life in a pending state, and then it becomes either fulfilled or rejected. And if it is fulfilled, it will follow the success path through all the subsequent thens that are chained onto it. And the success path is the left hand argument of the then here. So if GetCat works, it will then get cat pics. It will then show cat pics. And at any point, one of these operations fails due to an exception being raised or just because whoever's programmed it has decided that that is a no longer fulfilled case, then it will switch and it will start following the error path or the right hand side of these arguments, GetCatError and GetCatPixError. So what does this look like in Ruby? So here is a tried example of what we were dealing with in Ruby. We had these huge methods with 15 functions that we had to do that all returned an ID. And we had to pass it into the next one. And it all had to happen sequentially. You can see what's going on here at three lines. It's not horrible. But imagine it at 15 or 20 lines. And then what if you wanted the whole thing to be run in the background, so it adds another level of obscurity to handling errors and deciding what you want to do. And then what if you wanted some of these things to be happening concurrently but waiting for one or two or more things to happen? Then this pattern is completely not applicable. So you need something a little more powerful. So what would the promise pattern look like in Ruby? It would look exactly the same as it does in JavaScript because the promises specification is just that. It's a specification. It's not a coding library. So you can do it in any language. And if you play by the rules, it's going to look the same. One thing to note here, so the assumptions that we're making in this code is line one is a getCat function, which is presumably getting a cat from some remote service and then it's returning a promise that will respond to them. So one thing to note here is that the arguments to then are lambdas. So they are closures. They're self-contained pieces of code with scope that can be executed. If you're not super clear on blocks, prox, and lambdas, this is a link to an article by Adam Waxman which explains the difference between all three of them really beautifully. Just for what we're doing here, the only real important part is that we're going to be using lambdas throughout. And it's just a self-contained piece of code. And you can invoke that code by .call. So if you have a lambda, you can pass it around wherever you want. Eventually you can hit .call. And it knows what to do, including the scope that it was created in. So we have a getcat method is returning a promise in a fulfilled state, hopefully. It then goes to line two and it runs getcatpicks. If it stays fulfilled, it runs showcatpick. One thing that is sometimes a little confusing. At first glance, it may seem like the right-hand side argument of then is there to catch errors of the left-hand side argument, but that's not the case. The then is meant to choose which lambda it runs based on the status of the promise that's being passed into it. So line one decides which lambda line two will run. Line two decides which lambda line three will run, and so on. So to expose, we're going to get into some code examples here in a second, but just to expose a little bit more of what's going on. If we were to peel away one level of that getcat, we have a very lightweight promise implementation that we're going to be working with. The promise.start is a little helper, little convenience class method that just instantiates a new promise and immediately fulfills it and sets the value to whatever the argument getting passed in here. So we're assuming that our cat class has a class method called get, goes out, gets a representation of the cat, and then the promise kicks off and passes a fulfilled promise with the value of cat into the first van on line two. It will run get cat pics, and if it's successful, it will show the cat pic. So let's take a look at some code. All right, so we just saw this promise start, and here's what it looks like in real life. Should I? Is that too big, too small? Should I shrink it so it's one line? You guys can read that. Good? All right. So we've called promise start. And we see that we get back a promise. We see that the state is fulfilled and the value is Garfield, the pending steps you can sort of ignore for right now, but we will get into them shortly. All right, so let's illustrate the then part of this. So we have the same exact promise start. It's going to be fulfilled. It's going to pass itself to .then, and it's going to execute the left-hand argument to then or the success lambda. In this case, all we're doing is just putting it. And we've omitted the error lambda because it's actually an optional argument. And if you omit the error lambda, it just passes it through to the next then so that you can handle that error whenever you feel like it. You don't have to deal with it right then because it will just know up all the way through the chain until it gets to something that says it can't handle it. So if we do this, then we see that we have the output as well as the return value of the promise. All right, so let's get a little more involved. Sorry, this is a little hard to read as it wraps, but we're doing the exact same thing. But now we're going to concatenate onto that string as we pass it through thens. We're not outputting it anymore. We're just sending the promise through these chains, and value is getting manipulated as we go through. So now we have a promise. Again, it's in its fulfilled state, and the value is now garfield-cat is lazy. One of the cool things about promises is that you can continue to tack thens onto them at any point in their life cycle. And if what they're doing is still running, then it will do nothing, and it will just wait for that promise to enter into either a fulfilled or rejected state, and then it will fulfill its contract that's been described in the then. But if it's already fulfilled or rejected, then it's just going to go ahead and run the code. So I'm using the underscore here. If you're not familiar with that in IRB, that just gives you the last thing you returned. So this is saying, take the promise that got returned from Garfield, the cat is lazy, and tack on and end fat. And because that promise was already resolved, it immediately executed the code inside the success lambda of then, and we have Garfield the cat is lazy and fat. So what happens if something goes wrong? So in this one, we start off. We get a fulfilled promise, value of Garfield, and we pass it to a success lambda that's actually going to raise an error. Garfield sucks. And then we're going to pass it to a then that has no success lambda just because I didn't want to confuse the example. So I just gave it nothing. But it does have a lambda in there to handle the error, which is just to tack on an alert in front of the error message. So here we can see we got a promise that's rejected, and the value is now alert. Garfield sucks. So in that way, starting to see the power of being able to manage your workflow, handle exceptions, where and when you want to. All right. So the value, as I said in that beginning quote by Dominic Denicola, is to provide correspondence between synchronous and asynchronous actions. So we're going to start getting into the asynchronous stuff, but just a quick review of this start method. So this is a convenience method, only returns of fulfilled promise with a value of Garfield. This is what it is doing. This is the actual implementation of that start method. And we will look at the source code at the end. We're going to go through the whole source code. It's not very long. So we're instantiating a new promise. The promise takes a block, and it exposes a fulfill and a reject method to the code inside that block, which is a really cool feature of yield that I had actually never used until I did this. And we'll go into that in a little bit more detail. But for now, the important part is to know that whatever block of code you pass into this new promise has everything it needs to fulfill or reject this instance of promise that it's creating. So we get here the state of fulfilled and the value of Garfield by calling call on the fulfill lambda that we have made available to this block of code. OK. So let's take a look at that in a little more detail. So that was a tried example because we're just trying to get a fulfilled promise, and there's no conditions in it. This would be an example of an instantiated promise in a more realistic world where you might want to make some decisions in the side whether or not to fulfill or reject this promise. So we try and get our cat pics. If we have any, we'll call call on fulfill, and we'll move on down our success path. If something happened, we can choose to reject this and throw the promise into a rejected state, and it'll then start walking down the rejected side of the bench. The actual initialized code, and this is where the yield stuff that I had never used before got pretty cool. So we set the state, we set the value, and then we say yield. And yield, if you pass a method, a block of code, and you can pass a block of code to any method in Ruby. But if you don't have a yield in there, it won't do anything. If you do have a yield, it will execute that code. If you put a yield there with some arguments, it will make those arguments available to the block of code that you're passing in. So in this way, we are providing lambdas with the implementation of fulfill and reject to the block of code that we instantiate the promise with. And this method method, if you haven't seen there, is a way to turn a method into a lambda. So those methods are defined in the promise class. And by calling method fulfill, we now get a passable version of that method. And then, as you saw before, when we raised an error in the Garfield-Sucks code, the promise will catch those errors and instead just choose to reject the promise as opposed to actually raising it and wait for you to decide that you are ready to handle that error. All right. More code. So you may have noticed a little false in the constructor right there. So that false says, just do this synchronously. So the default value is to execute the code that you pass in here asynchronously. And we're doing that with thread.new. We'll go over that code, too, in a second. But when we run this code asynchronously, you'll see that we got a promise in the state of pending with the value of Garfield. So this is pretty important because the promise is supposed to return immediately so that we can continue working with a handle onto this workflow. And we can wait for it to resolve. We can check in with it when we want. We can do a whole lot of things with it. So even though fulfill.call happened almost instantaneously, we still got our promise back in our pending state. And we'll see if we look at it now. It's actually fulfilled. And it fulfilled right after it returned to us. But it's still important to note that we got our promise back first. Then it ran the code. And now we still have a handle on this promise that if that were a longer running process, we could be watching for changing state. So let's try something that's a little longer running. Woo. Hang on a second. I have a better version of that, I think. All right, well, anyway. I was supposed to output on one line. But as you'll see, sooner or later, we get to a fulfilled promise. So the promise returned immediately. Let me just see if I can't get that to work. It's kind of hard to watch. One more try. No, all right. Don't know what happened there. But anyway. So this unfortunately just keeps outputting. But you'll see after five seconds, the code finally runs inside and fulfills the promise. And we then get back a state of fulfilled promise. This time, let's do a little more here. So now we're going to sleep. And instead of outputting, we're going to, I'm sorry. So instead of manipulating the value, this is just going to output. So we get our promise back immediately. And then we see that, eventually, we're putsing the value that has been manipulated by the then method for Garfield the cat. So just see that again. We get our promise back immediately. It's pending. We wait. We finally execute. And then we see that where our promise is now fulfilled. And our value has been updated. So I'm not sure. I'm guessing this is not going to work either. But we'll see. I had this little watch many. There we go. This is better. All right. So now what we're doing is we're setting up a whole bunch of promises that are sleeping for random amounts of time. And then they were almost outputting nicely. But what we're watching here is an array that has all the promises and that it's outputting the value. So you'll see it finally returned when it got down to the bottom. Garfield slept seven. Felix slept eight. Grumpy slept zero. And this is actually looping through them and attempting to just replace this line. Sometimes works, it looks like. So you can see they're all returning at different times. And we can watch them. We can keep an eye on them. We can monitor these asynchronous actions in our synchronous code. OK, so here is another just a little more involved example of this. We're doing the same thing that we did above. But then at the bottom, we have promises.all. So this one is where promises start getting super useful. So if you have a couple of things that you need to happen concurrently and then you need to wait for all of them to finish and then you can move on, promise.all is exactly what you need. So we're setting up three promises that are sleeping for a random amount of time. When they call fulfilled, they're going to go ahead and output their own value. And then they're going to tack on some more text to their own value. And when all of the promises has fulfilled, the then method from all will finally run. And it will tell us that everyone is up. And it will output the rest of that stuff. So each one of these promises is fulfilling individually right now, take their time. And then at the end, when every one of them has fulfilled, the then off the promise.all executes and we see that everyone is up and it tells us who has woken up when. So another cool thing is that, and we sort of demonstrated this a second ago, but if I run the block by itself and let it finish, I still have a handle on all of these promises. So I've assigned them to an array here. So I can still call all on that array. And now it's not going to wait because everything has executed already. So it's just going to go ahead and do what it was supposed to do. So it doesn't matter how long you wait, if you have a handle onto these processes. And no matter how many nested background processes are launched within these promises, everything eventually is going to come back to the caller if you decide you want to wait for it. And that's where the real power and the real communication between these synchronous and asynchronous activities starts becoming clear. So let's look at the implementation of all. Excuse me. All right. So in promise.all, we have a promise.new constructor. And we've already seen we're exposing the fulfill and reject method. Skip down to line 10. And what we're doing is we're looping through each of the promises that we've been giving. And we're adding a van onto them. And we're saying that whenever you're completely finished, please call an onSuccess method that we're going to give you. Or if you fail, just reject this outer promise. So then if we back up to line four where we're defining this onSuccess method, we are creating a new array. And we're calling onSuccess with the result of each one of these promises as it comes through. We're pushing the result into an array. And then we're checking to see if we've made it through all of these promises. And if the size of the results is the size of the promises that got passed to us, then we know that all of the promises that were given to us have resolved. And we ask whichever promise was the last one to go ahead and fulfill this outer promise that we're in. So this was where my head started melting a little bit when I was first explained to this because it's just such a cool example of passing closures and scope around. So again, we're telling each promise to call an onSuccess that we've provided to it. And if it turns out it's the last promise, that onSuccess is going to fulfill this promise that we have instantiated to let us know when all the others have fulfilled. All right, so as a sort of logical extension of all, we also have an any. And we've set up any to be a race here. So each cat is gonna go to sleep for a little while and then it's gonna wake up. And whichever one wakes up first, we're gonna go ahead and pass that to then tacked onto this promises.any and it will tell us that it has woken up. All right, so we're now waiting for somebody to wake up and there we go, we have a winner. So again, we've set up three promises and then we've set up a new one that is waiting for any one of them to wake up. And we can do this a whole bunch of times at the same time if we want and all these races will happen concurrently and all of our anys will track them individually. And return whenever anything has come back. So let's take a look at any real quick. Any is very similar, but it's sort of an inverse. Again, if you go straight to line eight, you'll see that for each of the promises where if it succeeds, we're asking it to just go ahead and fulfill this promise. Because remember, we're only waiting for one successful promise to come through. So if any one of them fulfills, go ahead and fulfill this promise. Then we're also gonna handle the case where none of them fulfill. So any in this implementation is waiting for anyone to fulfill to be successful as opposed to anyone just to resolve. Resolution and fulfillment can be a little confusing, especially when you're reading the promises A plus spec. In this implementation anyway, I think of resolution as either fulfilling or rejecting and fulfilling is a successful path and rejecting is obviously a negative path. So in this case, the on error lambda that we're passing in is just trying to make sure that every promise has not rejected itself. So we're keeping count, we're decrementing the count if we get a rejection and if we're down to zero, if this is the last promise and it is also rejected, then we're gonna reject this outer containing promise. Otherwise, whichever one comes in first will fulfill our containing promise. All right, so just a little recap. The colleague of mine, Mike Davis, put it really well, I think, wouldn't it be nice if we can see these flows the way we think about them? And I think that's kind of what I'm always looking for when I'm looking to refactor code. It sucks to have pieces of code in your code base that you're just afraid to go back to and that you're kind of embarrassed about and you just always know they're there and they're eating at you. As opposed to times when you have really beautiful pieces of code that you're proud of and you wanna share and you're psyched when you see them or you touch them when you're doing other stuff. So that's kind of where we're always trying to get to with our refactors and the truth of the matter is that those beautiful pieces of code don't always live that long. Like it'll be great for the problem you were trying to solve when you first wrote it down. But then two months later or a week later or whatever, something happens and it starts getting bastardized, it starts getting sort of eaten away at the corners and things like that. So we were just pretty excited to come across this pattern because it's very elegant, it's very powerful. To go over some of the just key concepts of the promise here it should return a promise immediately. It fulfills or rejects so that state is pretty important and one of the contractual obligations of a promise is that it will never execute its code more than once. So only a promise in pending state will execute, fulfill the rejected is an immutable state and that promise is finished with its work and it should be chainable, it should respond to them. So let's take a look at the source code to this in a little more detail. All right, so how's that? Can I shrink it a little bit? No, leave it like that? All right, I'll do whatever I want. Okay, so we'll look at just some of the, sorry, it's a little hard to read. We have a couple of just convenience methods here, fulfilled and rejected, which just checks state, are fulfill and reject methods. I guess you know what, I'll start at the initializer just so we have some whole thing. All right, the whole thing showing up. Okay, so we saw this method already but there's a little more to it than we saw at first. So line 70 is the same, 71, 72, we're just initializing some variables and then instead of just directly yielding this block and catching it, we're actually deciding whether or not to async or sync it. So you saw that false that we had in the true so it's not much more complex but then at the bottom we just either execute the block that we've defined on 73 in a new thread or we just execute it immediately. I don't really know what the ramifications are of throwing thread.new around when your production code are, so I wouldn't recommend trying this right away but I would also love to chat about it and if anybody has some thoughts on that. So we have a couple of class methods that we went over the implementations of already, any and all, and start. It's just a convenience method to kick off the workflow because the whole point of this chainable thens is that you can carry on so you need a way to start. You need something to kick it off. Ending value, all right, so the then method. The then method is kind of the meat and potatoes here so we have our on success and our on error lambdas. If either of them are not defined, we just go to a no op so we just pass the value through onto the next then and then we're actually creating a new promise within each then. The thens in this implementation all happens synchronously so if you want asynchronous code to happen within a then you would pass it code that creates a new promise so the asynchronous part would be the beginning of the chain and then you would do whatever you needed to and then that would return you to your synchronous context even if that context were actually in a background thread of a parent process. So we have all we need to fulfill these steps in our asynchronous world, all the context we need is we need to fulfill and reject method for just the plain promise instance that has started this off and then we need to know what the success and the error path are. The then just stores that step if we're not ready for it yet if the promise, the beginning is still running or it resolves the step and resolve is where the magic all happens and here we go. So when we go to resolve a step we look at the state of the promise and we decide whether it's fulfilled or not and then we execute the appropriate lambda with the current value of value. So value keeps getting updated as we pass our promises through then so it will always be a representation of the value as it has been manipulated up until this point in the chain. When we get that result back that result is either gonna be a promise or just another value and I thought it was kind of interesting Matt's talk talking about softly type languages and stuff this would be kind of a nice place to implement something like that because we're checking here if it's an instance of class promise but it doesn't actually have to be an instance of class promise. It just has to fulfill the promises contract. So when you start reading about the promises and you start reading about where people think it will go promise pattern will be native and ECMAScript six and there are a lot of people who talk about everything should return a promise. So HTTP clients should just return a promise to you immediately. So what would be happening here was if result is anything that fulfills the promise interface or the promise contract then we can go ahead and chain that on to it and tell it that after it's done with whatever it's doing please fulfill or reject this promise. If it's not a promise and it's just a value then we're just gonna go ahead and fulfill or reject this promise with that value. Let me see and I think that is pretty much it fulfill and reject look exactly the same except for the value of the state. We just set the state we set the value and then we call resolve steps which just loops through all of the steps that we've pushed in depending steps and calls resolve on them. And that's pretty much the entire a lot that's actually really is the entire code base there. So that's it. Thank you guys for listening. As I said, there's a, we just kind of just started with this stuff and if anybody is interested in doing or has some thoughts about implementations or usages I would love to chat about it anytime but in particular tomorrow night constant contact is hosting a drink up, meet up, open coding night, doing some work on whatever you wanna do. We're doing some work around our API. We have what other events are going on there? Free beer and wine and that's the important part. So thanks to a couple people. Brian Mitchell is a friend of mine who explained all this stuff to me. Mike Davis for helping me write the talk. Jed is another coworker of mine who wrote a great jam from making these presentations just out of a markdown file. And that's it. Any questions, comments? Yeah. So this has a test suite and actually one of the things that I found a little tricky was how you test asynchronous stuff going on. So I do have some tests in so if you hit the code base you'll see there's an RSpec file for it. And I ended up just evaluating it waiting for one and then doing my assertions which is not the greatest thing to do I don't think but it did get me through. But yeah, it's tricky. I'm not quite sure how you go about testing asynchronous stuff much less nested asynchronous stuff. But there is a test suite there for your pool around that so feel free to use it at the start point. Anybody else? Question? Yeah. So the question was between pending resolved and failed. Do I think there's a need for another state for while the promise is executing? I think pending covers it because it's never gonna wait, let's see, I never thought about this but it's not gonna wait to execute the code, right? So if it's pending, it's executing. And if there is a promise in one of the thens, okay, maybe, I'm not quite sure. I can't imagine a use case where you would wanna be checking in with a promise that's part of a flow that it hasn't even gotten to yet because you have a handle on the, yeah, maybe. I'm not sure, we'd have to chat about it, but possibly. Yeah. So the question was are any and all thread safe? No, they are not. And it's not too tough to make them thread safe though. If you go into the, and I don't mean necessarily absolutely definitely thread safe but one of the things that we had in the initial implementation was just to wrap these state transitions into mutex. So then only one of these, only one process can fulfill or reject the promise. And I think we actually did see those race conditions surface when we were first coding this up but then the examples for the talk were clean enough that I left it out. That being said though, even if you did that, I'm not saying that this is thread safe. So, you know, don't go throwing this into your app unless you really know what you're doing. Yeah. So what, so the question was what was I talking about at the beginning where I said we's had a partial implementation and I had missed the point and that is named sequential workflow and it's a gem that we've used in a couple of apps and it's just the then syntax. And it was, or it is helpful. We still, I feel like we're just at the point now where we're kind of like pushing the edges of its helpfulness and it remains to be seen whether it's really gonna mature into something robust or whether it may be replaced by actual promises or whether it'll fall over and just kind of be more of a pain in the ass than it is helpful. But at first, it's really, it's a really nice way to look at these flows. It's much more aesthetically pleasing than just the variable assignment being passed to the next function. But as it adds its own complexity, I'm still on the fence as to whether or not it's really a win or not. I can show you that, the implementation that we're actually using if you would like to see that. But there's no threading in it, there's no backgrounding. It's just, it's basically everything except the backgrounding. So you just have chainable lambdas and you're just passing them in. Anybody else, questions? Yeah. I did not. So celluloid, if you're not familiar with it, question was did I explore celluloid is concurrency library that's implementing the actor pattern and there are a whole bunch of other implementations of the promise A plus spec. If you go to the code base that I have linked to, you will see one reference to a similar project. And on his page is four or five similar projects. And one of them is celluloid based. There's at least one that uses event machine. And then there are a couple others. I didn't really get all that into them. To be honest, when I first started looking at the various promise code bases, I was pretty confused by it. So it was a lot easier for me to start by just kind of rolling it myself so that I could understand it all the way through. And if there's any new Rubyists in here who don't quite get everything that happened up here, I've been writing Ruby for close to 10 years and it took me like four or five days to understand this stuff even after I had written it. So it's not obvious. Yes. It makes sense. So the question was in languages that are designed with concurrency in mind and have immutability built in, this would be easy and safe to do. But in a language like Ruby, maybe not you would have to be worried about your threads acting on the same object and doing things at race conditions and stuff. And I would say, yes, you should be worried. Like I don't know how this would actually work in a production environment. So it started as an exercise. I do think that some of the other libraries that are linked to are much more mature and have definitely been written by people who have a deeper understanding of concurrency issues in Ruby. So there are certainly resources to figure out where the problems are. Yeah, correct. That's a whole little secret. Yes. So the question was, are we using any of this in production? And the answer is no, we're not using this in production. Not because we won't, but just because finished writing it a few days ago. I'm sorry. In our production code. So the app that we're currently working on deploys in TorqueBox. So we get backgrounding baked in in the JVM and stuff like that. Any other questions? All right. Thank you guys very much. Happy RubyCon.