24
Timestamping in Active Record models
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 created_at
and updated_at
fields.
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 datetime
field.
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.completed?
, task.not_completed?
), record or overwrite a timestamp (task.complete
, task.complete!
), as well as record multiple timestamps at once (Task.complete_all
).
Additionally, the macro defines two scope methods for retrieving objects with and without a recorded timestamp (Task.completed
, Task.not_completed
).
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. aasm
, workflow
or 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.
24