 All right, five random Ruby tips for October. So I have this thing where I really don't want to go with a theme when I do the five random Ruby tips because it's supposed to be random, right? So it feels a bit like cheating. So these tips are going to be a bit all over the place. So make sure you don't get lost. But first, as Gosian said, my name is Ted and I work for Tinkerbox, which is a local Ruby on Rails agency. So up to the first tip. So this is a bit of a warm up, which is that you can pass an offset to the with index method. So if you're not familiar with it, you can call with index on an enumerator. And it will also give you an index inside the enumerating block. And one of the reasons I wanted to include this was I actually didn't know this myself until one of our interns showed this in one code review that I was doing. So I think it's a good point to make that even as seniors, we can still learn things from our interns that are fairly basic, but that we might have overlooked. And quite frequently I have seen from my other seniors that they will just, for example, add one to the index inside of the loop, which is now no longer necessary. So it's quite common that you want to change from a zero based index to a one based index, for example. All right, moving fast. Using refinements for domain semantics. So if you remember roughly a year ago, there was a lot of talk about this new feature called refinements that lets you monkey patch things in a very limited scope. And it all seemed really cool, but I for one didn't really know how to use this. So I just idly waited and waited for someone smarter than me to just tell me how to use it. So the credit for this one goes to Justin Searles. The idea and all the examples are blatantly copied from a gist that he posted. In his example, there is some calculation done that is relating to a marketing effort. One nice feature of Enumerable is that you can change things together and get this sort of neat looking chain of things. But if we are frank, then we probably need quite a lot of time to figure out what this does, even though we might have written it ourselves just a few minutes ago. And one of the nice things that Justin came up with was that we can use refinements to add some semantics to this. So here he's creating a marketing refinement. So he's refining the array class with some new instance methods. So he refines an exclude incomes under method that basically just takes part of all this and wraps it in a more meaningful method. And he also refines to separate people by method, which does some grouping. And he refines hash with another method that says average income by smoking. And the final result, to use refinements, you just put using and it will apply in the lexical scope where you refine it. And now we can write the same method like this. And I think any new developer and even myself, like two weeks from now, can come back to this method and immediately understand what it does. And I just put the original method here as a comparison. So this is what we started with, which is a bit obscure. Like if I had to explain to someone in plain English what this does, it's a bit of a hassle. But with this, I can just say, oh, it excludes incomes under 10,000, then it separates people by smoker or not and takes the average income by smoking. All right, so the third tip is about running tests in Atom. So I recently started using Atom myself for no other reason than being a bit of a GitHub fanboy. And I'm using the Ruby test package, which seems to be fairly standard in Atom, to run my tests, which gave me all these nice things. Like I can use shortcuts to run all the tests for my projects, or I can run all the tests for the current file or run the tests at my cursor location, which turns out to be super convenient because I don't have to leave my editor to run the test case that I just wrote to see that it's failing, for example. So I can just demonstrate here. So here I have a test file for RuboCop, for example. So I'm not going to run all the tests for RuboCop because it's going to take way too long. But I might, for example, run all the tests for this file by doing command control t. So it's a bit tiny here, but you can see that 47 examples were run and they are passing. Or I could go to this particular line and run the tests for this test case only. And you can see now two examples are passing. Or if I am somewhere completely unrelated, I can just do command control e, and it will rerun the previous test. So the way I use this is I will write usually a failing test case. I'll run it using command control r. Then I will go to the actual code file and start implementing the method that the test is testing. And from there I can just use command control e to rerun the tests for this particular method, which saves me a lot of context switching with having to go to my terminal and specify the particular line of the test and things like that. All right, so the fourth tip is how to use RuboCop to help fix legacy code. So the way you use RuboCop is you run it from your command line using the RuboCop command. I think most people using it now know about the auto correct feature, which is really convenient if you have say thousands of tiny style offenses. You probably don't want to go and manually fix all those. So if you're brave enough, you can run the auto correct, and it can auto correct most of the cops, but not all of them. But what I want to highlight is the second much less known option, which is auto gen config. So even if you auto correct all the style offenses, RuboCop also has metrics cops that checks, for example, how complex methods are or how many lines are in a block and things like that. And what you do when you run auto gen config is you generate a RuboCop to do YAML file. And I can show you, so there's actually a RuboCop to do in RuboCop because there's some legacy code in there. So you can see, for example, we're overriding the ABC size max to allow it to be 18, which is the most smelly code that we have right now. And what makes this so nice is that now I don't have to go and fix everything in one go. So I can do a concentrated effort, for example. Say we want to bring the metrics ABC size down to 17. I think it was 19 originally. So you can see right now there are no offenses because it was set to 19. Now I want to make a concentrated effort to reduce the ABC size of my code base. I'll change the RuboCop to do to 18. We'll rerun RuboCop. And suddenly we have all these offenses. And these are all the places where the ABC size is higher than 18 but lower than 19. So one important thing to remember if you generate this to-do file, you also need to intend to actually do these things because while this is here, I can write new methods that are up to 19 complexity without RuboCop saying anything. So I could potentially make the situation worse if I keep this file for too long without actually fixing the problems. All right. So the last tip and the most abstract one is about the robustness principle. What the robustness principle says is that we should be conservative in what we do and liberal in what we accept from others. And the way at least I think about this in programming in particular, in general and in Ruby in particular is something like this. So what does this actually mean? So this symbol might look familiar because it sort of looks like the filter symbol that you have in Excel or Google Sheets. And that is sort of how I think about it. In that to satisfy the robustness principle, I should accept different inputs but I should be very conservative in the number of outputs that I generate. So you can think of these different shapes as ducks or other animals except ducks. And then you can see as soon as I call a method, I automatically reduce the number of things that something can be in my application. Whereas if I do the opposite, I only allow one thing to come in but I have two possible outputs. And I'm actually increasing the number of things that anything can be at one point in time. And one of the struggles of working with a dynamic language is that we need a strong understanding of what am I actually going to receive when I call this method because there are no interfaces or contracts like that. So I gave a talk at the July Meetup called An Object Existential Crisis and I talked about some rules of non-existence which were a special case of this when dealing with NIL because NIL tends to be the other thing that you return instead of a duck. So I thought I would just clarify by expressing those things in terms of this sort of funnel analogy. So the first rule I set up was that methods with basic return types must never return NIL. So if the method normally returns a string instead of NIL, maybe it's appropriate to return an empty string or not available or whatever string makes sense as the null object. Which in terms of what we just talked about means you should avoid things like this that increase the entropy of your application and especially so in public interfaces that others can use because a lot of programmers will think about the positive path which might be this one. And forget about this one because you actually have to go in and read and understand the method to see that, oh, I can actually get this other thing as well. And then our apps blow up with no method errors. And the other thing, the other rule I set up was that a method depending on another which can return NIL must itself not return NIL. And what I meant by this is that if your method needs to return NIL as the other thing, then design its collaborators so they can undo the entropy that you just introduced. So it would look something like this. Your first method, you know that it can now return a duck or NIL. So you must let its collaborators handle that, of course. But that collaborator might not itself need to return NIL because one of the downsides of NIL is they tend to cascade down until someone doesn't handle it and then the application blows up there but the issue was actually somewhere much higher up in your application. So I think this is fine internally, the private API of objects. But in the end, in the public side, only one thing comes out. And those were the five random Ruby tips. Then all the box with the Ruby aspect and the test with this aspect. So in the configuration, you actually tell it which command to run when you, yeah. So you can run this with Minitest or anything, basically. Or any environment variables. Yeah, yeah, you can pass any flags you want as well. Oh, okay. Take a look at the snarky remark as a big fanboy. We had it five years ago. It looks like a cat after the end of the meetup if you have any questions. So once again, thank you, Pat, for sharing us with us.