Recording a timestamp in order to mark that an event occurred to an object is a common practice when dealing with Active Record models. Active Record itself gives us a good example of such an approach. It automatically stores the time when a record was created or updated in the
Similarly, we can create custom timestamp fields by adding
datetime columns in the database. Yet in order to conveniently manage such fields, we would need to accompany each of them with a bunch of methods in the model class. The more timestamp fields we want to manage, the more methods we need. As a result, the model gets bigger and bigger.
That's where the
active_record-events gem comes into play.
The gem provides us with the
has_event macro which adds convenience methods on top of a
Let's look at the following example. Assume we have a
Task model with a
completed_at field. Now, let's add the
has_event macro inside the model class:
class Task < ActiveRecord::Base has_event :complete end
As a result, we get plenty of methods for managing the field without the need to define them explicitly.
task = Task.create! task.completed? # => false task.complete task.completed? # => true task.completed_at # => Sun, 20 Dec 2020 16:54:11 UTC +00:00
The generated methods allow us to check if a timestamp was recorded (
task.not_completed?), record or overwrite a timestamp (
task.complete!), as well as record multiple timestamps at once (
Additionally, the macro defines two scope methods for retrieving objects with and without a recorded timestamp (
All of this can be achieved at the cost of a simple one-liner.
Before we can record a timestamp, we need to add the
completed_at column to the
tasks table in our database. In order to do that, we could manually create a migration file, but it's much easier to use a generator provided by the gem:
$ rails generate active_record:event task complete
This will create a necessary migration and insert a
has_event statement into the corresponding model class.
# db/migrate/XXX_add_completed_at_to_tasks.rb class AddCompletedAtToTasks < ActiveRecord::Migration[6.0] def change add_column :tasks, :completed_at, :datetime end end
# app/models/task.rb class Task < ActiveRecord::Base has_event :complete end
In more complex scenarios, you might consider using a state machine gem (e.g.
statesman). Another alternative is
ActiveRecord::Enum, which offers similar functionality with a different underlying mechanism.
This article was originally published on Planet Ruby as a part of the Ruby Advent Calendar series.