notey 0.2.0 → 0.3.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/README.md +20 -0
- data/app/delivery_methods/notey/email.rb +4 -0
- data/app/models/notey/delete_old_attempts.rb +19 -0
- data/app/models/notey/notification.rb +2 -1
- data/lib/notey/version.rb +1 -1
- data/lib/notey.rb +10 -1
- data/the_local/agents/notey-develop.md +25 -8
- data/the_local/agents/notey-info.md +3 -2
- data/the_local/agents/notey-install.md +21 -3
- data/the_local/interface.yml +4 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 52c58921ef29966128ff879a465b658aecc03be72d9854b20e35fcf374bdf227
|
|
4
|
+
data.tar.gz: f33f53d08767a17f9409255954c1561a9d334e58c8d4e141410a3f1e3af8d429
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 679238d4b8f289a4e653a11a86002544d1eabbba16d381e691bbbfc2d8a813c4c86d4a988d2d008f7d88d637bbfd8ab24b9f8b844bae12270d04f63150dbb6b0
|
|
7
|
+
data.tar.gz: 4a0fe1f0a1f63ce23242147a896c8f45a6acad9bee3118b668a553aa9af68bf565e358d0ecc821511c5fa55b66c60dec7005bc99dd11bc43a9af2d9220608508
|
data/README.md
CHANGED
|
@@ -198,6 +198,26 @@ Each run enqueues one job per person, so one failing send does not stop the
|
|
|
198
198
|
rest. A window is sent once even if the run overlaps itself; a send that fails
|
|
199
199
|
releases the window so it can be sent again.
|
|
200
200
|
|
|
201
|
+
## Deleting old delivery records
|
|
202
|
+
|
|
203
|
+
Notey writes one record per notification per channel that leaves the
|
|
204
|
+
application, and nothing deletes them on its own. Say how long to keep them:
|
|
205
|
+
|
|
206
|
+
```ruby
|
|
207
|
+
# config/initializers/notey.rb
|
|
208
|
+
Notey.attempt_retention = 90.days
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Then run the deletion on a schedule, the way you run the digest windows:
|
|
212
|
+
|
|
213
|
+
```ruby
|
|
214
|
+
Notey::DeleteOldAttempts.new.call
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Everything older than the period goes and everything inside it stays, so
|
|
218
|
+
running it twice leaves the same records as running it once. With no period set
|
|
219
|
+
it refuses to run rather than deleting nothing quietly.
|
|
220
|
+
|
|
201
221
|
## Destinations
|
|
202
222
|
|
|
203
223
|
A channel like email reaches a person at an address the app already holds. A
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Notey
|
|
4
|
+
class DeleteOldAttempts
|
|
5
|
+
UNSET = "notey deletes delivery records older than attempt_retention, which is not set"
|
|
6
|
+
|
|
7
|
+
def call
|
|
8
|
+
raise MissingRetention, UNSET if retention.nil?
|
|
9
|
+
|
|
10
|
+
Attempt.where(created_at: ...retention.ago).delete_all
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def retention
|
|
16
|
+
Notey.attempt_retention
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -27,7 +27,8 @@ module Notey
|
|
|
27
27
|
config = ActiveSupport::OrderedOptions.new
|
|
28
28
|
config[:class] = channel.delivery_method if channel.delivery_method
|
|
29
29
|
config[:notey_channel] = channel.name
|
|
30
|
-
config[:if] = Notey.sends(notey_notification_type, on: channel.name, addressed: channel.addressed
|
|
30
|
+
config[:if] = Notey.sends(notey_notification_type, on: channel.name, addressed: channel.addressed?,
|
|
31
|
+
via: Notey.known_delivery_method(channel))
|
|
31
32
|
config
|
|
32
33
|
end
|
|
33
34
|
private_class_method :delivery_config
|
data/lib/notey/version.rb
CHANGED
data/lib/notey.rb
CHANGED
|
@@ -19,9 +19,11 @@ module Notey
|
|
|
19
19
|
class MissingSender < StandardError; end
|
|
20
20
|
class UnsendableChannel < StandardError; end
|
|
21
21
|
class UnsendableAttempt < StandardError; end
|
|
22
|
+
class MissingRetention < StandardError; end
|
|
22
23
|
|
|
23
24
|
class << self
|
|
24
25
|
attr_writer :notification_url, :mailer_sender
|
|
26
|
+
attr_accessor :attempt_retention
|
|
25
27
|
end
|
|
26
28
|
|
|
27
29
|
def self.mailer_sender
|
|
@@ -95,6 +97,12 @@ module Notey
|
|
|
95
97
|
"the channel #{channel.name} needs the option #{required.first}, which notey does not supply"
|
|
96
98
|
end
|
|
97
99
|
|
|
100
|
+
def self.known_delivery_method(channel)
|
|
101
|
+
delivery_method_for(channel)
|
|
102
|
+
rescue UnsendableChannel
|
|
103
|
+
nil
|
|
104
|
+
end
|
|
105
|
+
|
|
98
106
|
def self.delivery_method_for(channel)
|
|
99
107
|
name = channel.delivery_method || "Noticed::DeliveryMethods::#{channel.name.camelize}"
|
|
100
108
|
name.to_s.constantize
|
|
@@ -108,11 +116,12 @@ module Notey
|
|
|
108
116
|
raise MissingSender, "notey sends digests by email and no mailer_sender is set"
|
|
109
117
|
end
|
|
110
118
|
|
|
111
|
-
def self.sends(notification_type, on:, addressed: false)
|
|
119
|
+
def self.sends(notification_type, on:, addressed: false, via: nil)
|
|
112
120
|
lambda do
|
|
113
121
|
decision = Channels.decision_for(recipient, notification_type, account_id: event.account_id)
|
|
114
122
|
|
|
115
123
|
next false unless decision.window == "immediate" && decision.channels.include?(on.to_s)
|
|
124
|
+
next false if via.respond_to?(:reachable?) && !via.reachable?(recipient)
|
|
116
125
|
next true unless addressed
|
|
117
126
|
|
|
118
127
|
Destinations.for(event.account_id, on, member: recipient).present?
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: notey-develop
|
|
3
3
|
description: Use PROACTIVELY for defining a notification type, sending one to the recipients your own code resolved, sending one when a domain event fires, addressing a channel at a person, scheduling daily and weekly digest runs, reading a person's inbox, and reading their channels and window — MUST BE USED instead of hand-rolling preference checks, per-person delivery addresses, or a digest loop.
|
|
4
4
|
tools: Read, Write, Edit, Grep
|
|
5
|
-
scope: notifications — application-wide channel registrations, notification types that name no channel, per-person per-account channel preferences, digest windows, an in-app inbox, per-account destinations, and a record of what was sent on each channel
|
|
5
|
+
scope: notifications — application-wide channel registrations, notification types that name no channel, per-person per-account channel preferences, digest windows, an in-app inbox, per-account destinations, and a record of what was sent on each channel that the host keeps for a period it sets
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
This local follows the steps below exactly and invents no others. Where a step
|
|
@@ -40,6 +40,9 @@ This local assumes the gem is already hooked into the app; if it is not, that is
|
|
|
40
40
|
- `Notey::DigestRun` — sends one window's digests, with `call` for everyone on
|
|
41
41
|
that window and `deliver_to_member(member, account_id)` for one person in one
|
|
42
42
|
account.
|
|
43
|
+
- `Notey::DeleteOldAttempts` — deletes every record of what was sent that is
|
|
44
|
+
older than `Notey.attempt_retention`, and refuses to run when no retention is
|
|
45
|
+
set.
|
|
43
46
|
- `Notey::Inbox.for(member, account_id:)` — returns that person's notifications
|
|
44
47
|
for that account, as a relation.
|
|
45
48
|
- `wants?(type, on:, account_id:)` — on the recipient model, true when that
|
|
@@ -133,7 +136,21 @@ This local assumes the gem is already hooked into the app; if it is not, that is
|
|
|
133
136
|
in this app — cron, a scheduler gem, or a platform scheduler — and at what
|
|
134
137
|
hour each window should go out.
|
|
135
138
|
|
|
136
|
-
7.
|
|
139
|
+
7. Schedule the deletion of old delivery records alongside the windows:
|
|
140
|
+
|
|
141
|
+
```ruby
|
|
142
|
+
Notey::DeleteOldAttempts.new.call
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
notey writes one record per notification per channel that leaves the
|
|
146
|
+
application and deletes none of them on its own, so a host that never runs
|
|
147
|
+
this keeps every row forever. It deletes everything older than
|
|
148
|
+
`Notey.attempt_retention` and refuses to run when that is not set, which
|
|
149
|
+
`notey-install` is the step that sets. Running it twice leaves the same
|
|
150
|
+
records as running it once, so a schedule that overlaps itself is safe. Ask
|
|
151
|
+
the developer how often it should run.
|
|
152
|
+
|
|
153
|
+
8. Read a person's notifications through the inbox rather than querying Noticed
|
|
137
154
|
directly:
|
|
138
155
|
|
|
139
156
|
```ruby
|
|
@@ -143,7 +160,7 @@ This local assumes the gem is already hooked into the app; if it is not, that is
|
|
|
143
160
|
It returns a relation, so chain `.unread`, `.order` and `.limit` onto it. With
|
|
144
161
|
no account it returns an empty relation, never another account's rows.
|
|
145
162
|
|
|
146
|
-
|
|
163
|
+
9. Read a person's preferences from the host's own code when the app needs to
|
|
147
164
|
branch on them:
|
|
148
165
|
|
|
149
166
|
```ruby
|
|
@@ -156,7 +173,7 @@ This local assumes the gem is already hooked into the app; if it is not, that is
|
|
|
156
173
|
three read a person's stored row for that type, and fall back to email and
|
|
157
174
|
in-app when they have never stored one.
|
|
158
175
|
|
|
159
|
-
|
|
176
|
+
10. In tests that register their own channels or notification types, call
|
|
160
177
|
`Notey.reset!` in teardown. Both are held for the life of the process, so a
|
|
161
178
|
test that skips this leaves its channels and types in place for every test
|
|
162
179
|
after it.
|
|
@@ -185,7 +202,7 @@ This local assumes the gem is already hooked into the app; if it is not, that is
|
|
|
185
202
|
no row saying it went out.
|
|
186
203
|
- **Notification types, subscribers and digest schedules all belong in the
|
|
187
204
|
host's code**, never in the gem.
|
|
188
|
-
- **Out of scope.** Registering the channels the application has,
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
`notey-install`.
|
|
205
|
+
- **Out of scope.** Registering the channels the application has, setting how
|
|
206
|
+
long delivery records are kept, making a model a recipient, setting the
|
|
207
|
+
current person and account, storing an account's addresses, and the pages a
|
|
208
|
+
person picks their own channels on all belong to `notey-install`.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: notey-info
|
|
3
3
|
description: Use to learn what notey offers — notification types and the channels they allow, per-person per-account delivery preferences, digest windows, an in-app inbox, and per-account destinations.
|
|
4
4
|
tools: Read
|
|
5
|
-
scope: notifications — application-wide channel registrations, notification types that name no channel, per-person per-account channel preferences, digest windows, an in-app inbox, per-account destinations, and a record of what was sent on each channel
|
|
5
|
+
scope: notifications — application-wide channel registrations, notification types that name no channel, per-person per-account channel preferences, digest windows, an in-app inbox, per-account destinations, and a record of what was sent on each channel that the host keeps for a period it sets
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
This local explains what notey is and which of its other two locals you need. It
|
|
@@ -80,4 +80,5 @@ has already been sent, you are building, and `notey-develop` owns it.
|
|
|
80
80
|
- **Attempt** — one row per notification per outbound channel, claimed before the
|
|
81
81
|
send and marked after, holding whether it was sent and what a failure said. It
|
|
82
82
|
is what answers why one person was not reached, and it is what stops the same
|
|
83
|
-
channel sending twice for the same notification.
|
|
83
|
+
channel sending twice for the same notification. The host says how long these
|
|
84
|
+
are kept and runs the deletion that enforces it.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: notey-install
|
|
3
3
|
description: Use to hook notey into a project — installing its tables, mounting its engine, registering the channels the application can send on, making a model a recipient, setting the current person and account, putting the account on Noticed's rows, supplying a notification url, and reaching the preferences, inbox and destinations pages.
|
|
4
4
|
tools: Bash, Read, Edit
|
|
5
|
-
scope: notifications — application-wide channel registrations, notification types that name no channel, per-person per-account channel preferences, digest windows, an in-app inbox, per-account destinations, and a record of what was sent on each channel
|
|
5
|
+
scope: notifications — application-wide channel registrations, notification types that name no channel, per-person per-account channel preferences, digest windows, an in-app inbox, per-account destinations, and a record of what was sent on each channel that the host keeps for a period it sets
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
This local follows the steps below exactly and invents no others. Where a step
|
|
@@ -33,6 +33,9 @@ different notifications in each.
|
|
|
33
33
|
through it.
|
|
34
34
|
- `Notey.notification_url=` — takes a lambda returning the host's own url for
|
|
35
35
|
one notification, used to link the rows in a digest email.
|
|
36
|
+
- `Notey.attempt_retention=` — takes how long notey keeps the record of what it
|
|
37
|
+
sent on each channel, such as `90.days`. Nothing is deleted until the host
|
|
38
|
+
runs the deletion.
|
|
36
39
|
- `Notey.check!` — raises when a registered channel names a delivery method that
|
|
37
40
|
does not exist or needs an option notey does not supply, and when notey has no
|
|
38
41
|
sender address for its digests. notey runs it itself after initialization when
|
|
@@ -162,7 +165,18 @@ moves all three.
|
|
|
162
165
|
the engine's copy. Ask the developer whether these pages should carry the
|
|
163
166
|
app's navigation.
|
|
164
167
|
|
|
165
|
-
12. Set
|
|
168
|
+
12. Set the retention for delivery records in
|
|
169
|
+
`config/initializers/notey.rb`:
|
|
170
|
+
|
|
171
|
+
```ruby
|
|
172
|
+
Notey.attempt_retention = 90.days
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Ask the developer how long this application should keep the record of what
|
|
176
|
+
was sent on each channel; there is no default. Scheduling the deletion is
|
|
177
|
+
`notey-develop`'s step, and nothing is deleted until it runs.
|
|
178
|
+
|
|
179
|
+
13. Set up destinations only if a registered channel needs an address. The stored credential is encrypted, so
|
|
166
180
|
Active Record encryption keys must be configured in the host's credentials
|
|
167
181
|
(`bin/rails db:encryption:init` generates a set) or saving a destination
|
|
168
182
|
raises. Ask the developer whether any channel needs this before doing it.
|
|
@@ -190,6 +204,10 @@ moves all three.
|
|
|
190
204
|
that changes it.
|
|
191
205
|
- **Re-run `bin/rails notey:install:migrations` after upgrading the gem.** It
|
|
192
206
|
copies only the migrations the host does not already have.
|
|
193
|
-
- **
|
|
207
|
+
- **Nothing deletes a delivery record until the host runs the deletion.** The
|
|
208
|
+
table grows for every notification on every outbound channel, so a host that
|
|
209
|
+
sets no retention and schedules no deletion keeps every row forever.
|
|
210
|
+
- **Out of scope.** Scheduling the digest runs and the deletion of old delivery
|
|
211
|
+
records, writing a notification type,
|
|
194
212
|
sending one when a domain event fires, and reading the inbox from the host's
|
|
195
213
|
own code all belong to `notey-develop`.
|
data/the_local/interface.yml
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
scope: notifications — application-wide channel registrations, notification types that name no channel, per-person per-account channel preferences, digest windows, an in-app inbox, per-account destinations, and a record of what was sent on each channel
|
|
1
|
+
scope: notifications — application-wide channel registrations, notification types that name no channel, per-person per-account channel preferences, digest windows, an in-app inbox, per-account destinations, and a record of what was sent on each channel that the host keeps for a period it sets
|
|
2
2
|
|
|
3
3
|
install:
|
|
4
4
|
- bin/rails notey:install:migrations
|
|
@@ -8,6 +8,7 @@ install:
|
|
|
8
8
|
- Notey::Notification
|
|
9
9
|
- Notey::Current
|
|
10
10
|
- Notey.notification_url=
|
|
11
|
+
- Notey.attempt_retention=
|
|
11
12
|
- Notey.check!
|
|
12
13
|
- /notey/preferences
|
|
13
14
|
- /notey/notifications
|
|
@@ -19,6 +20,7 @@ develop:
|
|
|
19
20
|
- notify
|
|
20
21
|
- Notey::Destinations.for
|
|
21
22
|
- Notey::DigestRun
|
|
23
|
+
- Notey::DeleteOldAttempts
|
|
22
24
|
- Notey::Inbox.for
|
|
23
25
|
- wants?
|
|
24
26
|
- channels_for
|
|
@@ -40,6 +42,7 @@ sources:
|
|
|
40
42
|
- app/models/notey/destination.rb
|
|
41
43
|
- app/models/notey/digest.rb
|
|
42
44
|
- app/models/notey/attempt.rb
|
|
45
|
+
- app/models/notey/delete_old_attempts.rb
|
|
43
46
|
- app/delivery_methods/notey/email.rb
|
|
44
47
|
- app/delivery_methods/notey/in_app.rb
|
|
45
48
|
- app/jobs/notey/digest_job.rb
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: notey
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- tylercschneider
|
|
@@ -88,6 +88,7 @@ files:
|
|
|
88
88
|
- app/models/notey/application_record.rb
|
|
89
89
|
- app/models/notey/attempt.rb
|
|
90
90
|
- app/models/notey/current.rb
|
|
91
|
+
- app/models/notey/delete_old_attempts.rb
|
|
91
92
|
- app/models/notey/destination.rb
|
|
92
93
|
- app/models/notey/digest.rb
|
|
93
94
|
- app/models/notey/kept.rb
|