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.