Rails State Machine and Public Activity for Customised Tracking
15 Jan 2019Depending 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.
ruby
def complete_task
@task.complete(extra_data= { user_id: current_user.id })
end
Calling
ruby
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:
ruby
en: states: tasks:
pending: 'Task has been Created'
completed: 'has been Completed'
cancelled: 'Task has been Cancelled'
Happy tracking! Image sourced from @grohsfabian on Unsplash