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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d05087cb016af8444f7668b81681675248b7b63f41f77a1d9fdb22b65ccb6845
|
4
|
+
data.tar.gz: d60851ccb9d9ea6557d131289942edf57b39c79a499e37d41f8f1d06b480446f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5a94a2fd3e2f19d9e99c0df6494752b23649802b064b390ea50d65506b01beb57650f31f5cd140495d6befa847a65423cc141ec8c313a6a116de69a4cd5576df
|
7
|
+
data.tar.gz: 4febe7f49e314d27f370642fa43085c5020dae8bac7fa9a3512514a6b861d17b726d4685ed91f081eab22f4c2af68a4117d37f8f29a576c78fe323f2c73b58e2
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,14 @@
|
|
2
2
|
|
3
3
|
## master
|
4
4
|
|
5
|
+
## 1.2.0 (2024-02-05)
|
6
|
+
|
7
|
+
- Add Minitest assertions (`assert_deliveries`, `assert_no_deliveries`, `assert_delivery_enqueued`). ([@palkan][])
|
8
|
+
|
9
|
+
## 1.1.0 (2023-12-01) ❄️
|
10
|
+
|
11
|
+
- Support delayed delivery options (e.g, `wait_until`). ([@palkan][])
|
12
|
+
|
5
13
|
## 📬 1.0.0 (2023-08-29)
|
6
14
|
|
7
15
|
- Add `resolver_pattern` option to specify naming pattern for notifiers without using Procs. ([@palkan][])
|
@@ -43,6 +51,20 @@
|
|
43
51
|
|
44
52
|
- Add `#deliver_via(*lines)` RSpec matcher. ([@palkan][])
|
45
53
|
|
54
|
+
- **BREAKING** The `#resolve_class` method in Line classes now receive a delivery class instead of a name:
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
# before
|
58
|
+
def resolve_class(name)
|
59
|
+
name.gsub(/Delivery$/, "Channel").safe_constantize
|
60
|
+
end
|
61
|
+
|
62
|
+
# after
|
63
|
+
def resolve_class(name)
|
64
|
+
name.to_s.gsub(/Delivery$/, "Channel").safe_constantize
|
65
|
+
end
|
66
|
+
```
|
67
|
+
|
46
68
|
- Provide ActionMailer-like interface to trigger notifications. ([@palkan][])
|
47
69
|
|
48
70
|
Now you can send notifications as follows:
|
data/README.md
CHANGED
@@ -10,6 +10,8 @@ Since v1.0, Active Delivery is bundled with [Abstract Notifier](https://github.c
|
|
10
10
|
|
11
11
|
📖 Read the introduction post: ["Crafting user notifications in Rails with Active Delivery"](https://evilmartians.com/chronicles/crafting-user-notifications-in-rails-with-active-delivery)
|
12
12
|
|
13
|
+
📖 Read more about designing notifications layer in Ruby on Rails applications in the [Layered design for Ruby on Rails applications](https://www.packtpub.com/product/layered-design-for-ruby-on-rails-applications/9781801813785) book.
|
14
|
+
|
13
15
|
<a href="https://evilmartians.com/?utm_source=action_policy">
|
14
16
|
<img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54"></a>
|
15
17
|
|
@@ -56,7 +58,7 @@ end
|
|
56
58
|
Add this line to your application's Gemfile:
|
57
59
|
|
58
60
|
```ruby
|
59
|
-
gem "active_delivery", "1.0
|
61
|
+
gem "active_delivery", "~> 1.0"
|
60
62
|
```
|
61
63
|
|
62
64
|
And then execute:
|
@@ -113,6 +115,9 @@ PostsDelivery.published(user, post).deliver_later
|
|
113
115
|
PostsMailer.published(user, post).deliver_later
|
114
116
|
PostsSMSNotifier.published(user, post).notify_later
|
115
117
|
|
118
|
+
# You can also pass options supported by your async executor (such as ActiveJob)
|
119
|
+
PostsDelivery.published(user, post).deliver_later(wait_until: 1.day.from_now)
|
120
|
+
|
116
121
|
# and whaterver your ActionCableDeliveryLine does
|
117
122
|
# under the hood.
|
118
123
|
```
|
@@ -288,17 +293,36 @@ MyDeliver.notify(:something_wicked_this_way_comes)
|
|
288
293
|
|
289
294
|
## Testing
|
290
295
|
|
291
|
-
|
296
|
+
### Setup
|
297
|
+
|
298
|
+
Test mode is activated automatically if `RAILS_ENV` or `RACK_ENV` env variable is equal to "test". Otherwise, add `require "active_delivery/testing/rspec"` to your `spec_helper.rb` / `rails_helper.rb` manually or `require "active_delivery/testing/minitest"`. This is also required if you're using Spring in the test environment (e.g. with help of [spring-commands-rspec](https://github.com/jonleighton/spring-commands-rspec)).
|
299
|
+
|
300
|
+
For Minitest, you also MUST include the test helper into your test class. For example:
|
301
|
+
|
302
|
+
```ruby
|
303
|
+
class ActiveSupport::TestCase
|
304
|
+
# ...
|
305
|
+
include ActiveDelivery::TestHelper
|
306
|
+
end
|
307
|
+
```
|
292
308
|
|
293
309
|
### Deliveries
|
294
310
|
|
295
|
-
Active Delivery provides an elegant way to test deliveries in your code (i.e., when you want to check whether a notification has been sent) through a `have_delivered_to` matcher:
|
311
|
+
Active Delivery provides an elegant way to test deliveries in your code (i.e., when you want to check whether a notification has been sent) through a `have_delivered_to` RSpec matcher or `assert_delivery_enqueued` Minitest assertion:
|
296
312
|
|
297
313
|
```ruby
|
314
|
+
# RSpec
|
298
315
|
it "delivers notification" do
|
299
316
|
expect { subject }.to have_delivered_to(Community::EventsDelivery, :modified, event)
|
300
317
|
.with(profile: profile)
|
301
318
|
end
|
319
|
+
|
320
|
+
# Minitest
|
321
|
+
def test_delivers_notification
|
322
|
+
assert_delivery_enqueued(Community::EventsDelivery, :modified, with: [event]) do
|
323
|
+
some_action
|
324
|
+
end
|
325
|
+
end
|
302
326
|
```
|
303
327
|
|
304
328
|
You can also use such RSpec features as compound expectations and composed matchers:
|
@@ -317,14 +341,27 @@ end
|
|
317
341
|
If you want to test that no notification is delivered you can use negation
|
318
342
|
|
319
343
|
```ruby
|
344
|
+
# RSpec
|
320
345
|
specify "when event is not found" do
|
321
346
|
expect do
|
322
347
|
described_class.perform_now(profile.id, "123", "one_hour_before")
|
323
348
|
end.not_to have_delivered_to(Community::EventsDelivery)
|
324
349
|
end
|
350
|
+
|
351
|
+
# Minitest
|
352
|
+
def test_no_notification_if_event_is_not_found
|
353
|
+
assert_no_deliveries do
|
354
|
+
some_action
|
355
|
+
end
|
356
|
+
|
357
|
+
# Alternatively, you can use the positive assertion
|
358
|
+
assert_deliveries(0) do
|
359
|
+
some_action
|
360
|
+
end
|
361
|
+
end
|
325
362
|
```
|
326
363
|
|
327
|
-
|
364
|
+
With RSpec, you can also use the `#have_not_delivered_to` matcher:
|
328
365
|
|
329
366
|
```ruby
|
330
367
|
specify "when event is not found" do
|
@@ -357,7 +394,7 @@ describe PostsDelivery do
|
|
357
394
|
end
|
358
395
|
```
|
359
396
|
|
360
|
-
You can also use the `#deliver_via`
|
397
|
+
You can also use the `#deliver_via` RSpec matcher as follows:
|
361
398
|
|
362
399
|
```ruby
|
363
400
|
describe PostsDelivery, type: :delivery do
|
@@ -384,8 +421,6 @@ describe PostsDelivery, type: :delivery do
|
|
384
421
|
end
|
385
422
|
```
|
386
423
|
|
387
|
-
**NOTE:** test mode activated automatically if `RAILS_ENV` or `RACK_ENV` env variable is equal to "test". Otherwise, add `require "active_delivery/testing/rspec"` to your `spec_helper.rb` / `rails_helper.rb` manually. This is also required if you're using Spring in the test environment (e.g. with help of [spring-commands-rspec](https://github.com/jonleighton/spring-commands-rspec)).
|
388
|
-
|
389
424
|
## Custom "lines"
|
390
425
|
|
391
426
|
The _Line_ class describes the way you want to _transfer_ your deliveries.
|
@@ -407,8 +442,8 @@ class EventPigeon
|
|
407
442
|
alias_method :with, :new
|
408
443
|
|
409
444
|
# delegate delivery action to the instance
|
410
|
-
def message_arrived(*
|
411
|
-
new.message_arrived(*
|
445
|
+
def message_arrived(*)
|
446
|
+
new.message_arrived(*)
|
412
447
|
end
|
413
448
|
end
|
414
449
|
|
@@ -444,17 +479,17 @@ class PigeonLine < ActiveDelivery::Lines::Base
|
|
444
479
|
# Called when we want to send message synchronously
|
445
480
|
# `sender` here either `sender_class` or `sender_class.with(params)`
|
446
481
|
# if params passed.
|
447
|
-
def notify_now(sender, delivery_action,
|
482
|
+
def notify_now(sender, delivery_action, *, **)
|
448
483
|
# For example, our EventPigeon class returns some `Pigeon` object
|
449
|
-
pigeon = sender.public_send(delivery_action,
|
484
|
+
pigeon = sender.public_send(delivery_action, *, **)
|
450
485
|
# PigeonLaunchService do all the sending job
|
451
486
|
PigeonService.launch pigeon
|
452
487
|
end
|
453
488
|
|
454
489
|
# Called when we want to send a message asynchronously.
|
455
490
|
# For example, you can use a background job here.
|
456
|
-
def notify_later(sender, delivery_action,
|
457
|
-
pigeon = sender.public_send(delivery_action,
|
491
|
+
def notify_later(sender, delivery_action, *, **)
|
492
|
+
pigeon = sender.public_send(delivery_action, *, **)
|
458
493
|
# PigeonLaunchService do all the sending job
|
459
494
|
PigeonLaunchJob.perform_later pigeon
|
460
495
|
end
|
@@ -472,8 +507,8 @@ class EventPigeon
|
|
472
507
|
alias_method :with, :new
|
473
508
|
|
474
509
|
# delegate delivery action to the instance
|
475
|
-
def message_arrived(*
|
476
|
-
new.message_arrived(*
|
510
|
+
def message_arrived(*)
|
511
|
+
new.message_arrived(*)
|
477
512
|
end
|
478
513
|
end
|
479
514
|
|
@@ -488,18 +523,18 @@ class EventPigeon
|
|
488
523
|
end
|
489
524
|
|
490
525
|
class PigeonLine < ActiveDelivery::Lines::Base
|
491
|
-
def notify_later(sender, delivery_action,
|
526
|
+
def notify_later(sender, delivery_action, *, **kwargs)
|
492
527
|
# `to_s` is important for serialization. Unless you might have error
|
493
|
-
PigeonLaunchJob.perform_later
|
528
|
+
PigeonLaunchJob.perform_later(sender.class.to_s, delivery_action, *, **kwargs.merge(params: line.params))
|
494
529
|
end
|
495
530
|
end
|
496
531
|
|
497
532
|
class PigeonLaunchJob < ActiveJob::Base
|
498
|
-
def perform(sender, delivery_action,
|
533
|
+
def perform(sender, delivery_action, *, params: nil, **)
|
499
534
|
klass = sender.safe_constantize
|
500
535
|
handler = params ? klass.with(**params) : klass.new
|
501
536
|
|
502
|
-
handler.public_send(delivery_action,
|
537
|
+
handler.public_send(delivery_action, *, **)
|
503
538
|
end
|
504
539
|
end
|
505
540
|
```
|
@@ -588,12 +623,12 @@ class ActionCableDeliveryLine < ActiveDelivery::Line::Base
|
|
588
623
|
# We want to broadcast all notifications
|
589
624
|
def notify?(...) = true
|
590
625
|
|
591
|
-
def notify_now(context, delivery_action,
|
626
|
+
def notify_now(context, delivery_action, *, **)
|
592
627
|
# Skip if no user provided
|
593
628
|
return unless context.user
|
594
629
|
|
595
630
|
payload = {event: [context.scope, delivery_action].join(".")}
|
596
|
-
payload.merge!(serialized_args(
|
631
|
+
payload.merge!(serialized_args(*, **))
|
597
632
|
|
598
633
|
DeliveryChannel.broadcast_to context.user, payload
|
599
634
|
end
|
@@ -706,7 +741,7 @@ end
|
|
706
741
|
|
707
742
|
### Background jobs / async notifications
|
708
743
|
|
709
|
-
To use `#notify_later` you **must** configure an async adapter for Abstract Notifier.
|
744
|
+
To use `#notify_later(**delivery_options)` you **must** configure an async adapter for Abstract Notifier.
|
710
745
|
|
711
746
|
We provide an Active Job adapter out of the box and enable it if Active Job is found.
|
712
747
|
|
@@ -718,13 +753,13 @@ class MyAsyncAdapter
|
|
718
753
|
def initialize(options = {})
|
719
754
|
end
|
720
755
|
|
721
|
-
# `
|
722
|
-
def
|
756
|
+
# `enqueue_delivery` method accepts notifier class, action name and notification parameters
|
757
|
+
def enqueue_delivery(delivery, **options)
|
723
758
|
# <Your implementation here>
|
724
759
|
# To trigger the notification delivery, you can use the following snippet:
|
725
760
|
#
|
726
761
|
# AbstractNotifier::NotificationDelivery.new(
|
727
|
-
# notifier_class
|
762
|
+
# delivery.notifier_class, delivery.action_name, **delivery.delivery_params
|
728
763
|
# ).notify_now
|
729
764
|
end
|
730
765
|
end
|
@@ -11,15 +11,24 @@ module AbstractNotifier
|
|
11
11
|
|
12
12
|
DEFAULT_QUEUE = "notifiers"
|
13
13
|
|
14
|
-
attr_reader :job
|
14
|
+
attr_reader :job, :queue
|
15
15
|
|
16
16
|
def initialize(queue: DEFAULT_QUEUE, job: DeliveryJob)
|
17
|
-
@job = job
|
17
|
+
@job = job
|
18
|
+
@queue = queue
|
18
19
|
end
|
19
20
|
|
20
21
|
def enqueue(...)
|
21
|
-
job.perform_later(...)
|
22
|
+
job.set(queue: queue).perform_later(...)
|
22
23
|
end; respond_to?(:ruby2_keywords, true) && (ruby2_keywords :enqueue)
|
24
|
+
|
25
|
+
def enqueue_delivery(delivery, **__kwrest__)
|
26
|
+
job.set(queue: queue, **__kwrest__).perform_later(
|
27
|
+
delivery.notifier_class.name,
|
28
|
+
delivery.action_name,
|
29
|
+
**delivery.delivery_params
|
30
|
+
)
|
31
|
+
end
|
23
32
|
end
|
24
33
|
end
|
25
34
|
end
|
@@ -0,0 +1,223 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AbstractNotifier
|
4
|
+
# NotificationDelivery payload wrapper which contains
|
5
|
+
# information about the current notifier class
|
6
|
+
# and knows how to trigger the delivery
|
7
|
+
class NotificationDelivery
|
8
|
+
attr_reader :action_name, :notifier_class
|
9
|
+
|
10
|
+
def initialize(notifier_class, action_name, params: {}, args: [], kwargs: {})
|
11
|
+
@notifier_class = notifier_class
|
12
|
+
@action_name = action_name
|
13
|
+
@params = params
|
14
|
+
@args = args
|
15
|
+
@kwargs = kwargs
|
16
|
+
end
|
17
|
+
|
18
|
+
def processed
|
19
|
+
return @processed if instance_variable_defined?(:@processed)
|
20
|
+
|
21
|
+
@processed = notifier.process_action(action_name, *args, **kwargs) || Notification.new(nil)
|
22
|
+
end
|
23
|
+
|
24
|
+
alias_method :notification, :processed
|
25
|
+
|
26
|
+
def notify_later(**__kwrest__)
|
27
|
+
if notifier_class.async_adapter.respond_to?(:enqueue_delivery)
|
28
|
+
notifier_class.async_adapter.enqueue_delivery(self, **__kwrest__)
|
29
|
+
else
|
30
|
+
notifier_class.async_adapter.enqueue(notifier_class.name, action_name, params: params, args: args, kwargs: kwargs)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def notify_now
|
35
|
+
return unless notification.payload
|
36
|
+
|
37
|
+
notifier.deliver!(notification)
|
38
|
+
end
|
39
|
+
|
40
|
+
def delivery_params ; {params: params, args: args, kwargs: kwargs}; end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
attr_reader :params, :args, :kwargs
|
45
|
+
|
46
|
+
def notifier
|
47
|
+
@notifier ||= notifier_class.new(action_name, **params)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Notification object contains the compiled payload to be delivered
|
52
|
+
class Notification
|
53
|
+
attr_reader :payload
|
54
|
+
|
55
|
+
def initialize(payload)
|
56
|
+
@payload = payload
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Base class for notifiers
|
61
|
+
class Base
|
62
|
+
class ParamsProxy
|
63
|
+
attr_reader :notifier_class, :params
|
64
|
+
|
65
|
+
def initialize(notifier_class, params)
|
66
|
+
@notifier_class = notifier_class
|
67
|
+
@params = params
|
68
|
+
end
|
69
|
+
|
70
|
+
# rubocop:disable Style/MethodMissingSuper
|
71
|
+
def method_missing(method_name, *args, **kwargs)
|
72
|
+
NotificationDelivery.new(notifier_class, method_name, params: params, args: args, kwargs: kwargs)
|
73
|
+
end
|
74
|
+
# rubocop:enable Style/MethodMissingSuper
|
75
|
+
|
76
|
+
def respond_to_missing?(*__rest__)
|
77
|
+
notifier_class.respond_to_missing?(*__rest__)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class << self
|
82
|
+
attr_writer :driver
|
83
|
+
|
84
|
+
def driver
|
85
|
+
return @driver if instance_variable_defined?(:@driver)
|
86
|
+
|
87
|
+
@driver =
|
88
|
+
if superclass.respond_to?(:driver)
|
89
|
+
superclass.driver
|
90
|
+
else
|
91
|
+
raise "Driver not found for #{name}. " \
|
92
|
+
"Please, specify driver via `self.driver = MyDriver`"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def async_adapter=(args)
|
97
|
+
adapter, options = Array(args)
|
98
|
+
@async_adapter = AsyncAdapters.lookup(adapter, options)
|
99
|
+
end
|
100
|
+
|
101
|
+
def async_adapter
|
102
|
+
return @async_adapter if instance_variable_defined?(:@async_adapter)
|
103
|
+
|
104
|
+
@async_adapter =
|
105
|
+
if superclass.respond_to?(:async_adapter)
|
106
|
+
superclass.async_adapter
|
107
|
+
else
|
108
|
+
AbstractNotifier.async_adapter
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def default(method_name = nil, **hargs, &block)
|
113
|
+
return @defaults_generator = block if block
|
114
|
+
|
115
|
+
return @defaults_generator = proc { send(method_name) } unless method_name.nil?
|
116
|
+
|
117
|
+
@default_params =
|
118
|
+
if superclass.respond_to?(:default_params)
|
119
|
+
superclass.default_params.merge(hargs).freeze
|
120
|
+
else
|
121
|
+
hargs.freeze
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def defaults_generator
|
126
|
+
return @defaults_generator if instance_variable_defined?(:@defaults_generator)
|
127
|
+
|
128
|
+
@defaults_generator =
|
129
|
+
if superclass.respond_to?(:defaults_generator)
|
130
|
+
superclass.defaults_generator
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def default_params
|
135
|
+
return @default_params if instance_variable_defined?(:@default_params)
|
136
|
+
|
137
|
+
@default_params =
|
138
|
+
if superclass.respond_to?(:default_params)
|
139
|
+
superclass.default_params.dup
|
140
|
+
else
|
141
|
+
{}
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def method_missing(method_name, *args, **kwargs)
|
146
|
+
if action_methods.include?(method_name.to_s)
|
147
|
+
NotificationDelivery.new(self, method_name, args: args, kwargs: kwargs)
|
148
|
+
else
|
149
|
+
super
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def with(params)
|
154
|
+
ParamsProxy.new(self, params)
|
155
|
+
end
|
156
|
+
|
157
|
+
def respond_to_missing?(method_name, _include_private = false)
|
158
|
+
action_methods.include?(method_name.to_s) || super
|
159
|
+
end
|
160
|
+
|
161
|
+
# See https://github.com/rails/rails/blob/b13a5cb83ea00d6a3d71320fd276ca21049c2544/actionpack/lib/abstract_controller/base.rb#L74
|
162
|
+
def action_methods
|
163
|
+
@action_methods ||= begin
|
164
|
+
# All public instance methods of this class, including ancestors
|
165
|
+
methods = (public_instance_methods(true) -
|
166
|
+
# Except for public instance methods of Base and its ancestors
|
167
|
+
Base.public_instance_methods(true) +
|
168
|
+
# Be sure to include shadowed public instance methods of this class
|
169
|
+
public_instance_methods(false))
|
170
|
+
|
171
|
+
methods.map!(&:to_s)
|
172
|
+
|
173
|
+
methods.to_set
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
attr_reader :params, :notification_name
|
179
|
+
|
180
|
+
def initialize(notification_name, **params)
|
181
|
+
@notification_name = notification_name
|
182
|
+
@params = params.freeze
|
183
|
+
end
|
184
|
+
|
185
|
+
def process_action(...)
|
186
|
+
public_send(...)
|
187
|
+
end; respond_to?(:ruby2_keywords, true) && (ruby2_keywords :process_action)
|
188
|
+
|
189
|
+
def deliver!(notification)
|
190
|
+
self.class.driver.call(notification.payload)
|
191
|
+
end
|
192
|
+
|
193
|
+
def notification(**payload)
|
194
|
+
merge_defaults!(payload)
|
195
|
+
|
196
|
+
payload[:body] = implicit_payload_body unless payload.key?(:body)
|
197
|
+
|
198
|
+
raise ArgumentError, "Notification body must be present" if
|
199
|
+
payload[:body].nil? || payload[:body].empty?
|
200
|
+
|
201
|
+
@notification = Notification.new(payload)
|
202
|
+
end
|
203
|
+
|
204
|
+
private
|
205
|
+
|
206
|
+
def implicit_payload_body
|
207
|
+
# no-op — override to provide custom logic
|
208
|
+
end
|
209
|
+
|
210
|
+
def merge_defaults!(payload)
|
211
|
+
defaults =
|
212
|
+
if self.class.defaults_generator
|
213
|
+
instance_exec(&self.class.defaults_generator)
|
214
|
+
else
|
215
|
+
self.class.default_params
|
216
|
+
end
|
217
|
+
|
218
|
+
defaults.each do |k, v|
|
219
|
+
payload[k] = v unless payload.key?(k)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
@@ -12,9 +12,9 @@ module ActiveDelivery
|
|
12
12
|
@metadata = metadata.freeze
|
13
13
|
end
|
14
14
|
|
15
|
-
def deliver_later ; owner.perform_notify(self); end
|
15
|
+
def deliver_later(**opts) ; owner.perform_notify(self, enqueue_options: opts); end
|
16
16
|
|
17
|
-
def deliver_now ; owner.perform_notify(self, sync: true); end
|
17
|
+
def deliver_now(**opts) ; owner.perform_notify(self, sync: true); end
|
18
18
|
|
19
19
|
def delivery_class ; owner.class; end
|
20
20
|
end
|
@@ -77,8 +77,8 @@ module ActiveDelivery
|
|
77
77
|
|
78
78
|
# The same as .notify but delivers synchronously
|
79
79
|
# (i.e. #deliver_now for mailers)
|
80
|
-
def notify!(mid, *
|
81
|
-
notify(mid, *
|
80
|
+
def notify!(mid, *__rest__, **hargs)
|
81
|
+
notify(mid, *__rest__, **hargs, sync: true)
|
82
82
|
end
|
83
83
|
|
84
84
|
alias_method :notify_now, :notify!
|
@@ -93,7 +93,7 @@ module ActiveDelivery
|
|
93
93
|
end
|
94
94
|
end
|
95
95
|
|
96
|
-
def register_line(line_id, line_class = nil, notifier: nil, **
|
96
|
+
def register_line(line_id, line_class = nil, notifier: nil, **__kwrest__)
|
97
97
|
raise ArgumentError, "A line class or notifier configuration must be provided" if line_class.nil? && notifier.nil?
|
98
98
|
|
99
99
|
# Configure Notifier
|
@@ -101,7 +101,7 @@ module ActiveDelivery
|
|
101
101
|
line_class = ActiveDelivery::Lines::Notifier
|
102
102
|
end
|
103
103
|
|
104
|
-
delivery_lines[line_id] = line_class.new(id: line_id, owner: self, **
|
104
|
+
delivery_lines[line_id] = line_class.new(id: line_id, owner: self, **__kwrest__)
|
105
105
|
|
106
106
|
instance_eval <<~CODE, __FILE__, __LINE__ + 1
|
107
107
|
def #{line_id}(val)
|
@@ -152,13 +152,13 @@ module ActiveDelivery
|
|
152
152
|
super
|
153
153
|
end
|
154
154
|
|
155
|
-
def method_missing(mid, *
|
155
|
+
def method_missing(mid, *__rest__, **__kwrest__)
|
156
156
|
return super unless respond_to_missing?(mid)
|
157
157
|
|
158
158
|
# Lazily define a class method to avoid lookups
|
159
159
|
delivers(mid)
|
160
160
|
|
161
|
-
public_send(mid, *
|
161
|
+
public_send(mid, *__rest__, **__kwrest__)
|
162
162
|
end
|
163
163
|
end
|
164
164
|
|
@@ -197,7 +197,7 @@ module ActiveDelivery
|
|
197
197
|
super
|
198
198
|
end
|
199
199
|
|
200
|
-
def method_missing(mid, *
|
200
|
+
def method_missing(mid, *__rest__, **__kwrest__)
|
201
201
|
return super unless respond_to_missing?(mid)
|
202
202
|
|
203
203
|
# Lazily define a method to avoid future lookups
|
@@ -211,27 +211,28 @@ module ActiveDelivery
|
|
211
211
|
end
|
212
212
|
CODE
|
213
213
|
|
214
|
-
public_send(mid, *
|
214
|
+
public_send(mid, *__rest__, **__kwrest__)
|
215
215
|
end
|
216
216
|
|
217
217
|
protected
|
218
218
|
|
219
|
-
def perform_notify(delivery, sync: false)
|
219
|
+
def perform_notify(delivery, sync: false, enqueue_options: {})
|
220
220
|
delivery_lines.each do |type, line|
|
221
221
|
next unless line.notify?(delivery.notification)
|
222
222
|
|
223
|
-
notify_line(type, line, delivery, sync: sync)
|
223
|
+
notify_line(type, line, delivery, sync: sync, enqueue_options: enqueue_options)
|
224
224
|
end
|
225
225
|
end
|
226
226
|
|
227
227
|
private
|
228
228
|
|
229
|
-
def notify_line(type, line, delivery, sync:)
|
229
|
+
def notify_line(type, line, delivery, sync:, enqueue_options:)
|
230
230
|
line.notify(
|
231
231
|
delivery.notification,
|
232
232
|
*delivery.params,
|
233
233
|
params: params,
|
234
234
|
sync: sync,
|
235
|
+
enqueue_options: enqueue_options,
|
235
236
|
**delivery.options
|
236
237
|
)
|
237
238
|
true
|
@@ -37,9 +37,21 @@ module ActiveDelivery
|
|
37
37
|
def notify_later(handler, mid, *__rest__, &__block__)
|
38
38
|
end; respond_to?(:ruby2_keywords, true) && (ruby2_keywords :notify_later)
|
39
39
|
|
40
|
-
def
|
40
|
+
def notify_later_with_options(handler, enqueue_options, mid, *__rest__, &__block__)
|
41
|
+
notify_later(handler, mid, *__rest__, &__block__)
|
42
|
+
end; respond_to?(:ruby2_keywords, true) && (ruby2_keywords :notify_later_with_options)
|
43
|
+
|
44
|
+
def notify(mid, *__rest__, params:, sync:, enqueue_options:, **__kwrest__)
|
41
45
|
clazz = params.empty? ? handler_class : handler_class.with(**params)
|
42
|
-
sync
|
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
|
43
55
|
end
|
44
56
|
|
45
57
|
def handler_class
|
@@ -19,6 +19,10 @@ module ActiveDelivery
|
|
19
19
|
def notify_later(mailer, mid, *__rest__, &__block__)
|
20
20
|
mailer.public_send(mid, *__rest__, &__block__).deliver_later
|
21
21
|
end; respond_to?(:ruby2_keywords, true) && (ruby2_keywords :notify_later)
|
22
|
+
|
23
|
+
def notify_later_with_options(mailer, enqueue_options, mid, *__rest__, &__block__)
|
24
|
+
mailer.public_send(mid, *__rest__, &__block__).deliver_later(**enqueue_options)
|
25
|
+
end; respond_to?(:ruby2_keywords, true) && (ruby2_keywords :notify_later_with_options)
|
22
26
|
end
|
23
27
|
|
24
28
|
ActiveDelivery::Base.register_line :mailer, Mailer, resolver: Mailer::DEFAULT_RESOLVER
|