Abstract Notifier
Important
The project has been merged into active_delivery. This repository is no longer maintained.
Abstract Notifier is a tool which allows you to describe/model any text-based notifications (such as Push Notifications) the same way Action Mailer does for email notifications.
Abstract Notifier (as the name states) doesn't provide any specific implementation for sending notifications. Instead, it offers tools to organize your notification-specific code and make it easily testable.
📖 Read the introduction post: "Crafting user notifications in Rails with Active Delivery"
Requirements:
- Ruby ~> 2.4
NOTE: although most of the examples in this readme are Rails-specific, this gem could be used without Rails/ActiveSupport.
Installation
Add this line to your application's Gemfile:
gem "abstract_notifier"
And then execute:
$ bundle
Usage
Notifier class is very similar to Action Mailer mailer class with notification
method instead of a mail
method:
class EventsNotifier < ApplicationNotifier
def canceled(profile, event)
notification(
# the only required option is `body`
body: "Event #{event.title} has been canceled",
# all other options are passed to delivery driver
identity: profile.notification_service_id
)
end
end
# send notification later
EventsNotifier.canceled(profile, event).notify_later
# or immediately
EventsNotifier.canceled(profile, event).notify_now
To perform actual deliveries you must configure a delivery driver:
class ApplicationNotifier < AbstractNotifier::Base
self.driver = MyFancySender.new
end
A driver could be any callable Ruby object (i.e., anything that responds to #call
).
That's a developer responsibility to implement the driver (we do not provide any drivers out-of-the-box; at least yet).
You can set different drivers for different notifiers.
Parameterized notifiers
Abstract Notifier support parameterization the same way as Action Mailer:
class EventsNotifier < ApplicationNotifier
def canceled(event)
notification(
body: "Event #{event.title} has been canceled",
identity: params[:profile].notification_service_id
)
end
end
EventsNotifier.with(profile: profile).canceled(event).notify_later
Defaults
You can specify default notification fields at a class level:
class EventsNotifier < ApplicationNotifier
# `category` field will be added to the notification
# if missing
default category: "EVENTS"
# ...
end
NOTE: when subclassing notifiers, default parameters are merged.
You can also specify a block or a method name as the default params generator.
This could be useful in combination with the #notification_name
method to generate dynamic payloads:
class ApplicationNotifier < AbstractNotifier::Base
default :build_defaults_from_locale
private
def build_defaults_from_locale
{
subject: I18n.t(notification_name, scope: [:notifiers, self.class.name.underscore])
}
end
end
Background jobs / async notifications
To use notify_later
you must configure async_adapter
.
We provide Active Job adapter out-of-the-box and use it if Active Job is present.
The custom async adapter must implement enqueue
method:
class MyAsyncAdapter
# adapters may accept options
def initialize(options = {})
end
# `enqueue` method accepts notifier class and notification
# payload.
# We need to know notifier class to use its driver.
def enqueue(notifier_class, payload)
# your implementation here
end
end
# Configure globally
AbstractNotifier.async_adapter = MyAsyncAdapter.new
# or per-notifier
class EventsNotifier < AbstractNotifier::Base
self.async_adapter = MyAsyncAdapter.new
end
Delivery modes
For test/development purposes there are two special global delivery modes:
# Track all sent notifications without peforming real actions.
# Required for using RSpec matchers.
#
# config/environments/test.rb
AbstractNotifier.delivery_mode = :test
# If you don't want to trigger notifications in development,
# you can make Abstract Notifier no-op.
#
# config/environments/development.rb
AbstractNotifier.delivery_mode = :noop
# Default delivery mode is "normal"
AbstractNotifier.delivery_mode = :normal
NOTE: we set delivery_mode = :test
if RAILS_ENV
or RACK_ENV
env variable is equal to "test".
Otherwise add require "abstract_notifier/testing"
to your spec_helper.rb
/ rails_helper.rb
manually.
NOTE: delivery mode affects all drivers.
Testing
Abstract Notifier provides two convinient RSpec matchers:
# for testing sync notifications (sent with `notify_now`)
expect { EventsNotifier.with(profile: profile).canceled(event).notify_now }
.to have_sent_notification(identify: "123", body: "Alarma!")
# for testing async notifications (sent with `notify_later`)
expect { EventsNotifier.with(profile: profile).canceled(event).notify_later }
.to have_enqueued_notification(identify: "123", body: "Alarma!")
Abstract Notifier provides two convinient minitest assertions:
require 'abstract_notifier/testing/minitest'
class EventsNotifierTestCase < Minitest::Test
include AbstractNotifier::TestHelper
test 'canceled' do
assert_notifications_sent 1, identify: "123", body: "Alarma!" do
EventsNotifier.with(profile: profile).canceled(event).notify_now
end
assert_notifications_enqueued 1, identify: "123", body: "Alarma!" do
EventsNotifier.with(profile: profile).canceled(event).notify_later
end
end
end
NOTE: test mode activated automatically if RAILS_ENV
or RACK_ENV
env variable is equal to "test". Otherwise add require "abstract_notifier/testing/rspec"
to your spec_helper.rb
/ rails_helper.rb
manually. This is also required if you're using Spring in test environment (e.g. with help of spring-commands-rspec).
Related projects
Active Delivery is the next-level abstraction which allows combining multiple notification channels in one place.
Abstract Notifier provides a notifier line for Active Delivery:
class ApplicationDelivery < ActiveDelivery::Base
# Add notifier line to you delivery
register_line :notifier, ActiveDelivery::Lines::Notifier,
# you may provide a resolver, which infers notifier class
# from delivery name (resolver is a callable).
resolver: ->(name) { resolve_somehow(name) }
end
NOTE: we automatically add :notifier
line with "*Delivery" -> *Notifier
resolution mechanism if #safe_constantize
method is defined for String, i.e., you don't have to configure the default notifier line when running Rails.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/palkan/abstract_notifier.
License
The gem is available as open source under the terms of the MIT License.