 This is a test drive, a browser game with JavaScript. So I'm Zach, this is Todd. Justin will be along shortly. We all work for, well, we all work for Test Double. Todd and Justin are the co-founders. Todd, would you mind just to give him a brief, let everybody know what Test Double is. Yeah, so Test Double we started two and a half years ago, largely because we wanted to prioritize developer happiness above all other things. So that's what we've been trying to do. We've been trying to find basically clients who like working with us have hard problems and value elite developers who kind of work on, with a level of craft. So that's why we got started. You know, whether you guys are potential clients or candidates or whatever, we'd love to talk to you more, even if it's not in the selling relationship as a consulting company or as a potential job. You know, we like to interact with people, help them get over the hump and get better about testing JavaScript. So feel free to reach out, what are we, you can email us at hello at testdouble.com, if you ever want to get in touch. That'd be a good thing to put on a slide I suppose. All right, so this is, so here's the general shape of the workshop. We're just going to briefly cover why you might care about JavaScript. Then we'll set up and pair up. And then we'll have a, I guess more of a code along session. So I'll type some stuff on the screen and then give everybody time to catch up and then we'll cut the pairs loose and check in every 25 minutes or so. So quick poll, how many people in here would consider themselves comfortable with JavaScript and testing it? Okay, cool. No, no, it's about the ratio I'm used to seeing. How many in here have used some kind of framework and anger in production? Yeah, right. Okay. How many of those hands are backbone? Okay, how about Angular? How about Ember? Couple, okay, okay, so just pretty even distribution. Okay, so we're going to use Angular to update the DOM today, but it's kind of more of a detail. It's not the core principle of the workshop. So first of all, why JavaScript? So everybody is here, everybody signed up. So I'm assuming you're not going to take too much convincing, but I just wanted to go over a few of the styles I see in projects that I often inherit from other people, like the different styles of JavaScript. And this is kind of the default style, I think is absolutely zero JavaScript in a Rails app, right? Nothing comes out, no, no, no, this never happens. There's always JavaScript. No JavaScript means that it's a swamp land and it's completely unmanageable because it wasn't really planned. Kind of forget that that's code two. And I've done this before too. It all just goes in the same file, slowly pile it up. And the bottom line is when we treat JavaScript like that, when we forget that it's code just like Ruby, it absolutely sucks. So our first goal here is to mitigate pain. It's to relieve the pressure from anybody that's feeling the suckage from the suckage. It's a word, very technical to relieve the suckage from the jQuery swamps. So this is a style that I'm starting to see more often that talking with some developers here at RailsConf, they kind of come to the same conclusion that Rails is really good at routing. HTTP works pretty good at determining what page you're on. And Angular right here is excellent at handling user interactions, right? So a little bit of rendering from Rails, a little bit of rendering from Angular and just picking whatever tool works at the time. And the next style is something more like Ember, something closer to a single-page app where you're doing all the routing in JavaScript, all the rendering in JavaScript, and Rails is just the API. They're both fully viable styles and they're both delightful. There's something about building out front-end code that's so close to the users that I feel like it brings an extra sense of happiness, right? Makes me super happy to watch something magically update without a page refresh. I mean, that's still so cool, right? So I want us to have fun building cool stuff. And I think we've canceled the test driving, right? Yeah, we're done with TDD, right? Okay, so... Does anybody, like, pull on, like, agree with THH's keynote earlier this week? I agree with parts of it, all right? So, like, you're not alone. Yeah, cool. I agreed with a ton of it as well. It's funny because we have kind of two dogmatic, like, sides of the spectrum. Like, one dogmatic side is you have to test drive every... Everything must be tested. Otherwise, you have to review it. The other side now is TDD is that don't test drive. I'm still of the opinion there's a pragmatic middle, and it's, like, 98% of the people with problems. You see some value in testing, right? Maybe it doesn't solve all of our problems, but maybe it actually does resolve a little bit more. So that's kind of the area that we operate. I think when you watch that code today, you'll see that not everything is test driven, all right? Some things may be browser driven, right? Sometimes I'm in deep motor driven in my development. These are all viable, right? What we want is a short feedback loop so that we can produce a design that's both functionally correct and hopefully a little bit easier to maintain for our future selves. That's my take on TDD, so... You won't get some TDD today, but you can feel free to just say hello. Absolutely. It's choose your own adventure. Yeah, and so I'd like to view JavaScript to the same lens that we view Ruby. The same good practices that you do in Ruby just makes sense to also do that in JavaScript, right? Whether or not that's TDD, whether or not it's following a style guideline. And all of the goals, right, is finding happiness, right? Instead of feeling like we're stuck with JavaScript, we have the option of embracing it and using it to delight other people and make our own lives a little happier, right? I mean, we have the option to not hate it. Feel free to continue hating it if you do, right? I don't want to take that away from anybody, right? Because there's plenty of stuff to be really angry about with JavaScript, but it's also possible to be a little bit happy with it. So what we're going to do now, I'd like everybody to find a pair if you're not sitting next to somebody that you'd like to pair with. And our TA volunteers. And once you get settled down and seated, go ahead and attempt to pull down the repo. And if there's Wi-Fi problems, we do have USB sticks up here. Okay, the project has two folders. One is lineman start and the other is static start. So if at this point you don't have lineman running, then go ahead and abandon it for right now and just work out of the static start folder. And that's just the exact same stuff rather than having something automatically rebuild and link the files. It's old school. It's just static files linked to your drive. So when you can, try to get the page open. And that'll either be localhost 8000 or opening the index.html file. All right, how do we feel? I'm going to type some code on the screen I'd like you all to follow along. Does anybody feel like that's panic inducing and you'll never catch up if I start that now? Okay, I'll go ahead and start doing it. So there's not going to be a ton of code like this, so don't worry. So if you fall too far behind, don't worry. We'll catch you up later. And if it's really boring, then just feel free to shoot ahead on your own as a pair. And one more quick aside, there's also samples of the finished app as finished as software ever gets. So feel free to peek ahead and look for syntax examples from that. All right, this file is in the readme. So every so often I get the chance to work with a designer. Obviously this is not the case. But it's really cool because I get mockups or prototype HTML pages. And from there, and even if I'm not working with a designer, I can make that myself and then mark it up with what I think could happen with the JavaScript objects. So here I've color coded the different chunks, the different concepts based on does it change after everything's gotten started or not. So the red is things that change, which is the shots that are made, and the blue are the areas that kind of stay the same. You know what, let me really quickly talk about what we're actually doing here. So we're building battleship, right? But it's a really simplified version where there's just one opponent. Well, yeah, there's one opponent who can't fire back, right? So it's essentially airstrikes, right? So think of it like watching CNN. I got unexpectedly political. So right now, looking at the page, so we have the standard grid. And if anybody isn't familiar with the old battleship game or is a little rusty on it, I have an excerpt from Wikipedia right here as far as the broad outline. The subset that we're going to be looking at is, once again, the airstrike version, where we're just going to enter in coordinates. I don't have this hooked up yet, so I won't do anything. Right, and then I'll hit fire, and then the status will update and let me know if I've hit the enemy ship or missed, the grid will light up and let me know, so I can keep track there, and it'll also let me know if I've won. So starting out, right? So we've got the individual notes in the middle, right? And that's a domain term from Wikipedia, because I find it's a good idea to research whatever domain I'm in so I can get the words, because I'm really lazy. The thorthes are hard to pronounce, apparently. And we also have status, and note and status are both calculated. They both require the changing data, so it would be really hard to do that server side, because if we did, then we'd have to reload the page with every fire, which wouldn't be a very good game. The stuff in blue, those are things that aren't going to change after the game starts. So we could render that server side to client side. It doesn't matter, right? Other considerations can take control of that. We're going to be rendering the grid client side just to keep things consistent, so that way we don't have to worry about getting a Rails app bundle installed. But in your own time, it doesn't matter. Yeah, so we have pure mutation in red, pure static in blue, and the derived in purple. And feel free to reference back to that. So from there, I'm going to take a look at the code itself. Go ahead and close that. The linemen start. So I just changed directories into linemen start, and I'm going to change my color scheme there so people can read it. And I'm going to pop open the index.html page. And if you have trouble finding index.html shop, we're general shape of the formerly neighborhood TA, and they'll help you out. And if the TAs don't know production, me. So right here, we see the mustaches that's sitting right there, like visible in the web page. So I left that there to start. So we always have something calculated showing in HTML. So if we break something, we get that instant feedback. So just like Todd said earlier, tests are really cool. I like them a lot, but it's not the only way. So I put something breakable right there, and it's already broken. So I'm going to throw this attribute into the body tag. Just ngapp. So please feel free to do the same thing. And then if you refresh the page, suddenly 1 plus 1 equals 2. All right, so now I'm going to... So we need ad hoc functionality here. We need more than just what the base angular templating provides. So I'm going to tell it to look for a module by the name of app, which is super descriptive. That was sarcasm. So I'm going to refresh the page again, and we're broken, right? And it's because I haven't actually declared the app yet. So I'm going to open another file in... called app.js. Okay, so if you're in linemen, you can see it's app.js, app.js. And it's going to be in a slightly different folder in the static version. All right, is anybody having trouble finding app.js? If you are, that's perfectly fine. Okay, yep, it's not working for me either. Okay, so yeah, it's supposed to be broken. It's a feature. It's a feature. Most sashes are in right now. Okay, so... Please. If I'm just... I often use app if it's something really small, but if I've got multiple Rails routes, each with their own Angular app, then I'll name them so I don't go insane. So here's how we declare the Angular module. So theoretically, if I refresh this, it goes back to two, right? And the way this syntax works, Angular is just a word that's available in the global namespace because we have the library. .module is a method. And then when we pass a string as the first argument and a raise the second argument, that's a declaration. Normally, if we had dependencies, then we would put them in there, right? So if I had a bunch of different Angular apps and one chunk of code that was responsible for the JSON API to talk to Rails, then I would put that JSON API chunk as a dependency. Oh, that's not a thing I want to do. Okay, so we have Angular up and running right there. So our first request, right, is to essentially let the user know that they can fire. So I'm going to add a method call in here before I actually create the method. So it's like DOM driven. So I'll just call this. Oh, sure. So if your body tag has ngapp equals app and you see Angular.module app in app.js. Okay, and in theory, if everything's saved and refreshed, then that shouldn't say two anymore. Well, that shouldn't say one plus one anymore. It shouldn't be broken. Okay. All right, so I'm going to go ahead and continue on. And once again, I'm not going to go too far. We're not going to go too far in lockstep, so I don't feel like you'll be left behind forever if something isn't quite working. All right, so I just called a function called status that doesn't exist, right? Actually, and I skipped a step. So I'm going to declare a controller. All right, and what this does, it's just like a Rails controller. Each controller is responsible for passing the view different chunks of data. So since this is fairly simple and I want to keep the number of concepts as minimal as possible, we're just going to use one giant controller. I wouldn't recommend doing this day-to-day. So the body has ngcontroller there, so I'm going to add the controller to our Angular module just by chaining off of the module declaration. So that controller, that was pretty awesome when it went off the screen, huh? Actually, what I'm going to do here is... It can be off this one too, but it's a good question. So I'm actually going to... No, this is fine for now. Okay, so in theory... All right, so I saved everything off, I refreshed the page, and I'm not seeing any of the mustaches. So if you're seeing mustaches, then something went wrong. So I've got a little less space than I wanted, so I'm going to just save this to a variable, and so that way we get line wrapping here. And this is a minor change here. It's just whether you change directly off of the module declaration up top or that controller, the two are equivalent. So our scope name parameter is... it's a bit of magic provided. So if we attach things to the scope, we can essentially give the DOM bits of information that will automatically update. So hit scope.status equals function. Give everybody a second here. Here, clickity click. And I'm going to pop back over here, and so I've got my controller, and I've got a call to status. So in theory, when I pop back over here, it says you may fire when ready. So the next thing I want to show you before I cut you loose is the ng-repeat attribute. So that way, if anybody's played in Backbone but not a more modern framework, then you know the white-hot pane that comes with attempting to render a list, right? You'd think something that simple would be easier. Well, it kind of comes for free here. So I'm just going to make an array available here, and I'm going to go ahead and just verify that I know the syntax for how to do that. So I want to be able to render the column headers from data. We've got a hand up in the back. So the columns are... every number is right, so I'm just going to say one, two, three, four, five, six, seven, dot, split. This is JavaScript's version of an array literal, right? So we don't have nice things. Yeah. Yeah, JavaScript's definitely the reason why we can't have nice things. Oh, yeah, absolutely. So I'm just going to say... So I'm going to put in some mustache here and just say... thing, right? So everything in the mustache is... I mean, that's just JavaScript, right? So I can just do... so I can refresh now, and that turns into two, because, like I said, it's just JavaScript. I can do, like... I think I can even do an alert from here, right? Right? I think this actually works. Never actually tried this for obvious reasons. Nope, that's fine. We don't want to do that anyways. See, this is called experimentation. We're learning together. So since this is just JavaScript, it's not super handy, because we want to do more things than just add integers. So when we say... So say we put just a random attribute on scope and set it to a string, and instead of a mustache, we just say test. Don't worry if you don't quite get every keystroke here. We'll be deleting it. It's just a matter of... It's just a way for us to get information from JavaScript land somewhere into the browser, and it just kind of works. Are we feeling a little better about scope here? Go ahead. Angular doesn't use... Angular's templating system is the DOM. So that's just... It just looks for... Yeah, there's two curly braces. So I'm sure there's some kind of official term standard, whatever, because it's no different than ERB with the angle brackets and the equals, right? And scope is no different than setting something to an instance variable inside of a Rails controller. Okay, so we can put anything on scope and display it. So we already have just a plain attribute, which is just a value, and we also have a function, which is also a value, but we've got the open and close prens. In JavaScript, remember, always look for the open and close prens, because that means you're calling a function. So we can call anything from inside of here, from the scope. So I want to get an array of column headers so that way we can iterate over those. So I'm just gonna say column headers. If anybody thinks of a better name for this, let me know. I'm not super happy with it. So one, two, three, four, five, six, seven. Got split. Array literal JavaScript. All right, and just delete test. And I'm just gonna throw the whole thing in here just to make sure that stuff's wired up properly. Yeah, so not super pretty, right? So yeah, we displayed a list of stuff. And we can go home, right? Yay. So there's a neat... So this is the magic angular syntax word displaying a list of stuff. NG repeat. I'm gonna set it equal to thing in collection. Yeah. So our collection in this case is column headers. And our thing is a column header, right? Sorry, let me see if I can get this whole thing on the screen at once. It'll jump. Ah, okay. Sorry about that. We'll just... And so we have column header in column headers, and a single column header inside of that NG repeat element. So it's like a block variable in Ruby. So in theory, if I refresh here... So these are actually being displayed from our array, and these over here are just the static bits from the HTML, right? So I just went ahead and I deleted the rest of those tables, the rest of those THs. And so all I have left is the headers that I can't get on the screen all at once. I've got to make this a little smaller. A little smaller. There we are. Oh, sure. And column headers, you know, the array literal. Okay. I'm gonna go ahead and... I'll go ahead and show the click handler next. So this is the button element right here, right? So let's call this... So I want a method called fire. So on the button element, I just threw a NG click called fire, or sat an NG click attribute to fire. And I'm gonna expose it here. I'll just set an alert. Let's see if this works for me. And then I'll go back and make sure... All right. So I'm not entirely full of crap right here. So we've got scoped up fire here and NG click equals fire there. All right. So how many people are completely lost right now? All right. All right. What's that? All right. How many people are totally bored? Way ahead. No, no, no. Okay. Okay. So let's get everybody caught up so they can see the click alert. All right. So we've got... So you have the NG repeat recipe. You've got the NG click recipe. The next recipe... The second to last recipe I'm gonna give you the second is NG model, which all this is saying is, okay, assign the thing that's inside of the text input field to a local variable called shop. And we're gonna just pass that into fire and I'm gonna make the text a little smaller. I'm slowly making everybody's eyesight better incrementally. You're welcome. All right. And back over in our controller, I'm just going to change this to take a name parameter called shop, right? And it's just gonna... It's just gonna set an alert for the thing that's inside of the input. So once again, our click handler is called fire, which we expose by assigning it to the scope here. We're passing in a local variable called shop into the function here that it gets from... That it gets from the input because we are setting it via NG model. So I'm gonna see if I'm lying. Let's see if this actually works. Refresh, don't forget to refresh. Right. All right. Eh, I don't know. It works. So the dirty secret here is I use as little as angular as possible because I just want to keep the skills and the code as transferable as possible. So in this case right here, as far as we know, it's just assigning it to a local variable called shop. It's visible in the view. Go ahead. So the question was if there's any reason why I did it this way instead of just accessing it off the scope. And the reason is I want to be able to... I want to keep this function more pure. I didn't want to say it, but I will. So I wanted to be able to move this function away if I needed to. And if I just assume that it's available on the scope, it's going to be a little bit harder. So it's because I'm lazy. That's why I'm passing it in. That's how I hand it up in the back. Okay. All right. So we've got a shop, an array, and a status, right? So at this point right here, we've got two options. I can keep showing you different pieces of angular and JavaScript. Or I can cut you loose on the rest of it because at this point, with these recipes and knowing a little bit about programming, we can eventually work our way through the requirements of the system. Oh, no, that's okay. So the next... So I'm going to give you a specific mission right here, right? So right now... So here's what we'll do. So I'm going to crack keynote open. We're sending you on a mission. Hopefully it's not suicide, but if we lose some resources, you know. So we're going to do 25 minutes working on this. So what I'd like you to concentrate on first is just displaying the square that we're targeting inside a status, right? And you have... And give it a shot for a little while and if you can't figure it out after five minutes or so, raise your hand. No big deal. This stuff is super hard. It is considerably harder than Rails just because there's a lot more moving pieces, a lot more things resident in memory, right? My Hello World Rails, for the first time took a week. My first Hello World, like writing stuff back to the server in JavaScript took three months, right? There's a huge difference between these technologies. So mission one, just show... If I type A1, just show A1 in the DOM. And then mission two is going to be a little bit further along. We're going to make the status show if we hit a ship or not, and we need to determine if a ship is there. And once again, try to work on it for a few minutes and then as you get stuck, because we'll all get stuck, just go ahead and raise your hand. Do we have questions before I let you loose? Go ahead. What's that? Oh, personal preference. Feel free to make yours a straight string. Any other questions? Okay, go ahead. I'm going to set a timer for 25 minutes. All right, we're going to show a solution, see how far we got, and then I'll show you how to go ahead and extract and write some tests. Cool. I think I misspelled YGA. Hypo, full request. Thank you. Don't forget to introduce yourself. Thank you, by the way. Yeah, go ahead. Go ahead and roll. All right, my name is Patrick. I'm from Toledo, Ohio. Are you going to come up? That's my partner, Becky. She's from San Francisco. We initially couldn't figure out where the ships were, and then we asked Zach, and he said there weren't any. So we decided to just create a little array to show where the ships would be. So we have that. And down here in the fire function, it takes the shot parameter, and then JavaScript doesn't really have any good, like, function for checking to see if something's in an array. So we had to kind of whip this up to see if the input was indeed in the ships array, which is declared up here. And if it is, the target would be a hit, and if it's not, it'd be a miss. And then the status function up here will grab that variable and return it if it is defined. And if you're just initially loading the page, it'll give you the fire one ready. So A1 is one of the ones that does work, so that gives you a hit, but A2 is not in the array, so that gives you a miss. So that's pretty much it. That solution is really close to what I came up with, so I just want to make sure to surround myself with people that agree with me. All right. So what I'm going to do is I'm going to go ahead and run through extracting some stuff, once again, technical term, out of the controller so that way we can test it. Because we are... What time is it? I figure an hour left to go. It's a good time to actually show how to do some tests. So I didn't move this ahead any, so I'm still being kind of a bum here. So right now scope has three things on it, right? And so long as it gets those three things, it doesn't matter where they live. So I'm just going to magically pre-create some specs here. And anybody who stumbled across the spec file already noticed there's a failing one, right? So if I take a look at the message, it says game is not defined. Okay, I'm mirroring. For a second I thought maybe I was giving an impressionist a presentation here. So I don't have game defined. So I'm just going to pop over to game.js. And don't worry about following along lockstep. This is kind of... This is more an overview of how to go about doing it. So in JavaScript when you see a capital letter at the beginning of a function, in your mind make that a class. That's really saying class game, right? Not only can we not test for arrayness, we have no class. So I've got a style I like because I hate dealing with this keyword. After using it heavily for a year, I still mess it up on a weekly basis, so I'm trying to not use it as much as possible. Old habits. All right, so what I'm going to do is take a look at my tests. So undefined is not a function. So if I take a look at the test, game.status, right? So game.status to be you may fire when ready. So I'm going to create a status function. And we're still failing because undefined is still not a function. Because right here on nobody in the outside world can see this function. But I really like function declarations. I tell people it's because they get hoisted so I can be a little less careful about the order of the declarations. But reality it's because I don't need a semicolon and JavaScript almost looks like a programming language when I'm looking at function declarations. So honestly it's aesthetic. As much as anything. But I really like this pattern right here. So self.status equals status. And so then I explicitly make it public by attaching it to self. So now we have a new exciting error. So because we're returning undefined from our newly minted public method status. So I'm just going to say return thing. So it's that expected thing to be you may fire when ready. Okay. No biggie. You may fire when ready. And passing. So passing. Come on. All right. But this isn't reality. Right. Because it's not hooked up to our DOM. So our user interface is completely disconnected from our test. So it's no good yet. So I'm going to take a look at the scope. So okay. I also need to implement column headers. So I'm going to go ahead and write this spec. I wasn't planning and I'll go ahead. I guess column headers. I just wanted to break right. I just wanted to break if like column headers goes away. Just like that's like just like extra bit of confidence. So I don't know for right now. Like you don't need to like you don't need the perfect thing to test for. I mean sometimes you just need something just to get unstuck. Right. Oh and that. Oh yeah. Let's let's keep that. We just get rid of it. So expect game dot column headers. Let's call it. Let's just index into the second element. That way we don't have to test a bunch of them but we sell a pretty good idea that it's going to be following the proper proper progression. I don't want to test length because maybe we can change the length later. Right. So this I believe this is two. It's one of those times when oh not read property one of undefined. Oh that's right because I didn't actually make it yet. So yeah you know what it's already made over here. So we'll just throw that here. I like the bar up top. So this is somewhere between a revealing module pattern and a simple constructor pattern if you're into pattern type things. So and suddenly we're passing. Okay. So we've got three things on the controller and two of them under test. So I want to get fire under test as well. And you notice that we're not we're not breaking the DOM. Right. It's like everything still everything still works because you know our highly advanced battleship alerts are firing still. So so we're not we're not cutting and pasting. We're copying and pasting and anybody that that will read a book on writing programs and retain information is not surprised by this. I was surprised by this Katrina owned showed a bunch of us at a class that how to refactor with not breaking things and it takes a lot less effort because you're not always fixing what you're breaking. So even if I'm not under test I still like to kind of follow the same philosophy and copy and paste instead of kind of paste. Okay. So let's get fire and now as well. I didn't write my test yet when I meant to so fire. So I'll just not expose the public API and take another look at the specs. All right. So I already have this one written in such. So I'm just going I'm going to comment out this expect right here because I because I'm not going to take the time to to implement this quite yet. This is this is more about me being lazy and showing you how to pull the stuff out. So yeah. So I just wanted to not blow up at this point. So super super stupid test to begin with because I just I just want some level of confidence that I'm not going to break the user interface when I change my code even when I not necessarily testing the user interface directly. Okay. So this is failing. So I'm going to just go ahead and put put fire on. They make fire public as well. Fire equals fire. Right. And we're passing. Hey, that's going to be irritating. Let's let's kill the alert. Well, we'll just make this. I'll keep it for a second. Hey, yeah, it's going to be awesome. Okay. Because what comment about how about that because what the next step here is taking a look at the controller. It's not controller. It's app.js. So we've got these three things. I'm going to put a fourth thing. Game equals new game. Right. And theoretically things should still work. Right. And it should still work because I didn't actually change anything in the HTML. I didn't change anything in the view. So index.html. So I've got three things. I've got status, fire, and one other thing, column headers. So that should be on game now, right? So game.status. Let's see if this still works. Yep. That's still showing you may fire one ready. So I'm going to do game.fire. I'm going to temporarily. No, that is still going. Okay. So in theory, so now we're no longer pointed at the controller. We're pointed at my function here. That's still popping up. And the column headers. And my column headers are still displaying. So in theory, my controller now looks like this. If it still works, right? So I've been told that Angular has a really good testing story and I have no idea. Yeah, no idea. That's reading stuff. Like words, right? Yeah, right. So yeah. So since Angular works really well with just JavaScript objects, in fact, that's the only thing it works with. This is all I need in my controller, right? And then I can add functionality and I'm not using Angular. And this is kind of the punchline right here. I can say this is my Angular app right here, right? It's one line. It's exposing the JavaScript we wrote to Angular. You don't have to write code this way. It's perfectly legit to go through and use the Angular testing tools to get everything under test. But boy, this is really cool. My pair and I, we moved an app from backbone to Angular and we had a ton of logic in the front end because it was a Chrome extension. We didn't have any place else, but we didn't have a server and we moved from backbone to Angular and we didn't have to touch 90% of the code, which is like a cool parlor trick. But I mean, the reality was it just made our lives easier the entire time. So I mean, this is why I like Angular. It's because I don't have to use it. Yeah, my Angular app right there. There's a lot of benefit that you still see, right? There's too many native bindings and things like that. You can just do a good thing with this. It's like, if you started to pull all of your logic into a game.js, what's to stop you from using Ember instead? What's to stop you from just maybe the one with some gingers? What if you thought you had an app shaped like this? You start out with an app like this, right? And this works really well, but it turns out that everything's going into a single view and more and more feature requests and surprise the apps actually about that one thing. And then suddenly you have something that fits this better. And if you're completely married to Angular, I mean, you can do routing in Angular, but boy, it's so much nicer in Ember. And you know, not everybody is going to want to switch. There's other less flashy benefits too. I have a lot of battle scars, and I just find my life to be moderately less painful when I just make JavaScript projects. I just don't care. You follow naming a lot, right? Instead of where we were operating under the Angular constructs, we're creating controllers, we're creating models. Now we're creating a game, right? I guess you follow this. If you're cracking out on this this weekend, you'll probably create a ship. You'll probably create an opponent or something like that. All of those things stand on the run. And you can test drive those outside of the browser, whatever you want to do. You're not bound to it. When you make the decision that, look, my stuff is in a controller, I'm going to have a game controller, I'm going to have a ship controller, I'm going to have a component controller. You don't have a lot of options on how you can test those things. It's the Angular. And then all these sides. And then you're following something that you'd be more likely to see in an Angular tutorial is per little chat. Go ahead. Yeah, I was just going to make the argument that the same approach that you're talking about, just making classes and having them do a good simple purpose thing. Doesn't have anything to do with Angular or any of these other things. It works really well in your Rails app, too. So if you have a lot of stuff in your controller, well, you can pull that stuff out in the same way. Yeah, right. It used to be like, hey, we want skinny this, but fat that, or whatever, right? Like, bullshit. We want skinny everything. That's what we want. We don't want anything fat. That's a smell that we learned. It's fat. So I think for Angular sign, you leave everything in there. It leads you to having better functions, better objects. Here, we have freedom to compose. And it really is all about class. Have you ever regretted pulling logic out of a controller? Sometimes, I mean, it... On the case of trans people, I know that more often than not, it's a lot more testable and a lot more critical. Yeah, it's nuanced, right? Absolutely. And if it's difficult to structure your code like this, I mean, this isn't the answer, right? I'm not selling... It's not a law. Right, it's not a law, right? This isn't a diet. This is just a neat trick that really blew me away by Angular that I enjoy. Okay, so we've got about another half an hour or so. So I figure... Let's do some more stuff. So before... So any other questions, comments? Am I full of shit? I mean... Yes. Oh, okay. Yeah. Okay, cool. So this is the final set of stuff to get as far as I did in the final folders. So feel free to, you know, work on that for the next half an hour if you want or not. So, cool, thank you.