Katrina Owen

Properly Factored MVC

By Katrina Owen & Jeff Casimir

Starting Rails applications is one thing, but how you apply the priciples of MVC as an application grows determine whether your application is modular and maintainable or a convoluted mess. In this session, we'll use an existing application to explore and practice some of the common mistakes, correct techniques, and concepts behind the techniques to improve your development patterns.

Help us caption & translate this video!

http://amara.org/v/FGaT/

RailsConf 2013

00:00:16.800 and so please don't take anything that i or we say today to be like whoever wrote this is an idiot
00:00:23.039 they focused very clearly on the goal of delivering the features that they wanted to have and they delivered on and they succeeded
00:00:29.599 at that goal we can today use that in the context of their success
00:00:36.640 to try and serve the second goal try and improve the quality of the code make it cleaner make it more resilient
00:00:42.559 to change can i add a couple of things this software project started in 2006.
00:00:48.719 uh in 2006 we really did not know as a as a community as a as a ruby or in
00:00:56.160 particularly a rails community we did not know which things would hurt us farther down
00:01:02.879 the road we were making a lot of choices that we we just had no idea
00:01:08.479 whether or not it was a good choice so this project has not only solved the
00:01:14.479 problem it's set out to solve it's also survived uh there are 200 forks of the project
00:01:20.080 it's in active development they released they keep releasing new versions they they upgrade um there are a lot of
00:01:27.360 people using it and and it's in many ways a success story even though
00:01:32.640 it has some of the the growing pains that long lived program projects have sure and projects like
00:01:39.600 this you know i think when people are new to the community and have this idea of like open source is cool
00:01:44.960 i want to write open i want to contribute to open source i'm going to think about rails and i'm
00:01:50.799 going to imagine some new features rails needs and then when i never come up with that idea i will build it and i'll contribute
00:01:57.920 it back to rails right that's that's an exceptionally large jump to try and make it is much more
00:02:03.200 practical to find an open source project such as this you probably have things to do in your
00:02:09.679 life you have some kind of system for it but you could try out the system and then kind of get the benefit of eating your
00:02:16.000 own dog food right you could download the app run it make some improvements to it and contribute it back
00:02:21.120 and once you learn about the processes that go into that you're much more likely to be successful at contributing to larger projects like
00:02:28.239 ruby itself or rails itself
00:02:34.160 so i'm jeff kasmer i'm kasrino uh we have a company called jumpstart
00:02:40.000 lab uh we teach technical training for companies so like
00:02:45.200 week two week long classes and most of much of our time right now is spent teaching a six-month developer training
00:02:51.680 program in denver called g-school previously we ran hungry academy at livingsocial
00:02:59.040 and this is our workshop one of the so why do rails projects go
00:03:05.920 wrong why do software projects go wrong right the
00:03:11.920 chorus on why software projects go wrong is communication it's always communication problem for projects to really burn down
00:03:19.120 you have to have communication problems that lead to those mismatches between expectations and reality
00:03:25.040 but when you look let's say we have clear expectations let's say we have good communication
00:03:30.400 rails kind of sets you up for trouble and i want to look at why that is let's
00:03:36.640 talk first about the mvc theory and how it should work your application
00:03:43.200 receives a request from there it trickles down to the router to the controller controller to
00:03:48.720 the model model over to the database database back to the model up back to the controller through the
00:03:54.720 view i'll put a response so request
00:03:59.760 http request with a verb and a path comes in it hits the router the router decides who this belongs to what
00:04:05.920 is the user trying to do it passes that information onto the controller and the controller is now the middleman
00:04:12.720 ideally the controller has almost no responsibility it is just
00:04:17.840 receiving information and dispatching information to other parts of the system controller
00:04:24.560 takes the information it needs from the router passes it down to the model the model does the smart interesting stuff
00:04:30.560 right the real programming talks that database gets the data out formats it puts it into ruby objects
00:04:37.199 passes those back to the controller the controller then prepares that data
00:04:42.320 for the view template and this is a little bit uh idealistic model this is not
00:04:47.759 exactly how it works but let's say that the controller kind of passes its data to the view
00:04:53.840 the view then injects that data into the template creates a html plain html response with
00:05:00.960 http headers and shovels it out to the user that's the idea of how it works
00:05:08.800 this area this area down here is pretty clear people agree about how it should
00:05:16.320 work and what it should do the code behind it is the
00:05:21.360 part that over time has changed the most in rails active record has probably seen
00:05:27.520 more work than any other component of the framework and part of that is because i think it
00:05:33.360 is a clearly understood problem you know what an orm is supposed to do if you're a
00:05:40.240 rails developer and so you can hammer on the library make it do that faster make it do it better make it do
00:05:45.680 it more flexibly up in these layers
00:05:50.880 there really hasn't been very much change if you were to take a rails 100 app from 2006
00:05:59.199 your controllers and your views would work out of the box
00:06:04.240 there are things that have been added since then but really we haven't improved much of the thinking
00:06:12.400 if we make it a little more realistic each of these items that's supposed to have
00:06:17.680 one actually has at least two on the way down the
00:06:23.840 controller is responsible for processing the request pulling out those parameters figuring figuring out more about what
00:06:30.639 needs to be done passing that down to the model and the model then starts doing the database communication
00:06:36.720 and then typically once it has fetched information from the database there's a second responsibility in there of business logic right doing
00:06:43.600 interesting stuff with that data it then sends that data back
00:06:48.800 to the controller and the controller acts as this funky data interface to the view template
00:06:56.319 so one of the things i hate explaining in rails is how does data get from a controller to a view
00:07:03.039 well you make it an instance variable okay say more well
00:07:10.880 it works it's fantastic okay the reality that you don't want to know is that the
00:07:17.840 controller instantiates another class another instance called a view context
00:07:24.319 but don't look for a class named view context because it's an anonymous class that's like created on the fly
00:07:29.759 and it cop it queries itself for its instance variables and then passes all those instance variables into the view context
00:07:36.800 and it's like a really jacked up system if you ever want to be terrified by this
00:07:42.560 go into a view template any view template any request and do erb tags percent equals
00:07:50.840 self.inspect and you'll see i think last time i did it was 5700
00:07:57.599 characters of information and you'll see in there like the same things embedded multiple times
00:08:03.520 same http parameters and it's a really crazy show for lack of a better word so
00:08:09.759 hopefully one day maybe some of you will fix action view and make it not suck
00:08:15.759 i shouldn't say suck because it works right make it easier to understand easier to pull
00:08:20.960 apart but if there's one area that causes the most problem in rails applications
00:08:27.840 it's this the controller and to say it has these two responsibilities is really
00:08:32.959 an oversimplification
00:08:38.000 it really has somewhere around 12 responsibilities in a typical app
00:08:44.480 it's doing everything it is effectively the god object of your request it's responsible
00:08:50.080 for setting messages queuing jobs sending stuff to a cash store and fetching it back
00:08:56.800 determining what kind of response the user wants etc all these things are according to mvc
00:09:05.040 these are at least plausibly the controller's responsibility and anytime you have an area of code
00:09:13.360 that has 12 responsibilities it is going to get out of control by nature unless you fight very hard against it
00:09:20.720 and so despite the talk of you know fat model
00:09:25.920 skinny controller that in itself is not a solution but the premise that controllers should be
00:09:32.000 concise is a goal that we should always push towards if your controller method
00:09:37.519 is creeping up above about seven lines of code you probably need to rethink how you're
00:09:43.680 doing it many controller methods and some that you'll see today are
00:09:49.519 dozens maybe even hundreds of lines of code particularly what you see this with is
00:09:54.720 the the parameters if i've got this parameter in this parameter do this one thing but if that thing didn't work out
00:10:00.240 do this other thing or if i have this other combination of parameters do this other thing and do that other thing
00:10:08.959 there are many reasons for that complexity we'll look at ways to reduce some of them today
00:10:16.000 so refactoring i feel like i shouldn't even say refactoring when you're here like this like katrina's wheelhouse but
00:10:22.240 i made the slide so i get to say it uh refactoring is a word that we love to
00:10:28.480 throw around in the ruby community from refactor and it simply means
00:10:34.560 changing according to many people like it means judging other people's code
00:10:41.600 sometimes that other person is past self and be like that person was an idiot i can't believe they wrote this
00:10:48.320 i'm gonna change some stuff and perhaps it will be better right but in reality there
00:10:53.600 is a process to refactoring it is more than just changing and
00:10:59.680 one of the reasons that we can follow this process on tracks is specifically
00:11:05.680 that tracks ensures its external behavior the critical piece about refactoring is
00:11:12.160 that from the business side that that left side is our top i ended
00:11:18.240 up top side that customer desire refactoring provides no value whatsoever customers don't pay
00:11:25.040 you to refactor what they do pay you for or you pay yourself or whoever's
00:11:30.720 dishing out money is that when you dream up the next feature you can build it in what seems like a
00:11:36.320 reasonable time without burning down the house and that's where refactoring provides
00:11:41.600 value to refactor effectively you show no except
00:11:48.000 external change and it's only possible when things work if something doesn't work you cannot
00:11:54.000 refactor it okay and so it is always okay uh one of the things we always push
00:11:59.440 students towards and they kind of hate it is doing the dumbest thing possible right just make it work
00:12:05.680 and then make it better if you try and make it correct on the first try you'll probably never
00:12:12.079 get there right it's more important to write a crappy implementation of something
00:12:17.440 and then and do it in a smart way where you're covered by tests that ensure the expectations ensure you're
00:12:23.040 delivering the value you want and then you can make the code better there's all there's always time for that
00:12:30.560 you have to have that test coverage and defining now well tested is like a whole another workshop you know hopefully
00:12:36.880 if you're interested in that topic you came yesterday and heard some great talks from thoughtbottom from noel that's a whole
00:12:43.600 nother layer of complexity but thankfully in this situation with tracks it's plausibly well tested it has
00:12:49.760 acceptance tests and so forth
00:12:55.920 our goal is to increase increase the ability to change increase the agility of the code
00:13:01.600 that typically means breaking it into smaller chunks that that can then be recombined in
00:13:07.200 interesting ways so tracks
00:13:13.200 i don't want to talk anymore uh thankfully katrina put together an
00:13:18.480 incredibly detailed tutorial for you and especially on day three of the
00:13:23.600 conference maybe you were pivotal labs boozing it up last night or whichever party it was last night
00:13:29.440 maybe playing board games or being very responsible you're probably better off programming
00:13:35.120 than you are listening to me so if you weren't here when i
00:13:40.160 started off uh we have a alternate wi-fi network named on ailes if you connect to it you
00:13:46.240 can actually use the internet if you would hop over to railsconf tutorials.com
00:13:52.800 down in the wednesday section you'll find a page for this session and in that session
00:13:59.839 has i will approximate 12 days of work for you i don't know
00:14:04.959 katrina went off writing tutorials on you all so
00:14:10.399 you will not finish it you probably won't get halfway through what i hope is that you can dig in and
00:14:16.959 start to get an idea of how you can apply these principles in code and then maybe take it with you
00:14:22.800 and continue to work on it uh in the next days and weeks we're gonna float around and answer
00:14:28.880 questions i think anybody else doing question answering can you raise your hand
00:14:36.160 there's at least one yes
00:14:41.600 so also please try and make use of the chat room i know people haven't really used the chat room because the connect
00:14:46.800 connectivity sucked now we have connectivity hopefully you can use the chat room and you can answer each other's questions in there
00:14:53.279 we'll do our best to pop around and we'll do a little wrap up at the end
00:14:58.880 we may also uh just kind of duck up for air every 15 minutes or so
00:15:06.720 just to see how you're doing if there are any questions i'm guessing that if one of you has a question about 80 of you have a question so it
00:15:13.839 would be good to get them out loud if
00:15:19.360 and there there may be concepts that would just be good to talk about so we can we can take breaks you don't
00:15:25.040 have to refactor 100 of the time like jeff said the tutorial is pretty detailed so you may
00:15:31.199 be able to just finish it on your own also not here
00:15:36.800 and you're strongly encouraged to work with somebody else they're probably nice people if
00:15:42.880 anybody's mean talking about things you're wondering out loud uh is very helpful in two ways first you
00:15:48.959 get to hear your own voice say it so you you actually get to sometimes answer your own question because you hear it
00:15:54.399 through a different sense sometimes it also helps us overhear
00:15:59.839 questions so that we know what to talk about up here i would go so far as to say if you would
00:16:05.199 kindly when you have a question ask at least one other person before you ask us
00:16:10.320 because you're both helping yourself in possibly answering your own question and helping them with a slightly deeper
00:16:16.639 understanding it's on the it's on the page if you go
00:16:22.399 it's like the top section on the page oh i got you yeah because this thing's
00:16:27.600 not interesting and then people will like be putting up all kinds of meme photos and stuff what not i'm sure
00:16:34.320 all right go to it there's a question answerer this masked
00:16:40.880 man so sorry
00:16:48.160 uh good plan didn't really work out i guess the router is fine but it's like outside the router we're
00:16:54.800 just traffic's just dying like i'm only one out of five of my packets is getting out
00:17:00.560 and i'm connected to the ethernet which sucks so it's not uh and looking at the
00:17:08.000 pieces i i don't think there's yes i don't based on what i see that i don't
00:17:15.199 think that's the issue i only see like 100 clients on there which is about like the size of the room
00:17:20.400 or 80. yeah so i thought about that what what i
00:17:26.000 think i want to try is katrina is going to work through some of the exercise here
00:17:31.440 so we can at least look at it discuss it because i worry that yeah if it's like
00:17:36.880 usb we're gonna just spend an hour passing usb sticks you know and so sorry that that
00:17:42.960 that sucks we thought we had a good solution turned out it didn't work um i while she's starting up with that i
00:17:49.600 might experiment with this thing that we don't actually have the passwords for and so forth but what could go wrong um
00:17:57.760 yeah that's i'll see if i can come up with any i if i can put a password on it i will all right so she's going to hook up to
00:18:04.480 that i would say uh using either the onails or the ruby central like still every once in a while try and
00:18:11.600 clone things or access the website as possible
00:18:29.039 i'm trying to think where to start one of the first things i wanted to do is show you what it looks like to
00:18:35.919 explore a project on code climate
00:18:40.960 but i don't have internet so i'm not going to do that
00:18:48.400 this project has a lot of very very big controllers it also has some very large models
00:18:56.080 but the most interesting problem areas in the application are in the controller layer
00:19:01.440 where in particular the controller that we're going to look at
00:19:07.280 has an index method let me just get back to my terminal here
00:19:14.559 oh no uh color
00:19:22.840 scheme
00:19:28.880 so the index method in this controller looks like it is about 12 lines long
00:19:36.080 um it sets a few index uh instance variables but it calls the most
00:19:42.480 important thing here is that it calls a number of helper methods and those helper methods
00:19:48.080 are defined in the controller and um all together they represent
00:19:56.240 200 lines of code so this controller action is 200 lines long and there's a lot
00:20:02.480 going on in here there is
00:20:10.159 um calculation lots and lots and lots of calculation
00:20:16.320 um there is sql there are sql like hand crafted sql
00:20:23.919 queries lots of them and you'll notice also that
00:20:30.400 as we page through here we keep setting more and more instance variables so we have
00:20:37.760 i i counted them it took a while uh i think i got to like 35 or 36 instance
00:20:43.919 variables that are set um so you have all of this state that's not visible at all
00:20:52.240 in in here it just looks like we have maybe five instance variables if you go to the view
00:20:58.799 they're referencing instance variables all over the place and you really have to know where to look to find those
00:21:04.799 instance variables because it's not in any way obvious in
00:21:09.919 in the method itself in what i'd really like to do to make all
00:21:17.120 of this obvious is to inline all of those helper methods that takes a long time um so
00:21:24.799 if i were the first time i did this refactoring i did i did go i went to the index action i found every
00:21:30.240 single helper method and every single helper method that the helper method's called and i just inlined everything and ended up with about 200 lines of
00:21:37.039 code and then it made it a lot more obvious what was going on here and um most of what's going on here is
00:21:46.159 this sharing of state this sharing of of all of the these values that are just kind of
00:21:52.400 floating around in the application between the controller and the view but the other major thing that's going on here is that we are
00:21:58.400 calculating lots of stuff and calculating lots of stuff is one of the things that we know
00:22:04.320 belongs in the model layer that's kind of sort of one of the easy things it's like oh everyone agrees that we can
00:22:09.679 put all the calculations in the model layer so i wanted to pick just a small portion of this index method the get
00:22:16.400 stats tags and when i when i did that we end up
00:22:21.840 with a refactoring that's going to concern itself with about 50 lines of code
00:22:29.840 so calculations
00:22:38.840 mostly uh get stats tags
00:22:45.600 there's a really nice comment here that kind of indicates that this is one thing it we got some
00:22:53.039 logic from a blog post we put it in the controller in a big method but we kind of
00:22:59.039 isolated it in this helper method so we can take this and we can isolate it
00:23:05.919 in the model layer so that's the first thing we're going to do is just create an uh
00:23:15.200 hard to type and talk at the same time we're going to create an empty ruby class in the model layer
00:23:22.799 and then we're just going to kind of put all this code in there and see what happens
00:23:30.159 so we don't actually have right now in this application at this point in time the all of the
00:23:36.320 models in the application are backed by the database everything inherits from active record
00:23:42.480 base uh and this what we're going to do here doesn't belong in the database at all
00:23:48.799 but it does belong in the model error so we're just just going to create a ruby class for it
00:23:54.640 we're in the stats controller so i'm just just going to create a subdirectory for stats because it
00:23:59.840 looks like there's a lot going on here the the controller is about 800 lines long so we can probably make quite a few
00:24:06.400 little classes and put this logic in the application layer in the model layer it's going to make a
00:24:13.360 directory and
00:24:23.440 create a new class so what this 50 line method is doing kind of crucial i guess to mention is
00:24:30.320 that when you render the index action you get a bunch of stats about how many to-do's items you've completed
00:24:38.000 how many you've created and you also get two tag clouds one
00:24:43.520 is tags for all of the to-do items that you've finished for all time and one is just
00:24:50.240 for the last three months so you have these two tag clouds you have one method that's generating them it's about 50 50 line long
00:24:56.960 lines long so we're just going to create a tag cloud
00:25:03.679 class and create a compute method in it and then we're going to go back
00:25:09.679 and we're going to copy all of this
00:25:19.200 and i just skipped the whole two first sections of the uh the tutorial because i'm so eager to get started with code
00:25:26.840 um i have more to say obviously uh we haven't run any tests
00:25:32.960 yet i haven't shown you how awesome the code coverage is in this application so i'm going to do
00:25:38.960 that first um ah whatever uh the tests actually take
00:25:45.360 17 minutes to run so i'm going to start them and then we're going to take a cotton oh sorry i'm going to run two of the test files
00:25:51.200 because we don't have time to run all of the tests i'm going to run only the tests that matter for this
00:25:58.720 for this controller action
00:26:08.840 frozen
00:26:14.080 sorry this isn't reacting
00:26:25.840 okay
00:26:32.960 i actually think i might not even have this right now sorry this
00:26:46.559 have i fallen out sorry i it would it's fine
00:26:55.919 test stats okay let's see if this works
00:27:10.640 okay so we're running tests there are a number of unit tests there are a
00:27:17.200 handful of cucumber features and i'm just running
00:27:22.320 the unit test right now one of the things that i noticed after about eight hours of
00:27:28.159 refactoring this the first time around was that when i went into the view
00:27:36.399 and deleted the partial with the tag clouds in it
00:27:43.200 and ran the tests again
00:27:50.960 nothing failed i looked at the code coverage and it had 97 code coverage so it's really well
00:27:57.600 covered it got all of it got executed uh but none there weren't no there
00:28:02.640 weren't any um assertions in it on it uh so what the first thing that i had to do
00:28:08.159 in order to prepare for this workshop was to create a test that actually tested the tag cloud and
00:28:14.159 it's probably one of the ugliest tests you'll ever see which is typical when
00:28:19.440 you're refactoring code bases hi um all right so
00:28:30.720 i'm just going to reset all of this
00:28:38.320 tag can i tell you something funny while you're doing that please do i just learned that we were supposed to
00:28:44.480 start at 11 30. you were right yeah i was just over eager i couldn't
00:28:49.600 wait to begin the refactoring so sorry if you came on time
00:28:54.799 and seemed like you were late all the stuff i said was probably stupid
00:29:01.840 you got to the good part i'm like the preamble for katrina she's the interesting part so you're
00:29:07.039 here for the good part continue have we gotten internet access yet not
00:29:12.240 that part no no all right um so one of the first things i did was to create what i
00:29:18.080 like to call a lockdown test um and it's basically a characterization test a characterization test
00:29:23.840 assumes that whatever is there is working and it basically generates some you take
00:29:30.480 whatever output you get and then you make an assertion that whatever
00:29:35.840 happens later is going to be exactly what you got the first time
00:29:53.440 i can't remember i have to go check the tutorial to see where i'm supposed to be at
00:30:01.279 i might have this locally
00:30:20.960 all right
00:30:30.320 sorry about this now that everyone gave up the wi-fi works not bad
00:30:38.080 so with the first row please start checking out the uh it's actually not that bad just get the
00:30:44.240 first row started first first two rows uh if you go to the rails
00:30:49.679 conf tutorials start cloning the repo once you've got it cloned down you won't need the internet
00:30:55.120 for the rest of the tutorial so if you just managed to get it set up
00:31:02.840 yes sorry clone and bundle cloned and bundled yes cloned and bundled that's correct uh so
00:31:10.320 i will keep typing until uh until we've got this show started
00:31:15.440 but hopefully you'll be able to actually program in a little while
00:31:22.720 so i'm going to i created a bunch of tags for throughout the whole refactoring so that
00:31:28.240 i can check the code out at certain points of the refactoring
00:31:34.399 and the first thing that i did was to create this terribly ugly test
00:31:44.960 that looks like this
00:31:50.960 it sets up a bunch of data you don't even have to know what data it
00:31:56.720 is just assume that it's good i make a hidden directory make a file in
00:32:02.559 that directory i do some tear down so that we can run the tests over and over again and then i assert that the page doesn't
00:32:09.200 change while we refactor and the way that i do that is i freeze time because there's a lot of time sensitive things
00:32:15.679 i i just render the index page as it is and i pipe all of what i rendered into a
00:32:21.039 file which is very very easy to do it's a slow test but it's easy to do and
00:32:28.799 then i compare it to the previously piped version in the in the previous file if those
00:32:35.279 have changed i raise an error if everything's good we're fine and we can continue so the way
00:32:42.799 i also wrote a rake task to run that one test um and the way you run it is bundle exec
00:32:48.080 rake whip whip is short for work in progress um
00:32:53.200 you'll probably see that often when working on tests this is
00:33:01.919 incorrect this is running all my stats tests
00:33:14.799 which has cucumber features which we'll be over in a moment then i'll run the real uh lockdown test
00:33:22.240 the cucumber looks fancy cucumber looks awesome
00:33:30.240 all right bundle exec rake test lockdown is the actual
00:33:37.279 test and that should be only one test
00:33:48.240 and it fails and so the reason it fails is that if i open the lockdown approved this is what
00:33:56.640 i have previously approved a blank page like we haven't actually approved
00:34:01.679 anything yet so our approved file is completely empty if i open the the received file
00:34:11.760 it's got all of the data in it and at the very bottom we have these tag clouds and it doesn't actually render any of the
00:34:17.440 css so it's not pretty like the the actual site would be but it's all it's the it's the source of
00:34:23.280 the index action so the way that i make this test pass the first time around
00:34:28.560 is to just say that uh whatever i received is now approved
00:34:36.079 and i can run the test again
00:34:42.720 and it should pass and now if i go ahead and delete that
00:34:49.520 partial that we deleted earlier and the test still passed uh
00:34:55.200 stats index the test should now fail
00:35:02.800 if the test fails we have a test that's probably good enough to actually start moving code around
00:35:11.920 and it's not
00:35:26.800 i have no idea why oh wait i didn't put the code back that would be why
00:35:35.200 that view stat index okay okay so the test failed when we were
00:35:41.680 missing the partial we're good to go so now let's actually make our class model stats
00:35:51.280 we have that stats tag cloud
00:35:58.320 we have that already that's very nice um let's go to the color scheme
00:36:09.839 so that's controller and we're just going to copy everything
00:36:19.520 and put it in the in the compute method so i still have everything in the
00:36:26.880 stats tag method i don't want to delete it because i would make my test fail and it would
00:36:32.800 it would it might be a very long path to get it passing again so the first thing i want to do is just
00:36:38.560 to instantiate my tag cloud and call compute on it and then see what fails
00:36:55.280 so the error is going to tell us that we probably missed there may be constants we that we missed we may be referencing
00:37:01.680 local variables that we know or instance variables that we no longer have access to or helper methods that we
00:37:07.040 no longer have access to and that's basically what this tells us it says that we're looking for an
00:37:13.040 undefined local variable or method current user for the tag cloud so we can just go ahead and pass the
00:37:22.240 current user but we need to take that into account inside of the tag
00:37:29.280 cloud we're going to create an initializer
00:37:51.280 and now run the test again
00:38:03.599 and this should tell us the next thing that we're actually missing how you doing on that getting it cloned
00:38:09.680 and set up done all right next three rows
00:38:21.440 wrong number of arguments in the initializer
00:38:29.119 i may have
00:38:34.320 thanks all right let's map this
00:38:39.520 a sec test lock down
00:38:54.240 all right we're good so now we can start um now we can start
00:39:01.200 actually using the new code so we have the old code the old code is what's making the tests pass the new code all we know about the new
00:39:07.680 code is that it's not raising any errors and this is going to be really tedious
00:39:14.560 so bear with me it's a lot funner when you're doing it yourself um i'm going to assign this cloud
00:39:21.599 to a local variable and then i'm going to find the first instance variable here that gets assigned
00:39:29.760 and i'm going to assign it using my new cloud tags for cloud and obviously i haven't
00:39:36.160 actually exposed this yet so i'm going to go ahead and expose it
00:39:41.680 tags for cloud
00:39:47.119 and i probably am just assigning the result of my
00:39:52.240 command there so i'm going to do this and now if i run the test
00:39:57.839 the new instance variable that i assigned is overshadowing the old instance variable so if i did this wrong
00:40:04.720 the test would would fail but it's passing so this means that i can probably get rid of this
00:40:12.079 tags for the first tags for cloud and since i'm no longer using query i can get rid of that string as well
00:40:21.920 run the test to make sure i haven't deleted too much
00:40:28.319 and it's looking good and so basically the rest of this
00:40:33.359 the rest of this screencast is just doing the same thing over and over again um tags min
00:40:41.760 cloud tags men and you occasionally get surprised think you've
00:40:48.160 done all good all correctly um
00:40:55.359 and you get these failures which is really it it helps keep you on your toes and helps make refactoring not the most tedious process
00:41:01.839 you'll ever encounter so this passes let's delete
00:41:07.839 the setup for it
00:41:15.200 this is one of those cases where we did it wrong so basically it's complaining that
00:41:22.240 we don't have a variable max so it turns out all the stuff that we deleted contains this variable
00:41:29.280 here the local variable max which is used down here so i'm going to go ahead and assign the
00:41:35.680 tags divisor cloud tags advisor
00:41:42.160 expose it advisor the tests and if that passes i can go
00:41:51.119 ahead and delete all the code that we were using
00:42:09.680 so this is the part of the screencast where you usually fast forward because we're going to do the same thing all over again
00:42:17.680 tags for cloud and i'm just going to go ahead and do all three of them even though that's usually a bad idea yes please do
00:42:24.880 you obviously love this you are flying so fast it's so hard i'm so glad you mentioned that all right
00:42:32.000 uh i feel like the typing is slowing me down so i'm trying to go as fast as i can
00:42:38.400 okay cool okay so uh should we just stop and
00:42:45.599 that's right awesome okay so um cool
00:42:51.760 so in the whole process of creating this test is there do you want to walk through
00:42:58.480 what we did and why why that's necessary cool so the test suite
00:43:06.079 when we ran the original tests for this project it has really really
00:43:11.680 good code coverage and it actually uses the simple curve gem i can actually show you
00:43:20.079 break i'm going to i'm going to run the tests for the whole stats controller
00:43:25.200 that controller that we're working with just so that i can show you what the code coverage looks like like
00:43:32.800 when we when we look at the output of that so the code coverage is very good it's
00:43:37.839 up in the 90s which means that the tests are when the tests run
00:43:44.560 all of the code in the stats controller actually gets hit actually gets executed
00:43:54.160 usually this is a good signal it means that you've written tests that actually exercise the code
00:44:00.720 there's a good chance that if you change the code the tests will break
00:44:06.400 and the first time i actually refactored this i thought i kind of trusted that i didn't i didn't
00:44:12.319 do the thing that i've learned to do later where i go to the top of the method that i'm
00:44:17.599 going to refactor and i raise an exception just to see that it happens or i delete
00:44:24.720 parts of the parts of the code to see that it fails i just kind of trusted that
00:44:29.920 the tests were good and this bites you enough times you
00:44:35.119 start you start not trusting the tests in a project you don't know very well
00:44:40.160 and most of the tests were very very good it was just in this piece of the of the controller the tag
00:44:46.319 cloud just the tag cloud wasn't really tested
00:44:52.000 and in the project it makes sense because people probably don't pay that much attention
00:44:57.920 to it mostly what they're doing is they're putting new to-do's in their system and they're tagging you know tagging them and
00:45:03.200 finishing them and they're getting stuff done they're usually not going to the stats page to look at the tag cloud of how productive they are um
00:45:10.880 so it makes sense that it's not very well uh well well covered so
00:45:16.480 the the coverage ends up in a directory that's um that's not actually saved in the in
00:45:22.160 the project source it's called coverage and you can just open the index page in your browser
00:45:27.440 um and right now the coverage looks terrible because we only ran you know two of the test files yes
00:45:35.680 this is simple cov yeah so it looks like the code coverage is
00:45:41.760 terrible that's just because we didn't run hardly any of the tests we only ran tests for for two
00:45:46.880 two files but the the
00:45:52.480 where is it controller stats controller right
00:45:59.440 so the stats controller is the the controller file that we're actually going to be refactoring
00:46:05.040 and this is green all the way through there's very little here that the tests
00:46:10.400 haven't actually touched so that's usually a good i
00:46:16.240 good just a good sign the the method that it goes on and on here we
00:46:22.480 have a little bit it hasn't been testing the failures there's a there's one action that's probably not tested we have
00:46:30.400 a bunch of helpers so the task get stats tag is the helper method
00:46:37.280 that we're actually going to be refactoring today and it is touched by
00:46:44.960 by the tests and what we did to actually test if the tests are for this are good
00:46:51.359 enough is we went into the view and we deleted all of the view code for that feature
00:46:56.960 and we ran the test again and the test passed which is not a good sign
00:47:03.359 so the the test that i wrote is a test that just calls the index
00:47:09.440 action and captures all of the raw html output from that action and puts it in a file
00:47:17.280 a file that i hopefully never have to look at if i do the refactoring correctly i'll never have to look at the
00:47:23.040 file it'll just be there it'll be this is the correct version of the output then every time
00:47:28.240 i make a change i rerun a test and it compares against that original version and if there's no change it's good i
00:47:34.640 don't have to look at it now when we deleted the partial again from the view we deleted out all the view code for the
00:47:40.800 tag clouds this thankfully failed so our the output the raw html output requires
00:47:49.040 that the tag cloud code the source code for for that feature is in the file so at that point we have
00:47:57.280 a test that just assumes that the original is is what we want
00:48:02.400 and we can make all the changes we want as long as the new output matches it exactly
00:48:24.079 nice
00:48:29.359 does that make sense
00:48:34.720 not really a little bit
00:48:43.040 so if i can recap slightly different way the premise was that the app was working
00:48:48.800 and it was creating some html and whatever that html was is the right
00:48:54.000 thing and so the strategy katrina has used here is to output that html to a file
00:49:01.680 and then as we make changes when we run the tests again
00:49:06.720 the html you get out should match the html that we got out before we started messing with stuff
00:49:12.559 we don't actually care what's in that file as long as it keeps matching software
00:49:23.520 yeah absolutely so terrible dirty hack a lib task
00:49:31.200 work in progress rake i created a rake file that has some some little tasks that
00:49:37.359 will run subsets of the of the tests because the whole test suite like i said it takes 17 minutes to run on my machine
00:49:44.000 and i just don't have the can you do a white background oh yeah color git hub
00:49:52.079 thanks um so i have a test called rake test colon stats
00:49:59.359 which just does the the controller test for the stats um i have one called run rake
00:50:06.800 sorry rate cucumber stats which just does the features for that controller and then
00:50:13.200 i have one called work in progress which runs both of those so the the unit or the the
00:50:19.359 controller tests that are just the sort of functional tests and the control uh cucumber features so those are just
00:50:25.359 combined into one and then i added one uh one task called test.lock or test colon
00:50:32.559 lockdown which runs that single test the one that pipes stuff to the to a file and then
00:50:39.839 goes and checks if that the new output it matches the old output
00:50:45.760 um and then the test functional lockdown test that test
00:50:52.839 is it's it's not the most interesting thing is i mean
00:50:58.400 you'll have access to the code base so you can actually go and kind of figure out the details of how it was put together if
00:51:03.920 you want to do something like it the main idea is is just the most important thing let's
00:51:11.599 see is this line here we're calling the index
00:51:16.640 and then we're grabbing the response body and just sticking it in a file can you
00:51:22.559 mention what time cop does if you're borrowing time cop yeah the first time i wrote this
00:51:28.640 the tests failed every minute even even if i hadn't changed anything because all of
00:51:35.200 the all of the to do's are created at and completed at certain times and the tag cloud
00:51:41.440 is for the last 90 days so time changes and then there are time stamps in the page in the footer of
00:51:47.520 right now it's this time but last time you actually did something was that time so it was a mess and time cop
00:51:55.280 is a gem that lets you freeze time it lets you say that well i'm going to assume that it's 2013
00:52:03.040 january 2nd at 3 4 minutes past 3 and 5 seconds or
00:52:08.800 whatever just some random time and i just kind of picked it randomly usually i use pi or e but this time i needed
00:52:16.800 something just kind of sequential and pretty it really doesn't matter what you use so then any calls to ruby's
00:52:24.079 time libraries will all return that time even as time passes time time is now
00:52:30.240 frozen time now minutes ago like everything is relative to assuming that right now it is january
00:52:38.319 2nd 2013 at a certain point in time and this time cop thing allows you to in your test
00:52:46.400 say that okay right now it's this time and now i'm going to travel two months to the future and check that whatever how needs to
00:52:53.440 happen happens but we're not doing that in this one we're just saying it's this time we're rendering the page and every time i render the page i want it
00:53:00.000 to be january 2nd 2013. generally anytime you're programming and
00:53:05.599 you're working with time or dates your life's about to get painful
00:53:10.960 and time cod makes it slightly slightly less painful by being able to yeah manipulate time to
00:53:17.440 stop it to increment it so you want to move it an hour right what's your first thought like well i'll just
00:53:22.880 sleep for an hour and then i'll run it on the ci server or whatever but no time cap will allow you to say
00:53:28.480 like it's this time okay now one millisecond later change it to one hour from now et cetera and write your
00:53:33.680 test yeah there's still a bug in this code so
00:53:39.599 that at five in the afternoon on wednesdays it starts failing again and i just have to re-render a new lock file
00:53:45.520 i haven't i have no idea what's going on um what at five pm
00:53:53.440 exactly in denver when i'm in denver at 5 p.m on wednesdays it starts failing again
00:54:00.960 so what i do is i i just if i'm programming at that time it's like oh it's five minutes to five i just
00:54:07.200 take a coffee break i wait and i don't change anything and i re-render a new
00:54:12.319 lock file and then i can keep going just i'm assuming it's good again
00:54:18.800 yeah so refactoring is dirty but kind of fun
00:54:25.440 so this is what the test looks like um and this is our safety net and we're not going to ever have to
00:54:31.359 actually look at how that test is implemented a few times we're going to get
00:54:37.599 white space differences in the html output and we're just going to say yeah white
00:54:43.440 space html source that's fine i'm just going to copy over the new source um so that's why i didn't just so the
00:54:50.480 again the first version of this test i just took the whole output of the html
00:54:55.839 and stuck it in a variable in the test it made my test file like a thousand lines long it's fine um but then once i started getting white
00:55:03.200 space changes i couldn't figure out what the change was like i had a string that was
00:55:09.040 i don't know how many you know thousands of characters long um and i couldn't do a good diff on that
00:55:14.319 string so i piped it into a file so that i would get new lines and then i could get actual output and i would see that oh i just
00:55:20.400 have an extra an extra white line empty line or this div has been
00:55:25.440 moved over to the you know by a few tabs um and then it's okay then you can just say well
00:55:31.599 it's it's it's the same enough it's good enough i approve
00:55:37.359 so after getting all of the test coverage in place the next step is going and looking at
00:55:44.720 um isolating the code itself so we have this controller action
00:55:51.760 let me just change my
00:56:06.720 all right controller stuff trina likes to use a prehistoric
00:56:11.760 development environment they known as the vim they tell me some
00:56:17.119 they tell me sublime text is very nice i might consider it she also for further
00:56:23.680 entertainment uh does not use a qwerty keyboard in fact your keyboard is in norwegian
00:56:29.359 right yes but then you type the vorak so like you cannot any key that it says on
00:56:34.640 her keyboard if you uh i'm like oh never mind like you will never get the key that it says
00:56:40.559 on there we call it security through obscurity
00:56:47.440 all right so we have a tag cloud we have two tag clouds it's a 50 line method
00:56:53.200 it does a bunch of stuff that's all we know it does stuff and we want all that stuff to be
00:56:58.720 isolated so that we can start digging into it and moving things around
00:57:03.760 and start understanding it because honestly when you're looking at 50 lines of code
00:57:08.880 and it's just doing stuff it's like i don't know what it does i mean now i do because i've refactored
00:57:14.319 it i don't know how many times but i mean the first time i saw it i was like i just i just want to get this somewhere where i can think about it
00:57:21.040 in a more controlled manner and that's what this little ruby class
00:57:27.680 is all about i um at model tag cloud
00:57:33.440 cloud create i just created an empty model it's an empty ruby class
00:57:40.640 it's exactly the same ruby empty ruby class that you would create if you're creating a
00:57:45.920 teenager named bob who has a very limited set of
00:57:50.960 answers or if you're i mean it's it's it's just it's nothing it's just an isolated
00:57:56.160 environment that you can put code into and you'll know that it's not interacting like uh jeff was saying that in the
00:58:03.119 controller it creates a view context but you'll never actually see it say view context
00:58:08.720 and it copies over all of its instance variables into the view content it's scary stuff like there's so much
00:58:16.079 going on there that you just don't know is happening you can't see it happen you're not sure when it happens if it
00:58:22.960 happened so by putting this all of this code into a class that's isolated you've
00:58:28.960 quarantined all of the all of the possible nastiness and you know that whatever happens in here is the
00:58:35.839 only thing happening there's no magic and it's passing you almost never say to
00:58:41.440 yourself i wish i didn't have all these classes that just are doing one thing
00:58:47.760 i wish i could really mash these together into something more awesome there's a there's a great quote i think
00:58:53.200 it started from a dhh tweet um a while back it said something like half the ruby half the rails community
00:59:01.040 is afraid it's gonna turn it okay let me think half the rails community is afraid
00:59:07.280 rails is turning into java and the other half is trying to turn it into java
00:59:13.599 and so i i do feel a little bit bad sometimes that the answer is always like put a class on it
00:59:19.440 but it usually is the answer like just make a class that's what classes are for is to
00:59:25.200 contain a responsibility and so here if you have this concept if there's something you can name
00:59:31.200 and give it a noun tag cloud it's a thing make it should be a class methods
00:59:38.319 should be verbs right so tag cloud has a build method or compute method
00:59:44.559 if you have a method like the names if you're writing ruby style methods you tend to
00:59:51.280 betray the missing domain objects when you name methods when you name a method like oh i'm making a
00:59:57.200 method named compute tag cloud it's like hey bro you got a noun in there
01:00:02.240 that's a thing like it shouldn't be compute tag cloud it should be tag cloud.compute that's how object-oriented software
01:00:08.400 works sorry yeah
01:00:17.200 yeah don't do that don't do that i don't i don't think it provides you any benefit you know
01:00:24.799 the question was sometimes you see people embed classes within other classes which yeah you can do it's it's cute
01:00:32.640 that's about the only advantage it has yeah often it's just a place so that you
01:00:38.000 can put some of the code you're working on close by so you have it at hand and you don't have to be jumping all over the
01:00:43.599 place and then later when you figured out what that thing is sometimes you just don't know what it is yet you figure out what it is and then you
01:00:49.520 can move it out and sometimes it's like well i want this thing segregated into its own thing but it's not really a
01:00:55.119 stable object in my system yet i don't know where i'm going with it i don't want people to think it's some
01:01:00.880 stable interface um in my application so i'm just going to leave it here so people kind of are wary of of using it from the
01:01:08.880 outside i mean they can obviously use anything they like that's kind of the beauty and and terrifying
01:01:16.960 freedom of ruby so we create a compute method and i'm
01:01:22.160 calling it compute because at this point we don't really know what
01:01:27.200 all of it does it's computations um it's a computer that's probably what it
01:01:32.319 does exactly so we have 50 lines of duplicated code
01:01:38.079 and this is my favorite favorite refactoring technique is duplication i mean they say don't
01:01:44.240 duplicate that's when you're writing fresh code don't duplicate when you are refactoring duplication is a lifesaver
01:01:50.960 because you can put all of the new code in the new place have all of the tests
01:01:56.160 running against the old code everything is working it's still working you know the application is working if
01:02:02.240 you get you know a level five priority one whatever uh bug in your lap you can just
01:02:10.079 check this code in it's okay nobody's calling it yet you can go and fix your code all of your tests are running
01:02:15.440 and you can get back to it later so duplication is awesome um for a while you have to then actually
01:02:23.200 work on getting getting rig rid of it but that's a systematic and step-by-step process that that is um not complicated it's tedious
01:02:31.119 you know it's it's a lot of doing the same thing over and over again um but it's it's a process that is is
01:02:37.760 fairly you know well understood and and your tests will be passing the whole time so you'll be fine and to clarify you
01:02:45.440 copied the paste the code from its original home yes haven't changed anything yet right so
01:02:51.440 here this is the original oh i changed a little bit of it but that's because we got distracted and i talked i typed a lot and then i forgot
01:02:58.640 to talk and stuff happened so yes
01:03:11.920 probably sure there's a gem for that i'm sure there's a gem for that i just go hard just delete it you know
01:03:19.039 it's deprecated when it stops working i i try to remove the duplication within
01:03:27.280 like a few hours just so that it's not in production a long time duplicated because
01:03:32.400 it once it's in production it's going to stay there forever and ever so you have all of this
01:03:39.839 duplicated code and you're not calling it from anywhere and the first thing i usually do is just
01:03:45.680 call it i just just to see if it actually doesn't blow up because blowing
01:03:53.200 up is like the usually the biggest problem if we had deleted everything in here and now we
01:03:58.400 were going to try to get all of the tes or the whole test running we have 30
01:04:03.839 i don't know how many maybe 10 instance variables that are being calculated we don't know
01:04:09.359 how a bunch of calculations the two really big sql queries like there's a lot of stuff going on so
01:04:16.960 it's very unlikely that especially on a first pass you would figure out like what are all the parameters that this method is going to need
01:04:23.119 what are the different uh pieces of data it needs to do its operation our friend ben ornstein gave a
01:04:30.799 refactoring talk a couple weeks ago and i really liked he changed my thinking a little bit on the process as far as
01:04:37.599 how little time can you spend with your test read like my inclination when i go into
01:04:44.000 a refactoring like this is to comment everything out and then start rewriting it
01:04:49.440 and what that means is that i'm probably gonna spend an hour maybe two hours where the tests do not
01:04:55.039 pass and instead what katrina is doing here is by line 555 there
01:05:01.200 calling the new method and then essentially throwing its results away right she is making it so that it is
01:05:08.880 at any time to get back to all green she could just comment out that call or comment out the
01:05:15.039 other method itself and so she's always close to green so run this
01:05:20.640 and then you see you know it's most likely going to have a uninitialized uh local variable or method within that
01:05:27.760 method whatever whatever data it's trying to access that we're not passing in yet so the first time we ran this we were
01:05:35.039 just calling compute and it said well i don't know about this thing called current user that you're
01:05:40.480 using um so we just gave it to it we just give it current user and that's why when we had to create
01:05:48.720 like that create this initialize method which is not an initializer apparently um an initialize method that
01:05:55.920 takes the current user and then give it an add a reader um and we
01:06:02.079 that's all so we just gave it we gave it the current user
01:06:07.200 uh and at that point we were still not using anything but we were calling it
01:06:12.880 correctly and it wasn't blowing up and that's a pretty good step like that's a good step in the right direction surprising surprising progress
01:06:19.359 i would say yeah so the second thing is to actually then save the object call compute on it
01:06:27.280 and start using it so and the way again what i was doing is take the first
01:06:34.799 i should back out of this i'm not sure um take the first find the first instance variable that
01:06:41.039 actually gets assigned and then assign a new one with the exact same name underneath it that uses the new code
01:06:46.880 and again this is as close as i can get to being like a second away from green
01:06:53.839 like if the old code is blowing up i can back it out in a second like i can be back in control in a very short
01:07:01.039 amount of time and all i really had to do in order to have the tags for cloud method in the new
01:07:09.200 code was to expose the instance variable because compute if you look at compute it assigns all of
01:07:15.680 the same instance variables that we had in the controller so after you've called compute
01:07:21.359 all of those values have been saved into the sort of the internal state of the object
01:07:27.440 and we can just expose it with an adder reader and then you're you're back to just kind
01:07:34.880 of figuring out one instance variable at a time and doing it again using duplication we're
01:07:40.880 going to duplicate the assignment of the instance variable we're going to expose that little piece of data inside of the class
01:07:47.119 and then we're going to use that data run the tests without having deleted all of the code deleting
01:07:52.240 code is sort of a it's a it's really it always feels really nice but every once in a while you're so eager to delete code you delete too much and you
01:07:59.359 can't remember what you deleted and you have to throw away you know 12 minutes of work to go back to your previous commit and
01:08:06.240 start over again and sometimes this is a really good thing because you just learned a bunch of things about your code but sometimes
01:08:11.680 it's nice to just be able to be one line away from green so we did this for the first three
01:08:18.719 variables instance variables just exposing that piece of data in the
01:08:25.359 class using them duplic duplicating that assignment figuring out that it's still working deleting the old assignment figuring out
01:08:31.839 that it's still working and then moving on so it's these tiny tiny steps and um
01:08:38.400 if it goes on for too long you you feel like you need more excitement because it really is boring it just works
01:08:44.239 it's just one thing at a time and it keeps working um every once in a while it throws a wrench in there for you
01:08:49.759 to keep you on your toes so that's kind of the next thing is to say okay that the next
01:08:55.759 instance variable that we have is this tags for cloud
01:09:02.239 90 days and we're going to call it on the cloud again tags for cloud 90 days we're going to
01:09:07.839 call the same thing we're not deleting any code yet and we haven't actually exposed it
01:09:16.000 we have one called tags for cloud tagsman tags divisor but we're missing
01:09:21.920 tags for cloud 90 days oops for cloud 90 days
01:09:30.719 and what we're starting to see is that there's duplication here we have one method doing two tag clouds
01:09:37.600 and they're doing it the same way over like both times um
01:09:45.040 so if we just run this kind of clarify what you're saying are you mean that one
01:09:52.000 cloud is both like a 90-day cloud and some other range of time is that your target yeah so there are
01:09:57.120 two clouds this this one controller method is doing all of the calculations for two clouds in the view
01:10:03.920 one is for all the tags for all your to-do's for all of history for all time and the
01:10:10.239 other one takes all the tags for the last 90 days for for the stuff you've the new new things you've added or the
01:10:16.080 things you've completed i don't know if it's added or completed sure so once you start to have this like
01:10:21.440 domain object of a cloud then conceptually it's not a large leap to say like all right well
01:10:27.920 i need to have two clouds i might need to have two clients one class and then you start to think like well in
01:10:33.360 this initializer i'm probably going to pass in some range of time and then just deal with
01:10:38.880 two instances of the same class rather than kind of having all this logic uh shared in one's place
01:10:45.520 and if we try to go there directly we're gonna have four hours of pain uh and we're not gonna get very far so
01:10:52.320 we're not gonna actually just jump to the two class we're just kind of noticing a little bit of duplication
01:10:57.520 it's like oh this looks familiar i might i might get back to this in a little while meanwhile i'm just going to kind of slog
01:11:04.560 through getting my instance variables assigned so run the test
01:11:18.080 and it's failing undefined method each for nil class this
01:11:25.440 one threw me for a loop for a while we're calling each
01:11:35.120 on this 90 days tags for cloud 90 days we've exposed it tags four cloud 90 days
01:11:45.120 here why do we not have
01:11:50.960 why do we not have value turns out and i'm not going to put you through the pain of actually debugging that turns out this cutoff three months is an
01:11:58.080 instance variable that is not defined inside of this class
01:12:03.280 so we're missing and instance variables are like the first problem we had was we don't
01:12:08.320 have a local variable or method current user that is such a good error message because we
01:12:14.239 know where to find a current user we go to the controller we have a current user in the controller the current
01:12:20.400 you know the cut off three months it's an instance variable that's not a sign so we have this nil floating around in
01:12:25.440 the system it's nowhere in the stack trace to be seen the fact that this this thing is missing
01:12:32.560 so it was um it's one of those things that drives you crazy for a little while and then you'll see it you'll go where
01:12:40.640 is it to expound on that just a little bit like if it was cut off three months as a local variable or as a method call
01:12:46.960 you would get an error of undefined local variable or method cut off three months but in ruby if you
01:12:53.199 use an instance variable and you've never set a value to it you will not get that error you will get
01:12:58.239 back nil and at some point we're trying to call each on nil
01:13:04.640 and we don't know why yes
01:13:10.640 the question is do i have a strategy for dissecting it my strategy is flail
01:13:17.840 you know it's i i kind of i guess kind of i kind of do i kind of
01:13:24.080 go to the last known point that i know where i know what's happening
01:13:29.520 and i look around near it and see if there's something that catches my eye and it's a really like
01:13:36.320 not a deterministic process i think it's a place where i especially after i spent if i spent five
01:13:42.320 minutes on it trying to figure out what was going on it's where i would bring in a little bit of tooling like a pry
01:13:47.760 or ruby's debugger or if you use an ide maybe use its debugger and try and walk
01:13:53.120 through execution a little bit because it's a situation where it's not that there's an error and you can't fix it it's like you
01:13:59.679 have no idea what's happening the chances of just stumbling on the fix are relatively small
01:14:05.600 but this idea of going to the last known point and then looking at each part of the code and saying well i know what the
01:14:10.880 value is here i know where this comes from i have no idea where this comes from i wonder where that is and then you kind of trace your way
01:14:18.239 back it usually now if it's not too far away from where where you are that's a good strategy
01:14:24.880 sometimes it's not and i'm afraid i don't have a good there's a there's a peep code that came out two
01:14:30.159 days ago about uh about debugging or troubleshooting um it's next on my list because that's
01:14:38.719 that's an area that's it's really complex but there are definitely strategies that you can use
01:14:44.080 um to be more efficient
01:14:52.480 so so the question was are there any strategies for avoiding that type of error
01:14:57.840 yeah don't reference your instance variables don't use instance variables like if you have them use local
01:15:04.080 variables to expose them and just go through the through the variables local variables or the methods methods
01:15:10.080 yeah so i would say in general when i'm writing a class i'll try and only use instance variables
01:15:15.840 in initialize and outside of that i would i would use them with great hesitation
01:15:21.199 that just being one reason i also just think they look stupid and they're it's easier if you're so the
01:15:28.640 alternative here is if you have an instance method instance variable is to write an accessor method
01:15:34.159 using adder accessor or just writing yourself def the name of the thing and then just
01:15:39.440 return the instance variable and then in your usage if it didn't exist you would get a good error message that
01:15:45.679 points you exactly to where you need to be yeah so yeah
01:15:51.199 sure so one of the things here
01:15:57.199 if i say cut off i guess three months and that's if i can
01:16:04.320 type eventually cut off three months so we just had a nil
01:16:11.440 problem cut off now i'm going to use this local variable
01:16:16.800 called cut off three months um i'm gonna see
01:16:21.840 this might this might not be the best uh
01:16:26.960 this might not do what i'm hoping but i'm gonna try
01:16:34.080 i think it'll i think it should still fail the same way it might
01:16:41.120 all right tags for undefined method tags for cloud 90 days well that's interesting uh
01:16:49.120 i guess i didn't expose my tags for cloud 90 days
01:16:57.840 i think the even if even if it gets the same error uh the process of pulling that instance
01:17:04.159 variable out to the tiny little method starts you thinking about like what's in
01:17:09.199 here like when did this get set up as opposed to the original big chunk of code there are instance variables littered
01:17:15.280 all over and it's kind of like yeah this came from somewhere some point in the past
01:17:21.840 i don't think it'll give us you know like a magical better error message yeah because it's
01:17:27.920 still going to return that nil yeah it's getting nil and yeah but the
01:17:33.120 process of pulling it out like helps you notice that it's a thing that it's a that it's a concept in this object yeah
01:17:39.520 and oh we need to set that some some other place
01:17:53.280 yeah so the the comment was that some like it might be nice to have an outer accessor that actually explodes if the
01:17:59.440 value inside of it is nil then again sometimes you want it to be nil and sometimes it's just a lot of work
01:18:05.120 um so i i finally figured out that it was this
01:18:12.560 cut off three months and i needed to figure out where it was
01:18:18.000 coming from now i'm assuming it's coming from the controller because that's where all
01:18:23.280 the state was that we um that we moved into this
01:18:28.320 class but it could have been for example in the app helpers directory like in this project there are
01:18:34.080 helpers that are view helpers that are using and setting and changing instance variables so it
01:18:40.159 could i mean there there are a lot of places it could be now it turns out that it is in the stats
01:18:45.840 controller um app controller stats controller
01:18:57.360 in a helper method called init deaf init
01:19:03.600 this is a really interesting discovery because we have a before filter on this controller that
01:19:10.080 initializes a whole chunk of instance variables that
01:19:15.760 most some maybe of the actions in the controller actually need to do their job so this is kind of a
01:19:23.360 thing that you don't see very often it's not considered a good practice
01:19:28.960 it was a surprise that i kind of discovered as i was as i was extracting this
01:19:35.360 i've also never seen append before filter as opposed to just before filter right but it has to happen before
01:19:41.120 everything because it's doing stuff and it's like pre-pen i don't know but i think it's also like a lot of what
01:19:48.640 happens in those before filters it's very common to yeah set one or more
01:19:54.560 instance variables in the before filter and then when the controller method is happening it has
01:20:00.400 that shared state and so it can access those but that's that's really pretty ugly right like you're what you're doing
01:20:06.639 is creating uh kind of a global state within your controller and it's just like oh this little thing is making his
01:20:12.560 data and this other thing is making her data and they just yeah share all their data right and you lead to these things of
01:20:18.560 like wait where is this instance method where is this instance variable coming from and and that's part of
01:20:24.080 like that's part of why i said earlier on i i hate the way that we get data from
01:20:29.840 controllers to view templates because it leads you down the road of like just instance variable everything
01:20:35.920 it's cool then you'll be able to find it later if you grip for it right you start
01:20:42.000 searching through your files one of the really interesting parts of this in it is this at me equal self comment for meta programming
01:20:49.120 i couldn't find a reference to at me anywhere in the project but
01:20:54.239 they've mentioned meta programming in the comment it might be used and we wouldn't know
01:21:00.880 like i'm not i don't know i don't know i don't i haven't dared touch that yet that's like this is just the
01:21:06.320 terrifying line right it's it's really scary um but also kind of for meta programming
01:21:12.840 yeah but what if it's not tested like the tag
01:21:19.199 cloud wasn't tested we could delete all the tag cloud stuff and it wouldn't like i don't know that i trust
01:21:25.600 the tests well enough i don't trust the same person who wrote this wrote the test right so it's like
01:21:33.120 a little skeptical so we have a lot of interesting stuff going on in this in it we have chart widths and heights we have these
01:21:39.440 primitives these little numbers magic numbers floating around in here that get used for a bunch of different
01:21:44.639 things we have jeff was saying that time whenever you're starting to use
01:21:49.679 time or date or anything your life is about to get worse here we have time zone now
01:21:56.480 utc yeah if you want to make time and dates worse start talking about time zones yeah um
01:22:02.719 and i maybe that bug i was talking about five o'clock on wednesday afternoons the test fails um it might be somewhere
01:22:10.639 here i wasn't able to figure it out um
01:22:16.320 yeah yeah so i yeah right
01:22:22.960 so i started digging down this road and i started seeing if i could force utc in the test
01:22:30.320 fixtures to not i lost an hour of my life um that i will never get back all right
01:22:36.320 so here cut off year cut off year plus three cut off month cut off three months we've
01:22:41.600 got this one cut off three months this is where it's defined and all we really need is to um use it
01:22:49.360 when we actually instantiate the tag cloud we want to say cut off
01:22:57.120 three months and we want to use it in here so you're still going to run your init
01:23:03.679 you're just passing the data that's been calculated down into the cloud object
01:23:10.000 cloud instance yeah so i'm just gonna i'm just gonna grab the data i need keep keep running with it we'll see where it
01:23:15.440 goes yeah please
01:23:26.480 i do i do it later once i've got everything using the code and working i often go back and say okay now i've
01:23:33.120 copied these variables in is it only used here then i can delete it or maybe it it should be passed in but
01:23:39.199 in a slightly different way but once uh so the first time i refactored the entire index method all 200 lines of
01:23:45.360 it i had 35 36 instance variables i think there were five of those that weren't actually
01:23:50.800 used in the views so they could be made local variables so you can start restricting the scope of things deleting
01:23:57.040 the ones that turn out to just be cruft from some earlier version of the code it's really the funnest part of a
01:24:03.840 refactoring is getting rid of all of those little bits and pieces that are left it's really nice
01:24:14.840 sorry yeah it's not a particular time it's usually when i feel like i'm done with
01:24:21.120 one piece like here i'm still trying to get all the all the instance variables um in and if i start going down that
01:24:27.440 road five hours will pass and i will have forgotten why i'm here um so i i i squirrel you know i tend to get
01:24:34.960 distracted really quickly so i try to stay on on on task um and then when i um
01:24:41.120 when i'm done with a particular piece i'll go back and delete things and rename methods and
01:24:46.480 whatever now i have a failure again i have no idea it's the same one i must be naming something wrong
01:24:55.520 cloud i deleted my compute call there you go that'll do it so i wasn't
01:25:02.960 calling compute so all of the instance variables that i was assigning were all nil because i had exposed them with adder
01:25:09.360 readers but they hadn't ever been assigned and now we're back to a green test
01:25:16.480 and this means that we can get rid of all of that all of that
01:25:25.440 and run the test again to see if we deleted too much or not
01:25:33.520 and we're good and so last time we tried to just assign and delete tags min
01:25:41.920 90 days
01:25:47.679 last time we tried to delete it that went badly because we actually needed the max local variable
01:25:52.840 tagsman 90 days
01:25:59.920 i'm going to just check that the value that i'm getting from the little class is the same same value as the one i need and it is
01:26:08.639 i'm going to go ahead and duplicate the next one is as well tags divisor 90 days cloud
01:26:17.520 tags advisor 90 days
01:26:31.920 and it's breaking because i haven't exposed it
01:26:47.600 and i'm now at the very bottom of my controller method i've exposed all of the instance
01:26:53.840 variables that i need in order to assign the instance variables that the controller uses to pass to the view
01:26:59.520 so the partial can show this tag cloud so i can start deleting all of the old
01:27:05.760 stuff here and just kind of tighten this up a little bit i can delete
01:27:13.280 so i don't need the comment this levels thing i have no idea where it's used but
01:27:18.400 certainly isn't here i'll delete the comment we still have that in the in the class
01:27:24.000 so now we have you know a few lines about 10 lines 11 lines of
01:27:29.760 code um and it's not doing anything interesting which is good in a controller a
01:27:36.320 controller that doesn't do anything interesting is awesome like it's it's it shouldn't have to
01:27:41.520 think about things it shouldn't have to know about things it just needs to know who to call and what to give it
01:27:47.920 that's like that's what a controller should know it should also know what to assign back from it and so this is still
01:27:53.440 doing a lot but it's a lot better
01:27:58.800 so this is where i commit because i'm gonna i'm probably gonna be making mistakes
01:28:04.639 every few minutes and it's so nice to have a point where you can just oh reset to wherever i was this is not working out i don't know
01:28:10.800 what i'm doing anymore just reset it to where i last knew that everything is sort of one one
01:28:17.040 thing so i need that controller i need the app model
01:28:23.040 stats thing i'm just going to say that i extracted
01:28:31.679 extract tag cloud model
01:28:39.120 and now i kind of want to go look at that model like it's still this is not pretty code
01:28:47.040 we just kind of dumped all of everything from the controller into here
01:28:54.320 but we haven't we haven't i mean there's there's no improvement in
01:29:00.960 this code yet i still don't know what any of it does really um but before i do that and now we're
01:29:08.960 going back to what but you said before i do do that i want to kind of pick better names for things because now
01:29:15.840 i'm saying tag cloud dot tags for cloud like there's a lot of redundancy in that
01:29:21.440 tag cloud tags divisor we know we're in a cloud we know we're dealing with tags so we
01:29:27.040 could say tag cloud tags and tag cloud tags
01:29:34.080 90 days that's definitely an improvement um in in my view
01:29:43.520 so tags not for cloud now i can't change the instance variable
01:29:49.199 because the view is still going to be using all of those same instance variable names and i don't want to be touching the view yet
01:29:55.360 i've got enough to do up here but tags definitely
01:30:03.760 a better name again tags min we don't care that it's the tags the
01:30:08.880 tags prefix is kind of already taken care of by the fact that our object is called a tag cloud so we can
01:30:14.719 take off the tags prefix of pretty much uh tags actually tags prefix
01:30:23.120 tags divisor tags 90 days we want that one tags min
01:30:28.880 we don't care about the tags min 90 days tags divisor 90 days so take off the tag prefix because it's
01:30:35.280 all redundant it's all superfluous um min divisor
01:30:43.199 and this is one of those things where it's like there's no good rule
01:30:48.880 for how to name things there are books written did i miss one
01:30:55.840 tags oh thanks tags thank you so there's no good rule for
01:31:01.600 how to name things but i try to read it out loud and say it's a it's a cloud and i want all the tags for
01:31:08.639 it tag cloud.tags that works for me if something has a lot of syllable or a lot of like
01:31:14.639 words in a variable has a lot of words in it i try to take out each of the words and see if it makes sense
01:31:20.960 still like is this part is this prefix or is this suffix actually providing a meaningful making a
01:31:27.760 meaningful distinction and often it's not it's like in input number well
01:31:34.080 yeah it came in with the method argument thing it's an input do we care
01:31:40.719 does the method care that it was an input it could have gotten gotten the number from anywhere and as long as it's a
01:31:45.760 number if it's doing some calculations with a number it doesn't care in that context where it came from so
01:31:52.080 that's sort of one of the one of the things that i use the heuristics that i use is to just check check if each of the prefixes
01:31:58.639 or or the suffixes are making a meaningful distinction
01:32:05.280 uh another thing is that this comment seems to be i don't know if you can see
01:32:10.719 it very well here this comment seems to be relevant for the whole tag cloud so i want to take that and just kind of
01:32:16.159 put it as a comment for the whole class like all of this
01:32:21.199 logic came out of this blog post from 2006 and um
01:32:28.159 who knows if it's still there but it's relevant to the whole class
01:32:33.440 uh i'm not going to expose the cut off three months i i do want to change the name of the
01:32:39.600 current user like in the controller it makes sense that we have a current user it's whoever's logged in in the model
01:32:46.080 the fact that this is someone who's logged in in a web application doesn't really make sense it doesn't have
01:32:52.000 any relevance to a model you might be in the console just kind of checking a tag cloud for some user or you might be
01:32:57.360 sending out a bunch of emails to all of the users for their here's your tag cloud for your
01:33:04.000 productivity this last month and it's not relevant who is the current user it's just a user so i'm going to change that from current
01:33:12.159 user to just user and run the test because now i've made a
01:33:18.960 bunch of changes and that's always a little bit scary
01:33:25.679 so i've done a little bit of renaming and i broke it it's awesome this happens all the time
01:33:32.960 size for nail class nil is like my bane
01:33:42.159 all right this is how i do this hit reset hard
01:33:51.440 model tag cloud all right so i just threw away my changes because it's not so painful
01:33:57.840 i've only i mean i've mostly been talking but i've only done like i don't know 90 seconds worth of
01:34:04.159 renaming things so it's okay to just throw it away i'm green again and i can go again so let's try this
01:34:09.920 current user change rename that and see if this still works
01:34:21.760 and i didn't make a lot of changes like and it still broke and i have no idea what i did oh you know
01:34:27.520 still passed uh it still passed but when i when i was renaming reading renaming variables i changed all
01:34:34.560 the tags for cloud geez
01:34:40.400 tags for cloud should to be just tags
01:34:47.360 at one time and i forgot and i'm doing it wrong
01:34:53.520 again i actually changed the names
01:34:58.719 sorry talking and talking and typing not a good combination for me um
01:35:05.199 see if we're greening again so i changed the names of the adder accessors without changing the
01:35:10.960 names of the instance variables so i broke all my code and because i didn't run my tests before then going on
01:35:17.360 to change the current username i was like oh yeah i'm good no i'm not
01:35:22.400 this is all breaking and i don't know why i can't couldn't remember 30 seconds in the past
01:35:28.800 to know what i actually did so smaller steps um and like smaller step by smaller
01:35:34.960 steps i mean like one instance variable at a time it's it's really surprising how small the
01:35:42.400 steps really can be all right so tags
01:35:49.520 for cloud should just be called tags we're going to do one of those
01:35:56.400 one of those
01:36:01.520 and run the test again of course now it's going to break because we haven't changed the controller but it's going to be a lot
01:36:08.239 easier to see the oh we're calling a method that doesn't exist anymore that's an easy that's an easy thing to fix
01:36:14.480 we don't have a tags for cloud on the tag cloud anymore
01:36:20.480 so now tags for cloud we don't want to change
01:36:28.480 the instance variables but we do want to change the method calls or the message sends
01:36:35.920 to the tag cloud
01:36:44.639 we did it right this time i did it right this time finally you people didn't do anything
01:36:49.840 you didn't do anything but you should be typing catching my typos
01:36:55.520 um all right tags min let's go ahead and just call it min
01:37:00.800 and tags min there as well and then tags men
01:37:09.199 just
01:37:18.320 and we're starting to get close to lunchtime and uh i hope you can appreciate that if
01:37:24.639 katrina had planned to live code for an hour and a half she probably would have practiced for like a hundred hours
01:37:30.320 but instead i was like hey can you do this right now while everyone's standing here watching
01:37:43.280 i think a lot of the time refactoring especially if you're newer to ruby the idea of
01:37:50.000 refactoring is like i'm gonna take this eight line thing and then i'm gonna use a ternary and it's gonna be a one line thing
01:37:56.239 and then everyone will know how smart i am and most of the time it's not glamorous you
01:38:02.480 know it is this long ugly process of like renaming eight variables and then finding the place
01:38:08.320 where you use those eight variables and renaming that two um you know i don't i was kind of
01:38:13.920 thinking like are we gonna hit the one big like you know and the answer is no um the the
01:38:19.520 great thing that happened here was creating the cloud class and the more we can push
01:38:27.119 down into that class the simpler things are going to get the simpler the controller is going to get
01:38:33.760 the simpler the testing around it is going to be because now it's just the ruby class it takes in
01:38:39.520 some data and it does stuff with that data and spits it out so now our ability to test
01:38:44.719 it we can unit test it for instance instead of otherwise the only way
01:38:49.920 there used to be to test this code was to write full integration tests that were testing from
01:38:54.960 the html layer now that it's a ruby object we can toy with it on its own we can
01:39:00.560 follow uh gary and corey's school of thought and write these tests that don't even load rails
01:39:06.880 right if it's just a ruby object we don't need rails to run to test that and we can not pay the 20 second penalty of
01:39:14.880 loading rails we could write a test suite for it that loads finishes in under a second
01:39:22.880 i think we should probably stop about here let people get to lunch the i hope uh
01:39:29.600 katrina did spend a ton of time putting together the tutorial itself so when you have this thing called internet
01:39:35.920 again um i would love for you to give it a shot try and walk through some of these
01:39:41.280 processes on your own i think you probably got through what like two three of the iterations i think i
01:39:47.600 got through me uh three of the iterations there are 11. there are 11.
01:39:52.880 if you have questions please please post issues um in the in the github repo
01:40:00.560 uh i will try to update that tutorial on an ongoing basis to improve it
01:40:06.159 um it's kind of long i think we were at 1700 lines this morning and it's it really is
01:40:12.960 useful to us because it's um this is a tutorial that we'll probably use again at other conferences like uh local comps and so forth
01:40:19.199 where we have more of like a day for refactoring and not an hour and a half aka two hours
01:40:26.639 so thank you very much for coming i want to shout out one last thing is tonight we are downstairs doing a heroku
01:40:34.159 performance workshop it is a little bit i thought it was
01:40:39.280 really cool that last night there was the game night i have this growing feeling that like
01:40:45.360 our conferences need to be less about drinking not that i have any personal objection to drinking but it's like we should have interesting things
01:40:51.280 to do uh so i talked to heroku we came up with this plan together of like let's do a little workshoppy contesty
01:40:58.639 hackathony thing where uh starting at seven promptly at seven tonight so the
01:41:04.320 keynote ends at 6 30. starting at seven downstairs uh 120 people
01:41:11.119 and us we're gonna do three little instructional segments half hour segments
01:41:16.159 about like here is one technique for making apps on heroku run faster and really it has to do with making
01:41:22.639 rails apps faster so if you want to make your blue box apps faster for instance you can do that
01:41:28.159 and so we'll go through three segments they are using threaded forking web servers
01:41:35.520 the second one is about caching specifically like the russian doll
01:41:40.960 caching you have heard so much about and the third is looking at database
01:41:46.880 queries how to optimize those to make those run faster and then after that we have a sample app
01:41:53.040 and you have about two hour work period from like nine to 11 30 to work on that and it has a performance
01:41:59.119 suite that's going to run in like 90 seconds and so try and apply those techniques to make it run faster and get it down to
01:42:05.920 probably 30 seconds which is pretty dramatic change to the end user and then there's some little like
01:42:11.600 achievements and stuff if you make it go fast then you get lots of uh heroku gif i think you can get like
01:42:18.800 400 300 bucks in heroku credits and some other stuff rails cast pro of the
01:42:24.880 grim contributed stuff we're bringing t-shirts there's all kinds of so i think it'll be a fun time to if you're interested in performance come
01:42:31.040 tonight and yes there will be food like dinnery food not just like oh here's a cracker
01:42:36.159 hack for four hours thank you have
01:43:08.840 so
01:43:18.000 you