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