• This repository has been archived on 29/Aug/2023
  • Stars
    star
    116
  • Rank 303,823 (Top 6 %)
  • Language
    Ruby
  • License
    MIT License
  • Created almost 6 years ago
  • Updated about 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

ActionMailer-like interface for any type of notifications

Gem Version Build

Abstract Notifier

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"

Sponsored by Evil Martians

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

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.

More Repositories

1

logidze

Database changes log for Rails
Ruby
1,596
star
2

action_policy

Authorization framework for Ruby/Rails applications
Ruby
1,404
star
3

isolator

Detect non-atomic interactions within DB transactions
Ruby
850
star
4

anyway_config

Configuration library for Ruby gems and applications
Ruby
735
star
5

active_delivery

Ruby framework for keeping all types of notifications (mailers, push notifications, whatever) in one place
Ruby
595
star
6

n_plus_one_control

RSpec and Minitest matchers to prevent N+1 queries problem
Ruby
556
star
7

store_attribute

ActiveRecord extension which adds typecasting to store accessors
Ruby
376
star
8

view_component-contrib

A collection of extension and developer tools for ViewComponent
Ruby
345
star
9

litecable

Lightweight Action Cable implementation (Rails-free)
Ruby
298
star
10

acli

Action Cable command-line client
Ruby
230
star
11

action-cable-testing

Action Cable testing utils
Ruby
212
star
12

rubanok

Parameters-based transformation DSL
Ruby
205
star
13

active_event_store

Rails Event Store in a more Rails way
Ruby
180
star
14

action_policy-graphql

Action Policy integration for GraphQL
Ruby
151
star
15

engems

Rails component-based architecture on top of engines and gems (showroom)
Ruby
136
star
16

influxer

InfluxDB ActiveRecord-style
Ruby
118
star
17

turbo-music-drive

Exploring Turbo future features while building a music library app
Ruby
102
star
18

wsdirector

All the world's a server, and all the men and women merely clients
Ruby
102
star
19

gem-check

GemCheck: Writing Better Ruby Gems Checklist
CSS
95
star
20

pgrel

ActiveRecord extension for querying hstore and jsonb
Ruby
93
star
21

rbytes

Ruby Bytes helps you build, deploy and install Ruby and Rails application templates
Ruby
68
star
22

faqueue

Researching background jobs fairness
Ruby
66
star
23

downstream

Straightforward way to implement communication between Rails Engines using the Publish-Subscribe pattern.
Ruby
51
star
24

newgem

Custom script to generate new gems
Ruby
35
star
25

turbo-view-transitions

View Transitions API for Turbo
TypeScript
33
star
26

influx_udp

Erlang InfluxDB UDP writer
Erlang
31
star
27

ruby-dip

Docker-based development environment for hacking Ruby MRI
Dockerfile
30
star
28

erlgrpc

GRPC client for Erlang
Erlang
25
star
29

as3_p2plocal

as3 lib for local p2p connections (serverless rtmfp)
ActionScript
25
star
30

rails-intest-views

Generate view templates dynamically in Rails tests
Ruby
21
star
31

docsify-namespaced

Docsify plugin to work with namespaces
JavaScript
12
star
32

sharelatex-vagrant-ansible

Vagrant + Ansible configuration for ShareLatex
Shell
12
star
33

slog-spy

Slog handler to temporary deliver formatted verbose logs to an arbitrary target
Go
12
star
34

docs-example

Playground for dealing with documentation engines
7
star
35

ruby-russia-2020

RubyRussia 2020 "Frontendless Rails" workshop demo app
Ruby
6
star
36

engine-cable-app

Experimenting with Action Cable and engines
Ruby
6
star
37

rails-on-wasm-playground

A minimal Rails app to showcase Rails on Wasm features
Ruby
5
star
38

palkan

It's me
4
star
39

ruby-compatibility-examples

Collections of reproduction cases for TruffleRuby vs. MRI (in)compatibility
Ruby
3
star
40

erffmpeg

Erlang wrapper for some ffmpeg
C
3
star
41

th-dummy

TH Dummy
Ruby
2
star
42

ulitos

Erlang utils modules
Erlang
2
star
43

meetings

Good old Teachbase Meetings client
ActionScript
2
star
44

rebar_templates

Custom rebar templates
Erlang
1
star
45

macos-setup

Shell
1
star
46

bitrix-orm

Bitrix kinda ORM for IBlockElements and CUser.
PHP
1
star
47

anycable-elements

Web Components for AnyCable console
JavaScript
1
star
48

adventofcode2018

https://adventofcode.com
Rust
1
star
49

tb_utils

ActionScript 3 library
ActionScript
1
star