diff --git a/app/models/concerns/noticed/deliverable.rb b/app/models/concerns/noticed/deliverable.rb index a15dc3aa..f45c3cfd 100644 --- a/app/models/concerns/noticed/deliverable.rb +++ b/app/models/concerns/noticed/deliverable.rb @@ -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 diff --git a/app/models/noticed/deliverable/deliver_by.rb b/app/models/noticed/deliverable/deliver_by.rb index 2b7ff89d..9778af1f 100644 --- a/app/models/noticed/deliverable/deliver_by.rb +++ b/app/models/noticed/deliverable/deliver_by.rb @@ -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) + end + def evaluate_option(name, context) option = config[name] diff --git a/app/models/noticed/ephemeral.rb b/app/models/noticed/ephemeral.rb new file mode 100644 index 00000000..14976055 --- /dev/null +++ b/app/models/noticed/ephemeral.rb @@ -0,0 +1,53 @@ +module Noticed + class Ephemeral + include ActiveModel::Model + include ActiveModel::Attributes + include Noticed::Deliverable + + attribute :record + attribute :params, default: {} + + 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 + + # Dynamically define Notification on each Ephemeral Notifier + def self.inherited(notifier) + super + notifier.const_set :Notification, Class.new(Noticed::Ephemeral::Notification) + end + + 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 + + def notification_methods(&block) + const_get(:Notification).class_eval(&block) + end + end +end diff --git a/lib/noticed.rb b/lib/noticed.rb index 444a34c2..18d3b4b9 100644 --- a/lib/noticed.rb +++ b/lib/noticed.rb @@ -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 diff --git a/lib/noticed/bulk_delivery_method.rb b/lib/noticed/bulk_delivery_method.rb index 1e3de5ad..58e27a17 100644 --- a/lib/noticed/bulk_delivery_method.rb +++ b/lib/noticed/bulk_delivery_method.rb @@ -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) diff --git a/lib/noticed/bulk_delivery_methods/test.rb b/lib/noticed/bulk_delivery_methods/test.rb new file mode 100644 index 00000000..2c629453 --- /dev/null +++ b/lib/noticed/bulk_delivery_methods/test.rb @@ -0,0 +1,11 @@ +module Noticed + module BulkDeliveryMethods + class Test < DeliveryMethod + class_attribute :delivered, default: [] + + def deliver + delivered << notification + end + end + end +end diff --git a/lib/noticed/delivery_method.rb b/lib/noticed/delivery_method.rb index c2d107a4..ad27246c 100644 --- a/lib/noticed/delivery_method.rb +++ b/lib/noticed/delivery_method.rb @@ -12,9 +12,15 @@ 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 + else + @notification = notification + @event = notification.event + end # Look up config from Notifier and merge overrides @config = event.delivery_methods.fetch(delivery_method_name).config.merge(overrides) diff --git a/test/dummy/app/notifiers/ephemeral_notifier.rb b/test/dummy/app/notifiers/ephemeral_notifier.rb new file mode 100644 index 00000000..9ab82da7 --- /dev/null +++ b/test/dummy/app/notifiers/ephemeral_notifier.rb @@ -0,0 +1,18 @@ +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 + config.params = -> { {recipient: recipient} } + end + + def email_args(recipient) + [recipient] + end +end diff --git a/test/ephemeral_notifier_test.rb b/test/ephemeral_notifier_test.rb new file mode 100644 index 00000000..379cc5b2 --- /dev/null +++ b/test/ephemeral_notifier_test.rb @@ -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