Rails State Machine and Public Activity for Customised Tracking

15 Jan 2019, by Andrija Petkovic

Rails State Machine Activity for Customised Tracking

Depending on how complex a web application might get, making use of good state driven development will help with records that require transitions. And, instead of using booleans we will be using states with the help of a gem called State Machines. But what if we wanted to track these transitions and everything that happens to the record at that point in time? Well, there's a gem for that too! Public Activity comes to the rescue! With its inbuilt tracking of models it will record any CRUD events on a model. But there lies a problem. We know that a model using state machine will record the changes to a model and in the case of State Machine, it will update the state attribute of the model and Public Activity will record the change with the key ```model.updated```. For us, that wasn't enough detail. We need to track the state that was being transitioned. For our model called ```Task``` we have multiple states that have been added in a ```TaskStateable``` concern and all the ```after_transition``` events associated with each transition. For a Task, once it was created it would be in a ```Pending``` state, followed by either a ```Completed``` or ```Cancelled``` state through the respective events: ``` event :cancel do transition pending: :cancelled end event :complete do transition pending: :completed end ``` Now that we have the events we need to create the ```after_transition``` callbacks within the same concern as per State Machines docs, BUT we also need to include a custom method that we can call on the Task model that will allow us to create custom ```PublicActivity``` records with the state transition and any other data we may wish to include that will be passed when the transition is called and stored in the parameters key as hashes: ``` def state_check(extra_data=nil) @activity = self.create_activity(key: 'task.' + self.state) @activity.parameters.merge!(extra_data: extra_data) if extra_data.present? end ``` That looks great, now to incorporate that into our ```TaskStateable``` concern. State Machines allows us to pass arguments through through the transition event which is as simple as: ``` after_transition on: :complete do |task, transition| task.state_check(transition.args.first) end after_transition on: :cancel do |task, transition| task.state_check(transition.args.first) end ``` Awesome! Finally, just call the transition and watch the record be created from whatever method you preferred. For me it was from a controller action. ``` def complete_task @task.complete(extra_data= { user_id: current_user.id }) end ``` Calling ```PublicActivity::Activity.last``` will allow you to grab the last record you just created. Now you can do store what you wish in the custom created trackable events using PublicActivity, but also you can do things with these stored events such as including translations for these states so that they can be presented in a user friendly way: ``` en: states: tasks: pending: "Task has been Created" completed: "Task has been Completed" cancelled: "Task has been Cancelled" ``` Happy tracking! Image sourced from @grohsfabian on Unsplash.


Cookies help us deliver our services. By using our services, you agree to our use of cookies.