Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement a RESTful resource for a state machine or finite automata

I'm a Rails and REST newbie and I'm trying to figure how best to expose a resource that is backed by a domain object that has a state machine (in other words is a finite automata).

I've seen a number of gems for making a model class a state machine, such as aasm, transitions, workflow, but none of them document examples of how they are actually used in a resource oriented controller. They all seem to imply that state transitions are triggered by an "event" , which is really a method call. Some questions I have with what this implies are:

  1. The update action (PUT method) is not appropriate because PUT is suppose to be idempotent. The only this would be possible is if the state was sent as part of the representation. This is inconsistet with an "event". Is this correct?
  2. Since, events aren't idempotent, then the a POST must be used. But, to which resource? Is there a subresource for each possible event? Or, is there one (/updatestate) that takes as its representation the event to trigger and any parameters to the event?
  3. Since the state of the resource is modified by an event triggered potentially by another resource, should the create action accept changes to the state attribute (or any other attributes that are dependent on the state machine)?
  4. [Updated question] What is a good way to expose the transitions in the UI? Since events aren't states, it would seem that it doesn't make sense to allow the state attribute (and any other attribute that is dependent on state transitions) to be updated. Does that mean that these attributes should be ignored in the update action?
like image 215
Galex Avatar asked Apr 08 '11 06:04

Galex


People also ask

Is finite state machine same as finite automata?

Both "Finite State Machine" FSM and "Finite Automata" (or Finite State Automata) FA means same, represents an abstract mathematical model of computation for the class of regular languages.

What is finite state automata used for?

A finite state machine (sometimes called a finite state automaton) is a computation model that can be implemented with hardware or software and can be used to simulate sequential logic and some computer programs. Finite state automata generate regular languages.


2 Answers

  • The update action (PUT method) is not appropriate because PUT is suppose to be idempotent. The only this would be possible is if the state was sent as part of the representation. This is inconsistet with an "event". Is this correct?

Correct.

  • Since, events aren't idempotent, then the a POST must be used. But, to which resource? Is there a subresource for each possible event? Or, is there one (/updatestate) that takes as its representation the event to trigger and any parameters to the event?

You can do it both ways. You can support both in the same application, with variation in event types being determined by either the incoming document or the receiving resource. Personally, I would prefer to do it by differing document types, but that's just my opinion. If you do go the multiple resources route, make sure they're discoverable (i.e., by having links to each of them described in the document returned when you GET their parent resource).

  • Since the state of the resource is modified by an event triggered potentially by another resource, should the create action accept changes to the state attribute (or any other attributes that are dependent on the state machine)?

Up to you; there's no real reason why you have to pay close attention to any particular attribute on creation. (You could rationalize this by saying that the state changes to a proper initial state for the state machine immediately after creation.) In the state machines I've done, the creation was by a POST anyway (and of a different – rather complex – document) so the whole thing was moot, but if you allow multiple initial states then it makes sense to take a “this is my preferred starting state” hint in the creation document. To be clear, just because the user wants it doesn't mean you have to do it; whether you want to complain to the user when you reject a suggestion of theirs is your call.

  • List item

[Stock answer.]

like image 176
Donal Fellows Avatar answered Oct 16 '22 17:10

Donal Fellows


A little late to the party here, but I was researching this exact issue for myself and found that the gem I'm currently using to manage my state machines (state_machine by pluginaweek) has some methods that deal with this issue quite nicely.

When used with ActiveRecord (and I'm assuming other persistence layers as well), it provides a #state_event= method that accepts a string representation of the event you would like to fire. See documentation here.

# For example,  vehicle = Vehicle.create          # => #<Vehicle id: 1, name: nil, state: "parked"> vehicle.state_event               # => nil vehicle.state_event = 'invalid' vehicle.valid?                    # => false vehicle.errors.full_messages      # => ["State event is invalid"]  vehicle.state_event = 'ignite' vehicle.valid?                    # => true vehicle.save                      # => true vehicle.state                     # => "idling" vehicle.state_event               # => nil  # Note that this can also be done on a mass-assignment basis:  vehicle = Vehicle.create(:state_event => 'ignite')  # => #<Vehicle id: 1, name: nil, state: "idling"> vehicle.state                                       # => "idling" 

This allows you to simply add a state_event field in your resource's edit forms and get state transitions as easily as updating any other attribute.

Now we're obviously still using PUT to trigger events using this method, which isn't RESTful. The gem does, however, provide an interesting example that at least "feels" quite RESTful, despite it using the same non-RESTful method under the covers.

As you can see here and here, the gem's introspection capabilities allow you to present in your forms either the event you would like to fire or the name of that event's resulting state.

<div class="field">   <%= f.label :state %><br />   <%= f.collection_select :state_event, @user.state_transitions, :event, :human_to_name, :include_blank => @user.human_state_name %> </div>  <div class="field">   <%= f.label :access_state %><br />   <%= f.collection_select :access_state_event, @user.access_state_transitions, :event, :human_event, :include_blank => "don't change" %> </div> 

Using the latter technique, you get simple form-based updating of the model's state to any valid next state without having to write any extra code. It's not technically RESTful, but it allows you to easily present it that way in the UI.

The cleanliness of this technique combined with the inherent conflicts in trying to cast an event-based state machine into a simple RESTful resource was enough to satisfy me, so hopefully it provides some insight to you as well.

like image 22
Luke Avatar answered Oct 16 '22 17:10

Luke