active_delivery 0.4.3 → 1.0.0.rc2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/CHANGELOG.md +58 -1
 - data/LICENSE.txt +19 -17
 - data/README.md +503 -32
 - data/lib/.rbnext/3.0/active_delivery/base.rb +124 -0
 - data/lib/.rbnext/3.0/active_delivery/callbacks.rb +97 -0
 - data/lib/.rbnext/3.0/active_delivery/lines/base.rb +63 -0
 - data/lib/.rbnext/3.0/active_delivery/lines/mailer.rb +25 -0
 - data/lib/.rbnext/3.1/active_delivery/base.rb +124 -0
 - data/lib/.rbnext/3.1/active_delivery/lines/base.rb +63 -0
 - data/lib/abstract_notifier/async_adapters/active_job.rb +27 -0
 - data/lib/abstract_notifier/async_adapters.rb +16 -0
 - data/lib/abstract_notifier/base.rb +178 -0
 - data/lib/abstract_notifier/testing/minitest.rb +51 -0
 - data/lib/abstract_notifier/testing/rspec.rb +164 -0
 - data/lib/abstract_notifier/testing.rb +49 -0
 - data/lib/abstract_notifier/version.rb +5 -0
 - data/lib/abstract_notifier.rb +74 -0
 - data/lib/active_delivery/base.rb +156 -27
 - data/lib/active_delivery/callbacks.rb +25 -25
 - data/lib/active_delivery/ext/string_constantize.rb +24 -0
 - data/lib/active_delivery/lines/base.rb +24 -17
 - data/lib/active_delivery/lines/mailer.rb +7 -18
 - data/lib/active_delivery/lines/notifier.rb +53 -0
 - data/lib/active_delivery/raitie.rb +9 -0
 - data/lib/active_delivery/testing/rspec.rb +59 -12
 - data/lib/active_delivery/testing.rb +19 -5
 - data/lib/active_delivery/version.rb +1 -1
 - data/lib/active_delivery.rb +8 -0
 - metadata +61 -56
 - data/.gem_release.yml +0 -3
 - data/.github/ISSUE_TEMPLATE/bug_report.md +0 -24
 - data/.github/ISSUE_TEMPLATE/feature_request.md +0 -24
 - data/.github/PULL_REQUEST_TEMPLATE.md +0 -23
 - data/.github/workflows/docs-lint.yml +0 -72
 - data/.github/workflows/rspec-jruby.yml +0 -35
 - data/.github/workflows/rspec.yml +0 -51
 - data/.github/workflows/rubocop.yml +0 -21
 - data/.gitignore +0 -43
 - data/.mdlrc +0 -1
 - data/.rspec +0 -2
 - data/.rubocop-md.yml +0 -16
 - data/.rubocop.yml +0 -28
 - data/Gemfile +0 -17
 - data/RELEASING.md +0 -43
 - data/Rakefile +0 -20
 - data/active_delivery.gemspec +0 -35
 - data/forspell.dict +0 -8
 - data/gemfiles/jruby.gemfile +0 -5
 - data/gemfiles/rails42.gemfile +0 -8
 - data/gemfiles/rails5.gemfile +0 -5
 - data/gemfiles/rails50.gemfile +0 -8
 - data/gemfiles/rails6.gemfile +0 -5
 - data/gemfiles/railsmaster.gemfile +0 -6
 - data/gemfiles/rubocop.gemfile +0 -4
 - data/lefthook.yml +0 -18
 - data/lib/active_delivery/action_mailer/parameterized.rb +0 -92
 
| 
         @@ -0,0 +1,124 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module ActiveDelivery
         
     | 
| 
      
 4 
     | 
    
         
            +
              # Base class for deliveries.
         
     | 
| 
      
 5 
     | 
    
         
            +
              #
         
     | 
| 
      
 6 
     | 
    
         
            +
              # Delivery object describes how to notify a user about
         
     | 
| 
      
 7 
     | 
    
         
            +
              # an event (e.g. via email or via push notification or both).
         
     | 
| 
      
 8 
     | 
    
         
            +
              #
         
     | 
| 
      
 9 
     | 
    
         
            +
              # Delivery class acts like a proxy in front of the different delivery channels
         
     | 
| 
      
 10 
     | 
    
         
            +
              # (i.e. mailers, notifiers). That means that calling a method on delivery class invokes the
         
     | 
| 
      
 11 
     | 
    
         
            +
              # same method on the corresponding class, e.g.:
         
     | 
| 
      
 12 
     | 
    
         
            +
              #
         
     | 
| 
      
 13 
     | 
    
         
            +
              #   EventsDelivery.notify(:one_hour_before, profile, event)
         
     | 
| 
      
 14 
     | 
    
         
            +
              #
         
     | 
| 
      
 15 
     | 
    
         
            +
              #   # under the hood it calls
         
     | 
| 
      
 16 
     | 
    
         
            +
              #   EventsMailer.one_hour_before(profile, event).deliver_later
         
     | 
| 
      
 17 
     | 
    
         
            +
              #
         
     | 
| 
      
 18 
     | 
    
         
            +
              #   # and
         
     | 
| 
      
 19 
     | 
    
         
            +
              #   EventsNotifier.one_hour_before(profile, event).notify_later
         
     | 
| 
      
 20 
     | 
    
         
            +
              #
         
     | 
| 
      
 21 
     | 
    
         
            +
              # Delivery also supports _parameterized_ calling:
         
     | 
| 
      
 22 
     | 
    
         
            +
              #
         
     | 
| 
      
 23 
     | 
    
         
            +
              #   EventsDelivery.with(profile: profile).notify(:canceled, event)
         
     | 
| 
      
 24 
     | 
    
         
            +
              #
         
     | 
| 
      
 25 
     | 
    
         
            +
              # The parameters could be accessed through `params` instance method (e.g.
         
     | 
| 
      
 26 
     | 
    
         
            +
              # to implement guard-like logic).
         
     | 
| 
      
 27 
     | 
    
         
            +
              #
         
     | 
| 
      
 28 
     | 
    
         
            +
              # When params are presents the parametrized mailer is used, i.e.:
         
     | 
| 
      
 29 
     | 
    
         
            +
              #
         
     | 
| 
      
 30 
     | 
    
         
            +
              #   EventsMailer.with(profile: profile).canceled(event)
         
     | 
| 
      
 31 
     | 
    
         
            +
              #
         
     | 
| 
      
 32 
     | 
    
         
            +
              # See https://api.rubyonrails.org/classes/ActionMailer/Parameterized.html
         
     | 
| 
      
 33 
     | 
    
         
            +
              class Base
         
     | 
| 
      
 34 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 35 
     | 
    
         
            +
                  attr_accessor :abstract_class
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                  alias_method :with, :new
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                  # Enqueues delivery (i.e. uses #deliver_later for mailers)
         
     | 
| 
      
 40 
     | 
    
         
            +
                  def notify(...)
         
     | 
| 
      
 41 
     | 
    
         
            +
                    new.notify(...)
         
     | 
| 
      
 42 
     | 
    
         
            +
                  end; respond_to?(:ruby2_keywords, true) && (ruby2_keywords :notify)
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                  # The same as .notify but delivers synchronously
         
     | 
| 
      
 45 
     | 
    
         
            +
                  # (i.e. #deliver_now for mailers)
         
     | 
| 
      
 46 
     | 
    
         
            +
                  def notify!(mid, *args, **hargs)
         
     | 
| 
      
 47 
     | 
    
         
            +
                    notify(mid, *args, **hargs, sync: true)
         
     | 
| 
      
 48 
     | 
    
         
            +
                  end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                  def delivery_lines
         
     | 
| 
      
 51 
     | 
    
         
            +
                    @lines ||= if superclass.respond_to?(:delivery_lines)
         
     | 
| 
      
 52 
     | 
    
         
            +
                      superclass.delivery_lines.each_with_object({}) do |(key, val), acc|
         
     | 
| 
      
 53 
     | 
    
         
            +
                        acc[key] = val.dup_for(self)
         
     | 
| 
      
 54 
     | 
    
         
            +
                      end
         
     | 
| 
      
 55 
     | 
    
         
            +
                    else
         
     | 
| 
      
 56 
     | 
    
         
            +
                      {}
         
     | 
| 
      
 57 
     | 
    
         
            +
                    end
         
     | 
| 
      
 58 
     | 
    
         
            +
                  end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                  def register_line(line_id, line_class, **options)
         
     | 
| 
      
 61 
     | 
    
         
            +
                    delivery_lines[line_id] = line_class.new(id: line_id, owner: self, **options)
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                    instance_eval <<~CODE, __FILE__, __LINE__ + 1
         
     | 
| 
      
 64 
     | 
    
         
            +
                      def #{line_id}(val)
         
     | 
| 
      
 65 
     | 
    
         
            +
                        delivery_lines[:#{line_id}].handler_class = val
         
     | 
| 
      
 66 
     | 
    
         
            +
                      end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                      def #{line_id}_class
         
     | 
| 
      
 69 
     | 
    
         
            +
                        delivery_lines[:#{line_id}].handler_class
         
     | 
| 
      
 70 
     | 
    
         
            +
                      end
         
     | 
| 
      
 71 
     | 
    
         
            +
                    CODE
         
     | 
| 
      
 72 
     | 
    
         
            +
                  end
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                  def unregister_line(line_id)
         
     | 
| 
      
 75 
     | 
    
         
            +
                    removed_line = delivery_lines.delete(line_id)
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                    return if removed_line.nil?
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                    singleton_class.undef_method line_id
         
     | 
| 
      
 80 
     | 
    
         
            +
                    singleton_class.undef_method "#{line_id}_class"
         
     | 
| 
      
 81 
     | 
    
         
            +
                  end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                  def abstract_class? ;  abstract_class == true; end
         
     | 
| 
      
 84 
     | 
    
         
            +
                end
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                attr_reader :params, :notification_name
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                def initialize(**params)
         
     | 
| 
      
 89 
     | 
    
         
            +
                  @params = params
         
     | 
| 
      
 90 
     | 
    
         
            +
                  @params.freeze
         
     | 
| 
      
 91 
     | 
    
         
            +
                end
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                # Enqueues delivery (i.e. uses #deliver_later for mailers)
         
     | 
| 
      
 94 
     | 
    
         
            +
                def notify(mid, *__rest__, &__block__)
         
     | 
| 
      
 95 
     | 
    
         
            +
                  @notification_name = mid
         
     | 
| 
      
 96 
     | 
    
         
            +
                  do_notify(*__rest__, &__block__)
         
     | 
| 
      
 97 
     | 
    
         
            +
                end; respond_to?(:ruby2_keywords, true) && (ruby2_keywords :notify)
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                # The same as .notify but delivers synchronously
         
     | 
| 
      
 100 
     | 
    
         
            +
                # (i.e. #deliver_now for mailers)
         
     | 
| 
      
 101 
     | 
    
         
            +
                def notify!(mid, *args, **hargs)
         
     | 
| 
      
 102 
     | 
    
         
            +
                  notify(mid, *args, **hargs, sync: true)
         
     | 
| 
      
 103 
     | 
    
         
            +
                end
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                private
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
                def do_notify(*args, sync: false, **kwargs)
         
     | 
| 
      
 108 
     | 
    
         
            +
                  delivery_lines.each do |type, line|
         
     | 
| 
      
 109 
     | 
    
         
            +
                    next if line.handler_class.nil?
         
     | 
| 
      
 110 
     | 
    
         
            +
                    next unless line.notify?(notification_name)
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                    notify_line(type, *args, params: params, sync: sync, **kwargs)
         
     | 
| 
      
 113 
     | 
    
         
            +
                  end
         
     | 
| 
      
 114 
     | 
    
         
            +
                end
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                def notify_line(type, *__rest__, &__block__)
         
     | 
| 
      
 117 
     | 
    
         
            +
                  delivery_lines[type].notify(notification_name, *__rest__, &__block__)
         
     | 
| 
      
 118 
     | 
    
         
            +
                end; respond_to?(:ruby2_keywords, true) && (ruby2_keywords :notify_line)
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                def delivery_lines
         
     | 
| 
      
 121 
     | 
    
         
            +
                  self.class.delivery_lines
         
     | 
| 
      
 122 
     | 
    
         
            +
                end
         
     | 
| 
      
 123 
     | 
    
         
            +
              end
         
     | 
| 
      
 124 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,97 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "active_support/version"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "active_support/callbacks"
         
     | 
| 
      
 5 
     | 
    
         
            +
            require "active_support/concern"
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            module ActiveDelivery
         
     | 
| 
      
 8 
     | 
    
         
            +
              # Add callbacks support to Active Delivery (requires ActiveSupport::Callbacks)
         
     | 
| 
      
 9 
     | 
    
         
            +
              #
         
     | 
| 
      
 10 
     | 
    
         
            +
              #   # Run method before delivering notification
         
     | 
| 
      
 11 
     | 
    
         
            +
              #   # NOTE: when `false` is returned the execution is halted
         
     | 
| 
      
 12 
     | 
    
         
            +
              #   before_notify :do_something
         
     | 
| 
      
 13 
     | 
    
         
            +
              #
         
     | 
| 
      
 14 
     | 
    
         
            +
              #   # You can specify a notification method (to run callback only for that method)
         
     | 
| 
      
 15 
     | 
    
         
            +
              #   before_notify :do_mail_something, on: :mail
         
     | 
| 
      
 16 
     | 
    
         
            +
              #
         
     | 
| 
      
 17 
     | 
    
         
            +
              #   # or for push notifications
         
     | 
| 
      
 18 
     | 
    
         
            +
              #   before_notify :do_mail_something, on: :push
         
     | 
| 
      
 19 
     | 
    
         
            +
              #
         
     | 
| 
      
 20 
     | 
    
         
            +
              #   # after_ and around_ callbacks are also supported
         
     | 
| 
      
 21 
     | 
    
         
            +
              #   after_notify :cleanup
         
     | 
| 
      
 22 
     | 
    
         
            +
              #
         
     | 
| 
      
 23 
     | 
    
         
            +
              #   around_notify :set_context
         
     | 
| 
      
 24 
     | 
    
         
            +
              module Callbacks
         
     | 
| 
      
 25 
     | 
    
         
            +
                extend ActiveSupport::Concern
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                include ActiveSupport::Callbacks
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                CALLBACK_TERMINATOR = ->(_target, result) { result.call == false }
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                included do
         
     | 
| 
      
 32 
     | 
    
         
            +
                  # Define "global" callbacks
         
     | 
| 
      
 33 
     | 
    
         
            +
                  define_line_callbacks :notify
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                  prepend InstanceExt
         
     | 
| 
      
 36 
     | 
    
         
            +
                  singleton_class.prepend SingltonExt
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                module InstanceExt
         
     | 
| 
      
 40 
     | 
    
         
            +
                  def do_notify(...)
         
     | 
| 
      
 41 
     | 
    
         
            +
                    run_callbacks(:notify) { super(...) }
         
     | 
| 
      
 42 
     | 
    
         
            +
                  end; respond_to?(:ruby2_keywords, true) && (ruby2_keywords :do_notify)
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                  def notify_line(kind, *__rest__, &__block__)
         
     | 
| 
      
 45 
     | 
    
         
            +
                    run_callbacks(kind) { super(kind, *__rest__, &__block__) }
         
     | 
| 
      
 46 
     | 
    
         
            +
                  end; respond_to?(:ruby2_keywords, true) && (ruby2_keywords :notify_line)
         
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                module SingltonExt
         
     | 
| 
      
 50 
     | 
    
         
            +
                  def register_line(line_id, *__rest__, &__block__)
         
     | 
| 
      
 51 
     | 
    
         
            +
                    super
         
     | 
| 
      
 52 
     | 
    
         
            +
                    define_line_callbacks line_id
         
     | 
| 
      
 53 
     | 
    
         
            +
                  end; respond_to?(:ruby2_keywords, true) && (ruby2_keywords :register_line)
         
     | 
| 
      
 54 
     | 
    
         
            +
                end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                class_methods do
         
     | 
| 
      
 57 
     | 
    
         
            +
                  def _normalize_callback_options(options)
         
     | 
| 
      
 58 
     | 
    
         
            +
                    _normalize_callback_option(options, :only, :if)
         
     | 
| 
      
 59 
     | 
    
         
            +
                    _normalize_callback_option(options, :except, :unless)
         
     | 
| 
      
 60 
     | 
    
         
            +
                  end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                  def _normalize_callback_option(options, from, to)
         
     | 
| 
      
 63 
     | 
    
         
            +
                    if (from = options[from])
         
     | 
| 
      
 64 
     | 
    
         
            +
                      from_set = Array(from).map(&:to_s).to_set
         
     | 
| 
      
 65 
     | 
    
         
            +
                      from = proc { |c| from_set.include? c.notification_name.to_s }
         
     | 
| 
      
 66 
     | 
    
         
            +
                      options[to] = Array(options[to]).unshift(from)
         
     | 
| 
      
 67 
     | 
    
         
            +
                    end
         
     | 
| 
      
 68 
     | 
    
         
            +
                  end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                  def define_line_callbacks(name)
         
     | 
| 
      
 71 
     | 
    
         
            +
                    define_callbacks name,
         
     | 
| 
      
 72 
     | 
    
         
            +
                      terminator: CALLBACK_TERMINATOR,
         
     | 
| 
      
 73 
     | 
    
         
            +
                      skip_after_callbacks_if_terminated: true
         
     | 
| 
      
 74 
     | 
    
         
            +
                  end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                  def before_notify(method_or_block = nil, on: :notify, **options, &block)
         
     | 
| 
      
 77 
     | 
    
         
            +
                    method_or_block ||= block
         
     | 
| 
      
 78 
     | 
    
         
            +
                    _normalize_callback_options(options)
         
     | 
| 
      
 79 
     | 
    
         
            +
                    set_callback on, :before, method_or_block, options
         
     | 
| 
      
 80 
     | 
    
         
            +
                  end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                  def after_notify(method_or_block = nil, on: :notify, **options, &block)
         
     | 
| 
      
 83 
     | 
    
         
            +
                    method_or_block ||= block
         
     | 
| 
      
 84 
     | 
    
         
            +
                    _normalize_callback_options(options)
         
     | 
| 
      
 85 
     | 
    
         
            +
                    set_callback on, :after, method_or_block, options
         
     | 
| 
      
 86 
     | 
    
         
            +
                  end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                  def around_notify(method_or_block = nil, on: :notify, **options, &block)
         
     | 
| 
      
 89 
     | 
    
         
            +
                    method_or_block ||= block
         
     | 
| 
      
 90 
     | 
    
         
            +
                    _normalize_callback_options(options)
         
     | 
| 
      
 91 
     | 
    
         
            +
                    set_callback on, :around, method_or_block, options
         
     | 
| 
      
 92 
     | 
    
         
            +
                  end
         
     | 
| 
      
 93 
     | 
    
         
            +
                end
         
     | 
| 
      
 94 
     | 
    
         
            +
              end
         
     | 
| 
      
 95 
     | 
    
         
            +
            end
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
            ActiveDelivery::Base.include ActiveDelivery::Callbacks
         
     | 
| 
         @@ -0,0 +1,63 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module ActiveDelivery
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Lines
         
     | 
| 
      
 5 
     | 
    
         
            +
                class Base
         
     | 
| 
      
 6 
     | 
    
         
            +
                  attr_reader :id, :options
         
     | 
| 
      
 7 
     | 
    
         
            +
                  attr_accessor :owner
         
     | 
| 
      
 8 
     | 
    
         
            +
                  attr_writer :handler_class
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  def initialize(id:, owner:, **options)
         
     | 
| 
      
 11 
     | 
    
         
            +
                    @id = id
         
     | 
| 
      
 12 
     | 
    
         
            +
                    @owner = owner
         
     | 
| 
      
 13 
     | 
    
         
            +
                    @options = options.tap(&:freeze)
         
     | 
| 
      
 14 
     | 
    
         
            +
                    @resolver = options[:resolver]
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                  def dup_for(new_owner)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    self.class.new(id: id, **options, owner: new_owner)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  def resolve_class(name)
         
     | 
| 
      
 22 
     | 
    
         
            +
                    resolver&.call(name)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                  def notify?(method_name)
         
     | 
| 
      
 26 
     | 
    
         
            +
                    handler_class.respond_to?(method_name)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  def notify_now(handler, mid, *__rest__, &__block__)
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end; respond_to?(:ruby2_keywords, true) && (ruby2_keywords :notify_now)
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                  def notify_later(handler, mid, *__rest__, &__block__)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end; respond_to?(:ruby2_keywords, true) && (ruby2_keywords :notify_later)
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                  def notify(mid, *args, params:, sync:, **kwargs)
         
     | 
| 
      
 36 
     | 
    
         
            +
                    clazz = params.empty? ? handler_class : handler_class.with(**params)
         
     | 
| 
      
 37 
     | 
    
         
            +
                    sync ? notify_now(clazz, mid, *args, **kwargs) : notify_later(clazz, mid, *args, **kwargs)
         
     | 
| 
      
 38 
     | 
    
         
            +
                  end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  def handler_class
         
     | 
| 
      
 41 
     | 
    
         
            +
                    return @handler_class if instance_variable_defined?(:@handler_class)
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                    return @handler_class = nil if owner.abstract_class?
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                    @handler_class = resolve_class(owner.name) ||
         
     | 
| 
      
 46 
     | 
    
         
            +
                      superclass_handler
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                  private
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                  def superclass_handler
         
     | 
| 
      
 52 
     | 
    
         
            +
                    handler_method = "#{id}_class"
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                    return if owner.superclass == ActiveDelivery::Base
         
     | 
| 
      
 55 
     | 
    
         
            +
                    return unless owner.superclass.respond_to?(handler_method)
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                    owner.superclass.public_send(handler_method)
         
     | 
| 
      
 58 
     | 
    
         
            +
                  end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                  attr_reader :resolver
         
     | 
| 
      
 61 
     | 
    
         
            +
                end
         
     | 
| 
      
 62 
     | 
    
         
            +
              end
         
     | 
| 
      
 63 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,25 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module ActiveDelivery
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Lines
         
     | 
| 
      
 5 
     | 
    
         
            +
                class Mailer < Base
         
     | 
| 
      
 6 
     | 
    
         
            +
                  alias_method :mailer_class, :handler_class
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                  DEFAULT_RESOLVER = ->(name) { name.gsub(/Delivery$/, "Mailer").safe_constantize }
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  def notify?(method_name)
         
     | 
| 
      
 11 
     | 
    
         
            +
                    mailer_class.action_methods.include?(method_name.to_s)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  def notify_now(mailer, mid, *__rest__, &__block__)
         
     | 
| 
      
 15 
     | 
    
         
            +
                    mailer.public_send(mid, *__rest__, &__block__).deliver_now
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end; respond_to?(:ruby2_keywords, true) && (ruby2_keywords :notify_now)
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  def notify_later(mailer, mid, *__rest__, &__block__)
         
     | 
| 
      
 19 
     | 
    
         
            +
                    mailer.public_send(mid, *__rest__, &__block__).deliver_later
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end; respond_to?(:ruby2_keywords, true) && (ruby2_keywords :notify_later)
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                ActiveDelivery::Base.register_line :mailer, Mailer, resolver: Mailer::DEFAULT_RESOLVER
         
     | 
| 
      
 24 
     | 
    
         
            +
              end
         
     | 
| 
      
 25 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,124 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module ActiveDelivery
         
     | 
| 
      
 4 
     | 
    
         
            +
              # Base class for deliveries.
         
     | 
| 
      
 5 
     | 
    
         
            +
              #
         
     | 
| 
      
 6 
     | 
    
         
            +
              # Delivery object describes how to notify a user about
         
     | 
| 
      
 7 
     | 
    
         
            +
              # an event (e.g. via email or via push notification or both).
         
     | 
| 
      
 8 
     | 
    
         
            +
              #
         
     | 
| 
      
 9 
     | 
    
         
            +
              # Delivery class acts like a proxy in front of the different delivery channels
         
     | 
| 
      
 10 
     | 
    
         
            +
              # (i.e. mailers, notifiers). That means that calling a method on delivery class invokes the
         
     | 
| 
      
 11 
     | 
    
         
            +
              # same method on the corresponding class, e.g.:
         
     | 
| 
      
 12 
     | 
    
         
            +
              #
         
     | 
| 
      
 13 
     | 
    
         
            +
              #   EventsDelivery.notify(:one_hour_before, profile, event)
         
     | 
| 
      
 14 
     | 
    
         
            +
              #
         
     | 
| 
      
 15 
     | 
    
         
            +
              #   # under the hood it calls
         
     | 
| 
      
 16 
     | 
    
         
            +
              #   EventsMailer.one_hour_before(profile, event).deliver_later
         
     | 
| 
      
 17 
     | 
    
         
            +
              #
         
     | 
| 
      
 18 
     | 
    
         
            +
              #   # and
         
     | 
| 
      
 19 
     | 
    
         
            +
              #   EventsNotifier.one_hour_before(profile, event).notify_later
         
     | 
| 
      
 20 
     | 
    
         
            +
              #
         
     | 
| 
      
 21 
     | 
    
         
            +
              # Delivery also supports _parameterized_ calling:
         
     | 
| 
      
 22 
     | 
    
         
            +
              #
         
     | 
| 
      
 23 
     | 
    
         
            +
              #   EventsDelivery.with(profile: profile).notify(:canceled, event)
         
     | 
| 
      
 24 
     | 
    
         
            +
              #
         
     | 
| 
      
 25 
     | 
    
         
            +
              # The parameters could be accessed through `params` instance method (e.g.
         
     | 
| 
      
 26 
     | 
    
         
            +
              # to implement guard-like logic).
         
     | 
| 
      
 27 
     | 
    
         
            +
              #
         
     | 
| 
      
 28 
     | 
    
         
            +
              # When params are presents the parametrized mailer is used, i.e.:
         
     | 
| 
      
 29 
     | 
    
         
            +
              #
         
     | 
| 
      
 30 
     | 
    
         
            +
              #   EventsMailer.with(profile: profile).canceled(event)
         
     | 
| 
      
 31 
     | 
    
         
            +
              #
         
     | 
| 
      
 32 
     | 
    
         
            +
              # See https://api.rubyonrails.org/classes/ActionMailer/Parameterized.html
         
     | 
| 
      
 33 
     | 
    
         
            +
              class Base
         
     | 
| 
      
 34 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 35 
     | 
    
         
            +
                  attr_accessor :abstract_class
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                  alias_method :with, :new
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                  # Enqueues delivery (i.e. uses #deliver_later for mailers)
         
     | 
| 
      
 40 
     | 
    
         
            +
                  def notify(...)
         
     | 
| 
      
 41 
     | 
    
         
            +
                    new.notify(...)
         
     | 
| 
      
 42 
     | 
    
         
            +
                  end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                  # The same as .notify but delivers synchronously
         
     | 
| 
      
 45 
     | 
    
         
            +
                  # (i.e. #deliver_now for mailers)
         
     | 
| 
      
 46 
     | 
    
         
            +
                  def notify!(mid, *args, **hargs)
         
     | 
| 
      
 47 
     | 
    
         
            +
                    notify(mid, *args, **hargs, sync: true)
         
     | 
| 
      
 48 
     | 
    
         
            +
                  end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                  def delivery_lines
         
     | 
| 
      
 51 
     | 
    
         
            +
                    @lines ||= if superclass.respond_to?(:delivery_lines)
         
     | 
| 
      
 52 
     | 
    
         
            +
                      superclass.delivery_lines.each_with_object({}) do |(key, val), acc|
         
     | 
| 
      
 53 
     | 
    
         
            +
                        acc[key] = val.dup_for(self)
         
     | 
| 
      
 54 
     | 
    
         
            +
                      end
         
     | 
| 
      
 55 
     | 
    
         
            +
                    else
         
     | 
| 
      
 56 
     | 
    
         
            +
                      {}
         
     | 
| 
      
 57 
     | 
    
         
            +
                    end
         
     | 
| 
      
 58 
     | 
    
         
            +
                  end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                  def register_line(line_id, line_class, **options)
         
     | 
| 
      
 61 
     | 
    
         
            +
                    delivery_lines[line_id] = line_class.new(id: line_id, owner: self, **options)
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                    instance_eval <<~CODE, __FILE__, __LINE__ + 1
         
     | 
| 
      
 64 
     | 
    
         
            +
                      def #{line_id}(val)
         
     | 
| 
      
 65 
     | 
    
         
            +
                        delivery_lines[:#{line_id}].handler_class = val
         
     | 
| 
      
 66 
     | 
    
         
            +
                      end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                      def #{line_id}_class
         
     | 
| 
      
 69 
     | 
    
         
            +
                        delivery_lines[:#{line_id}].handler_class
         
     | 
| 
      
 70 
     | 
    
         
            +
                      end
         
     | 
| 
      
 71 
     | 
    
         
            +
                    CODE
         
     | 
| 
      
 72 
     | 
    
         
            +
                  end
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                  def unregister_line(line_id)
         
     | 
| 
      
 75 
     | 
    
         
            +
                    removed_line = delivery_lines.delete(line_id)
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                    return if removed_line.nil?
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                    singleton_class.undef_method line_id
         
     | 
| 
      
 80 
     | 
    
         
            +
                    singleton_class.undef_method "#{line_id}_class"
         
     | 
| 
      
 81 
     | 
    
         
            +
                  end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                  def abstract_class? = abstract_class == true
         
     | 
| 
      
 84 
     | 
    
         
            +
                end
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                attr_reader :params, :notification_name
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                def initialize(**params)
         
     | 
| 
      
 89 
     | 
    
         
            +
                  @params = params
         
     | 
| 
      
 90 
     | 
    
         
            +
                  @params.freeze
         
     | 
| 
      
 91 
     | 
    
         
            +
                end
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                # Enqueues delivery (i.e. uses #deliver_later for mailers)
         
     | 
| 
      
 94 
     | 
    
         
            +
                def notify(mid, ...)
         
     | 
| 
      
 95 
     | 
    
         
            +
                  @notification_name = mid
         
     | 
| 
      
 96 
     | 
    
         
            +
                  do_notify(...)
         
     | 
| 
      
 97 
     | 
    
         
            +
                end
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                # The same as .notify but delivers synchronously
         
     | 
| 
      
 100 
     | 
    
         
            +
                # (i.e. #deliver_now for mailers)
         
     | 
| 
      
 101 
     | 
    
         
            +
                def notify!(mid, *args, **hargs)
         
     | 
| 
      
 102 
     | 
    
         
            +
                  notify(mid, *args, **hargs, sync: true)
         
     | 
| 
      
 103 
     | 
    
         
            +
                end
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                private
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
                def do_notify(*args, sync: false, **kwargs)
         
     | 
| 
      
 108 
     | 
    
         
            +
                  delivery_lines.each do |type, line|
         
     | 
| 
      
 109 
     | 
    
         
            +
                    next if line.handler_class.nil?
         
     | 
| 
      
 110 
     | 
    
         
            +
                    next unless line.notify?(notification_name)
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                    notify_line(type, *args, params: params, sync: sync, **kwargs)
         
     | 
| 
      
 113 
     | 
    
         
            +
                  end
         
     | 
| 
      
 114 
     | 
    
         
            +
                end
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                def notify_line(type, ...)
         
     | 
| 
      
 117 
     | 
    
         
            +
                  delivery_lines[type].notify(notification_name, ...)
         
     | 
| 
      
 118 
     | 
    
         
            +
                end
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                def delivery_lines
         
     | 
| 
      
 121 
     | 
    
         
            +
                  self.class.delivery_lines
         
     | 
| 
      
 122 
     | 
    
         
            +
                end
         
     | 
| 
      
 123 
     | 
    
         
            +
              end
         
     | 
| 
      
 124 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,63 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module ActiveDelivery
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Lines
         
     | 
| 
      
 5 
     | 
    
         
            +
                class Base
         
     | 
| 
      
 6 
     | 
    
         
            +
                  attr_reader :id, :options
         
     | 
| 
      
 7 
     | 
    
         
            +
                  attr_accessor :owner
         
     | 
| 
      
 8 
     | 
    
         
            +
                  attr_writer :handler_class
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  def initialize(id:, owner:, **options)
         
     | 
| 
      
 11 
     | 
    
         
            +
                    @id = id
         
     | 
| 
      
 12 
     | 
    
         
            +
                    @owner = owner
         
     | 
| 
      
 13 
     | 
    
         
            +
                    @options = options.tap(&:freeze)
         
     | 
| 
      
 14 
     | 
    
         
            +
                    @resolver = options[:resolver]
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                  def dup_for(new_owner)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    self.class.new(id: id, **options, owner: new_owner)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  def resolve_class(name)
         
     | 
| 
      
 22 
     | 
    
         
            +
                    resolver&.call(name)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                  def notify?(method_name)
         
     | 
| 
      
 26 
     | 
    
         
            +
                    handler_class.respond_to?(method_name)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  def notify_now(handler, mid, ...)
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                  def notify_later(handler, mid, ...)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                  def notify(mid, *args, params:, sync:, **kwargs)
         
     | 
| 
      
 36 
     | 
    
         
            +
                    clazz = params.empty? ? handler_class : handler_class.with(**params)
         
     | 
| 
      
 37 
     | 
    
         
            +
                    sync ? notify_now(clazz, mid, *args, **kwargs) : notify_later(clazz, mid, *args, **kwargs)
         
     | 
| 
      
 38 
     | 
    
         
            +
                  end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  def handler_class
         
     | 
| 
      
 41 
     | 
    
         
            +
                    return @handler_class if instance_variable_defined?(:@handler_class)
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                    return @handler_class = nil if owner.abstract_class?
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                    @handler_class = resolve_class(owner.name) ||
         
     | 
| 
      
 46 
     | 
    
         
            +
                      superclass_handler
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                  private
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                  def superclass_handler
         
     | 
| 
      
 52 
     | 
    
         
            +
                    handler_method = "#{id}_class"
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                    return if owner.superclass == ActiveDelivery::Base
         
     | 
| 
      
 55 
     | 
    
         
            +
                    return unless owner.superclass.respond_to?(handler_method)
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                    owner.superclass.public_send(handler_method)
         
     | 
| 
      
 58 
     | 
    
         
            +
                  end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                  attr_reader :resolver
         
     | 
| 
      
 61 
     | 
    
         
            +
                end
         
     | 
| 
      
 62 
     | 
    
         
            +
              end
         
     | 
| 
      
 63 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,27 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module AbstractNotifier
         
     | 
| 
      
 4 
     | 
    
         
            +
              module AsyncAdapters
         
     | 
| 
      
 5 
     | 
    
         
            +
                class ActiveJob
         
     | 
| 
      
 6 
     | 
    
         
            +
                  class DeliveryJob < ::ActiveJob::Base
         
     | 
| 
      
 7 
     | 
    
         
            +
                    def perform(notifier_class, payload)
         
     | 
| 
      
 8 
     | 
    
         
            +
                      AbstractNotifier::Notification.new(notifier_class.constantize, payload).notify_now
         
     | 
| 
      
 9 
     | 
    
         
            +
                    end
         
     | 
| 
      
 10 
     | 
    
         
            +
                  end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  DEFAULT_QUEUE = "notifiers"
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  attr_reader :job
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  def initialize(queue: DEFAULT_QUEUE, job: DeliveryJob)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    @job = job.set(queue: queue)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  def enqueue(notifier_class, payload)
         
     | 
| 
      
 21 
     | 
    
         
            +
                    job.perform_later(notifier_class.name, payload)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
              end
         
     | 
| 
      
 25 
     | 
    
         
            +
            end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
            AbstractNotifier.async_adapter ||= :active_job
         
     | 
| 
         @@ -0,0 +1,16 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module AbstractNotifier
         
     | 
| 
      
 4 
     | 
    
         
            +
              module AsyncAdapters
         
     | 
| 
      
 5 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 6 
     | 
    
         
            +
                  def lookup(adapter, options = nil)
         
     | 
| 
      
 7 
     | 
    
         
            +
                    return adapter unless adapter.is_a?(Symbol)
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                    adapter_class_name = adapter.to_s.split("_").map(&:capitalize).join
         
     | 
| 
      
 10 
     | 
    
         
            +
                    AsyncAdapters.const_get(adapter_class_name).new(**(options || {}))
         
     | 
| 
      
 11 
     | 
    
         
            +
                  rescue NameError => e
         
     | 
| 
      
 12 
     | 
    
         
            +
                    raise e.class, "Notifier async adapter :#{adapter} haven't been found", e.backtrace
         
     | 
| 
      
 13 
     | 
    
         
            +
                  end
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
              end
         
     | 
| 
      
 16 
     | 
    
         
            +
            end
         
     |