auto_alert
provides a simple DSL to declare conditions that should raise alerts on your ActiveRecord models.
Alerts are saved in a database table with a polymorphic association to the model which triggered their creation.
It's possible to define different conditions to raise, resolve, and re-raise an alert.
Imagine a to-do list application where tasks with a due date in the past should be flagged with an alert.
With auto_alert
, you can modify your Task model like so:
class Task < ApplicationRecord
acts_as_alertable
raises_alert :past_due,
on: :past_due_date?,
message: "was due"
private
def past_due_date?
due_date < Date.current and !done
end
Then you can call the scan_for_alerts!
on any task and, if past_due_date?
returns true
, an alert record belonging to that task will be created.
Instances of Task
will also have a past_due_alert
getter method to fetch their associated past_due
alert, if any.
Add this line to your application's Gemfile:
gem 'auto_alert'
And then execute:
$ bundle
auto_alert
assumes there is an Alert
model to store your alerts in the database.
An appropriate model can be built using the Rails generator by running something like, rails generate model Alert alertable:references resolved:boolean kind:string message:string
.
Then edit the resulting migration so that it looks like this example.
Make sure to edit the alert model file to register it with auto_alert
:
# app/models/alert.rb
class Alert < ApplicationRecord
acts_as_alert # add this line
end
(In the future I'd like to add an install
rake task to automate this process.)
auto_alert
assumes the alert relation uses the table called alerts
, but you can specify a different relation:
# app/models/task.rb
class Task < ApplicationRecord
acts_as_alertable # will default to using the Alert model
end
# app/models/task_list.rb
class TaskList < ApplicationRecord
acts_as_alertable with_table: :special_alerts # will look for a SpecialAlert model
end
Calling acts_as_alertable
in your model definition:
- Allows you to use the
raises_alert
class method to declare the conditions that should raise or resolve an alert - Registers that model instances have alerts
- Uses polymorphic association, so you can use all the instance methods it provides
- Also creates convenience getters for each kind of alert that model can raise (i.e.
task.past_due_alert
)
- Provides the instance method
scan_for_alerts!
, which creates or updates alert records according to the declaredraises_alert
conditions.
auto_alert
makes no assumptions about when you'd like to check if alerts should be raised or resolved.
For simple use cases, it can make sense to call scan_for_alerts!
on specific instances from your controller, or to hook into one of the ActiveRecord callbacks:
class MyModel < ApplicationRecord
acts_as_alertable
# ...
after_save :scan_for_alerts!
For more complicated use cases, especially where alert conditions rely on factors external to the model instance itself, it can make sense to use a job to asynchronously call scan_for_alerts!
on big batches of records.
You can also call the class method scan_all_unresolved!
on your Alert model to check if any unresolved alerts should be resolved. Again, it might be best to use a job to do this periodically in the background.
auto_alert
assumes there is an ActiveRecord model for the table alerts
, with at least:
- A polymorphic
alertable
reference (pointing to the record which triggered the alert) - A boolean
resolved
attribute (indicating the alert is no longer relevant) - A
kind
attribute (each alertable record has only one alert of each kind—past_due
in the example above)- Can be a text column or a Rails enumerable using a numeric column (example)
- A string
message
attribute describing the alert details
Instructions for creating such a model are in the Installation secion under Create Alert model.
The on
parameter to raises_alert
can take a method name as above, or a proc:
raises_alert :past_due,
on: ->(t) { t.due_date < Date.current and !t.done }
You can also pass a resolve_on
parameter to specify the condition for marking the alert resolved
.
A proc or method name is acceptable.
class Order < ApplicationRecord
acts_as_alertable
raises_alert :week_old,
on: ->(order) { order.placed <= Date.current - 1.week },
resolve_on: :shipped
# Now changing the order date won't resolve the alert, but marking it `shipped` will.
If no resolve_on
parameter is passed, the inverse of the on
condition is used.
By default, an alert which is resolved
will not be re-raised, even if the on
condition is met again.
If you pass the reraise
option true
, the alert's resolved
attribute will be updated to false
every time the on
condition is met.
class Temperature < ApplicationRecord
acts_as_alertable
raises_alert :too_cold,
on: ->(temp) { temp.farenheit < 32 },
reraise: true
You can also give a proc or method name to the reraise
option to use a condition other than the initial on
condition.
Alert messages can be built using a string, a proc, or a method name. When an alert is re-raised, its message will be built again.
The gem is available as open source under the terms of the MIT License.