Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ephemeral notifiers #382

Merged
merged 11 commits into from
Jan 29, 2024
11 changes: 7 additions & 4 deletions app/models/concerns/noticed/deliverable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ module Deliverable

attribute :params, default: {}

if Rails.gem_version >= Gem::Version.new("7.1.0.alpha")
serialize :params, coder: Coder
else
serialize :params, Coder
# Ephemeral notifiers cannot serialize params since they aren't ActiveRecord backed
if respond_to? :serialize
if Rails.gem_version >= Gem::Version.new("7.1.0.alpha")
serialize :params, coder: Coder
else
serialize :params, Coder
end
end
end

Expand Down
17 changes: 13 additions & 4 deletions app/models/noticed/deliverable/deliver_by.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,23 @@ def validate!
end

def perform_later(event_or_notification, options = {})
options[:wait] = evaluate_option(:wait, event_or_notification) if config.has_key?(:wait)
options[:wait_until] = evaluate_option(:wait_until, event_or_notification) if config.has_key?(:wait_until)
options[:queue] = evaluate_option(:queue, event_or_notification) if config.has_key?(:queue)
options[:priority] = evaluate_option(:priority, event_or_notification) if config.has_key?(:priority)
options[:wait] ||= evaluate_option(:wait, event_or_notification) if config.has_key?(:wait)
options[:wait_until] ||= evaluate_option(:wait_until, event_or_notification) if config.has_key?(:wait_until)
options[:queue] ||= evaluate_option(:queue, event_or_notification) if config.has_key?(:queue)
options[:priority] ||= evaluate_option(:priority, event_or_notification) if config.has_key?(:priority)

constant.set(options).perform_later(name, event_or_notification)
end

def ephemeral_perform_later(notifier, recipient, params, options = {})
options[:wait] ||= evaluate_option(:wait, recipient) if config.has_key?(:wait)
options[:wait_until] ||= evaluate_option(:wait_until, recipient) if config.has_key?(:wait_until)
options[:queue] ||= evaluate_option(:queue, recipient) if config.has_key?(:queue)
options[:priority] ||= evaluate_option(:priority, recipient) if config.has_key?(:priority)

constant.set(options).perform_later(name, "#{notifier}::Notification", recipient: recipient, params: params, overrides: config)
end

def evaluate_option(name, context)
option = config[name]

Expand Down
42 changes: 42 additions & 0 deletions app/models/noticed/ephemeral.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
module Noticed
class Ephemeral
include ActiveModel::Model
include ActiveModel::Attributes
include Noticed::Deliverable

class Notification
include ActiveModel::Model
include ActiveModel::Attributes

attribute :recipient
attribute :event

delegate :params, :record, to: :event

def self.new_with_params(recipient, params)
instance = new(recipient: recipient)
instance.event = module_parent.new(params: params)
instance
end
end

attribute :params, default: {}

def deliver(recipients)
recipients = Array.wrap(recipients)
bulk_delivery_methods.each do |_, deliver_by|
deliver_by.ephemeral_perform_later(self.class.name, recipients, params)
end

recipients.each do |recipient|
delivery_methods.each do |_, deliver_by|
deliver_by.ephemeral_perform_later(self.class.name, recipient, params)
end
end
end

def record
params[:record]
end
end
end
2 changes: 1 addition & 1 deletion lib/generators/noticed/delivery_method_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class DeliveryMethodGenerator < Rails::Generators::NamedBase
desc "Generates a class for a custom delivery method with the given NAME."

def generate_notification
template "application_delivery_method.rb", "app/notifiers/delivery_methods/application_delivery_method.rb"
template "application_delivery_method.rb", "app/notifiers/application_delivery_method.rb"
template "delivery_method.rb", "app/notifiers/delivery_methods/#{singular_name}.rb"
end
end
Expand Down
1 change: 1 addition & 0 deletions lib/noticed.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ def self.deprecator # :nodoc:
module BulkDeliveryMethods
autoload :Discord, "noticed/bulk_delivery_methods/discord"
autoload :Slack, "noticed/bulk_delivery_methods/slack"
autoload :Test, "noticed/bulk_delivery_methods/test"
autoload :Webhook, "noticed/bulk_delivery_methods/webhook"
end

Expand Down
12 changes: 9 additions & 3 deletions lib/noticed/bulk_delivery_method.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,15 @@ class BulkDeliveryMethod < ApplicationJob

attr_reader :config, :event

def perform(delivery_method_name, event)
@event = event
@config = event.bulk_delivery_methods.fetch(delivery_method_name).config
def perform(delivery_method_name, event, recipients: nil, params: {}, overrides: {})
# Ephemeral notifications
if event.is_a? String
@event = @notification.event
@config = overrides
else
@event = event
@config = event.bulk_delivery_methods.fetch(delivery_method_name).config.merge(overrides)
end

return false if config.has_key?(:if) && !evaluate_option(:if)
return false if config.has_key?(:unless) && evaluate_option(:unless)
Expand Down
11 changes: 11 additions & 0 deletions lib/noticed/bulk_delivery_methods/test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module Noticed
module BulkDeliveryMethods
class Test < DeliveryMethod
class_attribute :delivered, default: []

def deliver
delivered << notification
end
end
end
end
17 changes: 12 additions & 5 deletions lib/noticed/delivery_method.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,19 @@ class DeliveryMethod < Noticed.parent_class.constantize
delegate :recipient, to: :notification
delegate :record, :params, to: :event

def perform(delivery_method_name, notification, overrides: {})
@notification = notification
@event = notification.event
def perform(delivery_method_name, notification, recipient: nil, params: {}, overrides: {})
# Ephemeral notifications
if notification.is_a? String
@notification = notification.constantize.new_with_params(recipient, params)
@event = @notification.event
@config = overrides
else
@notification = notification
@event = notification.event

# Look up config from Notifier and merge overrides
@config = event.delivery_methods.fetch(delivery_method_name).config.merge(overrides)
# Look up config from Notifier and merge overrides
@config = event.delivery_methods.fetch(delivery_method_name).config.merge(overrides)
end

return false if config.has_key?(:if) && !evaluate_option(:if)
return false if config.has_key?(:unless) && evaluate_option(:unless)
Expand Down
17 changes: 17 additions & 0 deletions test/dummy/app/notifiers/ephemeral_notifier.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
class EphemeralNotifier < Noticed::Ephemeral
bulk_deliver_by :test

deliver_by :test do |config|
config.wait = 5.minutes
end

deliver_by :email do |config|
config.mailer = "UserMailer"
config.method = "new_comment"
config.args = :email_args
end

def email_args(recipient)
[recipient]
end
end
16 changes: 16 additions & 0 deletions test/ephemeral_notifier_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
require "test_helper"

class EphemeralNotifierTest < ActiveSupport::TestCase
include ActionMailer::TestHelper
include ActiveJob::TestHelper

test "can enqueue delivery methods" do
assert_enqueued_jobs 3 do
EphemeralNotifier.new(params: {foo: :bar}).deliver(User.last)
end

assert_emails 1 do
perform_enqueued_jobs
end
end
end