What is the Rails way for dealing with “multiple controller actions”? In my application i have a special “shopping cart” flow, that has influence on several models. Basically the flow is available to a special kind of user when he/she is logged in, and goes like this:
- Add a user.
- Here it’s possible to “add a user”. If you know about Basecamp, this step is a bit like adding people to a project. So at this step i’m thinking in terms of a user resource.
- Here it’s possible to “buy” stuff for each of the previously added users.
- This is sort of like a basic shopping cart. So, in terms of resources i’m thinking about a “shop” or a “cart”.
- This is a confirmation step. Again i think of this as part of the cart.
After confirmation, several things should happen.
- The users that were added in step 1, should somehow be activated. That is, they won’t be able to login before the are activated.
- An order should be placed from the buys in step 2.
- The “stuff” you buy in step 2 is some kind of virtual product. That is, the products appear as “invitations” when the user logs in. The details of this is not too important, and a little difficult to explain. The key point is that, after confirmation, invitations should be created in the database, associated to the users.
Again, the details of the system is a little difficult to explain, but my main question is how this sort of stuff is best implemented in Rails. In involves several steps, and affects several models across all steps.
I’ve been thinking about doing it with some kind of statemachine. The statemachine would then be responsible for transitioning between the steps or states, and execute the required actions. So i guess i would have a StateMachineController or something, without a model, that would implement the main logic. Is this something to work with? It seems that Rails is really biased towards RESTfull resources, but i can’t seem to think of a RESTfull approach for something like the above. Thanks.
Think of the controller action as events that users cause. What happens after that is logic that should typically be associated with one or more models.
So placing an order is something in the Order model, activating a user is in the User model, sending an invitation is in the Invitation model and so on. There’s nothing wrong with having models know about each other. When an event spans multiple models, I put a method in the one most closely related to the event the user causes, probably a method like “purchase” in Order, and that’s what you would call from Order controller.
So if there are dependencies on state in other models, create little methods in them to test (if needed), like
activated?in User, and others to do the work, likeactivate(that updates the user state), and then “invite” in Invitation that saves a new invitation that’s associated with the user, and so on. If they fail, make sure they raise an exception.Once you have a bunch of nice granular methods, you can bundle them together, even wrapping them in a transaction in your
Order#purchasemethod. Something likeBy doing it all in a transaction, your data is either all correct, or not.
Somethin’ like that?