active_delivery 1.0.0 → 1.2.0
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 +22 -0
- data/README.md +60 -25
- data/lib/.rbnext/3.0/abstract_notifier/async_adapters/active_job.rb +12 -3
- data/lib/.rbnext/3.0/abstract_notifier/base.rb +223 -0
- data/lib/.rbnext/3.0/active_delivery/base.rb +14 -13
- data/lib/.rbnext/3.0/active_delivery/lines/base.rb +14 -2
- data/lib/.rbnext/3.0/active_delivery/lines/mailer.rb +4 -0
- data/lib/.rbnext/3.0/active_delivery/testing/minitest.rb +58 -0
- data/lib/.rbnext/3.0/active_delivery/testing.rb +1 -0
- data/lib/.rbnext/3.1/abstract_notifier/async_adapters/active_job.rb +36 -0
- data/lib/.rbnext/3.1/abstract_notifier/base.rb +16 -10
- data/lib/.rbnext/3.1/active_delivery/base.rb +14 -13
- data/lib/.rbnext/3.1/active_delivery/lines/base.rb +14 -2
- data/lib/.rbnext/3.1/active_delivery/testing/minitest.rb +58 -0
- data/lib/.rbnext/3.2/abstract_notifier/async_adapters/active_job.rb +36 -0
- data/lib/.rbnext/3.2/abstract_notifier/base.rb +223 -0
- data/lib/.rbnext/3.2/abstract_notifier/testing/rspec.rb +164 -0
- data/lib/.rbnext/3.2/abstract_notifier/testing.rb +53 -0
- data/lib/.rbnext/3.2/active_delivery/base.rb +249 -0
- data/lib/.rbnext/3.2/active_delivery/lines/base.rb +101 -0
- data/lib/.rbnext/3.2/active_delivery/lines/notifier.rb +57 -0
- data/lib/.rbnext/3.2/active_delivery/testing/rspec.rb +222 -0
- data/lib/abstract_notifier/async_adapters/active_job.rb +12 -3
- data/lib/abstract_notifier/base.rb +15 -9
- data/lib/abstract_notifier/testing/minitest.rb +1 -1
- data/lib/abstract_notifier/testing/rspec.rb +4 -4
- data/lib/abstract_notifier/testing.rb +2 -2
- data/lib/active_delivery/base.rb +14 -13
- data/lib/active_delivery/lines/base.rb +14 -2
- data/lib/active_delivery/lines/mailer.rb +4 -0
- data/lib/active_delivery/lines/notifier.rb +8 -4
- data/lib/active_delivery/testing/minitest.rb +58 -0
- data/lib/active_delivery/testing/rspec.rb +4 -4
- data/lib/active_delivery/testing.rb +1 -0
- data/lib/active_delivery/version.rb +1 -1
- metadata +20 -7
@@ -0,0 +1,164 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AbstractNotifier
|
4
|
+
class HaveSentNotification < RSpec::Matchers::BuiltIn::BaseMatcher
|
5
|
+
attr_reader :payload
|
6
|
+
|
7
|
+
def initialize(payload = nil)
|
8
|
+
@payload = payload
|
9
|
+
set_expected_number(:exactly, 1)
|
10
|
+
end
|
11
|
+
|
12
|
+
def exactly(count)
|
13
|
+
set_expected_number(:exactly, count)
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
def at_least(count)
|
18
|
+
set_expected_number(:at_least, count)
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def at_most(count)
|
23
|
+
set_expected_number(:at_most, count)
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
def times
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
def once
|
32
|
+
exactly(:once)
|
33
|
+
end
|
34
|
+
|
35
|
+
def twice
|
36
|
+
exactly(:twice)
|
37
|
+
end
|
38
|
+
|
39
|
+
def thrice
|
40
|
+
exactly(:thrice)
|
41
|
+
end
|
42
|
+
|
43
|
+
def supports_block_expectations?
|
44
|
+
true
|
45
|
+
end
|
46
|
+
|
47
|
+
def matches?(proc)
|
48
|
+
raise ArgumentError, "have_sent_notification only supports block expectations" unless Proc === proc
|
49
|
+
|
50
|
+
raise "You can only use have_sent_notification matcher in :test delivery mode" unless AbstractNotifier.test?
|
51
|
+
|
52
|
+
original_deliveries_count = deliveries.count
|
53
|
+
proc.call
|
54
|
+
in_block_deliveries = deliveries.drop(original_deliveries_count)
|
55
|
+
|
56
|
+
@matching_deliveries, @unmatching_deliveries =
|
57
|
+
in_block_deliveries.partition do |actual_payload|
|
58
|
+
next true if payload.nil?
|
59
|
+
|
60
|
+
if payload.is_a?(::Hash) && !payload[:via]
|
61
|
+
actual_payload = actual_payload.dup
|
62
|
+
actual_payload.delete(:via)
|
63
|
+
end
|
64
|
+
|
65
|
+
payload === actual_payload
|
66
|
+
end
|
67
|
+
|
68
|
+
@matching_count = @matching_deliveries.size
|
69
|
+
|
70
|
+
case @expectation_type
|
71
|
+
when :exactly then @expected_number == @matching_count
|
72
|
+
when :at_most then @expected_number >= @matching_count
|
73
|
+
when :at_least then @expected_number <= @matching_count
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def deliveries
|
78
|
+
AbstractNotifier::Testing::Driver.deliveries
|
79
|
+
end
|
80
|
+
|
81
|
+
def set_expected_number(relativity, count)
|
82
|
+
@expectation_type = relativity
|
83
|
+
@expected_number =
|
84
|
+
case count
|
85
|
+
when :once then 1
|
86
|
+
when :twice then 2
|
87
|
+
when :thrice then 3
|
88
|
+
else Integer(count)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def failure_message
|
93
|
+
(+"expected to #{verb_present} notification: #{payload_description}").tap do |msg|
|
94
|
+
msg << " #{message_expectation_modifier}, but"
|
95
|
+
|
96
|
+
if @unmatching_deliveries.any?
|
97
|
+
msg << " #{verb_past} the following notifications:"
|
98
|
+
@unmatching_deliveries.each do |unmatching_payload|
|
99
|
+
msg << "\n #{unmatching_payload}"
|
100
|
+
end
|
101
|
+
else
|
102
|
+
msg << " haven't #{verb_past} anything"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def failure_message_when_negated
|
108
|
+
"expected not to #{verb_present} #{payload}"
|
109
|
+
end
|
110
|
+
|
111
|
+
def message_expectation_modifier
|
112
|
+
number_modifier = (@expected_number == 1) ? "once" : "#{@expected_number} times"
|
113
|
+
case @expectation_type
|
114
|
+
when :exactly then "exactly #{number_modifier}"
|
115
|
+
when :at_most then "at most #{number_modifier}"
|
116
|
+
when :at_least then "at least #{number_modifier}"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def payload_description
|
121
|
+
if payload.is_a?(RSpec::Matchers::Composable)
|
122
|
+
payload.description
|
123
|
+
else
|
124
|
+
payload
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def verb_past
|
129
|
+
"sent"
|
130
|
+
end
|
131
|
+
|
132
|
+
def verb_present
|
133
|
+
"send"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
class HaveEnqueuedNotification < HaveSentNotification
|
138
|
+
private
|
139
|
+
|
140
|
+
def deliveries
|
141
|
+
AbstractNotifier::Testing::Driver.enqueued_deliveries
|
142
|
+
end
|
143
|
+
|
144
|
+
def verb_past
|
145
|
+
"enqueued"
|
146
|
+
end
|
147
|
+
|
148
|
+
def verb_present
|
149
|
+
"enqueue"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
RSpec.configure do |config|
|
155
|
+
config.include(Module.new do
|
156
|
+
def have_sent_notification(*__rest__)
|
157
|
+
AbstractNotifier::HaveSentNotification.new(*__rest__)
|
158
|
+
end
|
159
|
+
|
160
|
+
def have_enqueued_notification(*__rest__)
|
161
|
+
AbstractNotifier::HaveEnqueuedNotification.new(*__rest__)
|
162
|
+
end
|
163
|
+
end)
|
164
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AbstractNotifier
|
4
|
+
module Testing
|
5
|
+
module Driver
|
6
|
+
class << self
|
7
|
+
def deliveries
|
8
|
+
Thread.current[:notifier_deliveries] ||= []
|
9
|
+
end
|
10
|
+
|
11
|
+
def enqueued_deliveries
|
12
|
+
Thread.current[:notifier_enqueued_deliveries] ||= []
|
13
|
+
end
|
14
|
+
|
15
|
+
def clear
|
16
|
+
deliveries.clear
|
17
|
+
enqueued_deliveries.clear
|
18
|
+
end
|
19
|
+
|
20
|
+
def send_notification(data)
|
21
|
+
deliveries << data
|
22
|
+
end
|
23
|
+
|
24
|
+
def enqueue_notification(data)
|
25
|
+
enqueued_deliveries << data
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module NotificationDelivery
|
31
|
+
def notify_now
|
32
|
+
return super unless AbstractNotifier.test?
|
33
|
+
|
34
|
+
payload = notification.payload
|
35
|
+
|
36
|
+
Driver.send_notification payload.merge(via: notifier.class)
|
37
|
+
end
|
38
|
+
|
39
|
+
def notify_later(**__kwrest__)
|
40
|
+
return super unless AbstractNotifier.test?
|
41
|
+
|
42
|
+
payload = notification.payload
|
43
|
+
|
44
|
+
Driver.enqueue_notification payload.merge(via: notifier.class, **__kwrest__)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
AbstractNotifier::NotificationDelivery.prepend AbstractNotifier::Testing::NotificationDelivery
|
51
|
+
|
52
|
+
require "abstract_notifier/testing/rspec" if defined?(RSpec::Core)
|
53
|
+
require "abstract_notifier/testing/minitest" if defined?(Minitest::Assertions)
|
@@ -0,0 +1,249 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveDelivery
|
4
|
+
class Delivery # :nodoc:
|
5
|
+
attr_reader :params, :options, :metadata, :notification, :owner
|
6
|
+
|
7
|
+
def initialize(owner, notification:, params:, options:, metadata:)
|
8
|
+
@owner = owner
|
9
|
+
@notification = notification
|
10
|
+
@params = params.freeze
|
11
|
+
@options = options.freeze
|
12
|
+
@metadata = metadata.freeze
|
13
|
+
end
|
14
|
+
|
15
|
+
def deliver_later(**opts) = owner.perform_notify(self, enqueue_options: opts)
|
16
|
+
|
17
|
+
def deliver_now(**opts) = owner.perform_notify(self, sync: true)
|
18
|
+
|
19
|
+
def delivery_class = owner.class
|
20
|
+
end
|
21
|
+
|
22
|
+
class << self
|
23
|
+
# Whether to memoize resolved handler classes or not.
|
24
|
+
# Set to false if you're using a code reloader (e.g., Zeitwerk).
|
25
|
+
#
|
26
|
+
# Defaults to true (i.e. memoization is enabled
|
27
|
+
attr_accessor :cache_classes
|
28
|
+
# Whether to enforce specifying available delivery actions via .delivers in the
|
29
|
+
# delivery classes
|
30
|
+
attr_accessor :deliver_actions_required
|
31
|
+
end
|
32
|
+
|
33
|
+
self.cache_classes = true
|
34
|
+
self.deliver_actions_required = false
|
35
|
+
|
36
|
+
# Base class for deliveries.
|
37
|
+
#
|
38
|
+
# Delivery object describes how to notify a user about
|
39
|
+
# an event (e.g. via email or via push notification or both).
|
40
|
+
#
|
41
|
+
# Delivery class acts like a proxy in front of the different delivery channels
|
42
|
+
# (i.e. mailers, notifiers). That means that calling a method on delivery class invokes the
|
43
|
+
# same method on the corresponding class, e.g.:
|
44
|
+
#
|
45
|
+
# EventsDelivery.one_hour_before(profile, event).deliver_later
|
46
|
+
# # or
|
47
|
+
# EventsDelivery.notify(:one_hour_before, profile, event)
|
48
|
+
#
|
49
|
+
# # under the hood it calls
|
50
|
+
# EventsMailer.one_hour_before(profile, event).deliver_later
|
51
|
+
#
|
52
|
+
# # and
|
53
|
+
# EventsNotifier.one_hour_before(profile, event).notify_later
|
54
|
+
#
|
55
|
+
# Delivery also supports _parameterized_ calling:
|
56
|
+
#
|
57
|
+
# EventsDelivery.with(profile: profile).canceled(event).deliver_later
|
58
|
+
#
|
59
|
+
# The parameters could be accessed through `params` instance method (e.g.
|
60
|
+
# to implement guard-like logic).
|
61
|
+
#
|
62
|
+
# When params are presents the parametrized mailer is used, i.e.:
|
63
|
+
#
|
64
|
+
# EventsMailer.with(profile: profile).canceled(event).deliver_later
|
65
|
+
#
|
66
|
+
# See https://api.rubyonrails.org/classes/ActionMailer/Parameterized.html
|
67
|
+
class Base
|
68
|
+
class << self
|
69
|
+
attr_accessor :abstract_class
|
70
|
+
|
71
|
+
alias_method :with, :new
|
72
|
+
|
73
|
+
# Enqueues delivery (i.e. uses #deliver_later for mailers)
|
74
|
+
def notify(...)
|
75
|
+
new.notify(...)
|
76
|
+
end
|
77
|
+
|
78
|
+
# The same as .notify but delivers synchronously
|
79
|
+
# (i.e. #deliver_now for mailers)
|
80
|
+
def notify!(mid, *__rest__, **hargs)
|
81
|
+
notify(mid, *__rest__, **hargs, sync: true)
|
82
|
+
end
|
83
|
+
|
84
|
+
alias_method :notify_now, :notify!
|
85
|
+
|
86
|
+
def delivery_lines
|
87
|
+
@lines ||= if superclass.respond_to?(:delivery_lines)
|
88
|
+
superclass.delivery_lines.each_with_object({}) do |(key, val), acc|
|
89
|
+
acc[key] = val.dup_for(self)
|
90
|
+
end
|
91
|
+
else
|
92
|
+
{}
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def register_line(line_id, line_class = nil, notifier: nil, **__kwrest__)
|
97
|
+
raise ArgumentError, "A line class or notifier configuration must be provided" if line_class.nil? && notifier.nil?
|
98
|
+
|
99
|
+
# Configure Notifier
|
100
|
+
if line_class.nil?
|
101
|
+
line_class = ActiveDelivery::Lines::Notifier
|
102
|
+
end
|
103
|
+
|
104
|
+
delivery_lines[line_id] = line_class.new(id: line_id, owner: self, **__kwrest__)
|
105
|
+
|
106
|
+
instance_eval <<~CODE, __FILE__, __LINE__ + 1
|
107
|
+
def #{line_id}(val)
|
108
|
+
delivery_lines[:#{line_id}].handler_class_name = val
|
109
|
+
end
|
110
|
+
|
111
|
+
def #{line_id}_class
|
112
|
+
delivery_lines[:#{line_id}].handler_class
|
113
|
+
end
|
114
|
+
CODE
|
115
|
+
end
|
116
|
+
|
117
|
+
def unregister_line(line_id)
|
118
|
+
removed_line = delivery_lines.delete(line_id)
|
119
|
+
|
120
|
+
return if removed_line.nil?
|
121
|
+
|
122
|
+
singleton_class.undef_method line_id
|
123
|
+
singleton_class.undef_method "#{line_id}_class"
|
124
|
+
end
|
125
|
+
|
126
|
+
def abstract_class? = abstract_class == true
|
127
|
+
|
128
|
+
# Specify explicitly which actions are supported by the delivery.
|
129
|
+
def delivers(*actions)
|
130
|
+
actions.each do |mid|
|
131
|
+
class_eval <<~CODE, __FILE__, __LINE__ + 1
|
132
|
+
def self.#{mid}(...)
|
133
|
+
new.#{mid}(...)
|
134
|
+
end
|
135
|
+
|
136
|
+
def #{mid}(*args, **kwargs)
|
137
|
+
delivery(
|
138
|
+
notification: :#{mid},
|
139
|
+
params: args,
|
140
|
+
options: kwargs
|
141
|
+
)
|
142
|
+
end
|
143
|
+
CODE
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def respond_to_missing?(mid, include_private = false)
|
148
|
+
unless ActiveDelivery.deliver_actions_required
|
149
|
+
return true if delivery_lines.any? { |_, line| line.notify?(mid) }
|
150
|
+
end
|
151
|
+
|
152
|
+
super
|
153
|
+
end
|
154
|
+
|
155
|
+
def method_missing(mid, *__rest__, **__kwrest__)
|
156
|
+
return super unless respond_to_missing?(mid)
|
157
|
+
|
158
|
+
# Lazily define a class method to avoid lookups
|
159
|
+
delivers(mid)
|
160
|
+
|
161
|
+
public_send(mid, *__rest__, **__kwrest__)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
self.abstract_class = true
|
166
|
+
|
167
|
+
attr_reader :params, :notification_name
|
168
|
+
|
169
|
+
def initialize(**params)
|
170
|
+
@params = params
|
171
|
+
@params.freeze
|
172
|
+
end
|
173
|
+
|
174
|
+
# Enqueues delivery (i.e. uses #deliver_later for mailers)
|
175
|
+
def notify(mid, *args, **kwargs)
|
176
|
+
perform_notify(
|
177
|
+
delivery(notification: mid, params: args, options: kwargs)
|
178
|
+
)
|
179
|
+
end
|
180
|
+
|
181
|
+
# The same as .notify but delivers synchronously
|
182
|
+
# (i.e. #deliver_now for mailers)
|
183
|
+
def notify!(mid, *args, **kwargs)
|
184
|
+
perform_notify(
|
185
|
+
delivery(notification: mid, params: args, options: kwargs),
|
186
|
+
sync: true
|
187
|
+
)
|
188
|
+
end
|
189
|
+
|
190
|
+
alias_method :notify_now, :notify!
|
191
|
+
|
192
|
+
def respond_to_missing?(mid, include_private = false)
|
193
|
+
unless ActiveDelivery.deliver_actions_required
|
194
|
+
return true if delivery_lines.any? { |_, line| line.notify?(mid) }
|
195
|
+
end
|
196
|
+
|
197
|
+
super
|
198
|
+
end
|
199
|
+
|
200
|
+
def method_missing(mid, *__rest__, **__kwrest__)
|
201
|
+
return super unless respond_to_missing?(mid)
|
202
|
+
|
203
|
+
# Lazily define a method to avoid future lookups
|
204
|
+
self.class.class_eval <<~CODE, __FILE__, __LINE__ + 1
|
205
|
+
def #{mid}(*args, **kwargs)
|
206
|
+
delivery(
|
207
|
+
notification: :#{mid},
|
208
|
+
params: args,
|
209
|
+
options: kwargs
|
210
|
+
)
|
211
|
+
end
|
212
|
+
CODE
|
213
|
+
|
214
|
+
public_send(mid, *__rest__, **__kwrest__)
|
215
|
+
end
|
216
|
+
|
217
|
+
protected
|
218
|
+
|
219
|
+
def perform_notify(delivery, sync: false, enqueue_options: {})
|
220
|
+
delivery_lines.each do |type, line|
|
221
|
+
next unless line.notify?(delivery.notification)
|
222
|
+
|
223
|
+
notify_line(type, line, delivery, sync:, enqueue_options:)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
private
|
228
|
+
|
229
|
+
def notify_line(type, line, delivery, sync:, enqueue_options:)
|
230
|
+
line.notify(
|
231
|
+
delivery.notification,
|
232
|
+
*delivery.params,
|
233
|
+
params:,
|
234
|
+
sync:,
|
235
|
+
enqueue_options:,
|
236
|
+
**delivery.options
|
237
|
+
)
|
238
|
+
true
|
239
|
+
end
|
240
|
+
|
241
|
+
def delivery(notification:, params: nil, options: nil, metadata: nil)
|
242
|
+
Delivery.new(self, notification:, params:, options:, metadata:)
|
243
|
+
end
|
244
|
+
|
245
|
+
def delivery_lines
|
246
|
+
self.class.delivery_lines
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
unless "".respond_to?(:safe_constantize)
|
4
|
+
require "active_delivery/ext/string_constantize"
|
5
|
+
using ActiveDelivery::Ext::StringConstantize
|
6
|
+
end
|
7
|
+
|
8
|
+
module ActiveDelivery
|
9
|
+
module Lines
|
10
|
+
class Base
|
11
|
+
attr_reader :id, :options
|
12
|
+
attr_accessor :owner
|
13
|
+
attr_accessor :handler_class_name
|
14
|
+
|
15
|
+
def initialize(id:, owner:, **options)
|
16
|
+
@id = id
|
17
|
+
@owner = owner
|
18
|
+
@options = options.tap(&:freeze)
|
19
|
+
@resolver = options[:resolver] || build_pattern_resolver(options[:resolver_pattern])
|
20
|
+
end
|
21
|
+
|
22
|
+
def dup_for(new_owner)
|
23
|
+
self.class.new(id:, **options, owner: new_owner)
|
24
|
+
end
|
25
|
+
|
26
|
+
def resolve_class(name)
|
27
|
+
resolver&.call(name)
|
28
|
+
end
|
29
|
+
|
30
|
+
def notify?(method_name)
|
31
|
+
handler_class&.respond_to?(method_name)
|
32
|
+
end
|
33
|
+
|
34
|
+
def notify_now(handler, mid, ...)
|
35
|
+
end
|
36
|
+
|
37
|
+
def notify_later(handler, mid, ...)
|
38
|
+
end
|
39
|
+
|
40
|
+
def notify_later_with_options(handler, enqueue_options, mid, ...)
|
41
|
+
notify_later(handler, mid, ...)
|
42
|
+
end
|
43
|
+
|
44
|
+
def notify(mid, *__rest__, params:, sync:, enqueue_options:, **__kwrest__)
|
45
|
+
clazz = params.empty? ? handler_class : handler_class.with(**params)
|
46
|
+
if sync
|
47
|
+
return notify_now(clazz, mid, *__rest__, **__kwrest__)
|
48
|
+
end
|
49
|
+
|
50
|
+
if enqueue_options.empty?
|
51
|
+
notify_later(clazz, mid, *__rest__, **__kwrest__)
|
52
|
+
else
|
53
|
+
notify_later_with_options(clazz, enqueue_options, mid, *__rest__, **__kwrest__)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def handler_class
|
58
|
+
if ::ActiveDelivery.cache_classes
|
59
|
+
return @handler_class if instance_variable_defined?(:@handler_class)
|
60
|
+
end
|
61
|
+
|
62
|
+
return @handler_class = nil if owner.abstract_class?
|
63
|
+
|
64
|
+
superline = owner.superclass.delivery_lines[id] if owner.superclass.respond_to?(:delivery_lines) && owner.superclass.delivery_lines[id]
|
65
|
+
|
66
|
+
# If an explicit class name has been specified somewhere in the ancestor chain, use it.
|
67
|
+
class_name = @handler_class_name || superline&.handler_class_name
|
68
|
+
|
69
|
+
@handler_class =
|
70
|
+
if class_name
|
71
|
+
class_name.is_a?(Class) ? class_name : class_name.safe_constantize
|
72
|
+
else
|
73
|
+
resolve_class(owner) || superline&.handler_class
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
attr_reader :resolver
|
80
|
+
|
81
|
+
def build_pattern_resolver(pattern)
|
82
|
+
return unless pattern
|
83
|
+
|
84
|
+
proc do |delivery|
|
85
|
+
delivery_class = delivery.name
|
86
|
+
|
87
|
+
next unless delivery_class
|
88
|
+
|
89
|
+
*namespace, delivery_name = delivery_class.split("::")
|
90
|
+
|
91
|
+
delivery_namespace = ""
|
92
|
+
delivery_namespace = "#{namespace.join("::")}::" unless namespace.empty?
|
93
|
+
|
94
|
+
delivery_name = delivery_name.sub(/Delivery$/, "")
|
95
|
+
|
96
|
+
(pattern % {delivery_class:, delivery_name:, delivery_namespace:}).safe_constantize
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
unless "".respond_to?(:safe_constantize)
|
4
|
+
require "active_delivery/ext/string_constantize"
|
5
|
+
using ActiveDelivery::Ext::StringConstantize
|
6
|
+
end
|
7
|
+
|
8
|
+
module ActiveDelivery
|
9
|
+
module Lines
|
10
|
+
# AbstractNotifier line for Active Delivery.
|
11
|
+
#
|
12
|
+
# You must provide custom `resolver` to infer notifier class
|
13
|
+
# (if String#safe_constantize is defined, we convert "*Delivery" -> "*Notifier").
|
14
|
+
#
|
15
|
+
# Resolver is a callable object.
|
16
|
+
class Notifier < ActiveDelivery::Lines::Base
|
17
|
+
DEFAULT_SUFFIX = "Notifier"
|
18
|
+
|
19
|
+
def initialize(**opts)
|
20
|
+
super
|
21
|
+
@resolver ||= build_resolver(options.fetch(:suffix, DEFAULT_SUFFIX))
|
22
|
+
end
|
23
|
+
|
24
|
+
def resolve_class(klass)
|
25
|
+
resolver&.call(klass)
|
26
|
+
end
|
27
|
+
|
28
|
+
def notify?(method_name)
|
29
|
+
return unless handler_class
|
30
|
+
handler_class.action_methods.include?(method_name.to_s)
|
31
|
+
end
|
32
|
+
|
33
|
+
def notify_now(handler, mid, *__rest__)
|
34
|
+
handler.public_send(mid, *__rest__).notify_now
|
35
|
+
end
|
36
|
+
|
37
|
+
def notify_later(handler, mid, *__rest__)
|
38
|
+
handler.public_send(mid, *__rest__).notify_later
|
39
|
+
end
|
40
|
+
|
41
|
+
def notify_later_with_options(handler, enqueue_options, mid, *__rest__)
|
42
|
+
handler.public_send(mid, *__rest__).notify_later(**enqueue_options)
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
attr_reader :resolver
|
48
|
+
|
49
|
+
def build_resolver(suffix)
|
50
|
+
lambda do |klass|
|
51
|
+
klass_name = klass.name
|
52
|
+
klass_name&.sub(/Delivery\z/, suffix)&.safe_constantize
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|