 This is Ruby Monsters Go Bump in the Night. I'm Joan. I'm also lead bot on the internet. My Twitter handle is a constant troll to everyone I know. I am the hardest one ever. If you read lead speak, as in you're probably like 30 years of age or older, then you can just look for lead bot, duh. Otherwise, you should Wikipedia lead speak or something. OK, I'm from Portland. It's a beautiful city. You should come visit us in beautiful Portland in the spring, preferably, or the summer, both nice times to visit Portland. They call me Webmaster. I am the webmaster for the Cedar Hills Kindergarten and Preschool Co-operative. It's pretty sweet. You can email me at webmaster at cedarhills.com. You've got to work if you have any webmaster needs. Mostly, I think if email is not working as you expected, I think that's my job. So I also build things at New Relic sometimes. We work on the Ruby agent at New Relic. And if you have questions about that, you can find me in that building there. We're up near the top. We have the 28th and 29th floor. If you were at Rails a couple years ago, you probably came by the office to hang out in the other party. It was fun. But if you're ever in Portland, seriously, look me up. Find me on the internet. And tweet at me. And we'll go to the carts and get some lunch. And you can come hang out in the office and look at our view. It's kind of awesome. I would love to see all of you there. So also, if you are at all interested in becoming one of us, I am quite pleased to share the cool aid with you. You can go to Bit.ly near life to see a list of current openings. Or you can just come and talk to me about working in New York and why it's awesome. So we're actually here today to talk about groovy monsters. Pokemon got a woo. Does anyone here play Pokemon? Or have you played Pokemon? Yes, excellent. Much to my seven-year-old daughter Kaia's disappointment, I am not a Pokemon player. But she's giving me lessons. She has Pokemon lessons for myself and my wife. And she reminds me, every time we talk about strengths and weaknesses, she's like, yeah, like how some people are bad at Pokemon. Like you. So Kaia, I'm trying. Okay, I've got the Pokemon in my slides here. So I want to talk about real quick that I'm not bringing this up as a long list of Ruby WTFs. My point is that there are some things in Ruby that are kind of counter-intuitive, but not if you understand why they are the way they are. So a lot of times when people come to Ruby as a new language, some things operate a little differently than maybe they expect. And those end up being Ruby monsters. But hopefully, much like the T-shirt that looks like an angry ninja bear in the middle of the night by your closet. It turns out to just be a T-shirt when you wake up. These Ruby monsters are not in fact monsters at all. I've also included some oddities just for your enjoyment. Like this little guy, this is my favorite oddity because I really like Ruby and there are some odd, idiosyncratic bits of Ruby that I find really fun, especially parts that we don't use very often because Ruby's adorable. Anybody know what this is? This little critter. This is fun and out here, right? This movie made me want to be an astronaut because being an astronaut is exactly like that movie. I don't know if anybody, this is a puppermen, he's fantastic. Okay, so ready, set, talk about Ruby monsters. Our first Ruby monster is the begin and block, a neutral hypoallergenic code wrapper made of opinions. So I kind of give away the end game there a little bit. But I want to talk about what I mean. The begin and end is something you can wrap around things in Ruby and it doesn't change anything really. So if we have, for example, some splines we need to reticulate, as you do, right? All splines need reticulating. When we wrap that reticulation of splines in a begin and block, it doesn't change anything. The return value doesn't change. The code still executes exactly the same way. And I can prove it actually by showing you the Ruby byte codes. If you haven't ever done this, this is super fun and you should. There's this Ruby VM instruction sequence thing and you can compile the codes as I have here. I just have a simple line of code, puts Ruby is weird and then you can disassemble them and you can see the actual Ruby instruction sequence like this, sort of looks like. Which is not as scary as it looks. I'm gonna go through it real quick. That top thing up there, the trace, the trace is for set trace function shows us where we are in the code later. We don't even kind of ignore the trace bits now. And it's a stack-based VM. So then that next line, the two there is to put self the receiver. We put ourselves on there because we're calling puts on self, right? We're calling it at the top level. We put self onto the stack. We put the argument Ruby is weird over the stack and then we put the method call, right? We call the method. We tell it it's got one argument. The R count is one. So it pulls the Ruby is weird off and then it sends it to self, right? No, we're done. That's the end of our little bit. So that code is the same as if we wrap it with begin and. We get the same thing, kind of. Discerning lies, well, notice there's an extra trace in there for set trace function. But again, we can mostly ignore that. It's to trace begin and block. In any case, for all intents and purposes, these are the same instructions. Exactly, they're generated. Only at the end, right? So it makes sense that if we are going to articulate the splines or articulate them in the begin and block, that is the same thing. So let's do this. If we have do it false, and we've decided not to articulate the splines in this case because do it is false if there will keep us from articulating the splines, right? Everyone believes me. How about this? Do you still believe me? That if do it is false, we put it in a begin and block, right? Nothing in that begin and block will happen, right? Because we've got an if here and do it is false. We're not going to do it. Correct? We do not articulate the splines, right? How about this? Well, false, right? Obviously, we're not going to articulate the splines. That makes sense, right? And so then if we put a begin and block on it, somebody snorted up here because they know the joke already, of course it's going to articulate the splines. What? What actually happened here is that this is a construct called a do well loop in Ruby. Begin and wow does not behave as you might expect. This is a while loop that executes once. So like a do well loop in C, this loop will always execute one time and then it will continue executing. So it maybe is better considered a continue if loop. So it will always articulate the splines one time and then it will continue doing it only if do it is true. Matt himself said that this was a thing that he regretted having in Ruby because it's kind of counterintuitive to add a begin and end around this while and suddenly the behavior changes significantly. So if you want a do well loop, he suggests that you use a loop with a break if at the bottom instead of this. Break. All right, other things we can do with a begin, we can rescue standard errors, right? I think you're all pretty familiar with this probably. We can else, I don't know if everyone uses that. You can else, so if you don't rescue an error then you do this thing. Also only if there are not errors raised. So we're going to run one or either of those and then we have the insure block we can put at the end, right? And that runs no matter what the last bit. So again, rescue runs if we have a standard error, else runs if we don't and insure runs every time, right? You always insure and Ruby insure when you absolutely probably maybe need it done approximately right. I may be being a little too hard on insuring this case but I'm going to explain my case. So if we are going to do something important like eject the core, you know, because we weaponized it and it's overheating and we need to kill the clay guns and not us. So we're going to send a red alert first to let everyone know we're ejecting the core and then we want to probably be pretty sure that that exploding ball of plasma goes out into space instead of saying where it is. So we insure that the core is ejected, right? So in this case here, if the alert happens to be broken, right, the intern was working on the alerting code and now it raises a standard error and the core eject is still going to get called, right? We can all agree. The insure block happens even in the case where we raise a standard error from that alert, right? How about this one? If we raise in the insure block, it doesn't, right? And that's probably pretty normal behavior. We would expect that. So people when they're using insure blocks, they tend not to put things that are dangerous about the really important thing that must happen. So obviously in your code, you're not ejecting cores, you're restoring some piece of state that's super important or unlocking some resource that you've locked, right? But you want to do that before anything risky, anything that might raise an error. Because if you raise an error in the insure block, you're out of there. You're not going to complete your insure block, right? So insure blocks aren't really insuring in that case, which is fine. You can see that in the code, you would expect that. But what if we're operating in a thread context? If we are ejecting the core inside of the thread, there's another thing we can do, which is to raise on that thread, right? You can use thread raise to raise an error in the context of the thread in the code. And so anywhere in our eject the core method, we can now raise. We could be raising in the insure block or before we've even taken the state that we think that we're resending, right? Lots of things could go wrong when you use thread raise, which is why people say don't use thread raise. You're probably saying I don't use thread raise, right? I don't use thread raise. You're awesome, man, you're right. But I was looking through your code the other day and I found this. Actually, you do use thread raise. Now it looks like here, to me, that you're raising on the current thread in the rescue block and in the else block. And so you're pretty much guaranteed to raise on the current thread. And you're thinking, how'd you get my code? Well, this is actually timeout in Ruby. So if you're using Ruby timeouts anywhere or maybe you're using anything that uses Ruby timeouts, I don't know if you've heard of this net HTTP thing, it's pretty popular. Timeouts will just raise on the thread, which is the desired behavior, right? If you tell something to timeout after three seconds, you don't mean like three seconds plus however long it takes the insure block to run. Like you mean done now, right? So it makes sense, but it's important to remember that when you're running an insure block, you're not necessarily insuring that whatever you think is gonna happen is gonna happen. Especially if your code is running in other people's stuff, like maybe you write the gem called New Relic RPM that everyone puts in their crazy Ruby code, however they want it, be aware. Okay, so next, and on the lovely little critter, this is the flip-flop, when only the occasional truth will do. Sometimes you maybe want to do a thing. So this is a behavior of ranges in Ruby. If you have an expression in a range or two expressions, moving expressions in a range, you have two versions of this show, two dots and three dots. And I always have to look this up every time, but two dots is inclusive and three dots is exclusive, right, I think. So the first range will be evaluated in a loop to process an if block to set a bit of state. And I'll explain this, I have a diagram here from the pickaxe. This is the state diagram here. So a flip-flop starts as unset. That whole range expression is false, it's unset, until the first expression evaluates to true for the first time, then it becomes set. So then we've set the state. And then when the second expression evaluates to true, then we unset the state and we go back. So if we had this in an if block, for example, then we would, when n first became able to four, we would set the state. And then when n later becomes equal to seven, we would unset the state. So the outcome of this with our flip-flop plot line is that we are going to get that four times here. After the four, the five, the six, and the seven, and then n is equal to seven and we stop. We flip back to the unset state, right. So flip-flop operators are actually pretty straightforward when you think of them that way. The difference between the two dot and the three dot version is that the two dot version evaluates both of the Boolean expressions for each loop. So if I'm looping through and n becomes equal to four, right, like this, the two dot version is going to check that the first expression is true and it's going to set the state. And then it's going to check that the second expression is true and it's going to unset the state. And like the do-well loop, it always runs the code in between one time. So we're going to get put flip-flop, pow, one time, like this. But it's going to end in the unset state and we're going to be done then, right. Because we add n equals four and n equals four on the two sides of the range. So if we use the three dot version of that, this is maybe the part that's a little bit unexpected. We're going to go through it. We're only going to evaluate one expression per state. So if we're in the unset state, we evaluate the first expression to transition to the sex state. And we don't evaluate the second expression right then. We evaluate it on the next iteration of the loop when n is five. So n is no longer four, right. And it never becomes four. So we just continue outputting every time in that flip-flop. Probably you don't use flip-flops because they're super weird but they're fun, right. You need more flip-flops in your code, you should do that. So to recap, expression one, expression two, in the inclusive version evaluates both expressions on each pass through the loop. And the three dot version evaluates just the first expression. So that's enough about flip-flops. Let's talk about scope. Like voting districts for your code with more gerrymandering. So scoping Ruby is not actually that difficult to understand, but it can be when you come from another language for a lot of reasons. But let's just start here. Hope, right? If you're scoping a higher being, you type hope. We all know hope doesn't exist, right? You'd be undefined global variable. There is no hope. There's never been any hope. But here is something interesting about Ruby. What about this? If I have hope inside of this if false block, surely this will not run and I don't have a hope, right? So when I check if hope is defined, then I wouldn't get global variable because hope is now defined. If I never ran that code and I said, hey, Ruby, this code that's coming, don't run this code, Ruby does anyway. So if you're creating local variables even inside of blocks that are not executed, those local variables are created by Ruby and then sent to new. It's a very strange thing once you see it for the first time and it can bite you in unexpected ways. So just something to keep out for. There is no hope, but sometimes hope is new. Let's launch the missiles. So I was working on this DOD project the other day where I was launching the missiles and we had this secret launch code, right? And I chose N because it's an embedded system. We need short variable names, right? It's very important to us. So N for my short variable name is the secret launch code, leaf bolt. And we're going to launch with the secret launch code, leaf bolt. And then the PM came to me and he said, Joan, we need a countdown. People love countdowns with missiles. So hook us up. And I was like, okay, I can totally do countdowns, right? We got this 10 times do N, right? And then we're gonna put launching in 10 minus N and sleeve one. And maybe somebody was like, whoa, N, you can't use N, you just use N. Actually, it's fine, right? Ruby shadows these variables. Inside of the loop here, inside of the times block from do to N, we made a block. That N is a different N than the outside N, right? Not surprising to anyone. So the inside of this 10 times loop, we've got N counting up. And then we go back out and we send the secret launch code and everything is glorious except that I'm, so the first time 10 times executes, it runs with N equal to zero. And that really annoys me because everyone knows that counting starts at one. It's a thing that comes up for me a lot in programming. So I decided to use this other construct that might not help the for loop because this one starts at one. And it's so much better, right? Because then I know when I come back to this code, it's gonna be easier for me if I was going on. I'm not gonna have that. Like how do you count a zero moment, right? So I use this for N in one to 10, but it's launching in 11 minus N. This is way better, okay? And then I send the secret launch code and the secret launch code is always N. That surprised anybody? Did you know for loops have no scope at all? Ruby like for loops will just manipulate all the things around them. They look like they have a block. They don't at all. So this is probably one of the reasons why for loops are not super popular in Ruby. But there are a lot of reasons why for loops are not super popular. We have so many other better options. So the point that I was trying to get out there is that you have a whole new world every time that you iterate through this block that we're yielding to. When I showed you that the iterator earlier in times, the C code, we are yielding to the block with a bit of an argument here. So the word here, right? So every time we iterate through a whole new world, A, and whole and new and world, our new arcs pass to this thing and they give it new go every time they go through it. So closures. They're exactly like that one thing from that one language, except sometimes when they're not. Closures in Ruby are mostly surprising to people who are not Rubyists because if you were born in brand on Ruby, it makes a lot of sense. But if you expect them to behave like closures in other languages, sometimes you will be surprised at the behavior. So Wikipedia describes these as in programming languages, a closure also, a lexical closure or a function closure is a function or reference to a function together with a reference to the environment. The table storing a reference to each of the non-local variables also called free variables or up values of that function. They closure them like a plain function pointer enables a function to access those non-local variables even when it's both outside it's immediate lexical scope. So I think we're clear on that and I'm just gonna move on to the next topic. I actually, I think I have a better way to say that and that's that they're portable code pods, right? That's what closures are. We just like scoop up this bit of code and that the code travels around we can pass it around like an object and it remembers where it came from. So the scope of that code, it picks up the pieces that it needs when it goes somewhere. So non-local variables from nearby are remembered and things that are called in context are taken with, right? So it's a portable code pod. Maybe that'll make it easier to think about as we talk about closures here a little bit. But there are three different forms of closures, basically and a couple of these are the same. You'll find out in a moment there's a block, right? A proc and a lambda in Ruby. So blocks, we'll start with that. We've seen these already. This is the do-in thing that you're doing on those curly braces, right? When we say 10 times do puts Ruby monster or we do it again with the curly braces, that was a little Japanese accent in front of you guys there. That's what you could see at RubyKaiji next year if you come here and we talk in Japanese. Ruby monster. Okay, so this is how we use blocks in Ruby, right? I'm sure this is not new to anybody. This is the thing that I was talking about or re-yielding inside of this do-it-see here. This is what times does internally. We've got this block and we're yielding with the counter. So we've got the counter as we're iterating end is 10 in this case and times internally is taking the number each time starting with zero and handing it off to the block that we get. So re-yielding, every time we go through 10 times do, we're yielding to the block and we're passing an argument, right? But that argument we're not accepting in this code that we wrote. When we wrote 10 times do end Ruby monster, we explicitly said we don't take any arguments. This block does not require arguments and we're giving it an argument and yet it works. It doesn't raise an argument error. So blocks don't really care about arguments. Here is another example of that where we're using the count, right? We're doing it 10 times. One, ah, ah, ah. I mean zero. I'm sorry, zero. Ah, ah, ah. The count would never have said that. It would count from zero, come on. Okay, so you count every time you go through and we take the variable and again, if we give it too many, right? That's fine. If we're like, hey, we actually needed four arguments. I don't know if you can do that because it can't, times it's just gonna give you the one. But you're not gonna raise an error. You're gonna raise an argument error because blocks need this functionality. You need to be able to have the flexibility when you're writing the code. You just say do end. If you don't care about the count, you don't care about the count. Just leave it out, right? And that's fine. So another way that blocks are used in Ruby is as implicit blocks. You pass a block to every method or you can pass a block to any method like this. I have this boom method. It doesn't do anything with a block but I can pass a block to it that puts on it just does its business and moves on its life. It kind of recommend it to care about who you want to deal with, right? So we can use yield to call that block. This is probably not a surprise to anyone. We say boom and ah, and then we yield to the block that was called in there. That's the implicit block. Every method in Ruby takes a block, you pass the block in, and then you can call it by yielding to the block and that code will execute that. Another way you can do the same thing is you can actually name that variable. I've named it block in this case for clarity but I could call it blueberry if I wanted. And then I could blueberry dot call and it would do the same thing. Interestingly, when you take that ampersand off of the block, it becomes a proc. So if you were to ask on this block dot call line, block dot class, it would tell you it's a proc but blocks are not procs. They just have very similar behavior and a proc is just basically a reified version of a block. So you can think of them as the same thing for all intents and purposes. It's just that one is not an object yet. So boo takes in this block and then boo ah happens because we're block dot calling. So let's talk about procs and the differences there. So proc is one of the ways you can create that. You can use the proc keyword. This only works from one nine forward confusingly from one nine or from one eight and back proc actually created lambdas. One of the reasons why closures in Ruby get a bad route. So proc that creates procs in one nine and you can also do proc dot new to create a proc. So you can do a proc return call and the return in this context is going to scoop up the context of routing. So when you create a proc and you type return, it's the same as if you would just type return. If I go into IRB and I type this, that return is going to be, as if I just type return in IRB, I'm gonna get a local jump error, unexpected return, right? So when I get this local jump error, it's saying I don't have anywhere to go. You're at the top level. There's nothing to return from. I'm not in the method you dirt, right? So that's the same problem that I would have with that proc. If I had that return, it's bound to that context where it was created. It scoops up that bit and takes it with it. So later when I call the proc from anywhere, it's going to try and return from that top level context and I'm gonna get a local jump error. So I'm gonna illustrate that a little differently here. These are two different implementations of the same thing. In my Kansas method here, I'm returning. I get a return immediately from Kansas. And when I have a proc new in that I return from that proc, I'm returning from Kansas. That is Kansas's return. I'm not returning from the proc. I'm returning from the method, right? So if I do something like this, where I go into Kansas and I create a proc and it's got a return in it, Kansas is returning. And I pass that proc containing Kansas is returning over to Oz, right? And then I call it from Oz. I'm going to immediately return from Oz. If there were more lines in Oz, I would not process those lines of code. And I'm going to go back to Kansas and I'm gonna immediately return from Kansas because that's what that return does. It was created in that context for that proc, right? So I return out of Oz and out of Kansas immediately. Different way to look at this. If I were to do it this way, where I call Oz and Oz creates the proc internally, the return in this case is bound to Oz, right? I'm returning from Oz. So when I call the second line in Oz, that return, I don't process the rest of Oz. I can get rid of that code. I just go right back to Kansas. But Kansas, of course, continues executing. So it's going to puts the welcome home, okay? So return in the context of a proc bounds to the local context, the outer root, right? So lambdas work differently. Two ways to create a lambda in Ruby. You can use the lambda keyword or you can use the new stabby lambda, which is really kind of an aggressive and violent aim for that. I think we should go with baby rocket, like baby hash rocket. So you can use the baby rocket lambda or you can type lambda keyword. And if you in Kansas create a lambda, this return is going to return from inside the lambda. If you call this lambda, it's going to return that. It's not going to return out of Kansas. If there were more lines in Kansas, they would continue to execute normally. It's more like you're doing this. You're creating an anonymous function and you're returning from that anonymous function and returns immediately doesn't do anything else. But it's not going to return from Kansas, right? So if you have one version of Kansas that has a lambda that puts what run, if you have a version that has a prog, the puts will not run. We'll return out of the entire anonymous. So the other difference between Ruby, prox and lambdas is the argument lists. On the top, we've got a baby rocket lambda created with A and B, two arguments. You must give it two arguments. If a lambda is told that it gets zero arguments or one arguments or two arguments, you give it what you told that you would give it or you will get an argument error. And prox don't care, right? Necessarily because we were looking at blocks earlier and it would be kind of annoying if you had to know exactly how many arguments times it was going to pass to every block that you ever created. So blocks are flexible as are prox with erity. You don't have to pass them the correct number of arguments. Too few or too many is fine. So just like we did here with the 10 times do where we had an argument and we ignored it, it's okay or we can ask for too many and that's also okay, right? So the reason that we're doing that is as we're iterating through this whole new world. Let's say like inside of here we were just putting hello. I would be able to use a whole new world that puts hello four times and that should be fine, right? I don't have to actually use that word variable inside of here. It doesn't make sense that I should be required to use that argument. So that's why blocks function that way they do with regard to arguments. The return bit, why they return from that scope is that I would expect this return here to return from whole new world. And I think you probably expect the same thing. But imagine if this function like a lambda, if that do end was returning from an iteration, then the each method would go through the first time and it would take a and it would hand that string a into this block that we've created this lambda as word and it would return and then it would go get world and then it would hand it to lambda. So it would operate like next does in that context. So return would just be skipping into the next iteration which is obviously not the desired behavior, right? That's why blocks and prox return the way that they do and why lambdas don't. So to go back overall that blocks and prox have arbitrary arguments, you can make it up as you go but they return home. The context of that return is the context that you'll return to and lambdas have strict arguments and they return locally. If I return how it's inside of a lambda, it just returns from itself back to whatever called it. So that hopefully helps you understand this bit of code here which is a whole chunk of code I don't want you to read all right now because I'm gonna break it down. So this first one we're gonna find a method dynamically called works fine. Takes a block, calls the block. It works fine because it works fine. I've called it that. So this is not gonna be a problem. We define this method works fine it also means. We have another one called also works fine. Guess what this works fine? That one's fine too. We can define the method with death and that will function just as the other one, right? We've got this block argument and we call it. Okay good. This one blows up. If we dynamically define a method called explodes and we yield, it's gonna blow up but this one's not gonna blow up. It seems like it should. This is the same thing that we just did, right? They're both yielding. Except that in the case of explodes, that yield binds to the class dynamical. And if you define a class dynamical and just type yield in it and try to run that code you get a local job error because you yield or you get a no block given. You get an error for no block given because you don't have a block there. So the yield is in the context of that entire class and then you are defining a method explodes and it takes that context with it. If you were to use a lambda to define that method it would be different. But in this case we're not. This is adding the yield proc block page if you're binding to this context. So I also wanna cover real quick ways to call a proc. I've been using call throughout all of this. Proxen lambdas can call it the same way. There are four ways to do it in Ruby. You can call, you can use square brackets. You can use dot parentheses and you can also use dot square brackets. I've never seen that one. I discovered that accidentally the other day and I can't figure out how or if it can take arguments. I've had some people try to explain it to me but if you have a way to try and explain to me why this exists it will call a proc. It just won't take an argument list. You'll get a syntax error. So teach me how to use that if you know. I have one final bit of Ruby awesome for you. I wanna tell you about go to in Ruby. Maybe you didn't know that this was actually a thing in Ruby, you can use go to. So the never ending story here if we have a start label and then a go to start and our tax would never have died in the stupid swamps who said this and my child would not have been ruined. So we just leave forever on the never ending story and we keep posting it and go to is definitely a thing that we need, right? So I'm gonna tell you how to turn this on in Ruby because it's super useful. You can go into your VM option center file in your Ruby source code and find the support joke definition here and you change that zero to a one and recompile Ruby and you get go to. Sweet, please don't use that. There are a couple other jokes, they're super fun. One is the answer to the universe and everything which I believe to you who made an exercise for the reader to figure out what that answer is. So then you two in your code can use things like go to start. Welcome to Ruby Monsters, go bump in the night. My name is Jonan, I also lead Bob on the internet. I'm from Portland, it's a beautiful city you should visit in the spring.