Expirable is a simple Rails mixin to handle deadlines in workflows. It builds on Delayed::Job and Clockwork to provide a painless approach to handling time-based events.
To install the Expirable and its dependencies, add expirable and an
appropriate Delayed::Jobs backend to your Gemfile:
gem 'delayed_job_active_record'
gem 'expirable'Next, set up Delayed::Job if you are not already using it in your project:
rails generate delayed_job:active_record
rake db:migrateFinally, set up Expirable itself by running rails g expirable:setup.
This will create a Clockwork configuration file called lib/clock.rb.
By default, it is configured to check deadlines at one minute past every hour.
See the Clockwork manual
for more information if you need to change this configuration.
To run Expirable, you will need to run a Clockwork worker and a Delayed::Job worker.
You can run the Clockwork worker by executing bundle exec clockwork lib/clock.rb
from the root of the Rails project, and Delayed::Job worker with ./bin/delayed_job run.
You can also use Foreman
to manage these processes. For example, if you run your Rails application on Unicorn, then
you can create a Procfile with the following content and run your application with foreman start:
cron: bundle exec clockwork lib/clock.rb
web: bundle exec unicorn_rails
worker: ./bin/delayed_job run
To be expirable, a class must do the following three things:
- include
Expirable; - define a
deadline_expiredinstance method that will be invoked when the model's deadline expires; and - define a class method named
newly_expiredthat returns a collection of model instances that have expired, but haven't yet received adeadline_expiredmessage.
As an example, the following model uses AASM and Expirable to transition tasks to a failed state when their deadline is passed:
class ExampleTask < ActiveRecord::Base
include AASM
include Expirable
scope :newly_expired,
-> { in_progress.where('deadline < ?', DateTime.now) }
aasm do
# ... state and event definitions skipped ...
event :deadline_expired do
transitions from: :in_progress, to: :missed
end
end
end