A flexible notification management system for Rails applications that automatically triggers notifications based on ActiveRecord model lifecycle events and attribute changes.
- 🔔 Trigger notifications on model lifecycle events (create, update, destroy)
- 🎯 Trigger notifications based on attribute status changes
- 🏷️ Tag-based notification clearing to prevent duplicate notifications
- 👥 Support for multiple recipients per notification
- 🔄 Polymorphic notification system supporting any notifiable model
- ⏭️ Conditional notification skipping
- 🎨 Extensible architecture with customizable notification managers
- Ruby >= 3.2.0
- Rails >= 6.1
Add this line to your application's Gemfile:
gem 'notification_manager'And then execute:
$ bundle installFirst, generate the notification model and migration:
$ rails generate notification_manager:notifications
$ rails db:migrateThis creates:
app/models/notification.rb- The Notification modeldb/migrate/[timestamp]_create_notifications.rb- The database migration
The notifications table includes:
user_id- Reference to the user receiving the notificationnotifiable- Polymorphic reference to any notifiable modeldescription- Notification messagelink- Optional link for the notificationtag- Tag for grouping/clearing related notificationsopened- Boolean flag to track read/unread status
Add the ActiveRecordNotifiable concern to any model you want to send notifications from:
class Order < ApplicationRecord
include NotificationManager::NotificationSources::ActiveRecordNotifiable
# Your model code...
endThis adds an after_commit callback that automatically triggers notifications based on your notification manager configuration.
Create a notification manager class for your notifiable model. The class name should follow the pattern [ModelName]NotificationManager:
# app/notification_managers/order_notification_manager.rb
class OrderNotificationManager < NotificationManager::NotificationManagers::Base
def notification_steps
[
order_created_notification,
order_shipped_notification,
order_delivered_notification
]
end
private
def order_created_notification
NotificationManager::NotificationDefinitions::Base.new(
lifecycle_triggers: [:created],
status_triggers: [],
notification_tags_to_clear: [],
notifications_to_send: [
{
recipients: :seller_recipient,
notification: :send_order_created_notification
}
]
)
end
def order_shipped_notification
NotificationManager::NotificationDefinitions::Base.new(
lifecycle_triggers: [:updated],
status_triggers: [
{
attr: :status,
triggers: -> { ['shipped'] }
}
],
notification_tags_to_clear: ['order_pending'],
notifications_to_send: [
{
recipients: :buyer_recipient,
notification: :send_order_shipped_notification
}
]
)
end
def order_delivered_notification
NotificationManager::NotificationDefinitions::Base.new(
lifecycle_triggers: [:updated],
status_triggers: [
{
attr: :status,
triggers: -> { ['delivered'] }
}
],
notification_tags_to_clear: ['order_shipped'],
notifications_to_send: [
{
recipients: :buyer_recipient,
notification: :send_order_delivered_notification
}
]
)
end
# Recipient methods return arrays of users
def seller_recipient
[notifiable.seller]
end
def buyer_recipient
[notifiable.buyer]
end
# Notification sender methods
def send_order_created_notification(order, recipient)
Notification.create!(
user: recipient,
notifiable: order,
description: "New order ##{order.id} received",
link: "/orders/#{order.id}",
tag: 'order_created'
)
end
def send_order_shipped_notification(order, recipient)
Notification.create!(
user: recipient,
notifiable: order,
description: "Your order ##{order.id} has been shipped",
link: "/orders/#{order.id}",
tag: 'order_shipped'
)
end
def send_order_delivered_notification(order, recipient)
Notification.create!(
user: recipient,
notifiable: order,
description: "Your order ##{order.id} has been delivered",
link: "/orders/#{order.id}",
tag: 'order_delivered'
)
end
endEach notification definition accepts the following options:
Specifies which lifecycle events should trigger the notification:
:created- Triggers when the record is created:updated- Triggers when the record is updated:destroyed- Triggers when the record is destroyed
Defines attribute-based conditions for triggering notifications:
status_triggers: [
{
attr: :status, # The attribute to watch
triggers: -> { ['value'] } # Lambda returning array of trigger values
}
]Use -> { '*' } to trigger on any change to the attribute.
Array of notification tags to clear before sending new notifications. This prevents duplicate or outdated notifications:
notification_tags_to_clear: ['order_pending', 'order_processing']Defines which notifications to send:
notifications_to_send: [
{
recipients: :method_name, # Method that returns array of users
notification: :method_name # Method that creates the notification
}
]You can temporarily skip notifications for a specific operation:
order = Order.new(...)
order.skip_notifications = true
order.savedef admin_recipients
User.where(role: 'admin').to_a
end
def stakeholder_recipients
[notifiable.owner, notifiable.manager].compact
endstatus_triggers: [
{
attr: :priority,
triggers: -> { ['high', 'critical'] }
},
{
attr: :assigned_to_id,
triggers: -> { '*' } # Trigger on any assignment change
}
]Clear previous notifications when sending new ones to maintain a clean notification state:
NotificationManager::NotificationDefinitions::Base.new(
lifecycle_triggers: [:updated],
status_triggers: [{ attr: :status, triggers: -> { ['completed'] } }],
notification_tags_to_clear: ['task_pending', 'task_in_progress'],
notifications_to_send: [
{ recipients: :task_owner, notification: :send_completion_notification }
]
)After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install.
Bug reports and pull requests are welcome on GitHub at https://github.com/CodeTectonics/notification-manager. This project is intended to be a safe, welcoming space for collaboration.
The gem is available as open source under the terms of the MIT License.