 So, I work at an agency called Industrial, we're based out of Ottawa, it might be the only Ottawa dev that's not working for Shopify at RailsConf, I don't know. But we're an agency and we work closely with clients, and the software is consistently evolving as we're working on it, and it's kind of just a nice way of saying that clients change their mind a lot, which is totally fine, but the challenge is finding ways of designing software that's open and flexible to change. So, I recently finished a project that was a big survey, it was a lot of forms, so the whole application was a form, and before I started I want to come up with a way that would be adaptable, because we knew the survey wasn't really set in stone, there would be new questions to add, old questions to remove, and the path that the user would go down from one form to the next was shifting. So, this is the challenge. If your data model matches your form, this is an easy problem. Rails has excellent defaults for creating a form that matches your data model. If you need to nest models, it means using accepts nested attributes for, and it's one of those things that's a little bit magical, and I don't quite know what's happening there, but once your form starts to get a bit complicated, you have to resort to hackas if statements in your models for validations, and these can get a bit unruly. It's difficult to understand the context of what's happening, and changing it becomes more difficult. So I've been curious about finding good ways to solve this problem for a long time, and there are countless ways of doing it. A lot sort of depends on your specific use case and requirements. For example, you can store fields in a session, that's a very good option. Say if you have a wizard where a user fills in their information, their name, their email, their address, and then you store all that data in a session at each step, and the process is done, and you save everything to the database at the very end. There's a gem called Wicked, which is really talented at splitting up single models into multiple steps. And then you add conditional validation to your model based on which step you're on. This is a state machine, which I'm not a huge fan of. They kind of start off simple, and they get unruly pretty quick. And for a form, it seems kind of like throwing a flamethrower at a candle, a flight of candle or something. Another option I've tried is to nest your models. There was a really good talk a few years ago at RailsConf by Andy Malay, and the basic idea is you have a big model, you split it up into nested chunks, and you validate each chunk separately. It's cool stuff. And all these options are great, but they're not really for me. And the reason is that a lot of these options struggle because the models are trying to bend to presentation details. They're trying to match the form to the database to make validating the form easier, but to me our models shouldn't care about presentation details. And I kind of totally get why this happens. Forms are kind of in this weird space. They're part view, they're part model. They're both those things at the same time. And we all know we're taught not to put business logic in our view, but what's never said is not to put view logic into our models. So let's not do it. This is Ruby after all, and we can do whatever we want. So if those previous options I mentioned, I don't really like them, what is a good option? So I really like form objects. It's a good alternative. They're model agnostic in that not all your data needs to live on one big model. You can have small models that are tightly focused and just grab what you need for the form. They're kind of these custom layers between your view and your model. And I'm gonna take you a few ways of writing them. I'm gonna roll your own and I'm gonna use a gem called reform. And first of all, let's just define it like what is it? So put simply a form object just an object that we pass into form for. So this is sort of the standard way of creating a form. We're just passing in a person active record model into form for here. This is not a form object, but the idea is the same. It doesn't have to be an active record model. It can be anything you want. And this is the real power behind form objects. Instead of being limited to active record models, we can compose objects from any attribute in our database. And as you'll see, this opens up the doors for some really flexible designs. So let's look at an example to make things clear. We're gonna be building a service where dog walking companies can sign up and manage their clients. So a user signs in and this is the onboarding wizard before they can use the application. It consists of three forms. The first thing we ask for is the name and phone number of the company. On the second form, we ask them to add in some addresses and they can add and remove multiple addresses. And finally there's a settings panel where we ask for their name, the size of their company, their subscription type, the time zone, their language preference. And that's it, so just those three forms. And it seems deceptively simple. But there's some challenges here. So in the first form, the challenge is nested data. The phone model is a child of company. And is there a better alternative than accepts nested attributes for? In the second form, our data matches our model pretty closely. Exactly in this form. And I'm sort of wondering, is using a form object in this example a good alternative or not? What are the advantages of doing that? And the challenge of the third form is that data is scattered all over the place. As you'll notice, we're saving to company again. And all these fields are required and we're going to save each step as we go along each form. And this can make validation difficult if we had to validate the entire model when it's saved. And we can imagine outside of this example, there might be many more attributes on these models that would have their own rules for validation, perhaps their own forms. This is just a simple example for the purpose of this talk. But you know you can be built forms, things get complicated quick. Okay, so let's build the first one. I'm gonna show you how to write this using a form object just with tools that Rails gives us. And then I'll refactor that with reform and then we'll compare afterwards. So this is your standard Rails controller, nothing too exciting here. The only difference is the instance variable for our view is companyform.new. For the create action, we're passing in some company parameters. And there's strong parameters and the attributes are name and number. And even though number is nested, we don't have to remember what the syntax is for strong parameters. It doesn't matter in this case. And this is our form view. I'm using simple form here, but form4 works great too. And you'll notice we're just passing our company form. We have our name for our company and our number for phone. And notice that these form inputs are flat. There's no nesting happening here at all. No need for a form fields block, even though the phones are nested under company. So let's make our form object, we just make a new class. We include active model model, which will give us validation, translations, and allows us to create an object that's very close to an active record model. And we do that by defining some attributes, name and number. And then we define those attributes where they're gonna go to once they're saved. So in this case, a new company and a nested company phone. Next we add in validation. This is your standard presence validation. You can get fancy here with some regex on the phone number, do all kinds of stuff, but we're just gonna keep it simple for this presentation. And this points to a method. And the purpose of this validation method is to display errors, validation errors. So Rails uses accept nested attributes for to display nested error messages. So if we didn't define this method, any nested models, like our phone model here would fail validation, but the error messages wouldn't show up because they're nested under company. And they're not gonna be bubbled up to the parent. So we write this method here, display errors, and that's all it does. It just bubbles up error messages from child relationships up to the parent. And speaking of which, we need to create that. So this method will now act like a company model. Company is gonna be our parent in this case. And it's gonna point to the company controller in the same way that the company model would. Next we have to define our own save method. So let's do that. So how this works is when we call save, it's gonna run through the validations. If it's not valid, it's gonna bubble up our error messages. If it is, we create a transaction. And then that calls save on everything. And these are bang methods. So if anything here fails, it's gonna be rolled back and nothing's gonna actually be saved to the database. And there's no way for the controller to infer the ID of the company. We're gonna need that when we redirect to the next form. So we just create that. We just need to create the company ID method and we're just gonna create that in our form object just with a delegate. That's our form object. So not too difficult to write. Stays really close to the rails way. Our controllers don't change much and it's a nice way to do it. The tricky thing about rolling your own is you're on your own. You have to write every little thing from scratch, from saving, to bubbling up validations. And the more complicated your form gets, perhaps the more complicated logic you need to figure out on these kind of base layer things that you might need to do. So there's a gem out there called reform. And it's part of the Trailblazer ecosystem maybe. So Trailblazer is a talk on its own. And I'm not gonna go too deep down that rabbit hole, but essentially it's a way of extending MVC. So the basic premise is you organize your code not by model, view controller, but by concept. And inside each concept or a component maybe you could call it, our service objects called operations. And reform is one kind of operation, the purpose of which is to build forms. And the nice thing about Trailblazer is you can use just bits and pieces of it, you don't need to use the whole thing. And in the last app I built, I just used reform and it worked out great. So let's refactor this form object that we just built to use reform. So as soon as we've installed the gem, we need to inherit from reform form. Reform doesn't know anything about Rails, so we'll remove active model, model. Next we have a very similar idea to our accessor methods here. And they're called properties, and they perform the exact same function, defining and writing to our attributes. We previously had to define the model that this object is gonna be talking to. And one difference is in the controller here, reform needs an argument. And so we're gonna do the exact same thing by passing in our company. So it is passing company.new into our form object, and we can get rid of this. And we don't need this delegate method anymore. So I'll talk more about validation in general. But the nice thing is that all this custom logic we wrote to handle error messages bubbling up from nest models is irrelevant. It's taken care of for us. So let's get rid of that. And now because we're passing in a new company from the controller, this method is already defined for us. It can infer what the new company is. It's now on the controller now instead of on the form object. So what we're left with is initializing the nested phone method. And we haven't passed it in through the controller, so it needs to define it here on the form itself. Because it's a nested object under company, reform has a specific way to define that type of relationship. It's called a collection. It's a block. And you just call it by the same name as the thing you're nesting. So phones, in this case. And then we define the properties inside the collection. So number in this case. And the one thing that has to change is our form. So we can no longer keep this flat view that's kind of nice. We have to introduce fields4 here. And fields4 just matches two collections inside reform. And for saving, we don't need to write our own logic to save it to the database. We'll do something mildly different on the controller to account for that. But for now, let's get rid of those strong parameters. Strong parameters are no longer a thing that we need to worry about. We set these attributes when we define properties on the form object. And reform will ignore any undefined parameters. So we can just safely remove those. And we'll just pass in a company here. And the main difference here is how we save it. In the other form object, we made sure to validate before saving. And instead, we'll do this on the controller here. So we just call companyform.validate. We pass in the parameters that the form gives us, and then we call save. But something is wrong. Should look like this, but it looks like this. Maybe you've had that experience before. So to fix that, we can do something like this. We just need to instantiate that nested model on the controller. And this will work, but reform has its own method for just this occasion. And it's called pre-populate. And we'll define that in the form object. All we need to do is add a pre-populator method to the collection. We're calling it build phone, but you can call it whatever you want. And it does pretty much what you'd expect. We grab our collection, and collections are in arrays, so we can't call dot build or dot new on it. And you just treat it like an array, and you append a new phone on it. Now it's displaying correctly, I think it's looking pretty good. One thing we have left to do is make sure that our primary flag gets set to true when we save this form. Let's remove this leftover from the previous example, and we'll deal with that. It's really easy, all we need to do is add the property primary, set a default. It's done. So one issue though is if you try to run this, if you run your tests, phones aren't saving. And the reason is that reform makes no assumptions about nested data. It understands the parent object company. We've passed that into the form object, but collections are different. So even though we've defined it, reform won't assume to know which model it's supposed to persist to. So we need to tell it. And we do that with something called a populator. So now I know what you might be thinking, there's pre-populators, populators, what's going on here? So an easy way to think about it is pre-populators are new in edit actions. They're called manually with pre-populate. They prepare the form for rendering. They're all about displaying it. And they can fill in some default fields into the form itself if you want. Populators are about creating and updating. They're called every time you validate. And they prepare the form for validating. And they make sure that everything goes back into the right model where it's supposed to go. So at its most basic, you can add a setting to the collection called Populative Empty. And you just pass in the class that you want to map it to. And that'll make sure that when you validate, it's going to persist to the phone class and try to validate against that. So that's our form object. We just need some validation in there and we're good to go. And that's it. So that's our first form, two ways to write it. And the first way, we rolled our own. The second way, we used reform. And here's a side-by-side comparison. So on the left is rolling our own reform on the right. And it's pretty obvious that there's a lot less code to write for the reform option. For the controller, I would say it's a bit of a wash. The roller, your own on the left, is a bit cleaner and simpler because we just call save on it. But we don't have to use strong parameters anymore. It's kind of a nice added side benefit. So let's go on to the next one. I'm going to go quickly through the next ones. So this form is adding and removing addresses. And it's happening on one model, the address model. As our controller, we're using the edit and update actions. And we're making a new address form. And we're passing in our previously created company right into that form. And we're calling pre-populate. In our form, we have a couple of things going on. We have fields for addresses here. And it's calling a partial called address form. And inside that partial, it looks like this. And this is just, it has all of our fields for address. And it has this hidden field called destroy. And that's just going to mark it as 1 or 0. Like if you say destroy this thing, it's going to mark it as 1. Otherwise, it's going to be 0. And then we just have some custom helper here with a little JavaScript. Just going to add a partial. And you can just add more addresses when you click a button. Remove one when you click another button. So in our form object, we inherit from reform form. We add in our collection for addresses. Destroy is flagged as a virtual attribute. So it's not actually anything that's going to the database. For pre-populator, we're doing very much the same thing we did before, just making sure we have one new address to display on the form. And our populator, we're going to have to write some custom logic to handle the adding and removal of addresses. So previously, we wrote populate if empty and pass in our class. And now we're going to write our own custom thing. So there's a couple methods in here that are part of reform's DSL, fragment, and collection. So collection is going to iterate over all your addresses. So if you called company.addresses, you might get five addresses back. That's the collection. The fragment is each individual address with an ID. And the populator will loop through every record, every fragment inside the collection. So we see if we have an address. We define it as a local variable. We try to find it on the collection to see if there's an ID of a fragment on there. And if we find a fragment, everything is good. We return that. If it's empty, we make one. And if we see that on the fragment destroy is set to one, we delete that fragment from the collection. And then the skip method will just skip to the next fragment in the loop. And the last thing we need here is our validation. And we'll just pop that in there. And we're done. We can now add or remove multiple addresses here. And the last form in this example, this one's kind of tricky because data's all over the place. So we have some stuff from companies, some stuff from user, an account. And we've previously saved a company. So we don't want to necessarily revalidate company again. We just want to validate it every time we put something into the database. And it's really easy to solve this. So this is our form. And you'll notice that there's no fields for anywhere in here. It's flat. There's another way to do it. We're just listing attributes. We're not caring about the data. And we're going to define that data on the controller. So there's a company, there's the user, an account. And then all we're going to do with this form is we're going to pass those three models as a hash into our form object, just like that. And in our form object, we include this module called composition that comes with reform. I'll show you what it looks like. So we define the company as the parent model that we're going to save to. And that's really easy. We just add in our properties. And then we just tell it which properties belong where. And we do that with this on attribute. So time zone is on company. First name is on user. You get the idea. Anywhere we might have a name clash, like each record's ID. We just give it a new property name. And we tell where it's from. And because we're explicitly passing in objects into the form object with the hash and using composition, reform can just figure out where everything is saving to. So there's no need to define populators or pre-populators on this form. And the last thing we do is just add in our validations. And it's all done. So what are the advantages of doing this? So form objects are model agnostic. You no longer need to even consider the view when setting up your data model. And there's kind of an ancillary benefit to this, which is like small, tightly focused models, not these giant behemoth models. And not that I'm recommending it, but you could forego having any validation on your models whatsoever. And instead, rely on the fact that the right data is getting to the right place based on the inputs that are coming in. And at the very least, this means no conditional validation in your models. We're validating the form, not the model itself, sort of a different way of thinking about it. And this follows rest. We essentially have one controller per form. And it's just following the rest actions. It's very tightly focused and easy to understand controller. There's nothing too exciting about it. These are easy to test. So to test these forms, I would typically have a copy bar integration test running through the whole wizard. And then testing the form objects themselves is really nice. With reform especially, you just pass in a hash into the form and call validate on it. And you can just make sure you're getting the right error messages that way. You don't need to set up a whole active record model to pass into the form. You can just set up the data that you're passing into the form. And so those tests are nice to work with. They're fast, and they're easy to write. So this is also easy to extend and modify. If our client comes back and wants to change the order of our wizard, or wants to change which attributes appear on which forms, we can do so. Without much effort. And the last thing, you can use them when you need to. There's nothing wrong with using the Rails defaults, but it makes sense to do so. But as soon as things get a bit more complicated, this is a really good tool to have in your back pocket. So give it a try on your next form. See what it's like. You might really like it. It's a nice thing. You can just incorporate it into legacy apps. It's a nice little pattern. So thank you. Yes, the question is about the reform jam. How stable is it? I think it's very stable from what I've been working on it. It's very actively maintained. So I don't know what the point releases are going to be like, but right now it's in version two. And I think there's some big plans for three. I'm not sure about that. So I'm not sure. But so far it's been really nice to work with. And I've just been using the latest gem. And it's been working really great. Yeah, so the question was about foregoing validation on the form, and when do you decide to validate just the model or just the form? And I guess it's like you kind of have to make that call. When is it crucial that your company has a name? Like if it doesn't have a name and the whole application blows up, then maybe it makes sense to have it on the model. And if you're, say, in the Rails console or something, and you as a developer are creating a company for somebody, maybe you want to make sure that the company at least has a name. So just kind of play it by ear. Yeah, you kind of want to be careful with that stuff. But in general, for the stuff that's not too, too critical, I would just say leave it off them well. OK, well thank you very much for coming.