Building advanced notification systems in Ruby on Rails requires careful consideration of multiple components and delivery mechanisms. A well-designed notification system enhances user engagement and provides timely information.
Real-time notifications form the foundation of modern web applications. Using Action Cable, we can establish WebSocket connections for instant message delivery:
# app/channels/notification_channel.rb
class NotificationChannel < ApplicationCable::Channel
def subscribed
stream_for current_user
end
end
# app/services/real_time_notifier.rb
class RealTimeNotifier
def self.notify(user, message)
NotificationChannel.broadcast_to(
user,
message: message,
timestamp: Time.current
)
end
end
Email notifications require proper template management and delivery tracking. Here’s an implementation using ActiveJob and Action Mailer:
class NotificationMailer < ApplicationMailer
def notify(user, content)
@content = content
@user = user
mail(
to: @user.email,
subject: content.subject,
template_name: content.template
)
end
end
class EmailNotificationJob < ApplicationJob
def perform(notification_id)
notification = Notification.find(notification_id)
NotificationMailer.notify(
notification.user,
notification.content
).deliver_now
notification.update(delivered_at: Time.current)
end
end
Notification grouping prevents overwhelming users with similar messages:
class NotificationGrouper
def self.group_notifications(user, time_window: 1.hour)
user.notifications
.where(created_at: time_window.ago..Time.current)
.group_by(&:category)
.transform_values do |notifications|
combine_notifications(notifications)
end
end
private
def self.combine_notifications(notifications)
return notifications.first if notifications.size == 1
NotificationCombiner.new(notifications).combine
end
end
Rate limiting ensures responsible notification delivery:
class NotificationRateLimiter
def initialize(user)
@user = user
@redis = Redis.new
end
def can_send_notification?
key = "notification_count:#{@user.id}"
count = @redis.get(key).to_i
return false if count >= max_notifications_per_hour
@redis.multi do
@redis.incr(key)
@redis.expire(key, 1.hour)
end
true
end
private
def max_notifications_per_hour
@user.notification_limit || 10
end
end
Custom user preferences allow personalized notification experiences:
class NotificationPreference < ApplicationRecord
belongs_to :user
validates :channel, presence: true
validates :frequency, inclusion: { in: %w[immediate daily weekly] }
scope :active, -> { where(active: true) }
def self.channels
%w[email push sms in_app]
end
end
class User < ApplicationRecord
has_many :notification_preferences
def notify(content)
active_channels.each do |channel|
NotificationDispatcher.dispatch(
self,
content,
channel
)
end
end
def active_channels
notification_preferences.active.pluck(:channel)
end
end
Analytics tracking helps measure notification effectiveness:
class NotificationAnalytics
def self.track_delivery(notification)
Analytics.track(
event: 'notification_delivered',
properties: {
notification_id: notification.id,
user_id: notification.user_id,
channel: notification.channel,
category: notification.category
}
)
end
def self.track_interaction(notification, action)
Analytics.track(
event: 'notification_interaction',
properties: {
notification_id: notification.id,
user_id: notification.user_id,
action: action,
timestamp: Time.current
}
)
end
end
Push notifications extend reach to mobile devices:
class PushNotificationService
def initialize(user)
@user = user
@fcm = FCM.new(ENV['FCM_SERVER_KEY'])
end
def send_push(notification)
return unless @user.device_tokens.present?
response = @fcm.send_notification(
@user.device_tokens,
notification: {
title: notification.title,
body: notification.content,
click_action: notification.action_url
}
)
handle_response(response, notification)
end
private
def handle_response(response, notification)
if response[:success] == 1
notification.update(
delivered_at: Time.current,
status: :delivered
)
else
notification.update(status: :failed)
NotificationLogger.error(response[:error])
end
end
end
Template management system for consistent notification content:
class NotificationTemplate < ApplicationRecord
validates :identifier, presence: true, uniqueness: true
validates :content, presence: true
def render(variables = {})
template = Liquid::Template.parse(content)
template.render(variables.stringify_keys)
end
end
class TemplateManager
def self.get_template(identifier)
Rails.cache.fetch("notification_template:#{identifier}") do
NotificationTemplate.find_by!(identifier: identifier)
end
end
def self.render_notification(identifier, variables)
template = get_template(identifier)
template.render(variables)
end
end
A comprehensive notification system implementation requires careful consideration of scalability, reliability, and user experience. Regular monitoring and optimization ensure effective message delivery across all channels.
The system should handle edge cases gracefully and provide clear feedback when issues occur. Proper error handling and logging are essential for maintaining system health and debugging problems.
Remember to implement proper security measures, including authentication and authorization, to protect sensitive notification data and prevent unauthorized access to the notification system.
Regular maintenance and updates keep the notification system current with evolving user needs and technological advancements. Continuous improvement based on user feedback and analytics data helps optimize the system’s effectiveness.