00:00:00.840
uh my name is Annie Kylie and I'm going
00:00:03.040
to talk about the command pattern in
00:00:07.120
Ruby all right uh Happy Halloween if you
00:00:11.400
celebrate it or live somewhere that
00:00:13.040
celebrates it um this is my family last
00:00:16.440
year at Halloween um because went went
00:00:20.560
with the theme today um but my name is
00:00:23.240
Annie uh I work at dep book um we are we
00:00:26.160
make Financial software and it's a big
00:00:28.279
old rails app with uh act too so I'm
00:00:31.560
doing this stuff every day um full
00:00:34.320
disclosure I've been working there
00:00:36.680
for one week and two days um so this is
00:00:40.399
actually not most of my experience um
00:00:42.760
for the last seven years I've been
00:00:45.039
working at viot which is a digital
00:00:46.879
agency um so I did lots and lots of
00:00:50.160
different kinds of projects for lots and
00:00:51.520
lots of different kinds of
00:00:53.800
clients um before that I actually worked
00:00:56.000
at two other agencies so I've been a
00:00:57.600
consultant for almost a decade um this
00:01:00.039
is my first like in-house product job um
00:01:04.119
but the nice thing about Consulting is
00:01:05.680
you get to do a lot of a lot of
00:01:07.119
different things and see what kind of
00:01:09.240
patterns end up being useful in a lot of
00:01:11.200
different places um and so that's why
00:01:14.799
I'm talking about the command pattern
00:01:16.520
today um it's one of my favorites and
00:01:18.280
it's come in useful for me um in a lot
00:01:20.479
of different
00:01:21.720
scenarios um also I love Ruby um I've
00:01:25.040
been I've done work in a bunch of
00:01:26.200
different languages and I I just feel
00:01:29.479
like is the most fun as a developer um
00:01:32.240
so I'm happy to be here talking to you
00:01:34.920
all all right what is the command
00:01:37.920
pattern um it's
00:01:40.399
a technical pattern and if you Google it
00:01:43.799
there's a lot of blog posts and a lot of
00:01:45.520
wik there's a Wikipedia article and a
00:01:47.000
whole bunch of information and I am not
00:01:49.119
going to talk about any of that today uh
00:01:51.159
we only have 15 minutes so I'm just
00:01:52.640
going to talk about it from a kind of
00:01:54.880
practical perspective um the idea here
00:01:57.880
is to introduce you to the concept
00:02:00.520
and if you want to kind of mess around
00:02:01.960
with it um that that's something that
00:02:04.640
hopefully you'll want to do on your own
00:02:06.360
after this so for our purposes today the
00:02:10.280
command pattern is a little bit of magic
00:02:13.080
that turns a method into a
00:02:16.239
class and we'll take a look at what that
00:02:19.000
means in just a
00:02:20.680
second but not just any class a class
00:02:23.599
with a predictable structure um and that
00:02:26.560
part's really important um you know if
00:02:29.360
we all do object-oriented programming
00:02:31.599
and you know you can make a class or a
00:02:33.560
method do basically anything you want
00:02:35.319
especially with Ruby with all its meta
00:02:36.800
programming you can you know the there's
00:02:39.519
infinite uh infinite options here but
00:02:42.280
the idea here is to really provide a
00:02:45.840
reliable structure um that lets you do
00:02:49.120
more um with the things you're trying to
00:02:51.959
do with your
00:02:53.640
app I realize that's all a little
00:02:55.519
abstract that's why I skipped all the
00:02:56.920
details we're just going to dive right
00:02:58.519
into to talking about it then hopefully
00:03:00.159
have some time for
00:03:01.480
questions all right here we
00:03:05.599
go um this is just a little bit of Ruby
00:03:08.000
code and we don't need to go in into it
00:03:12.000
too far but all we're doing is creating
00:03:13.720
an object called a house and a house has
00:03:16.200
a certain amount of candy for Halloween
00:03:19.599
um and then I'm just instantiating uh an
00:03:21.840
instance of a house so my neighbor's
00:03:23.840
house has 500 pieces of
00:03:26.040
candy um similarly we're just making a
00:03:28.799
class called kid kids also have a
00:03:30.879
certain amount of candy on Halloween and
00:03:32.879
we're going to talk about a kid called
00:03:34.439
Adam and he has zero candies to start so
00:03:37.760
all you really need to know is that this
00:03:40.280
this house instance exists Neighbor
00:03:42.200
House they' got 500 candy and Adam
00:03:44.959
exists and he has no
00:03:46.720
candy and down at the bottom we're just
00:03:49.080
kind of printing it so we can see what's
00:03:50.360
going to happen and when we run this
00:03:53.640
code it'll print
00:03:55.959
this uh my neighbor has 500 treats left
00:03:59.360
and has zero pieces of
00:04:02.040
candy uh let's add a method um so all of
00:04:06.519
this was just sort of set up let's add a
00:04:08.360
method so this is the same code we saw
00:04:10.560
before except down kind of in the middle
00:04:12.439
you've got a trick-or treat method where
00:04:14.840
a kid goes to a house and the house
00:04:17.959
loses a piece of candy and the kid gains
00:04:19.959
a piece of candy and then I'm actually
00:04:22.079
running the so I'm just defining the
00:04:23.479
method first and then I'm actually
00:04:24.600
running it and then again I'm just
00:04:26.000
printing out the results so if I run
00:04:29.360
this file
00:04:32.400
now because I ran the trick-or treat
00:04:35.759
method The Neighbor House lost a piece
00:04:39.240
of candy and Adam gained a piece of
00:04:40.560
candy
00:04:43.720
great so we're not going to worry about
00:04:45.880
the setup stuff anymore um I'm just
00:04:47.880
going to talk about the method from here
00:04:49.240
on out um so that that that code is
00:04:51.560
still there it's still running but uh
00:04:53.840
it's it's not super important so we're
00:04:55.800
just going to focus on the method and
00:04:58.000
we're going to move it over here so uh
00:05:00.440
some pretty standard Ruby code right
00:05:02.639
here um but let's adjust
00:05:06.080
it again
00:05:08.199
remember uh this is what we get when we
00:05:10.199
run
00:05:12.080
it let's try this so instead of a method
00:05:15.639
trick-or treat let's make a class called
00:05:17.960
trick-or treat we'll initialize it with
00:05:20.240
a kid in a house and then it has an
00:05:22.600
execute method which does the exact same
00:05:25.240
thing that um that our method did before
00:05:29.560
then if you look down at the bottom I'm
00:05:30.759
just running this code so I
00:05:34.520
instantiate uh Make an instance of this
00:05:36.680
class so trick-or treat. new pass in
00:05:39.199
adom pass in the neighbor house and then
00:05:40.919
run execute when I run this code you
00:05:44.199
will not be
00:05:45.400
surprised again the exact same thing
00:05:48.319
happens so you're probably
00:05:51.160
thinking great we went from you know
00:05:54.360
five lines of code to 10 lines of code
00:05:57.400
and we're doing the exact same thing why
00:05:59.520
why would that be useful and that's what
00:06:01.880
we're going to talk
00:06:03.160
about
00:06:05.080
so I'm actually going to make this even
00:06:07.000
more
00:06:07.759
complicated uh I'm going to add a class
00:06:11.000
method down at the bottom here that does
00:06:13.720
the work of instantiating the new one
00:06:15.919
and the reason I like that is if you
00:06:17.240
look all the way down at the bottom here
00:06:19.240
when I'm running the trick-or treat
00:06:20.800
method trick-or treat. run and then I
00:06:23.599
pass in Adam in the neighbor house
00:06:26.160
so this looks a lot like running the
00:06:29.639
method over here on the left but we're
00:06:32.280
doing it with a class and again you're
00:06:34.919
probably thinking great we have five
00:06:37.479
more lines of code why why would anybody
00:06:39.960
do this which is a good question and um
00:06:44.000
I'll show you in a little bit so again
00:06:48.120
more code does the same
00:06:50.680
thing why is this
00:06:53.319
useful let's find out this lets you
00:06:56.919
actually do a few things once it's it
00:06:59.479
starts getting when things start getting
00:07:00.759
more complicated this becomes really
00:07:02.639
powerful it lets you manage the inputs
00:07:06.680
in a much more robust way than you can
00:07:08.759
do with a method on its own it lets you
00:07:11.800
run validations that a usual part of the
00:07:14.599
command pattern is to kind of add in a a
00:07:16.520
validation step so you can actually
00:07:18.560
validate the inputs coming into your
00:07:20.360
method it lets you do the thing which is
00:07:23.120
the same thing your method was doing
00:07:24.759
before so that's the same it lets you
00:07:27.919
manage the output of your method in a
00:07:29.599
very explicit way and it lets you
00:07:32.520
standardize errors um so that I'm sure
00:07:36.759
all sounds great and this has all been
00:07:38.479
very abstract um so let's let's dive in
00:07:42.120
a little bit to how this might actually
00:07:43.400
be useful in your day-to-day life my
00:07:45.800
favorite place to use the command
00:07:47.240
pattern is in rails
00:07:50.000
controllers so let's look at some code
00:07:52.400
that actually hopefully is a little more
00:07:55.159
familiar um for this I'm going to be
00:07:57.080
using the active interaction gem um I
00:07:59.440
highly suggest you check it out the read
00:08:01.599
me of this gem has really really good
00:08:04.280
examples of of how to use this and how
00:08:06.080
to make it useful um way more than I can
00:08:08.280
fit into a 15minute talk so um if you
00:08:10.879
are at all interested in this after I
00:08:12.280
talk about it I highly recommend that
00:08:14.159
you um just take a look at this Gem and
00:08:16.919
just reading the read me will do a
00:08:18.599
better job explaining it than than I can
00:08:21.560
today so using this
00:08:23.960
gem let's talk
00:08:26.639
rails um this is all going to be a
00:08:28.960
little hand wavy because if I did all
00:08:30.520
the details it um it just was like
00:08:33.599
looked like a lot so I'm just saying
00:08:37.039
candy. all here right but if you were
00:08:38.760
doing a traditional application you do
00:08:40.640
you know at Candy equals candy. all and
00:08:42.519
use that in your um or at you know
00:08:45.320
candies equal candy do and use that in
00:08:47.200
your view or maybe if you were running
00:08:49.040
an API you'd render Jason or something
00:08:51.760
um I'm kind of just skipping that but it
00:08:55.480
just for like readability purposes but
00:08:58.240
other than that consider this a pretty
00:09:00.320
typical controller where part of what's
00:09:01.959
happening in the index action is that we
00:09:03.440
need to fetch all the
00:09:05.240
candy so let's see how the command
00:09:08.360
pattern can help us make sense of our
00:09:11.640
controllers all right all right so over
00:09:13.880
here on the right we have uh the
00:09:17.279
same candy
00:09:19.200
controller except instead of straight
00:09:22.120
calling candy doall I'm calling another
00:09:25.200
class called candy index that's going to
00:09:27.079
be our Command um and I'm saying run
00:09:30.320
which is do the thing um and then I've
00:09:33.360
just moved the candy doall into into
00:09:36.399
that class so that class is right below
00:09:38.640
so again you're probably thinking great
00:09:40.399
we went from one class and one method to
00:09:42.680
two classes and two methods and why why
00:09:45.000
would we do this um let's let's take a
00:09:50.440
look before we show you that let me let
00:09:52.800
me say pretty often you start with
00:09:54.640
something like this and somebody says oh
00:09:56.240
shoot we need to be able to filter our
00:09:58.560
candies right maybe by the date they
00:10:01.240
were created right if cand is too old we
00:10:03.240
don't want to we don't want to get it um
00:10:06.079
so now I'm going to change both of these
00:10:08.200
things to manage that change so over
00:10:11.120
here on the left I've updated the the
00:10:12.720
non-command pattern controller um to
00:10:15.480
take in a Pam start date and a Pam's end
00:10:17.519
date and we'll filter based on that over
00:10:21.600
here on the right I've done the exact
00:10:24.200
same thing but with the the command
00:10:27.000
pattern so I'm now passing in pams
00:10:31.240
to to my command candy index and then
00:10:35.200
here's where it starts to get
00:10:36.120
interesting so down at the bottom on the
00:10:38.680
right candy
00:10:40.440
index notice what I have up here it says
00:10:42.959
date time start date date time end date
00:10:46.040
I'm managing my inputs really really
00:10:48.720
clearly here and what active interaction
00:10:50.880
will do for you here is actually a bunch
00:10:52.360
of things so first it's only going to
00:10:55.079
accept those inputs if you try to pass
00:10:57.120
anything else in here it just ignores
00:10:58.680
them you can also make it strict and
00:10:59.959
make it throw an error if you want to do
00:11:01.399
that too but so you're already once
00:11:04.560
you're once you're kind of running this
00:11:05.920
class you're only going to deal with the
00:11:08.320
inputs that you've explicitly said are
00:11:10.120
okay and that's that's really nice so if
00:11:12.200
you use this pattern you actually don't
00:11:13.560
need to use strong pams um because it's
00:11:16.000
going to do all that same stuff for you
00:11:17.519
and then actually a little bit more um
00:11:19.600
you can still use strong prams if you
00:11:21.079
want to do that you just don't have to
00:11:23.720
it's also going to cast whatever comes
00:11:25.600
in here so start date and end date as
00:11:27.880
inputs it's going to cast them to date
00:11:30.240
times so if somebody tries to pass in a
00:11:33.160
start date that's not a valid date time
00:11:35.720
or a string that like can't be cast to a
00:11:39.240
valid date time this is gonna throw an
00:11:41.760
error which means it's not going to try
00:11:43.880
to do anything else because the inputs
00:11:46.000
can't be what they're supposed to be so
00:11:48.040
I've actually already gained a lot here
00:11:50.560
um if some nonsense prams are coming in
00:11:54.440
this this this command is going to throw
00:11:56.760
an error and it's not going to try to do
00:11:59.160
anything else which is really really
00:12:01.040
valuable this is how you can kind of
00:12:03.079
catch any problems with your inputs very
00:12:05.240
first thing instead of getting like an
00:12:07.160
error way down the line where it's like
00:12:09.040
you know string nonsense can't be cast
00:12:11.279
to a date time or whatever um and it's
00:12:14.120
not just going to throw any error it's
00:12:15.680
going to throw an error that says start
00:12:17.320
date is not a valid date time that's a
00:12:19.399
human readable error that you can return
00:12:21.079
back to your users in your controller so
00:12:23.959
while this looks really similar the fact
00:12:26.639
that you can specify what the inputs are
00:12:29.000
really gives you a lot of control over
00:12:30.880
what's going to kind of move move down
00:12:32.839
through your um through your code all
00:12:36.760
right so you've added this you get start
00:12:39.279
date and end date and then next up
00:12:42.480
somebody some weird stuff starts
00:12:44.240
happening when people try to pass in
00:12:45.760
pams where end date is actually before
00:12:48.279
the start date so you end up wanting to
00:12:50.000
validate it' be really nice if you could
00:12:52.160
just control the fact that end date has
00:12:53.760
to be after start date well the command
00:12:55.800
pattern and specifically active
00:12:57.440
interaction will let you do do that too
00:13:00.680
um so keep in mind that that's the new
00:13:03.000
feature that we want and then I'm just
00:13:04.320
going to move some code around all right
00:13:07.639
I'm not going to talk about the
00:13:08.519
non-command pattern controller anymore
00:13:10.639
um we don't really need the comparison
00:13:13.240
um but I am going to
00:13:14.880
separate these two classes so over on
00:13:17.600
the left we have our controller that's
00:13:19.040
running the command and over on the
00:13:21.040
right we have the the actual command
00:13:22.839
class so the same thing we were just
00:13:24.760
looking at I just moved it so it's no
00:13:26.440
longer a comparison this is all all code
00:13:29.199
that would be an outw application right
00:13:31.920
now we also need to handle errors so
00:13:35.199
this is actually one of the really cool
00:13:36.959
Parts about this pattern and I kept
00:13:40.000
saying that the candy Index this command
00:13:42.399
is going to throw errors if the inputs
00:13:44.440
aren't valid that's pretty great but you
00:13:46.720
got to do something with those errors
00:13:48.839
active interactions errors look a lot
00:13:51.160
like active model errors and you can do
00:13:53.120
all the same things with them um so you
00:13:58.000
like you'll say outcome. valid this is
00:14:00.279
going to look really similar to how
00:14:01.440
active model does things um so that
00:14:04.120
means if everything went great in the
00:14:06.320
command you can do whatever you want
00:14:08.279
right like outcome. results is going to
00:14:10.240
be whatever is returned in the command
00:14:11.839
in this case all the candies and
00:14:13.440
outcome. errors is going to be an error
00:14:14.959
as object that looks a lot like what
00:14:17.160
you're used to with active
00:14:18.920
model that's really powerful um this
00:14:22.920
kind of if else statement over here in
00:14:24.480
the controller that's going to be the
00:14:26.720
same in all of your controller actions
00:14:29.720
if if you're using this pattern and so
00:14:31.440
you can abstract it out and your errors
00:14:33.600
are always going to look exactly the
00:14:35.279
same which means it's really easy to
00:14:37.600
kind of manage what your errors look
00:14:40.040
like when they when they get back to
00:14:41.399
your user um I didn't go into all the
00:14:45.360
details of how you would do something
00:14:47.040
with these error objects because of time
00:14:49.800
and um like I said just kind of the
00:14:51.440
complexity it's a little much for this
00:14:53.639
this size of a talk but um this is what
00:14:57.440
your controller end up looking like
00:14:59.959
though the the bottom part can just be
00:15:03.120
abstracted out right
00:15:05.079
away so let's so let's go back to that
00:15:08.000
feature we wanted to add where we really
00:15:10.440
don't want people passing in end dates
00:15:12.600
that are after start dates you can
00:15:15.839
validate your commands exactly the same
00:15:18.519
way you're used to validating your um
00:15:21.639
your models so over on the right I've
00:15:24.519
added a validate and date must be after
00:15:26.519
start date and then I've you know add a
00:15:29.319
little private method that does exactly
00:15:31.360
that so I can if the end date isn't
00:15:36.120
after the start date I'll explicitly add
00:15:37.880
an error I can add it to the end date
00:15:41.079
input you can also add errors to the
00:15:42.519
base which are just general errors um
00:15:44.800
and it'll say must day after start date
00:15:47.680
so what this does is we've now added
00:15:49.720
kind of another step to our life cycle
00:15:51.319
first we're going to process our inputs
00:15:53.319
then we're even going to validate our
00:15:55.079
inputs and you can get more and more
00:15:56.680
complicated validations the nice thing
00:15:59.040
about this is if at any step something
00:16:01.959
goes wrong active interaction stops what
00:16:05.199
it's doing and returns errors it's not
00:16:07.360
going to go try to get all the candies
00:16:09.319
if the in inputs are aren't valid um and
00:16:13.199
then your controller even though you've
00:16:14.880
added this validation step doesn't have
00:16:16.920
to change at all you're already handling
00:16:19.399
errors um and most in all of these
00:16:22.560
errors are going to come back in in a
00:16:24.000
human readable way if you're smart about
00:16:25.639
it um and you can just you know throw
00:16:28.880
those you know straight into your error
00:16:30.959
message um there's a there's a lot of
00:16:33.720
power in this especially as things get
00:16:35.920
more complicated right maybe you want to
00:16:38.199
be able to filter by a like type of
00:16:44.279
candy right you only want to get
00:16:46.040
chocolate um but you want to restrict
00:16:50.120
the filters you you could even so you
00:16:52.839
can
00:16:53.680
validate that the filter prams that are
00:16:56.240
coming in are are exact ly the way you
00:16:59.079
want them to be um over time this
00:17:02.920
pattern so your controllers hardly ever
00:17:04.600
change they all just kind of look like
00:17:06.120
this you just pass things straight
00:17:07.480
through to the commands but over time I
00:17:10.240
found this really really really helps
00:17:12.000
clean up a rails application um it gives
00:17:15.360
you a clear place to put um all the
00:17:17.720
logic that's that's happening in a
00:17:19.679
controller and um it lets you really
00:17:23.480
cleanly manage errors and make sure that
00:17:26.880
you're always returning nice human
00:17:28.439
readable errors um back to the user so
00:17:32.559
if nothing else I highly recommend you
00:17:34.280
check out active interaction it's just a
00:17:37.280
fun it's a really good gem I've used it
00:17:39.520
a lot um it's wellmaintained
00:17:41.840
and
00:17:43.440
um it it can help you do these things
00:17:46.400
right so manage your inputs run
00:17:48.160
validations do the thing you want to do
00:17:50.200
manage your output and standardize your
00:17:51.960
error handling um so it's more code than
00:17:55.600
just a method but if you think about
00:17:57.440
sometimes how complicated at your
00:17:58.760
controller methods can get this kind of
00:18:01.440
abstraction this this service layer has
00:18:04.280
been has been the best one that I've
00:18:05.840
tried um I've also used it in a bunch of
00:18:07.760
other places but I think the controller
00:18:09.960
example is is probably the best
00:18:12.520
one and that's all I've got
00:18:20.159
thanks yeah sorry I might have
00:18:24.080
um yeah and again I highly recommend
00:18:26.200
checking out the active interaction gem
00:18:28.080
as
00:18:28.840
uh just like an like just a just an
00:18:31.000
exploration I think putting together
00:18:33.080
this talk made me realize that 15
00:18:34.480
minutes is not really long enough for
00:18:35.960
this talk but the idea was like show you
00:18:39.240
what we got and then we can go from
00:18:40.640
there uh yeah any thank you for your
00:18:44.360
time and thank you um to to wnb uh we
00:18:48.120
were talking in the breakout room this
00:18:49.320
is a great community and really
00:18:51.280
appreciate everything all you're doing