-
ANDY MALEH: Sorry everybody. I lost the slides.
-
I had to reconstruct them right now.
-
Right, like in ten minutes.
-
Ultra light and maintainable Rails wizards.
-
Who has written a wizard in their lifetime?
-
OK. It's, it's almost like the most common
web
-
use case, yet it's the least under, under-valued
with
-
regards to providing patterns for doing, like,
writing good
-
code in order to provide for maintainable
wizards.
-
A lot of the time, people write, like, multi-step
-
wizards, where they end up doing a lot of
-
copy-paste between the steps or between a
bunch of
-
controllers. And that makes it a hell of a
-
problem to maintain that code a year or two
-
later. And every code base is, you know, meant
-
to be created for a year at least. Maintenance
-
cost is what's really expensive. It's not,
you know,
-
I can write a wizard in two weeks but
-
will I be able to maintain it cheaply over
-
a year. And that, that's really why I'm giving
-
a talk about this subject.
-
So just to give you an overview, I'll be
-
talking about, why do we even use a wizard?
-
Provide an example. Some implementation goals.
The 1001 wizard
-
implementations out there. And, finally, I'll
talk about what
-
I think is a good ultra light and maintainable
-
wizard approach.
-
So first of all, we don't want to overwhelm
-
the user with a huge form of information,
kind
-
of like those government forms that we have
in
-
Canada. So I come from Montreal, by the way.
-
This is painful on a computer screen. Computers
should
-
enable people to do better than actual physical
paper.
-
So one way of tackling this problem is to
-
divide it into multiple steps in a wizard.
-
So also it's about simplifying the workflow
into multiple
-
steps that make them digestible, just like
this protein
-
shake. And finally it, it gives you the opportunity
-
to provide more explanation for what each
form does,
-
by being able to fit more information when
you
-
break it up across multiple pages, like what
they
-
do with TurboTax.
-
Who here has filed their taxes?
-
Yeah. I did not. I just did this so
-
I see who raised his hand.
-
OK. So I had a, a software architecture gig
-
at EarlyShare about a couple of years ago
where
-
I helped them launch their site. EarlyShare
is kind
-
of like KickStarter or Indiegogo, except it's
focused on
-
allowing people to do crowd investment in
businesses.
-
And it was a website that was being built
-
fast in order to catch up with some legal
-
laws in the U.S. that would allow crowd investment.
-
So I helped them launch the site and they
-
had, they, as part of their website, they
needed
-
a couple of onboarding wizards. One for investors
and
-
one for business people.
-
But there were other requirements. Like, the
business was
-
bootstrapped. We were only two developers.
Me as the
-
senior and then there was a junior with a
-
CTO and a designer and that's it. He wanted
-
us to move super fast, and I was brought
-
in as the Rails expert.
-
So I had not written a wizard in like
-
four years before that. Or maybe five years.
Like,
-
maybe since the days I did Java development.
And
-
when I started tackling this problem in Ruby,
you
-
know, I like, I went online, checked some
Google
-
guides and all that or StackOverflow, whatever.
-
And none of their approaches satisfied me.
So let's
-
talk about what I found.
-
So the wizard example, though, is basically,
you have
-
four steps. Step one is collect basic info.
Step
-
two is details, more details. Step three is
upload
-
some document content. Step four is just preview
before
-
you finish the wizard. And then once it's
done,
-
it shows you a summary, like a landing page
-
for the project that, that the business is
proposing
-
for investment.
-
OK. So, I mean, the goals I had was
-
the Rails server had to persist every progress
on
-
every step. So no, like, js client-side tricks.
That
-
was out of scope. I wanted it to still,
-
like, be RESTful, like, which is a common
issue
-
with wizard, like, building wizards. How to
make them
-
properly RESTful.
-
I wanted to also stick with MVC, object-oriented
principles
-
because we're using an object-oriented language.
So I wanted
-
to make sure that the code is maintainable
by
-
other developers going to the feature.
-
And then some non-functional requirements,
like productivity. So that
-
was part of the concern that the CTO had,
-
which is he wanted us to move fast, like,
-
really, really fast. That was part of the
reason
-
why he brought me in. Well, big mistake. I,
-
I pay attention to details and nice design
concerns.
-
So I will slow him down but for good
-
reasons.
-
I'll slow him down and then he'll go much
-
faster later on.
-
Still, the story does have a happy ending.
So
-
maintainability, by both junior and senior
developers. They had
-
one senior developer in Brazil as well. Which
I
-
just remembered. He was brought in a little
later
-
on.
-
Performance concerns. Security concerns.
-
So it's pretty, it's pretty basic stuff. Like,
I
-
mean, these are the concerns that we should
care
-
about whenever we build any feature, really.
-
So one approach that I've seen on actual code
-
bases, I actually saw it on a code base
-
that I maintained on a following project after
that
-
one, was one controller per wizard step.
-
So you create a REST resource per wizard step.
-
And you had multiple controllers, multiple
sets of views
-
and helpers. And then each controller redirects
to the
-
next one. So something like this. And you
could
-
do it either with one ActiveRecord that has
conditional
-
validations for each step, where it says OK,
if
-
step one, validate presence of name, if step
two,
-
validate presence of blah, blah, blah. Or
you could
-
have multiple ActiveRecords.
-
But either way, who here could find concerns
with
-
this approach, or at least something that
could be
-
improved on? Somebody volunteer? Go ahead.
-
AUDIENCE: [indecipherable - 00:07:10]
-
A.M.: And what's the concern with that? So
what
-
if you have a whole bunch of controllers?
-
AUDIENCE: [indecipherable - 00:07:16]
-
A.M.: OK. I mean, I've built applications
that managed
-
user profiles, user accounts, blog posts,
for example, whatever.
-
You need a controller for each one of those.
-
I don't think you could escape that.
-
So I'm gonna give other people a chance to
-
talk, but I do get your point. I want
-
to clarify it.
-
Go ahead.
-
AUDIENCE: I was gonna say repetition-
-
A.M.: Exactly.
-
AUDIENCE: Re-usability and dependencies.
-
A.M.: Yeah. So quite a bit of that code
-
is repetitive. It was just, it was always
loading
-
the resource. It's almost the same resource.
Actually, if
-
you use one ActiveRecord, it is the same resource.
-
And then we'd run some validations and then
it
-
would pass to the next controller. So, I mean,
-
there was quite a bit of repetition.
-
We try to add features a couple of months
-
after the, after a developer had built that
wizard
-
on that project, and they wanted us to deliver
-
something in a week, and apparently another
developer, before
-
I joined that team, had tried to implement
that
-
feature and it took him a month. And he
-
still couldn't do it with the, with the design
-
they had.
-
It was still, it was just taking a long
-
time. Like, he was still not done. And then
-
that guy left. So I ended up solving the
-
problem with another senior guy. And it was,
so,
-
I ended up applying this ultra light maintainable
wizard
-
approach that I discovered on the EarlyShares
dot com
-
project, and it worked out really well. So,
which,
-
I'll talk about a little later. But that helped
-
us actually develop it in, if I remember right,
-
it was about seven days.
-
Test first, and rewriting the entire thing,
also.
-
But it was specifically because we didn't
have that
-
many controllers anymore. So, we wrote a lot
less
-
tests so we had a lot less code to
-
maintain. So that was part of it.
-
So, another approach I've seen is one controller.
Sorry.
-
Oh, OK, that's just the critique. We already
went
-
over that. I don't think I want to go
-
too much into details for that cause we're
limited
-
on time.
-
But yeah, let's go next to one action and
-
presenter per wizard step. So, I mean, another
approach
-
is, OK, keep one ActiveRecord, but I've also
seen
-
this approach in a code base, where there
were
-
different, say, new_step1, create_step1, new_step2,
create_step2. So there were
-
just, like, eight actions on that controller,
each mimicking
-
the new and, and, and create, say, on the
-
RESTful resource.
-
So, although it feels RESTful, it was not
REST
-
anymore. It already broke out of the REST
paradigm.
-
So we can improve over that.
-
Also, it still had some repetitive code across
the
-
actions. So, I mean, it was, it was just
-
a slight improvement to the problem. Not much.
-
Using presenters, which is an abstraction
layer between the
-
ActiveRecord and the controller is an improvement
in the
-
sense that you can put the validations for
each
-
presenter per step separately and not have
conditional validations.
-
I'll talk more about that going forward.
-
So it was more something like this, where
the
-
controller had a whole bunch of actions that
are
-
connecting to presenters that are talking
to an ActiveRecord.
-
OK.
-
So I already went over the caveats of that.
-
OK. Who here has written a wizard with session
-
accumulation approach? How, how did that work
out for
-
you or, do you think-
-
AUDIENCE: That's why I'm here.
-
-well, I'm sorry if it sounds. Well, tell
me
-
why you're here. I'm curious.
-
AUDIENCE: Well, I mean, right now it's just
that
-
it's to the point where we're breaking it
down
-
further, it was a very basic general implementation.
And
-
now dealing with the fact that we have so
-
much session iteration that I have to pass
it
-
between, you know, controllers. We deal with
it from
-
multiple angles, and it's, I mean, I can't
put
-
that stuff in a model. So my controllers are
-
getting really out of hand.
-
A.M.: Exactly. Yeah. Yup. Yup. Yup.
-
So you end up with the live session management
-
code in the controller, which breaks MVC.
So if
-
you're not breaking REST, you break MVC. It's
really
-
tough. It's a tough problem.
-
AUDIENCE: Can you explain what session accumulation
is?
-
A.M.: Yes. So, actually, maybe I should have
somebody
-
explain that. I saw you raise your hand. Would
-
you mind explaining it to the audience.
-
AUDIENCE: Sure. It's basically, as you're
going through the
-
specs, that you're storing all of the information
that
-
needs to be in the session. So then, you
-
go through step
-
one. You gather the basic info from the form.
-
When submitting the form, instead of storing
that in
-
an ActiveRecord, you actually store it in,
in the
-
session, using the session helper in the,
in the
-
Rails controller. And then once you move to,
and
-
then you redirect to step two, and then you
-
submit that form again, and then you add more
-
stuff to the session.
-
Once you reach the last step, kind of like
-
what you see in this diagram, that's when
you're
-
ready to create the ActiveRecord. So you pass
all
-
of this as the params and the ActiveRecord
will
-
validate it and then you're done.
-
OK. So, so I mean as far as critique.
-
So reliance of session, storing objects in
the session
-
has implications on scalability. Usually you
want to store
-
ids of primitives because they're easier to
move across
-
servers when it's primitive data and be able
to
-
support multiple servers.
-
My understanding is if you have actual objects
in
-
the session, it makes it harder for you to
-
scale.
-
Controller code is more complex because of
managing session
-
data. Validations could get defined twice,
because you might
-
have to validate on every step as well, in
-
JavaScript or in a, in a presenter or something.
-
And, also present at the last step in the
-
model.
-
So again, I mean, if you're not breaking REST,
-
you're breaking MVC. If you're not breaking
MVC, you're
-
breaking duplication whatever, concerns, so,
it's a tough problem.
-
Hidden value accumulation. Somebody share
with us what this
-
is, or, it's very similar to session accumulation.
-
Yeah, go ahead.
-
AUDIENCE: Really, when you're submitting the
form at each
-
step you're shoving all the values from the
form
-
into hidden fields on the page, and then eventually,
-
when you hit submit, the final version will
just
-
set everything to the server.
-
A.M.: Yup.
-
So it's not stateful, it's stateless, because
it keeps,
-
like, each request has its state. You don't
have
-
to maintain the state in a session. So the
-
performance implications are gone. Like, it
has no problems
-
in scalability.
-
But you might, you, you might not want to
-
expose the values all the time on the, on
-
the user page. You can hash them or do
-
encoding on them, so that could improve that,
that
-
problem with regards to keeping form data
in a,
-
in hidden fields on the page every step of
-
the way.
-
But there's, yeah, I mean, but the complexity
is
-
still there, with having to manage the accumulation
and
-
having to construct the model at the end.
-
So it's a slight improvement.
-
Who here has used the state matching for a
-
wizard?
-
OK. Do you mind sharing with us your experience
-
with it?
-
AUDIENCE: I think main problem that you run
into
-
is that you get fat models, cause you have
-
to put all of the different validations into
different
-
states. But overall, I found that it was a
-
better compromise than the other options.
-
A.M.: Generally it is. One, yeah. So, you
create
-
one ActiveRecord. You make the ActiveRecord
a state machine.
-
You have to add a step column on that
-
model to support which step you're on when
it,
-
in order for the validation to know which
validations
-
to run for what step. So that way you
-
say, OK, on step one, if you have that
-
column, you'll say, OK, you'll have validations
that say,
-
OK, if it's step one, then I'm gonna check
-
for first name and last name presence. If
it's
-
step two I'm gonna, I'm gonna check that the
-
project details are present. And so on and
so
-
forth, depending on what each form, what field,
what
-
field each form contains on the, in the specific
-
step.
-
So yeah, different view per step. I already
went
-
over conditional validations.
-
To share an example, it looks something like
that,
-
like validate :phone, presence: true, if current_step
is shipping.
-
So that's just one way of doing it. There's
-
other better ways of doing it. There's also
gems
-
out there that help you with that. But that's
-
one way of doing it.
-
AUDIENCE: ActiveRecord has the ability to
run conditional validations
-
like that. You don't have to run a block.
-
A.M.: Mhmm. Yup. Yup. I'm familiar with that.
Yeah,
-
that's why I mentioned, there's multiple ways
of doing
-
that. That's just one example.
-
AUDIENCE: How does, how is the state machine
different
-
than the first, better than any of the ones
-
you mentioned, [indecipherable - 00:16:39]
-
A.M.: OK. With the other one, you could cheat
-
a bit and set an in memory variable that
-
represents the step name that you're on and
then
-
do that conditional validation that way, whereas
with this
-
one, you have, you're only working with one
model
-
and you don't, you haven't managed the stepping.
So
-
yeah, with the other approach, the controller
is doing
-
management of the stepping. In this one the
model
-
is doing the management of the stepping.
-
So critique. Well, first of all, it puts in
-
presentation concerns, like adding an extra
column to our
-
presenter's state, sorry, step, is not part
of the
-
domain, the business domain. So when you're
doing MVC,
-
usually the model, you're trying to put in
it
-
as much decoupled logic that's focused on
the business
-
at hand as possible in order to maintain that
-
separately from any view concerns or controller
concerns.
-
I mean, you can put anything in the model,
-
really. But the reason why we do that is,
-
in my experience, when I'm maintaining a code
base,
-
if I'm not having to manage view concerns
like
-
stepping into a state machine and a model
concern,
-
like the business rules of, of what happens
when,
-
you know, like the project description is
not present
-
or whatever, then it's easier for me to maintain
-
that model, cause I'm not thinking on one
thing
-
at a time. I'm not thinking multiple things
at
-
the same time.
-
Also, it makes those models smaller files,
if you
-
separate those concerns. You don't want a
huge model
-
as maintaining a state machine and maintaining
business rules
-
and maintaining like ten other things. You
could manage
-
that with splitting that into modules or concerns,
but
-
still, when I'm working with that model, my
head
-
will have the context of everything at once.
SO
-
it wouldn't, like, this is more of an advanced
-
programming thing. Like, once you've been
programming for three
-
years at least, you'll, you'll start noticing
that.
-
You'll start noticing the subtleties with
regards to mixing
-
concerns. Like, you start understanding why
people say follow
-
the single responsibility principle. I'm not
a fan of
-
following it dogmatically, I, but I think
it's a
-
good guideline, like any other guideline,
where if you
-
could minimize responsibilities in a model
and have it
-
not manage view concerns, then do that. Especially
if
-
MVC prescribes that as well as that's what
all
-
Rails developers on the field would expect.
-
So I think I pretty much sold that. So
-
yeah, so I mean, I think that makes it
-
pretty clear why I don't like this approach
that
-
much.
-
Also, it's a bit techy. Like, thinking of
the
-
wizard as a state machine is a bit computer
-
science-y. Like, I mean, I have a background
in
-
computer science, but, the, the point of anything
you
-
learn is to apply it in the right place
-
for it, and I don't feel like, when I'm
-
thinking about a wizard I'm thinking about
the business
-
problem. That's what I really want to think
about.
-
I don't want to think about a state machine.
-
As cool as that is, that's not the time
-
to think about it.
-
So I mean, a thousand and one approaches is,
-
there's a whole bunch of gems out there. Most
-
of them will simplify the things I mentioned,
or
-
give you better, shorter DSLs for doing the
approaches
-
I mentioned. But none of them achieve all
the
-
goals at once, of having MVC, REST, and all
-
of that.
-
I mean, to get back to that, there's REST,
-
MVC, OO, and then the non-functional requirements.
So, let's
-
go to, jump into this. I think we have
-
about ten minutes left.
-
So the first thing that, so I, I'm like,
-
OK, let's try to solve this wizard problem
from
-
scratch, like, as if I just, I'm just gonna,
-
like, get my tools out there. Like, the object-oriented
-
principles, the domain-driven design principles.
Who, who here has
-
read the book Domain Driven Design? Or heard
of
-
it, at least?
-
It's a book that I, my team did a
-
book club on, or a previous team, like six
-
years ago, did a book club on for the
-
sake of learning how to do object-oriented
design on
-
real business problems. Cause a lot of the
time
-
you learn object orientation, but it's hard
to figure
-
out how to create the right objects for the
-
real, for the right person's problem. It's,
that, that
-
book is a very good book on how to
-
tackle that.
-
So I, I started using those tools. Like, whatever
-
I learned from that book, whatever I learned
from
-
object-oriented programming. Whatever I learned
from, like REST. To
-
try to figure out what a wizard is.
-
Before I go ahead and talk more of what
-
a wizard is, what do you think a wizard,
-
the wizard's highest goal is?
-
Go ahead.
-
AUDIENCE: To serve views for the user.
-
A.M.: That's correct. So, you stumped me.
Cause I
-
was gonna ask about the highest goal from
the
-
developer's point of view, but you're right,
we should
-
think about the user's perspective first.
-
Now, let's dig, let's dig a little. No, that's
-
good. Let's dig a level lower. So, OK, so
-
we know that. That's our guiding principle,
is OK,
-
to serve to make things easier for the user.
-
But, next, what, why, why, OK, technically
what, what
-
is a wizard doing?
-
OK, that's my next question. What is a wizard
-
really doing? Go ahead.
-
AUDIENCE: Collect the proper set of validated
values.
-
A.M.: That's part of the work. What else?
-
AUDIENCE: Break down the form so they're just
small
-
steps.
-
A.M.: OK. Break down the data. Yup. Like separate
-
it. What else?
-
AUDIENCE: I was gonna say something really
similar to
-
that. Organize the data into, like, you know,
making
-
it weighted or in comprehensible sections.
-
A.M.: OK. And what's the end goal of running
-
through the entire wizard?
-
AUDIENCE: Creating an object.
-
A.M.: Yup. Pretty much. So a wizard is nothing
-
but the good old builder design pattern. Anybody's
heard
-
of it. I mean, I used to be a
-
hardcore Java geek and design patterns were
big in-
-
[audio jump] seven days - in Ruby.
-
But it's still good to know about things like
-
that, cause that pattern flashed in my head
right
-
away. I'm like, oh wow, a wizard is nothing
-
but a builder. Like, it, all it does is
-
like an assembly line of building a car, where
-
step one, you know, whatever, you put the
chassis,
-
second, like, step two is you add more, I
-
don't know, you add the doors. Step three,
you
-
add the windows. Four, five, and then all
of
-
the sudden you've built a car. So that's really
-
what it is.
-
Second part of the philosophy that I was following
-
is, each step in a wizard is nothing but
-
a partial view of that main, full object you're
-
building. So, one, one step is about, say
I'm
-
ordering a car and I want to customize that
-
car. Like, one step will show me the exterior
-
body and another will show me the interior
to
-
customize the interior with, whatever, leather
or mahogany front-panel,
-
whatever. And then, and then a third part
lets
-
me customize the engine.
-
So it's just, so all what steps are, are
-
views. Like, instead of thinking about them
as states
-
and in a state machine, this is a more
-
higher level way of thinking about it. It's
less
-
technical and more, it's just, I'm viewing
one part
-
of a model.
-
Third part is if you were to, so, with
-
that in mind, if you ever think about the
-
REST resources, it's very simple now. It's
done. Like,
-
you have the main model, that's the main resource.
-
And then you have the model parts, nested
model
-
part, under the main model. That's the second
resource.
-
That's it. You have two RESTful resources.
Very clean.
-
So every time you're walking through the steps
of
-
a wizard, you're actually editing a model
part. So,
-
and, so that makes it very, very clear what
-
the REST resource is.
-
Another thing in my philosophy about it was
I
-
did not want to have conditional validations,
cause they
-
make a model hard to maintain. It's harder
to
-
read if statements. Like, if I can have those
-
without if statements, it would be better.
Especially when
-
you come back to maintain that wizard six
months
-
later and then a year later and, on, on
-
both projects I was on, they actually added
steps
-
to the wizard. So they started with four steps
-
and then they grew to nine steps.
-
And the more I can separate that stuff, the,
-
the better. And then finally I just wanted
to
-
maintain the views in separate view files
as well.
-
I didn't want a single view file that would
-
do it the way I used to write code
-
in ASB where I'd have a crazy if-else statement
-
that says if step1 show this part of the
-
form, if step2, show me the project details,
if
-
step3, show me a document content upload.
I don't
-
want that. That's, yeah. That's ASP programming.
-
AUDIENCE: If they change the order of the
steps,
-
you want the steps [indecipherable] step three
became step
-
four.
-
A.M.: Yeah. It'll work.
-
OK. So, so really I mean, high level is
-
just, I have the main model. That's the main
-
resource. And then the, on the, nested under
it,
-
there's the four different, so, what I end
up
-
doing is creating four different presenters.
One per step.
-
Which manages the validations for that step
separately, as
-
well as any stepping logic related to what
happens
-
when you land on that page. What are the
-
defaults for that form? Should we initialize
the phone
-
number with zero, zero, zero, zeros, or should
we,
-
like, should, should we prefil the name from
the
-
logged in user account? These kinds of concerns
now
-
are uploaded cleanly to the model.
-
So we're adhering to MVC. You handle all the
-
wizard intelligence and business logic in
the models now.
-
You're not, or presenters. I mean, a presenter
is
-
just another form of a model that focuses
on
-
presented a vi- a part of a model. So,
-
really, I'm, I'm using it in a loose sense.
-
There's many ways to do presenters. I don't
care
-
which way.
-
I do have a prescribed way here, but, the
-
point of first to grasp is that you're operating
-
on a part of the model. You're not operating
-
on the full model. And you're doing the logic
-
in the, in a model, not in a controller.
-
And then the, the controller is the, yeah.
So
-
there's two of them. There's the one that
manages
-
the main model creation. So the first step
of
-
a wizard, when you create it, you create it
-
with a main model controller. So if I have
-
projects controller, I have a create action.
And then
-
that triggers the wizard. It'll, it'll create
it and
-
then redirects me to the first step.
-
So when it redirects me to the first step
-
it takes me to the edit page of model
-
part, the nested model part, with id_step1,
for example.
-
Or id basic info. So you use the step
-
names as the ids of that RESTful resource.
Which
-
is perfect REST. Like, that's, that, that
goes with,
-
that gives you an example of why, when people
-
talk about REST outside of Rails, they tell
you
-
REST does not relate to having a database
table.
-
You can, so this, this, this is an example
-
where the RESTful resource is a step that
is
-
a view of the model, but it's not a
-
resource. It's not a database table. It's
not a
-
separate database table. It's just a virtual
resource.
-
I need to wrap it up and then I'll
-
take, I'll take questions. So in a nutshell,
you
-
have the model resource, nested model. So
you end
-
up with URLs like that, which is very restful,
-
again, cause, like, so you have projects,
with the
-
id. We were using friendly id on the projects,
-
so the project says, yeah, so project1, then
project_parts,
-
and then the name of the step. And there'll
-
be four steps for, you know, every time you
-
run through that wizard. So there'll be four
ids,
-
only. It's a finite set. You don't have to
-
store it in the database.
-
So yeah, step names. There's id, contains
validations, yeah.
-
I already talked about all of that. So let's
-
skip.
-
So the routes is very, are very simple. You
-
just have a resources projects. It's actually
only create
-
and show. I left the show out. There should
-
be a show as well. Show is the landing
-
page of the project when you finish the wizard.
-
And then the project parts, which is edit
and
-
update. And it's that simple.
-
So, so here's the project model. It's got
the
-
basic definition of that model. And associations
and so
-
on and so forth. However, what I end up
-
doing is creating a presenter per wizard step,
so,
-
and I nest them under a directory that matches
-
the model name, so, project, for example.
And that's
-
one way of doing it. There's many ways of
-
doing it, but.
-
In this case, only step one and two had
-
customizations over that model. Step three
did not have
-
validations of its own, so I didn't even have
-
to create a file for it. And step four
-
actually didn't have anything either so I
didn't have
-
to create a file for it.
-
But yeah. Step one, you can't see the details,
-
but the point is that this is, these are
-
validations for step one, only. So that's
the first,
-
and then the first step, as well as some
-
business logic related to it, like initializing
default, like
-
initialize from user, which initializes from
the signed in
-
user.
-
And then there's the project detail model,
which has
-
a few validations, only. Only three, cause
it only
-
has three fields on it. So that one is
-
also, like, cleanly separated. Nice, easy
to maintain. So
-
you go back to maintain the code and it's
-
like, it's like, it's squeaky clean. You know
it's
-
like so easy. It's like the way programming
should
-
be.
-
So the project's controller, the create action,
all it
-
does is it creates the project and then redirects
-
to the edit page of the first step, which
-
is basic info. And then the project parts
controller
-
has the edit and the update and all it
-
does is it steps through the wizard. Now,
what
-
I ended up doing here is the way I
-
reasoned about it is that the project in MVC,
-
or, sorry project controller in MVC is, or
project
-
parts controller, is actually, although it's
a controller, it's
-
a model of sorts. It's a model focused on
-
control flow.
-
And stepping through a wizard is control flow.
So
-
then I made the controller responsible for
it. So
-
the, so here, at the top, it, it defines
-
the order of the steps, and then based on
-
that it walks through them. So in a way
-
the controller is the state machine, except
I didn't
-
need a state machine cause I'm not maintaining
state.
-
I do it with, I don't need to maintain
-
state. Every time we finish a step we can
-
redirect to the next step. And I can pass
-
the id of the next step RESTfully and redirect
-
to the next resource.
-
So I, I don't need to maintain what step
-
I'm at, on. It's always on each page. Like,
-
on each page, I can know, if I'm on
-
step two, I know that if I hit submit
-
it'll take me to step three next. I don't
-
have to, like I just know that from this
-
array that we define over here, which orders
the
-
steps in the wizard.
-
So, yeah, we're almost done. But yeah. I mean,
-
it's got stepping logic. There is, there's
a gem
-
out there called wicked that helps to implement
this
-
sort of logic in a controller. I, I'm starting
-
a gem called ultralightwizard that will do
similar stuff,
-
except it'll add the, the concept of presenters
to
-
it as well. But until then you could use
-
wicked for the controller part.
-
The views, you'll just have a different wizard
part
-
view, or sorry, step view. And one thing I
-
didn't know is that this edit action, it actually,
-
when it renders the view, you don't render
and
-
edit dot html dot erb, you polymorphically
just render
-
the step name. And then it ends up picking
-
a view matching that step name.
-
So if I render basic_info, it renders basic_info
dot
-
html dot erb, which contains the edit form
for
-
that step. If I render the, the detail step,
-
then it renders detail dot html dot erb. So
-
that's why I have these. So that way we
-
have the views separated as well. So we achieve
-
that goal.
-
The form that you put can be the same
-
on all views actually. Because you have, you're
editing
-
the same model. You're just editing different
parts of
-
that model, but it's, it's the same model
on
-
all pages. So the root model is project, but
-
on one page I'm editing nested document content,
upload
-
the documents. On another page I'm, I'm, I'm
editing
-
just the first name and last name, and so
-
on and so forth.
-
So you could actually wrap this whole thing
up
-
in the helper, call it project_form_for. That's
super short.
-
Just use that. I know on one of my
-
projects, another senior developer on the
team did that,
-
like he did that as a refactoring step.
-
So, I mean, this is an example, but a
-
view, a very straightforward view form, like,
straight Rails.
-
Nothing special about it.
-
So that concludes the talk. So, I mean, I
-
talked about why use a wizard, provide a wizard
-
example, implementation goals, other implementations
out there, and finally
-
talked about what's my recommended approach
for sticking with
-
REST, MVC, OO, and, you know, all the things
-
that would help ensure that your code is not
-
maintainable only today but also a year from
now,
-
and also by other developers that will join
the
-
team with minimal training efforts, hopefully.
-
This is the project that I launched and is
-
empty right now, but I would like to, but
-
I mean, you can star it and monitor it
-
and hopefully I have something out soon. Maybe
I'll
-
do it at RailsConf. Somebody want to pair
with
-
me on this, you're welcome to. So yeah, my
-
name is Andy Maleh. I'm the VP of Engineering
-
at a remote only, or 100% remote option consulting
-
company called BigAstronaut. These are my
folks over here.
-
That's Lance, CTO.
-
Oh, oh yeah, Chief Fun Officer as well.
-
AUDIENCE: We've got t-shirts and stickers
and we're hiring.
-
A.M.: Sweet. Yeah. Thank you everybody.