noticed 1.6.3 → 2.0.1

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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +269 -237
  3. data/app/jobs/noticed/application_job.rb +9 -0
  4. data/app/jobs/noticed/event_job.rb +19 -0
  5. data/app/models/concerns/noticed/deliverable.rb +115 -0
  6. data/app/models/concerns/noticed/notification_methods.rb +17 -0
  7. data/app/models/concerns/noticed/readable.rb +78 -0
  8. data/app/models/noticed/application_record.rb +6 -0
  9. data/app/models/noticed/deliverable/deliver_by.rb +43 -0
  10. data/app/models/noticed/event.rb +15 -0
  11. data/app/models/noticed/notification.rb +14 -0
  12. data/db/migrate/20231215190233_create_noticed_tables.rb +25 -0
  13. data/lib/generators/noticed/delivery_method_generator.rb +1 -1
  14. data/lib/generators/noticed/install_generator.rb +19 -0
  15. data/lib/generators/noticed/{notification_generator.rb → notifier_generator.rb} +2 -2
  16. data/lib/generators/noticed/templates/README +5 -4
  17. data/lib/generators/noticed/templates/delivery_method.rb.tt +4 -8
  18. data/lib/generators/noticed/templates/notifier.rb.tt +24 -0
  19. data/lib/noticed/api_client.rb +44 -0
  20. data/lib/noticed/bulk_delivery_method.rb +46 -0
  21. data/lib/noticed/bulk_delivery_methods/discord.rb +11 -0
  22. data/lib/noticed/bulk_delivery_methods/slack.rb +17 -0
  23. data/lib/noticed/bulk_delivery_methods/webhook.rb +18 -0
  24. data/lib/noticed/coder.rb +2 -0
  25. data/lib/noticed/delivery_method.rb +51 -0
  26. data/lib/noticed/delivery_methods/action_cable.rb +7 -39
  27. data/lib/noticed/delivery_methods/discord.rb +11 -0
  28. data/lib/noticed/delivery_methods/email.rb +13 -45
  29. data/lib/noticed/delivery_methods/fcm.rb +23 -64
  30. data/lib/noticed/delivery_methods/ios.rb +25 -112
  31. data/lib/noticed/delivery_methods/microsoft_teams.rb +5 -22
  32. data/lib/noticed/delivery_methods/slack.rb +6 -16
  33. data/lib/noticed/delivery_methods/test.rb +2 -12
  34. data/lib/noticed/delivery_methods/twilio_messaging.rb +37 -0
  35. data/lib/noticed/delivery_methods/vonage_sms.rb +20 -0
  36. data/lib/noticed/delivery_methods/webhook.rb +17 -0
  37. data/lib/noticed/engine.rb +1 -9
  38. data/lib/noticed/required_options.rb +21 -0
  39. data/lib/noticed/translation.rb +7 -3
  40. data/lib/noticed/version.rb +1 -1
  41. data/lib/noticed.rb +30 -15
  42. metadata +29 -40
  43. data/lib/generators/noticed/model/base_generator.rb +0 -47
  44. data/lib/generators/noticed/model/mysql_generator.rb +0 -18
  45. data/lib/generators/noticed/model/postgresql_generator.rb +0 -18
  46. data/lib/generators/noticed/model/sqlite3_generator.rb +0 -18
  47. data/lib/generators/noticed/model_generator.rb +0 -63
  48. data/lib/generators/noticed/templates/notification.rb.tt +0 -27
  49. data/lib/noticed/base.rb +0 -160
  50. data/lib/noticed/delivery_methods/base.rb +0 -95
  51. data/lib/noticed/delivery_methods/database.rb +0 -34
  52. data/lib/noticed/delivery_methods/twilio.rb +0 -51
  53. data/lib/noticed/delivery_methods/vonage.rb +0 -40
  54. data/lib/noticed/has_notifications.rb +0 -49
  55. data/lib/noticed/model.rb +0 -85
  56. data/lib/noticed/notification_channel.rb +0 -15
  57. data/lib/noticed/text_coder.rb +0 -16
  58. data/lib/rails_6_polyfills/actioncable/test_adapter.rb +0 -70
  59. data/lib/rails_6_polyfills/actioncable/test_helper.rb +0 -143
  60. data/lib/rails_6_polyfills/activejob/serializers.rb +0 -240
  61. data/lib/rails_6_polyfills/base.rb +0 -18
  62. data/lib/tasks/noticed_tasks.rake +0 -4
@@ -0,0 +1,9 @@
1
+ module Noticed
2
+ class ApplicationJob < ActiveJob::Base
3
+ # Automatically retry jobs that encountered a deadlock
4
+ # retry_on ActiveRecord::Deadlocked
5
+
6
+ # Most jobs are safe to ignore if the underlying records are no longer available
7
+ discard_on ActiveJob::DeserializationError
8
+ end
9
+ end
@@ -0,0 +1,19 @@
1
+ module Noticed
2
+ class EventJob < ApplicationJob
3
+ queue_as :default
4
+
5
+ def perform(event)
6
+ # Enqueue bulk deliveries
7
+ event.bulk_delivery_methods.each do |_, deliver_by|
8
+ deliver_by.perform_later(event)
9
+ end
10
+
11
+ # Enqueue individual deliveries
12
+ event.notifications.each do |notification|
13
+ event.delivery_methods.each do |_, deliver_by|
14
+ deliver_by.perform_later(notification)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,115 @@
1
+ module Noticed
2
+ module Deliverable
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ class_attribute :bulk_delivery_methods, instance_writer: false, default: {}
7
+ class_attribute :delivery_methods, instance_writer: false, default: {}
8
+ class_attribute :required_param_names, instance_writer: false, default: []
9
+
10
+ attribute :params, default: {}
11
+
12
+ if Rails.gem_version >= Gem::Version.new("7.1.0.alpha")
13
+ serialize :params, coder: Coder
14
+ else
15
+ serialize :params, Coder
16
+ end
17
+ end
18
+
19
+ class_methods do
20
+ def inherited(base)
21
+ base.bulk_delivery_methods = bulk_delivery_methods.dup
22
+ base.delivery_methods = delivery_methods.dup
23
+ base.required_param_names = required_param_names.dup
24
+ super
25
+ end
26
+
27
+ def bulk_deliver_by(name, options = {})
28
+ raise NameError, "#{name} has already been used for this Notifier." if bulk_delivery_methods.has_key?(name)
29
+
30
+ config = ActiveSupport::OrderedOptions.new.merge(options)
31
+ yield config if block_given?
32
+ bulk_delivery_methods[name] = DeliverBy.new(name, config, bulk: true)
33
+ end
34
+
35
+ def deliver_by(name, options = {})
36
+ raise NameError, "#{name} has already been used for this Notifier." if delivery_methods.has_key?(name)
37
+
38
+ config = ActiveSupport::OrderedOptions.new.merge(options)
39
+ yield config if block_given?
40
+ delivery_methods[name] = DeliverBy.new(name, config)
41
+ end
42
+
43
+ def required_params(*names)
44
+ required_param_names.concat names
45
+ end
46
+ alias_method :required_param, :required_params
47
+
48
+ def with(params)
49
+ record = params.delete(:record)
50
+ new(params: params, record: record)
51
+ end
52
+
53
+ def deliver(recipients = nil, options = {})
54
+ new.deliver(recipients, options)
55
+ end
56
+ end
57
+
58
+ # CommentNotifier.deliver(User.all)
59
+ # CommentNotifier.deliver(User.all, priority: 10)
60
+ # CommentNotifier.deliver(User.all, queue: :low_priority)
61
+ # CommentNotifier.deliver(User.all, wait: 5.minutes)
62
+ # CommentNotifier.deliver(User.all, wait_until: 1.hour.from_now)
63
+ def deliver(recipients = nil, options = {})
64
+ validate!
65
+
66
+ transaction do
67
+ save!
68
+
69
+ recipients_attributes = Array.wrap(recipients).map do |recipient|
70
+ recipient_attributes_for(recipient)
71
+ end
72
+
73
+ if Rails.gem_version >= Gem::Version.new("7.0.0.alpha1")
74
+ notifications.insert_all!(recipients_attributes, record_timestamps: true) if recipients_attributes.any?
75
+ else
76
+ time = Time.current
77
+ recipients_attributes.each do |attributes|
78
+ attributes[:created_at] = time
79
+ attributes[:updated_at] = time
80
+ end
81
+ notifications.insert_all!(recipients_attributes) if recipients_attributes.any?
82
+ end
83
+ end
84
+
85
+ # Enqueue delivery job
86
+ EventJob.set(options).perform_later(self)
87
+
88
+ self
89
+ end
90
+
91
+ def recipient_attributes_for(recipient)
92
+ {
93
+ type: "#{self.class.name}::Notification",
94
+ recipient_type: recipient.class.name,
95
+ recipient_id: recipient.id
96
+ }
97
+ end
98
+
99
+ def validate!
100
+ validate_params!
101
+ validate_delivery_methods!
102
+ end
103
+
104
+ def validate_params!
105
+ required_param_names.each do |param_name|
106
+ raise ValidationError, "Param `#{param_name}` is required for #{self.class.name}." unless params[param_name].present?
107
+ end
108
+ end
109
+
110
+ def validate_delivery_methods!
111
+ bulk_delivery_methods.values.each(&:validate!)
112
+ delivery_methods.values.each(&:validate!)
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,17 @@
1
+ module Noticed
2
+ module NotificationMethods
3
+ extend ActiveSupport::Concern
4
+
5
+ class_methods do
6
+ # Generate a Notification class each time a Notifier is defined
7
+ def inherited(notifier)
8
+ super
9
+ notifier.const_set :Notification, Class.new(Noticed::Notification)
10
+ end
11
+
12
+ def notification_methods(&block)
13
+ const_get(:Notification).class_eval(&block)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,78 @@
1
+ module Noticed
2
+ module Readable
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ scope :read, -> { where.not(read_at: nil) }
7
+ scope :unread, -> { where(read_at: nil) }
8
+ scope :seen, -> { where.not(seen_at: nil) }
9
+ scope :unseen, -> { where(seen_at: nil) }
10
+ end
11
+
12
+ class_methods do
13
+ def mark_as_read
14
+ update_all(read_at: Time.current)
15
+ end
16
+
17
+ def mark_as_unread
18
+ update_all(read_at: nil)
19
+ end
20
+
21
+ def mark_as_seen
22
+ update_all(seen_at: Time.current)
23
+ end
24
+
25
+ def mark_as_unseen
26
+ update_all(seen_at: nil)
27
+ end
28
+ end
29
+
30
+ def mark_as_read
31
+ update(read_at: Time.current)
32
+ end
33
+
34
+ def mark_as_read!
35
+ update!(read_at: Time.current)
36
+ end
37
+
38
+ def mark_as_unread
39
+ update(read_at: nil)
40
+ end
41
+
42
+ def mark_as_unread!
43
+ update!(read_at: nil)
44
+ end
45
+
46
+ def mark_as_seen
47
+ update(seen_at: Time.current)
48
+ end
49
+
50
+ def mark_as_seen!
51
+ update!(seen_at: Time.current)
52
+ end
53
+
54
+ def mark_as_unseen
55
+ update(seen_at: nil)
56
+ end
57
+
58
+ def mark_as_unseen!
59
+ update!(seen_at: nil)
60
+ end
61
+
62
+ def read?
63
+ read_at?
64
+ end
65
+
66
+ def unread?
67
+ !read_at?
68
+ end
69
+
70
+ def seen?
71
+ seen_at?
72
+ end
73
+
74
+ def unseen?
75
+ !seen_at?
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,6 @@
1
+ module Noticed
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ self.table_name_prefix = "noticed_"
5
+ end
6
+ end
@@ -0,0 +1,43 @@
1
+ module Noticed
2
+ module Deliverable
3
+ class DeliverBy
4
+ attr_reader :name, :config, :bulk
5
+
6
+ def initialize(name, config, bulk: false)
7
+ @name, @config, @bulk, = name, config, bulk
8
+ end
9
+
10
+ def constant
11
+ namespace = bulk ? "Noticed::BulkDeliveryMethods" : "Noticed::DeliveryMethods"
12
+ config.fetch(:class, [namespace, name.to_s.camelize].join("::")).constantize
13
+ end
14
+
15
+ def validate!
16
+ constant.required_option_names.each do |option|
17
+ raise ValidationError, "option `#{option}` must be set for `deliver_by :#{name}`" unless config[option].present?
18
+ end
19
+ end
20
+
21
+ def perform_later(event_or_notification, options = {})
22
+ options[:wait] = evaluate_option(:wait, event_or_notification) if config.has_key?(:wait)
23
+ options[:wait_until] = evaluate_option(:wait_until, event_or_notification) if config.has_key?(:wait_until)
24
+ options[:queue] = evaluate_option(:queue, event_or_notification) if config.has_key?(:queue)
25
+ options[:priority] = evaluate_option(:priority, event_or_notification) if config.has_key?(:priority)
26
+
27
+ constant.set(options).perform_later(name, event_or_notification)
28
+ end
29
+
30
+ def evaluate_option(name, context)
31
+ option = config[name]
32
+
33
+ if option&.respond_to?(:call)
34
+ context.instance_exec(&option)
35
+ elsif option.is_a?(Symbol) && context.respond_to?(option)
36
+ context.send(option)
37
+ else
38
+ option
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,15 @@
1
+ module Noticed
2
+ class Event < ApplicationRecord
3
+ include Deliverable
4
+ include NotificationMethods
5
+ include Translation
6
+ include Rails.application.routes.url_helpers
7
+
8
+ belongs_to :record, polymorphic: true, optional: true
9
+ has_many :notifications, dependent: :delete_all
10
+
11
+ accepts_nested_attributes_for :notifications
12
+
13
+ scope :newest_first, -> { order(created_at: :desc) }
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ module Noticed
2
+ class Notification < ApplicationRecord
3
+ include Rails.application.routes.url_helpers
4
+ include Readable
5
+ include Translation
6
+
7
+ belongs_to :event
8
+ belongs_to :recipient, polymorphic: true
9
+
10
+ scope :newest_first, -> { order(created_at: :desc) }
11
+
12
+ delegate :params, :record, to: :event
13
+ end
14
+ end
@@ -0,0 +1,25 @@
1
+ class CreateNoticedTables < ActiveRecord::Migration[6.1]
2
+ def change
3
+ create_table :noticed_events do |t|
4
+ t.string :type
5
+ t.belongs_to :record, polymorphic: true
6
+ if t.respond_to?(:jsonb)
7
+ t.jsonb :params
8
+ else
9
+ t.json :params
10
+ end
11
+
12
+ t.timestamps
13
+ end
14
+
15
+ create_table :noticed_notifications do |t|
16
+ t.string :type
17
+ t.belongs_to :event, null: false
18
+ t.belongs_to :recipient, polymorphic: true, null: false
19
+ t.datetime :read_at
20
+ t.datetime :seen_at
21
+
22
+ t.timestamps
23
+ end
24
+ end
25
+ end
@@ -12,7 +12,7 @@ module Noticed
12
12
  desc "Generates a class for a custom delivery method with the given NAME."
13
13
 
14
14
  def generate_notification
15
- template "delivery_method.rb", "app/notifications/delivery_methods/#{singular_name}.rb"
15
+ template "delivery_method.rb", "app/notifiers/delivery_methods/#{singular_name}.rb"
16
16
  end
17
17
  end
18
18
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Noticed
4
+ module Generators
5
+ class ModelGenerator < Rails::Generators::Base
6
+ include Rails::Generators::ResourceHelpers
7
+
8
+ source_root File.expand_path("../templates", __FILE__)
9
+
10
+ def create_migrations
11
+ rails_command "railties:install:migrations FROM=noticed", inline: true
12
+ end
13
+
14
+ def done
15
+ readme "README" if behavior == :invoke
16
+ end
17
+ end
18
+ end
19
+ end
@@ -4,7 +4,7 @@ require "rails/generators/named_base"
4
4
 
5
5
  module Noticed
6
6
  module Generators
7
- class NotificationGenerator < Rails::Generators::NamedBase
7
+ class NotifierGenerator < Rails::Generators::NamedBase
8
8
  include Rails::Generators::ResourceHelpers
9
9
 
10
10
  source_root File.expand_path("../templates", __FILE__)
@@ -12,7 +12,7 @@ module Noticed
12
12
  desc "Generates a notification with the given NAME."
13
13
 
14
14
  def generate_notification
15
- template "notification.rb", "app/notifications/#{file_path}.rb"
15
+ template "notifier.rb", "app/notifiers/#{file_path}.rb"
16
16
  end
17
17
  end
18
18
  end
@@ -1,7 +1,8 @@
1
1
 
2
- 🚚 Your notifications database model has been generated!
2
+ 🚚 You're ready to start sending notifications!
3
3
 
4
4
  Next steps:
5
- 1. Run "rails db:migrate"
6
- 2. Add "has_many :notifications, as: :recipient, dependent: :destroy" to your User model(s).
7
- 3. Generate notifications with "rails g noticed:notification"
5
+ 1. Run `rails db:migrate`
6
+ 2. Add `has_many :notifications, as: :recipient, dependent: :destroy, class_name: "Noticed::Notification"` to your User model(s).
7
+ 2. Add `has_many :notifications, as: :record, dependent: :destroy, class_name: "Noticed::Event"` to your model(s) that notifications reference.
8
+ 3. Generate notifiers with "rails g noticed:notifier"
@@ -1,12 +1,8 @@
1
- class DeliveryMethods::<%= class_name %> < Noticed::DeliveryMethods::Base
1
+ class DeliveryMethods::<%= class_name %> < Noticed::DeliveryMethod
2
+ # Specify the config options your delivery method requires in its config block
3
+ required_options # :foo, :bar
4
+
2
5
  def deliver
3
6
  # Logic for sending the notification
4
7
  end
5
-
6
- # You may override this method to validate options for the delivery method
7
- # Invalid options should raise a ValidationError
8
- #
9
- # def self.validate!(options)
10
- # raise ValidationError, "required_option missing" unless options[:required_option]
11
- # end
12
8
  end
@@ -0,0 +1,24 @@
1
+ # To deliver this notification:
2
+ #
3
+ # <%= class_name %>.with(record: @post, message: "New post").deliver(User.all)
4
+
5
+ class <%= class_name %> < Noticed::Event
6
+ # Add your delivery methods
7
+ #
8
+ # deliver_by :email do |config|
9
+ # config.mailer = "UserMailer"
10
+ # config.method = "new_post"
11
+ # end
12
+ #
13
+ # bulk_deliver_by :slack do |config|
14
+ # config.url = -> { Rails.application.credentials.slack_webhook_url }
15
+ # end
16
+ #
17
+ # deliver_by :custom do |config|
18
+ # config.class = "MyDeliveryMethod"
19
+ # end
20
+
21
+ # Add required params
22
+ #
23
+ # required_param :message
24
+ end
@@ -0,0 +1,44 @@
1
+ require "net/http"
2
+
3
+ module Noticed
4
+ module ApiClient
5
+ extend ActiveSupport::Concern
6
+
7
+ # Helper method for making POST requests from delivery methods
8
+ #
9
+ # Usage:
10
+ # post_request("http://example.com", basic_auth: {user:, pass:}, headers: {}, json: {}, form: {})
11
+ #
12
+ def post_request(url, args = {})
13
+ args.compact!
14
+
15
+ uri = URI(url)
16
+ http = Net::HTTP.new(uri.host, uri.port)
17
+ http.use_ssl = true if uri.instance_of? URI::HTTPS
18
+
19
+ headers = args.delete(:headers) || {}
20
+ headers["Content-Type"] = "application/json" if args.has_key?(:json)
21
+
22
+ request = Net::HTTP::Post.new(uri.request_uri, headers)
23
+
24
+ if (basic_auth = args.delete(:basic_auth))
25
+ request.basic_auth basic_auth.fetch(:user), basic_auth.fetch(:pass)
26
+ end
27
+
28
+ if (json = args.delete(:json))
29
+ request.body = json.to_json
30
+ elsif (form = args.delete(:form))
31
+ request.set_form(form, "multipart/form-data")
32
+ end
33
+
34
+ logger.debug("POST #{url}")
35
+ logger.debug(request.body)
36
+ response = http.request(request)
37
+ logger.debug("Response: #{response.code}: #{response.body.inspect}")
38
+
39
+ raise ResponseUnsuccessful.new(response, url, args) unless response.code.start_with?("20")
40
+
41
+ response
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,46 @@
1
+ module Noticed
2
+ class BulkDeliveryMethod < ApplicationJob
3
+ include ApiClient
4
+ include RequiredOptions
5
+
6
+ class_attribute :logger, default: Rails.logger
7
+
8
+ attr_reader :config, :event
9
+
10
+ def perform(delivery_method_name, event)
11
+ @event = event
12
+ @config = event.bulk_delivery_methods.fetch(delivery_method_name).config
13
+
14
+ return false if config.has_key?(:if) && !evaluate_option(:if)
15
+ return false if config.has_key?(:unless) && evaluate_option(:unless)
16
+
17
+ deliver
18
+ end
19
+
20
+ def deliver
21
+ raise NotImplementedError, "Bulk delivery methods must implement the `deliver` method"
22
+ end
23
+
24
+ def fetch_constant(name)
25
+ option = config[name]
26
+ option.is_a?(String) ? option.constantize : option
27
+ end
28
+
29
+ def evaluate_option(name)
30
+ option = config[name]
31
+
32
+ # Evaluate Proc within the context of the notifier
33
+ if option&.respond_to?(:call)
34
+ event.instance_exec(&option)
35
+
36
+ # Call method if symbol and matching method
37
+ elsif option.is_a?(Symbol) && event.respond_to?(option)
38
+ event.send(option)
39
+
40
+ # Return the value
41
+ else
42
+ option
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,11 @@
1
+ module Noticed
2
+ module BulkDeliveryMethods
3
+ class Discord < BulkDeliveryMethod
4
+ required_options :json, :url
5
+
6
+ def deliver
7
+ post_request evaluate_option(:url), headers: evaluate_option(:headers), json: evaluate_option(:json)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,17 @@
1
+ module Noticed
2
+ module BulkDeliveryMethods
3
+ class Slack < BulkDeliveryMethod
4
+ DEFAULT_URL = "https://slack.com/api/chat.postMessage"
5
+
6
+ required_options :json
7
+
8
+ def deliver
9
+ post_request url, headers: evaluate_option(:headers), json: evaluate_option(:json)
10
+ end
11
+
12
+ def url
13
+ evaluate_option(:url) || DEFAULT_URL
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ module Noticed
2
+ module BulkDeliveryMethods
3
+ class Webhook < BulkDeliveryMethod
4
+ required_options :url
5
+
6
+ def deliver
7
+ Rails.logger.debug(evaluate_option(:json))
8
+ post_request(
9
+ evaluate_option(:url),
10
+ basic_auth: evaluate_option(:basic_auth),
11
+ headers: evaluate_option(:headers),
12
+ json: evaluate_option(:json),
13
+ form: evaluate_option(:form)
14
+ )
15
+ end
16
+ end
17
+ end
18
+ end
data/lib/noticed/coder.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require "active_job/arguments"
2
+
1
3
  module Noticed
2
4
  class Coder
3
5
  def self.load(data)
@@ -0,0 +1,51 @@
1
+ module Noticed
2
+ class DeliveryMethod < ApplicationJob
3
+ include ApiClient
4
+ include RequiredOptions
5
+
6
+ class_attribute :logger, default: Rails.logger
7
+
8
+ attr_reader :config, :event, :notification
9
+ delegate :recipient, to: :notification
10
+ delegate :record, :params, to: :event
11
+
12
+ def perform(delivery_method_name, notification, overrides: {})
13
+ @notification = notification
14
+ @event = notification.event
15
+
16
+ # Look up config from Notifier and merge overrides
17
+ @config = event.delivery_methods.fetch(delivery_method_name).config.merge(overrides)
18
+
19
+ return false if config.has_key?(:if) && !evaluate_option(:if)
20
+ return false if config.has_key?(:unless) && evaluate_option(:unless)
21
+
22
+ deliver
23
+ end
24
+
25
+ def deliver
26
+ raise NotImplementedError, "Delivery methods must implement the `deliver` method"
27
+ end
28
+
29
+ def fetch_constant(name)
30
+ option = config[name]
31
+ option.is_a?(String) ? option.constantize : option
32
+ end
33
+
34
+ def evaluate_option(name)
35
+ option = config[name]
36
+
37
+ # Evaluate Proc within the context of the Notification
38
+ if option&.respond_to?(:call)
39
+ notification.instance_exec(&option)
40
+
41
+ # Call method if symbol and matching method on Notifier
42
+ elsif option.is_a?(Symbol) && event.respond_to?(option)
43
+ event.send(option, self)
44
+
45
+ # Return the value
46
+ else
47
+ option
48
+ end
49
+ end
50
+ end
51
+ end