 I'm Gregory Brown and that's a picture of me from Christmas and that's pretty much my introduction. The picture is worth probably fifty sixty thousand words All right, so this is a picture of a guy on a horse attacking a windmill and Why is he doing that anyone? What's that? Yeah, a dragon or some giant or some terrifying beast and And the reason why I decided to pick this picture to start off this talk is because that's pretty much how I feel all The time as a programmer So today what I want to show you is some really bad code But it's really bad code that at the time I thought was really great And the reason why I thought it was great is because it was so hard for me to write it It was complicated It was confusing and it was difficult and then when I was done with it I was proud of it it took me five years to get to the point where I realized what total crap it was and Now I'd like to share it with you So I have to put this disclaimer up because I'm showing you code from way before anyone cared about Ruby reports this reporting library that I started just because it was a fun side project and in addition to the work that I was doing by It's 1.0 release we had some smart people working on this and I had learned some stuff and Rupor can be useful for reporting stuff So if if you're doing reporting work, you could check it out in 2010 It may seem just a little bit outdated. We weren't really up to date with the latest Ruby idioms and things like that simply because we stopped developing it at that point. It was stable. It's run out its course, but Just keep this in mind anything that you see in this talk. That's terrible. It's my fault so really what I'd like to talk to you about is sort of the holy grail of What I was trying to do with Rupor I came from a pearl background and I was doing some reporting stuff in pearl and was forced into doing some.net stuff at one point and things like that and But when I was doing things like reports, I tended to just write off these one-off hacks and As those hacks grew when we started off with something simple like oh, we just need a CSV file Oh now we need an Excel file now. We need a PDF now We need HTML etc etc etc What I wanted was a way to take all of that those little hacks and bring them together into a system That would work now. I wasn't looking for something to do the job for me. I was just doing looking for something that would help me Make sure that I don't hurt myself and In order to do that. I wanted to build something really simple and low-level. That was my goal Unfortunately to make that goal make any sense to you I'm gonna have to show you some good code first because if we go and look at the bad code I Will get so confused that I won't be able to explain it to you so I'm gonna just give you a very rough taste of how I might approach this problem in 2010 given what I know now So here's the idea and the reason why I'm using this example is because it maps to an example that I wrote in 2006 for report, but It's a little bit convoluted, but at the same time it's It's representative of the sort of things you might want to do you've got some raw data in this case It's a bunch of points and it's an nested array structure and they form lines And you want to output them in a couple of formats here We're gonna look at SVG and text output the idea is that we want to just be able to do nothing more than Change a word to be able to get the different output now Of course just by making the interface is the same you can do that But we want to encourage it farther down the stack Finding a nice place for the common code to lie and finding a nice way to hook in new formats While taking advantage of the existing API that's there now. I want to Point out that I'm showing you the most basic part about this idea it goes on to other things But I think we should be able to figure it out So we had the SVG and the text and the only thing we changed was the symbol that we were using to tell it what to render so Starting in sort of a modern outlook to this I was looking at it and thinking okay Well, I could have a line plotter class and it could be a subclass of a formatter that provides some things for me and a text Format class and it's a subclass of a format that provides some things for me and then each of these are going to have some render methods and All they do is do something with the parameters that are being passed in and return a string Pretty straightforward stuff, but the line plotter is going to need to know what formats it supports So we can just store them in a hash and that's sort of the rough idea of where I would start with this Very low ceremony very simple So this is the implementation of that and you see that formats is just a class method that returns a hash and Render basically will look up a format in the format class in that hash instantiate it assign the parameters so that they're there and then call render now Format is basically just a container sort of abstract class for now It's got params which are fed to it by the formatter And it's got this abstract method that you need to override called render and that can do whatever you want So long as it returns a string so as of right now yet, there's nothing special about this this is just sort of ordinary Ruby development and With just that code you get something like this Now I've ignored what you would do inside that render Because it doesn't really matter for the purposes of what we're talking about just keep in mind Whatever we've passed in this hash is available in that that format Object so you can do whatever you want with it so long as it returns a string Okay, so what I'd like to do is lift that up a little bit because it's tedious to keep writing those definitions over and over again So I'd like to be able to write something like this which is absolutely You know in the style of Sinatra or something like that totally completely stolen straight from there But the idea is okay. Well, we'd like to be able to register and define these steps all at the same time and To do that is way easier than you might think so You've got the format method which takes the format that you want to work with and a block and then it uses that block to Create an anonymous subclass of format and then just stick it in the hash so that it knows how to look it up later And that actually all that does is the end result is the same as our sort of raw code So now we want to talk about just going one step higher And that's if there's some common code between two different formats since both of those formats have access to the same Source data if there's something you're going to be doing for both of them It would be nice to be able to share some common code between them So of course Ruby gives us something to do that in modules So the most simple thing that we could possibly do is just create a module and say okay Well, we had these raw arrays. It would really be nice to work with something a little bit more structured so we made these line structs and Then inside of the text formatter We include those helpers and now we can call the lines method directly and do whatever we want with it And this shows you a little example of like how you would generate that stuff But again What we'd like to do is lift this up because if we're doing it again and again You don't really want to type that include helpers thing into every single format that you're working with So stealing again straight from Sinatra. We make something that allows you to Have helpers and you define the you've defined your helper module through the simple block form And now every format that you create in the context of this formatter will have access to those methods Implementation here again is simple all you do is you create a class method method called helpers that takes a block It creates an anonymous module in essence all it's doing is Doing exactly what you did manually dynamically Now render only needs one line change. We don't need to include it into the whole class necessarily We could just extend into an instance that works fine these are now singleton methods on that the format instances and With those changes you've got something in which you can pick and choose you can use the explicit interface or the implicit sort of pretty sugar both work and And in the end you end up with something like this maybe hard to see but it's just all the code that you've been looking at so far you've got your common code at the top and then two separate formats and That gives you What we wanted in the first place a nice way to abstract common code out and then have a standard interface between two different Formats or as many as you want now I've been experimenting with these ideas And there's a lot of things that you want to do in addition to that if you were going to do something interesting parameter validation things like More complex Pre-processing allowing you to use different base classes say say for example I make a PDF formatter and I include it as an extension in prawn And then you could just pull it in and then drop your formatter from that I've been playing around with things like that, but I didn't want to get into that I wanted to just go to the basics Rupert will make this significantly confusing enough with just these features I promise you okay, so the idea here is that this code is nice, but it's not that exciting kind of like a windmill you know Your work is the thing that really needs to be exciting if you happen to be writing exciting code That's great, but if you're trying to write exciting code Then unless you're doing it just for the fun of it, which I think is awesome You can learn so much how many people like things like golfing, you know those sort of yeah, not not the sport, but the shortening the code Or just sort of convoluted code contests things like that you can learn so much from that stuff But if you're actually focusing on I am a producer of things that people use then your work is the thing that matters but we are hackers and that means that we love a good challenge and When I was done with this, I didn't feel like donkey hote. I felt like David versus Goliath You know and not with this first code, but with this Rupert code that we're gonna look at because that's the day that I thought I killed the giant so all right, I Am going to Go directly into the Rupert code. This was inspired from Before I do that. Are there any questions about the stuff that I showed so far? Okay, cool. So I have to warn you I'm going to get confused and so are you this is going to be terrible but it's going to be a good exercise in code reading and Just to give you the analogies that you need We're working with the same exact example but a format in the context of Rupert in 2006 is a format plugin a Formatter is a format engine and I'm not a text mate user. So I'm only using this because it's easy to click at And I'm afraid to use my own workflow. So if I do a really bad job using this editor, just forgive me but anyway Let's see so I've got a bunch of questions and when I said this would be a good exercise in code reading I definitely meant that if how many people go out and read code on a weekly basis Okay, other people's code Nice. All right, that's that's really good. How about like is it code that's not related to your work? Awesome. Okay, perfect. So That's a really really good thing and that's probably because we're at Mountain West, you know, and this is a hackers conference And that's awesome, but I mean the thing is that when I sit down to read code I try to come up with some sort of questions to sort of guide me and then I just follow those questions wherever they may lead me so Let's walk through this example first and just take a quick look through it at the top Can everybody is this good size? Yeah, okay. Good. All right So at the top you see something that's sort of like a twilight zone version of what we did in the first place But this is because this is the actual code example from 2006 You can see that it's pretty similar. You're telling it what Format you're using and what data you have and then it just does it for you And if we had a text plug-in or whatever an XML plug-in just straight XML, whatever it is it it would just look like that and that's the goal and And when we look at The SVG plug-in The content for this It looks pretty much exactly the way that it would look using the other approach that I had mentioned And we've got this idea of defining a renderer, you know instead of just doing def render Rupert gave you a little helper for that not a big deal and then it registers itself on the line plotting engine so the line plotter is the engine and you can see this code again looks sort of similar so The interesting thing here is not in the interface so much even though there's some little things we could talk about here It's in the implementation So we're gonna drill down into that implementation and decide whether or not this is a good idea to be working in this way all right so When I when I read code if I'm looking for a particular problem I tend to go through wherever I happen to enter it You know from that the outside API and then work my way down in to find where the problem might be But if I'm reading for explanation I tend to do go from the bottom up just because it helps me to work with the most simple objects first Try and understand what they are and then see how they interact with other things. So that's what we're gonna do So a good first question might be how is the format plug-in registered with the engine? Okay, so we see this line right register on All right, so let's go take a look for that Yes, okay, so immediately. I see that it does an unnecessary type check. Awesome. Okay, then Then it calls the brilliantly informed method format engine Engine classes so why is there a K on classes in the argument that's being passed to this method? Right classes a keyword, but why is there a K on engine classes? Because I'm an idiot And this is clear that what's up? Okay so Classic example of cargo colting I knew that I had to do Class because class wouldn't parse, but I didn't get why so I just made it consistent, you know I think Novelises are amazingly good at being consistent with their mistakes You know and at this time I would be definitely a ruby novice although Definitely a ruby novice. Okay, so all right But now let's play around with this. Okay, so class right now is some Format engine class. That's what I'm reading from this and it accepts a format plug-in by injecting itself All right. Well now let's go over to engine Except format plug-in. Okay, great. Now we have to go back to the plug-in and get its name So we've got a little circle going on here Which means that clearly one of these things or the other should have been doing the job But at the end of the day, this is doing something that looks almost the same as what the code that I showed that I said was Good does which is it's storing these format classes in a hash so that I could look it up later Now we saw before that you could have just done that on just the formatter, but hey, I didn't know it at the time So Let's see Okay, so How does alias engine work? Oh Wait, we had one more thing to look at before we did that. Okay, so Let's see follow it back up except format is class format name Now if we go in there, we see something sort of interesting, which is that it really it didn't ask you to Explicitly define your format with a symbol like we did before it infers it from the name of the class Which in theory is great because it's convention over configuration But in practice means that you're doing a completely unnecessary step Because you have to say the word SVG or text at some point anyway So that great little bit of code right here does that but that's how it matches SVG to the SVG plug-in Of course introduces all sorts of interesting edge cases and bugs and things like that And that's exactly the sort of stuff that when I started to feel like I just knew a tiny little bit about Programming that I just wanted to get out of my life, you know Convenience at the cost of clarity is only useful if you're just trying to spike on something and get it done right then and there Okay, so now we've got through that and The next question I had was how does form how does format engine alias engine work? Okay, so We've got this register on line plotting engine. It doesn't explicitly refer to any of the classes or anything like that But we see that alias engine line plotter line plotting engine is here So that links a name of a class with a symbolic name. So let's look for that Well Yes, okay Thankfully this one is straightforward, but Okay, so again cargo quilting with the classes thing, but it's just storing these things in a hash Again, we saw before that you don't need anything like this. This is sort of reworked. You know, we're doing we're doing the same thing more or less twice But whatever now we get how that works alright Now let's see Format plugin renderer Okay, great. Okay, so format plug-in renderer. All right Let's go look for that one up Okay, so Exciting again. All right, so let's let's go back to our original code And We've got renderer plot so it's renderer and then some name for the the kind of renderer that it is and Let's see. So that's here. This would be a symbol plot it It does some more awesome cargo quilting Which is that it takes this string and it converts it to a symbol we can ignore this line because it's not relevant to our example But then it does something great, which is that it uses that to define a method But that method is being defined on the class Which is a little bit confusing and we'll mention that before but here's the cargo colt that I mentioned before This Why why did I convert it to a symbol? No reason at all. Perfect. Thank you All right, but has anyone else ever seen people do that? Has anyone else themselves done that? Oh, yeah. Yeah, you don't need to do that Maybe Learn the API's instead of copying and pasting from blog posts. I took I still do it to some extent But but this is this is a classic example of that. All right, so Seven okay, that's all right. All right, so things are going on at the class level this here is just because we're at the class level we want to define a class method and This here is because we're doing something a little bit weird. So we have to do you send So be it whatever not that interesting if I've got seven minutes left. I want to bring you to the cool stuff Alright, so how does engine renderer work? Oh? We didn't actually say how it worked I just ripped on it All right, so so the plug-in renderer defines a method called something like render underscore plot and that makes some Sense because you can see that that's being called in the plug-in. So when You do this it makes a render underscore something and then you call it from this Okay, so Format engine renderer they both got the same name. You would think they do something quasi similar Am I miss yes. All right, whoever's saying up is the best. Thank you. All right so Did I lie to you again? Okay, let's see send to find method render. Okay, so it is doing the same thing good It's basically defining a method called render which then gets used but this is where things Start to get interesting Where does the magic happen? Sorry, okay, so now the the big question is What does this format build interface for line plotter thing do? Okay so format Okay, build interface for great. So again on every single line. I'm reopening Classes and doing all of this awesome stuff Apparently it's defining a method That has a lambda around it that passes options into something called simple interface which sounds simple, right? So then Name object is just it's defining a method called name object and then doing that This is just because we want to have both a way to get back an object and to do a direct render We're only interested in the direct render, but that drags us down to this crazy stuff Okay, my engine equals engine dupe. That is a Shallow copy of a class object, okay So my engine send plug-in equals options plug-in. Okay, so now we're dealing with both Engines and plug-ins that are both classes and we don't subclass them. We don't instantiate them. We don't do anything. We make shallow copies of them all over the place and Now let's see Then we can ignore this option stuff not relevant and that's pretty much how all of this crap works So what's going on? When you see active plug-in here, this is a a duped plug-in class and You're setting stuff on it and that means that because at the time plug-in didn't have any sort of safe way of doing a copy It didn't you know it didn't do this right that if you had any state that you happen to put on your subclass or something like that that it's being shared with pretty much everything else that you work with and The class itself creates a new copy that the engine itself creates a new duped copy of itself. Okay, so I Didn't use anonymous subclasses. I didn't use instantiation. I chose the best Possible way of object-oriented design Classes and Ruby are objects. You can make copies of them. That's how you make more objects. Yes All right So just to give you a sense all of this discussion has been about how to do What I can show you right here that does the same thing and I explained it in just a couple minutes in the beginning, but I did it the other way and Why did I do that? because Once I started down a path of expecting something to be hard insurmountable scary difficult Impossible my mindset was that it was going to be impossibly hard And the pain that I suffered in doing it was just the cost of being brave As it turns out It was just the cost of being ignorant So read others people's code Learn from it go back and read your own code. How many people have read code that they wrote more than two years ago in the last month or so How many people love the programmer who wrote that code? All right, so Am I pretty much out of time? Two minutes. All right, so I'm gonna close on that and if you have any questions, I'd be happy to answer them. Oh For yeah, exactly. So, you know the donkey hoday story of attacking a windmill because you think it's a dragon or a giant or something like that Whatever things that you think are giants right now in a couple years Introduce yourself to that windmill, you know, that's that's pretty much the the giants of today are the windmills of tomorrow, you know We are Profoundly more blind to our own mistakes than we are of others. We tend to do a good job Although if if you think okay, so for example this book Ruby best practices and this is my code from just a couple years ago this this this trash that Would make you want to return this book if you maybe buy it three years from now The good news is that it's open-source and that everyone can fix it now. So At least it's all of our faults if this isn't good three years from now Questions unless a block has been given. Hey, there you go There's another horrible thing that I did which is if you're going to use block given the the keyword that Ruby provides you Use it with yield. This only happens. Matt's why does this work? All right, so I don't know Just for the fun of it. I see okay, so yeah, that's that's basically it No No, no, no, no, no, no, no, no, sorry. Sorry data is a Yes, I mean instance variable of the object if you realize that all of the data was being passed through Copies of the class object, so it's all class data. Everything's a class All right, so I guess we'll wrap it there. Thank you very much