active_delivery 0.1.1 → 0.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/.rubocop.yml +3 -0
- data/CHANGELOG.md +15 -0
- data/README.md +40 -25
- data/Rakefile +2 -0
- data/gemfiles/rails42.gemfile +2 -0
- data/gemfiles/railsmaster.gemfile +2 -0
- data/lib/active_delivery.rb +2 -0
- data/lib/active_delivery/base.rb +15 -10
- data/lib/active_delivery/callbacks.rb +10 -7
- data/lib/active_delivery/lines/base.rb +2 -0
- data/lib/active_delivery/lines/mailer.rb +2 -0
- data/lib/active_delivery/testing.rb +2 -0
- data/lib/active_delivery/testing/rspec.rb +2 -0
- data/lib/active_delivery/version.rb +3 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fab4f00d6e9540f490e987b355133449d9213d2a1f9cb683aced62e16ae36a84
|
4
|
+
data.tar.gz: e9728da5a0a19ba23e7412a2ed678cabb28e7c1c6ac16ae89246302a80b8f2dc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c3be343f6a4151bcd434105b22c0e4a028dcc0bc4a22ea1e70e13de1213a3469d4cc6ef942959df8005784eab0d41b2bcf42858aa3d7915743a4b8b337e13f0e
|
7
|
+
data.tar.gz: ef751694baca35139d81ef11a277adde948e63b59528d040ef08390c36952a6ff35da49174b80070cf5df6d836c054d1d6d30ee23cbd7103ab0c73c32b723b83
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
ADDED
data/README.md
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
|
4
4
|
# Active Delivery
|
5
5
|
|
6
|
-
Framework providing an
|
6
|
+
Framework providing an entry point (single _interface_) for all types of notifications: mailers, push notifications, whatever you want.
|
7
7
|
|
8
8
|
<a href="https://evilmartians.com/?utm_source=action_policy">
|
9
9
|
<img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54"></a>
|
@@ -17,13 +17,13 @@ Requirements:
|
|
17
17
|
|
18
18
|
We need a way to handle different notifications _channel_ (mail, push) in one place.
|
19
19
|
|
20
|
-
From the business-logic point of view we want to _notify_ a user, hence we need a _separate abstraction layer_ as an
|
20
|
+
From the business-logic point of view we want to _notify_ a user, hence we need a _separate abstraction layer_ as an entry point to different types of notifications.
|
21
21
|
|
22
22
|
## The solution
|
23
23
|
|
24
|
-
Here comes
|
24
|
+
Here comes _Active Delivery_.
|
25
25
|
|
26
|
-
In the simplest case when we have only mailers Active Delivery is just a wrapper for Mailer with (possibly) some additional logic provided (e.g
|
26
|
+
In the simplest case when we have only mailers Active Delivery is just a wrapper for Mailer with (possibly) some additional logic provided (e.g., preventing emails to unsubscribed users).
|
27
27
|
|
28
28
|
Motivations behind Active Delivery:
|
29
29
|
- organize notifications related logic:
|
@@ -59,7 +59,7 @@ $ bundle
|
|
59
59
|
|
60
60
|
## Usage
|
61
61
|
|
62
|
-
_Delivery_ class is used to trigger notifications. It describes how to notify a user (e.g
|
62
|
+
The _Delivery_ class is used to trigger notifications. It describes how to notify a user (e.g., via email or push notification or both):
|
63
63
|
|
64
64
|
```ruby
|
65
65
|
class PostsDelivery < ActiveDelivery::Base
|
@@ -68,7 +68,7 @@ class PostsDelivery < ActiveDelivery::Base
|
|
68
68
|
end
|
69
69
|
```
|
70
70
|
|
71
|
-
It acts
|
71
|
+
It acts as a proxy in front of the different delivery channels (i.e., mailers, notifiers). That means that calling a method on delivery class invokes the same method on the corresponding _sender_ class, e.g.:
|
72
72
|
|
73
73
|
```ruby
|
74
74
|
PostsDelivery.notify(:published, user, post)
|
@@ -88,7 +88,7 @@ Delivery also supports _parameterized_ calling:
|
|
88
88
|
PostsDelivery.with(user: user).notify(:published, post)
|
89
89
|
```
|
90
90
|
|
91
|
-
The parameters could be accessed through `params` instance method (e.g
|
91
|
+
The parameters could be accessed through the `params` instance method (e.g., to implement guard-like logic).
|
92
92
|
|
93
93
|
**NOTE**: When params are presents the parametrized mailer is used, i.e.:
|
94
94
|
|
@@ -100,11 +100,11 @@ See [Rails docs](https://api.rubyonrails.org/classes/ActionMailer/Parameterized.
|
|
100
100
|
|
101
101
|
## Callbacks support
|
102
102
|
|
103
|
-
**NOTE:** callbacks are only available if ActiveSupport is present in the app's
|
103
|
+
**NOTE:** callbacks are only available if ActiveSupport is present in the app's runtime.
|
104
104
|
|
105
105
|
```ruby
|
106
106
|
# Run method before delivering notification
|
107
|
-
# NOTE: when `false` is returned the
|
107
|
+
# NOTE: when `false` is returned the execution is halted
|
108
108
|
before_notify :do_something
|
109
109
|
|
110
110
|
# You can specify a notification method (to run callback only for that method)
|
@@ -116,11 +116,26 @@ after_notify :cleanup
|
|
116
116
|
around_notify :set_context
|
117
117
|
```
|
118
118
|
|
119
|
+
Example:
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
# Let's log notifications
|
123
|
+
class MyDelivery < ActiveDelivery::Base
|
124
|
+
after_notify do
|
125
|
+
# You can access the notificaion name within the instance
|
126
|
+
MyLogger.info "Delivery triggered: #{notification_name}"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
MyDeliver.notify(:something_wicked_this_way_comes)
|
131
|
+
#=> Delivery triggered: something_wicked_this_way_comes
|
132
|
+
```
|
133
|
+
|
119
134
|
## Testing
|
120
135
|
|
121
136
|
**NOTE:** RSpec only for the time being.
|
122
137
|
|
123
|
-
Active Delivery provides an elegant way to test deliveries in your code (i.e
|
138
|
+
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:
|
124
139
|
|
125
140
|
```ruby
|
126
141
|
it "delivers notification" do
|
@@ -131,7 +146,7 @@ it "delivers notification" do
|
|
131
146
|
You can also use such RSpec features as [compound expectations](https://relishapp.com/rspec/rspec-expectations/docs/compound-expectations) and [composed matchers](https://relishapp.com/rspec/rspec-expectations/v/3-8/docs/composing-matchers):
|
132
147
|
|
133
148
|
```ruby
|
134
|
-
it "delivers to
|
149
|
+
it "delivers to RSVPed members via .notify" do
|
135
150
|
expect { subject }.
|
136
151
|
to have_delivered_to(Community::EventsDelivery, :canceled, an_instance_of(event)).with(
|
137
152
|
a_hash_including(profile: another_profile)
|
@@ -141,7 +156,7 @@ it "delivers to rsvped members via .notify" do
|
|
141
156
|
end
|
142
157
|
```
|
143
158
|
|
144
|
-
If you want to test that no notification is
|
159
|
+
If you want to test that no notification is delivered you can use negation:
|
145
160
|
|
146
161
|
```ruby
|
147
162
|
specify "when event is not found" do
|
@@ -155,11 +170,11 @@ end
|
|
155
170
|
|
156
171
|
## Custom "lines"
|
157
172
|
|
158
|
-
_Line_ class describes the way you want to _transfer_ your deliveries.
|
173
|
+
The _Line_ class describes the way you want to _transfer_ your deliveries.
|
159
174
|
|
160
|
-
|
175
|
+
We only provide only Action Mailer _line_ out-of-the-box.
|
161
176
|
|
162
|
-
|
177
|
+
A line connects _delivery_ to the _sender_ class responsible for sending notifications.
|
163
178
|
|
164
179
|
If you want to use parameterized deliveries, your _sender_ class must respond to `.with(params)` method.
|
165
180
|
|
@@ -168,10 +183,10 @@ Assume that we want to send messages via _pigeons_ and we have the following sen
|
|
168
183
|
```ruby
|
169
184
|
class EventPigeon
|
170
185
|
class << self
|
171
|
-
# Add `.with`
|
186
|
+
# Add `.with` method as an alias
|
172
187
|
alias with new
|
173
188
|
|
174
|
-
# delegate delivery action to instance
|
189
|
+
# delegate delivery action to the instance
|
175
190
|
def message_arrived(*args)
|
176
191
|
new.message_arrived(*arsg)
|
177
192
|
end
|
@@ -182,13 +197,13 @@ class EventPigeon
|
|
182
197
|
end
|
183
198
|
|
184
199
|
def message_arrived(msg)
|
185
|
-
# send pigeon with the message
|
200
|
+
# send a pigeon with the message
|
186
201
|
end
|
187
202
|
end
|
188
203
|
```
|
189
204
|
|
190
|
-
Now we want to add a _pigeon_ line to our `EventDelivery
|
191
|
-
we call `EventDelivery.notify(:message_arrived, "ping-
|
205
|
+
Now we want to add a _pigeon_ line to our `EventDelivery,` that is we want to send pigeons when
|
206
|
+
we call `EventDelivery.notify(:message_arrived, "ping-pong!")`.
|
192
207
|
|
193
208
|
Line class has the following API:
|
194
209
|
|
@@ -201,7 +216,7 @@ class PigeonLine < ActiveDelivery::Lines::Base
|
|
201
216
|
name.gsub(/Delivery$/, "Pigeon").safe_constantize
|
202
217
|
end
|
203
218
|
|
204
|
-
# This method should return true if sender recognizes the delivery action
|
219
|
+
# This method should return true if the sender recognizes the delivery action
|
205
220
|
def notify?(delivery_action)
|
206
221
|
# `handler_class` is available within the line instance
|
207
222
|
sender_class.respond_to?(delivery_action)
|
@@ -217,8 +232,8 @@ class PigeonLine < ActiveDelivery::Lines::Base
|
|
217
232
|
PigeonService.launch pigeon
|
218
233
|
end
|
219
234
|
|
220
|
-
# Called when we want to send message asynchronously.
|
221
|
-
# For example, you can use background job here.
|
235
|
+
# Called when we want to send a message asynchronously.
|
236
|
+
# For example, you can use a background job here.
|
222
237
|
def notify_later(sender, delivery_action, *args)
|
223
238
|
pigeon = sender.public_send(delivery_action, *args)
|
224
239
|
# PigeonLaunchService do all the sending job
|
@@ -231,13 +246,13 @@ end
|
|
231
246
|
You can disable automatic inference of sender classes by marking delivery as _abstract_:
|
232
247
|
|
233
248
|
```ruby
|
234
|
-
# we don't
|
249
|
+
# we don't want to use ApplicationMailer by default, don't we?
|
235
250
|
class ApplicationDelivery < ActiveDelivery::Base
|
236
251
|
self.abstract_class = true
|
237
252
|
end
|
238
253
|
```
|
239
254
|
|
240
|
-
|
255
|
+
The final step is to register the line within your delivery class:
|
241
256
|
|
242
257
|
```ruby
|
243
258
|
class EventDelivery < ActiveDelivery::Base
|
data/Rakefile
CHANGED
data/gemfiles/rails42.gemfile
CHANGED
data/lib/active_delivery.rb
CHANGED
data/lib/active_delivery/base.rb
CHANGED
@@ -78,7 +78,7 @@ module ActiveDelivery
|
|
78
78
|
end
|
79
79
|
end
|
80
80
|
|
81
|
-
attr_reader :params
|
81
|
+
attr_reader :params, :notification_name
|
82
82
|
|
83
83
|
def initialize(**params)
|
84
84
|
@params = params
|
@@ -86,13 +86,9 @@ module ActiveDelivery
|
|
86
86
|
end
|
87
87
|
|
88
88
|
# Enqueues delivery (i.e. uses #deliver_later for mailers)
|
89
|
-
def notify(mid, *args
|
90
|
-
|
91
|
-
|
92
|
-
next unless line.notify?(mid)
|
93
|
-
|
94
|
-
notify_line(type, mid, *args, params: params, sync: sync)
|
95
|
-
end
|
89
|
+
def notify(mid, *args)
|
90
|
+
@notification_name = mid
|
91
|
+
do_notify(*args)
|
96
92
|
end
|
97
93
|
|
98
94
|
# The same as .notify but delivers synchronously
|
@@ -103,8 +99,17 @@ module ActiveDelivery
|
|
103
99
|
|
104
100
|
private
|
105
101
|
|
106
|
-
def
|
107
|
-
delivery_lines
|
102
|
+
def do_notify(*args, sync: false)
|
103
|
+
delivery_lines.each do |type, line|
|
104
|
+
next if line.handler_class.nil?
|
105
|
+
next unless line.notify?(notification_name)
|
106
|
+
|
107
|
+
notify_line(type, *args, params: params, sync: sync)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def notify_line(type, *args)
|
112
|
+
delivery_lines[type].notify(notification_name, *args)
|
108
113
|
end
|
109
114
|
|
110
115
|
def delivery_lines
|
@@ -41,7 +41,7 @@ module ActiveDelivery
|
|
41
41
|
end
|
42
42
|
|
43
43
|
module InstanceExt
|
44
|
-
def
|
44
|
+
def do_notify(*)
|
45
45
|
run_callbacks(:notify) { super }
|
46
46
|
end
|
47
47
|
|
@@ -64,16 +64,19 @@ module ActiveDelivery
|
|
64
64
|
skip_after_callbacks_if_terminated: true
|
65
65
|
end
|
66
66
|
|
67
|
-
def before_notify(
|
68
|
-
|
67
|
+
def before_notify(method_or_block = nil, on: :notify)
|
68
|
+
method_or_block ||= Proc.new
|
69
|
+
set_callback on, :before, method_or_block
|
69
70
|
end
|
70
71
|
|
71
|
-
def after_notify(
|
72
|
-
|
72
|
+
def after_notify(method_or_block = nil, on: :notify)
|
73
|
+
method_or_block ||= Proc.new
|
74
|
+
set_callback on, :after, method_or_block
|
73
75
|
end
|
74
76
|
|
75
|
-
def around_notify(
|
76
|
-
|
77
|
+
def around_notify(method_or_block = nil, on: :notify)
|
78
|
+
method_or_block ||= Proc.new
|
79
|
+
set_callback on, :around, method_or_block
|
77
80
|
end
|
78
81
|
end
|
79
82
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_delivery
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vladimir Dementyev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-01-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -77,6 +77,7 @@ files:
|
|
77
77
|
- ".rspec"
|
78
78
|
- ".rubocop.yml"
|
79
79
|
- ".travis.yml"
|
80
|
+
- CHANGELOG.md
|
80
81
|
- Gemfile
|
81
82
|
- LICENSE.txt
|
82
83
|
- README.md
|
@@ -114,7 +115,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
114
115
|
version: '0'
|
115
116
|
requirements: []
|
116
117
|
rubyforge_project:
|
117
|
-
rubygems_version: 2.7.
|
118
|
+
rubygems_version: 2.7.6
|
118
119
|
signing_key:
|
119
120
|
specification_version: 4
|
120
121
|
summary: Rails framework for managing all types of notifications in one place
|