noticed 1.6.3 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +122 -147
- data/app/jobs/noticed/application_job.rb +9 -0
- data/app/jobs/noticed/event_job.rb +19 -0
- data/app/models/concerns/noticed/deliverable.rb +115 -0
- data/app/models/concerns/noticed/notification_methods.rb +17 -0
- data/app/models/concerns/noticed/readable.rb +62 -0
- data/app/models/noticed/application_record.rb +6 -0
- data/app/models/noticed/deliverable/deliver_by.rb +43 -0
- data/app/models/noticed/event.rb +15 -0
- data/app/models/noticed/notification.rb +16 -0
- data/db/migrate/20231215190233_create_noticed_tables.rb +25 -0
- data/lib/generators/noticed/delivery_method_generator.rb +1 -1
- data/lib/generators/noticed/install_generator.rb +19 -0
- data/lib/generators/noticed/{notification_generator.rb → notifier_generator.rb} +2 -2
- data/lib/generators/noticed/templates/README +5 -4
- data/lib/generators/noticed/templates/notifier.rb.tt +24 -0
- data/lib/noticed/api_client.rb +44 -0
- data/lib/noticed/bulk_delivery_method.rb +46 -0
- data/lib/noticed/bulk_delivery_methods/discord.rb +11 -0
- data/lib/noticed/bulk_delivery_methods/slack.rb +17 -0
- data/lib/noticed/bulk_delivery_methods/webhook.rb +18 -0
- data/lib/noticed/coder.rb +2 -0
- data/lib/noticed/delivery_method.rb +50 -0
- data/lib/noticed/delivery_methods/action_cable.rb +7 -39
- data/lib/noticed/delivery_methods/discord.rb +11 -0
- data/lib/noticed/delivery_methods/email.rb +9 -45
- data/lib/noticed/delivery_methods/fcm.rb +23 -64
- data/lib/noticed/delivery_methods/ios.rb +25 -112
- data/lib/noticed/delivery_methods/microsoft_teams.rb +5 -22
- data/lib/noticed/delivery_methods/slack.rb +6 -16
- data/lib/noticed/delivery_methods/test.rb +2 -12
- data/lib/noticed/delivery_methods/twilio_messaging.rb +37 -0
- data/lib/noticed/delivery_methods/vonage_sms.rb +20 -0
- data/lib/noticed/delivery_methods/webhook.rb +17 -0
- data/lib/noticed/engine.rb +1 -9
- data/lib/noticed/required_options.rb +21 -0
- data/lib/noticed/translation.rb +7 -3
- data/lib/noticed/version.rb +1 -1
- data/lib/noticed.rb +30 -15
- metadata +29 -40
- data/lib/generators/noticed/model/base_generator.rb +0 -47
- data/lib/generators/noticed/model/mysql_generator.rb +0 -18
- data/lib/generators/noticed/model/postgresql_generator.rb +0 -18
- data/lib/generators/noticed/model/sqlite3_generator.rb +0 -18
- data/lib/generators/noticed/model_generator.rb +0 -63
- data/lib/generators/noticed/templates/notification.rb.tt +0 -27
- data/lib/noticed/base.rb +0 -160
- data/lib/noticed/delivery_methods/base.rb +0 -95
- data/lib/noticed/delivery_methods/database.rb +0 -34
- data/lib/noticed/delivery_methods/twilio.rb +0 -51
- data/lib/noticed/delivery_methods/vonage.rb +0 -40
- data/lib/noticed/has_notifications.rb +0 -49
- data/lib/noticed/model.rb +0 -85
- data/lib/noticed/notification_channel.rb +0 -15
- data/lib/noticed/text_coder.rb +0 -16
- data/lib/rails_6_polyfills/actioncable/test_adapter.rb +0 -70
- data/lib/rails_6_polyfills/actioncable/test_helper.rb +0 -143
- data/lib/rails_6_polyfills/activejob/serializers.rb +0 -240
- data/lib/rails_6_polyfills/base.rb +0 -18
- data/lib/tasks/noticed_tasks.rake +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f2466298cbeef0bde1ae34d07ed5317032659ba413cc15797e211012c9d7847c
|
4
|
+
data.tar.gz: b4edc8f7a819331a2d66a39c04d491f8c534f44fae8c14edbc0ce2b366556c8f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bf817631a96cf372b864e6df07c38f3c0a4e97f288ae63b934af36fa9102e22408802176e9642f6a1c3a22b46854c764dfd6a25d19da00c3da5c0eeeec778694
|
7
|
+
data.tar.gz: 4d7a596f35331bc8f38e1337398f72fdf38af34b631bae27db48c32b4f58f702023c3a5d44e00ebb9f7e8e990b1053d8bc76c5941db8c9b7a296e03e9f4c2a96
|
data/README.md
CHANGED
@@ -1,88 +1,124 @@
|
|
1
|
-
|
2
|
-
<h1>Noticed</h1>
|
3
|
-
</p>
|
4
|
-
|
1
|
+
# Noticed
|
5
2
|
### 🎉 Notifications for your Ruby on Rails app.
|
6
3
|
|
7
4
|
[![Build Status](https://github.com/excid3/noticed/workflows/Tests/badge.svg)](https://github.com/excid3/noticed/actions) [![Gem Version](https://badge.fury.io/rb/noticed.svg)](https://badge.fury.io/rb/noticed)
|
8
5
|
|
9
|
-
|
6
|
+
Noticed helps you send notifications in your Rails apps. Notifications can be sent to any number of recipients. You might want a Slack notification with 0 recipients to let your team know when something happens. A notification can also be sent to 1+ recipients with individual deliveries (like an email to each recipient).
|
7
|
+
|
8
|
+
The core concepts of Noticed are:
|
9
|
+
|
10
|
+
1. `Notifier` - Classes that define how notifications are delivered and when.
|
11
|
+
2. `Noticed::Event` - When a `Notifier` is delivered, a `Noticed::Event` record is created in the database to store params for the delivery.`Notifiers` are ActiveRecord objects inherited from `Noticed::Event` using Single Table Inheritance.
|
12
|
+
3. `Noticed::Notification` - Keeps track of each recipient for `Noticed::Event` and the seen & read status for each.
|
13
|
+
4. Delivery methods are ActiveJob instances and support the same features like wait, queue, and priority.
|
14
|
+
|
15
|
+
## Delivery Methods
|
16
|
+
Individual Delivery methods (one notification to each recipient):
|
17
|
+
|
18
|
+
* [ActionCable](docs/delivery_methods/action_cable.md)
|
19
|
+
* [Apple Push Notification Service](docs/delivery_methods/ios.md)
|
20
|
+
* [Email](docs/delivery_methods/email.md)
|
21
|
+
* [Firebase Cloud Messaging](docs/delivery_methods/fcm.md) (iOS, Android, and web clients)
|
22
|
+
* [Microsoft Teams](docs/delivery_methods/microsoft_teams.md)
|
23
|
+
* [Slack](docs/delivery_methods/slack.md)
|
24
|
+
* [Twilio Messaging](docs/delivery_methods/twilio_messaging.md) - SMS, Whatsapp
|
25
|
+
* [Vonage SMS](docs/delivery_methods/vonage_sms.md)
|
26
|
+
* [Test](docs/delivery_methods/test.md)
|
10
27
|
|
11
|
-
|
12
|
-
* Email
|
13
|
-
* ActionCable channels
|
14
|
-
* Slack
|
15
|
-
* Microsoft Teams
|
16
|
-
* Twilio (SMS)
|
17
|
-
* Vonage / Nexmo (SMS)
|
18
|
-
* iOS Apple Push Notifications
|
19
|
-
* Firebase Cloud Messaging (Android and more)
|
28
|
+
Bulk delivery methods (one notification for all recipients):
|
20
29
|
|
21
|
-
|
30
|
+
* [Discord](docs/bulk_delivery_methods/discord.md)
|
31
|
+
* [Slack](docs/bulk_delivery_methods/slack.md)
|
32
|
+
* [Webhook](docs/bulk_delivery_methods/webhook.md)
|
22
33
|
|
23
34
|
## 🎬 Screencast
|
24
35
|
|
25
|
-
<
|
26
|
-
<a href="https://www.youtube.com/watch?v=Scffi4otlFc"><img src="https://i.imgur.com/UvVKWwD.png" title="How to add Notifications to Rails with Noticed" /></a>
|
27
|
-
</div>
|
36
|
+
<a href="https://www.youtube.com/watch?v=Scffi4otlFc"><img src="https://i.imgur.com/UvVKWwD.png" title="How to add Notifications to Rails with Noticed" width="50%" /></a>
|
28
37
|
|
29
38
|
[Watch Screencast](https://www.youtube.com/watch?v=Scffi4otlFc)
|
30
39
|
|
31
40
|
## 🚀 Installation
|
32
|
-
Run the following command to add Noticed to your Gemfile
|
41
|
+
Run the following command to add Noticed to your Gemfile:
|
33
42
|
|
34
43
|
```ruby
|
35
44
|
bundle add "noticed"
|
36
45
|
```
|
37
46
|
|
38
|
-
|
47
|
+
Add the migrations:
|
39
48
|
|
40
|
-
```
|
41
|
-
rails
|
49
|
+
```bash
|
50
|
+
rails noticed:install:migrations
|
51
|
+
rails db:migrate
|
42
52
|
```
|
43
53
|
|
44
|
-
This will generate a Notification model and instructions for associating User models with the notifications table.
|
45
|
-
|
46
54
|
## 📝 Usage
|
47
55
|
|
48
|
-
To generate a
|
56
|
+
To generate a Notifier, simply run:
|
49
57
|
|
50
|
-
`rails generate noticed:
|
58
|
+
`rails generate noticed:notifier CommentNotifier`
|
51
59
|
|
52
|
-
####
|
53
|
-
|
54
|
-
To send a notification to a user:
|
60
|
+
#### Add Delivery Methods
|
61
|
+
Then add delivery methods to the Notifier. See [docs/delivery_methods](docs/) for a full list.
|
55
62
|
|
56
63
|
```ruby
|
57
|
-
#
|
58
|
-
|
64
|
+
# app/notifiers/comment_notifier.rb
|
65
|
+
class CommentNotifier < Noticed::Event
|
66
|
+
bulk_deliver_by :webhook do |config|
|
67
|
+
config.url = "https://example.org..."
|
68
|
+
config.json = ->{ text: "New comment: #{record.body}" }
|
69
|
+
end
|
59
70
|
|
60
|
-
|
61
|
-
|
71
|
+
deliver_by :email do |config|
|
72
|
+
config.mailer = "UserMailer"
|
73
|
+
config.method = :new_comment
|
74
|
+
end
|
75
|
+
end
|
76
|
+
```
|
62
77
|
|
63
|
-
|
64
|
-
notification.deliver(@comment.post.author)
|
78
|
+
#### Sending Notifications
|
65
79
|
|
66
|
-
|
67
|
-
|
80
|
+
To send a notification to user(s):
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
# Instantiate a new notifier
|
84
|
+
CommentNotifier.with(record: @comment, foo: "bar").deliver_later(User.all)
|
68
85
|
```
|
69
86
|
|
70
|
-
This
|
87
|
+
This instantiates a new `CommentNotifier` with params. Similar to ActiveJob, you can pass any params can be serialized. `record:` is a special param that gets assigned to the `record` polymorphic association in the database.
|
88
|
+
|
89
|
+
Delivering will create a `Noticed::Event` record and associated `Noticed::Notification` records for each recipient.
|
71
90
|
|
72
|
-
|
91
|
+
After saving, a job will be enqueued for processing this notification and delivering it to all recipients.
|
73
92
|
|
74
|
-
|
93
|
+
Each delivery method also spawns its own job. This allows you to skip email notifications if the user had already opened a push notification, for example.
|
75
94
|
|
76
|
-
|
95
|
+
#### Notifier Objects
|
77
96
|
|
78
|
-
|
97
|
+
Notifiers inherit from `Noticed::Event`. This provides all their functionality and allows them to be delivered.
|
79
98
|
|
80
99
|
```ruby
|
81
|
-
class
|
82
|
-
deliver_by :database
|
100
|
+
class CommentNotifier < Noticed::Event
|
83
101
|
deliver_by :action_cable
|
84
|
-
deliver_by :email
|
102
|
+
deliver_by :email do |config|
|
103
|
+
config.mailer = "UserMailer"
|
104
|
+
config.if = ->(recipient) { !!recipient.preferences[:email] }
|
105
|
+
config.wait = 5.minutes
|
106
|
+
end
|
107
|
+
end
|
108
|
+
```
|
109
|
+
|
110
|
+
**Shared Options**
|
85
111
|
|
112
|
+
* `if: :method_name` - Calls `method_name` and cancels delivery method if `false` is returned. This can also be specified as a Proc / lambda.
|
113
|
+
* `unless: :method_name` - Calls `method_name` and cancels delivery method if `true` is returned
|
114
|
+
* `wait:` - Delays the delivery for the given duration of time. Can be an `ActiveSupport::Duration`, Proc / lambda, or Symbol.
|
115
|
+
|
116
|
+
##### Helper Methods
|
117
|
+
|
118
|
+
You can define helper methods inside your Notifier object to make it easier to render.
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
class CommentNotifier < Noticed::Event
|
86
122
|
# I18n helpers
|
87
123
|
def message
|
88
124
|
t(".message")
|
@@ -94,77 +130,38 @@ class CommentNotification < Noticed::Base
|
|
94
130
|
post_path(params[:post])
|
95
131
|
end
|
96
132
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
# Anything you want
|
133
|
+
# Defines methods added to the Noticed::Notification
|
134
|
+
notification_methods do
|
135
|
+
def personalized_welcome
|
136
|
+
"Hello #{recipient.first_name}."
|
137
|
+
end
|
103
138
|
end
|
104
139
|
end
|
105
140
|
```
|
106
141
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
##### Helper Methods
|
115
|
-
|
116
|
-
You can define helper methods inside your Notification object to make it easier to render.
|
142
|
+
In your views, you can loop through notifications and access
|
143
|
+
```erb
|
144
|
+
<%= current_user.notifications.includes(:event).each do |notification| %>
|
145
|
+
<%= link_to notification.personalized_welcome, notification.event.url %>
|
146
|
+
<% end %>
|
147
|
+
```
|
117
148
|
|
118
149
|
##### URL Helpers
|
119
150
|
|
120
|
-
|
121
|
-
|
122
|
-
Don't forget, you'll need to configure `default_url_options` in order for Rails to know what host and port to use when generating URLs.
|
151
|
+
URL helpers are included in Notifier classes so you have full access to them just like in your controllers and views. Configure `default_url_options` in order for Rails to know what host and port to use when generating URLs.
|
123
152
|
|
124
153
|
```ruby
|
125
154
|
Rails.application.routes.default_url_options[:host] = 'localhost:3000'
|
126
155
|
```
|
127
156
|
|
128
|
-
**Callbacks**
|
129
|
-
|
130
|
-
Like ActiveRecord, notifications have several different types of callbacks.
|
131
|
-
|
132
|
-
```ruby
|
133
|
-
class CommentNotification < Noticed::Base
|
134
|
-
deliver_by :database
|
135
|
-
deliver_by :email, mailer: 'CommentMailer'
|
136
|
-
|
137
|
-
# Callbacks for the entire delivery
|
138
|
-
before_deliver :whatever
|
139
|
-
around_deliver :whatever
|
140
|
-
after_deliver :whatever
|
141
|
-
|
142
|
-
# Callbacks for each delivery method
|
143
|
-
before_database :whatever
|
144
|
-
around_database :whatever
|
145
|
-
after_database :whatever
|
146
|
-
|
147
|
-
before_email :whatever
|
148
|
-
around_email :whatever
|
149
|
-
after_email :whatever
|
150
|
-
end
|
151
|
-
```
|
152
|
-
|
153
|
-
When using `deliver_later` callbacks will be run around queuing the delivery method jobs (not inside the jobs as they actually execute).
|
154
|
-
|
155
|
-
Defining custom delivery methods allows you to add callbacks that run inside the background job as each individual delivery is executed. See the Custom Delivery Methods section for more information.
|
156
|
-
|
157
157
|
##### Translations
|
158
158
|
|
159
|
-
|
159
|
+
`translate` and `t` helpers are available in Notifiers. If the key starts with a period, it will automatically scope the key under `notifiers` and the underscored name of the notification class it is used in.
|
160
160
|
|
161
161
|
For example:
|
162
162
|
|
163
|
-
|
164
|
-
|
165
|
-
Or when notification class is in module:
|
166
|
-
|
167
|
-
`t(".message") # in Admin::NewComment` looks up `en.notifications.admin.new_comment.message`
|
163
|
+
`t(".message")` looks up `en.notifiers.new_comment.message`
|
164
|
+
`t(".message") # in Admin::NewComment` looks up `en.notifiers.admin.new_comment.message`
|
168
165
|
|
169
166
|
##### User Preferences
|
170
167
|
|
@@ -173,30 +170,20 @@ You can use the `if:` and `unless: ` options on your delivery methods to check t
|
|
173
170
|
For example:
|
174
171
|
|
175
172
|
```ruby
|
176
|
-
class
|
177
|
-
deliver_by :email
|
178
|
-
|
179
|
-
|
180
|
-
recipient.email_notifications?
|
173
|
+
class CommentNotifier < Noticed::Base
|
174
|
+
deliver_by :email do |config|
|
175
|
+
config.mailer = 'CommentMailer'
|
176
|
+
config.method = :new_comment
|
177
|
+
config.if = ->{ recipient.email_notifications? }
|
181
178
|
end
|
182
179
|
end
|
183
180
|
```
|
184
181
|
|
185
|
-
## 🐞 Debugging
|
186
|
-
|
187
|
-
In order to figure out what's up when you run in to errors, you can set the `debug` parameter to `true` in your notification, which will give you a more detailed error message about what went wrong.
|
188
|
-
|
189
|
-
Example:
|
190
|
-
|
191
|
-
```ruby
|
192
|
-
deliver_by :slack, debug: true
|
193
|
-
```
|
194
|
-
|
195
182
|
## ✅ Best Practices
|
196
183
|
|
197
184
|
### Creating a notification from an Active Record callback
|
198
185
|
|
199
|
-
|
186
|
+
Always use `after_commit` hooks to send notifications from ActiveRecord callbacks. For example, to send a notification automatically after a message is created:
|
200
187
|
|
201
188
|
```ruby
|
202
189
|
class Message < ApplicationRecord
|
@@ -207,58 +194,46 @@ class Message < ApplicationRecord
|
|
207
194
|
private
|
208
195
|
|
209
196
|
def notify_recipient
|
210
|
-
|
197
|
+
NewMessageNotifier.with(message: self).deliver_later(recipient)
|
211
198
|
end
|
212
199
|
```
|
213
200
|
|
214
|
-
If you are creating the notification on a background job (i.e. via `#deliver_later`), make sure you use a `commit` hook such as `after_create_commit` or `after_commit`.
|
215
|
-
|
216
201
|
Using `after_create` might cause the notification delivery methods to fail. This is because the job was enqueued while inside a database transaction, and the `Message` record might not yet be saved to the database.
|
217
202
|
|
218
203
|
A common symptom of this problem is undelivered notifications and the following error in your logs.
|
219
204
|
|
220
205
|
> `Discarded Noticed::DeliveryMethods::Email due to a ActiveJob::DeserializationError.`
|
221
206
|
|
222
|
-
### Renaming
|
207
|
+
### Renaming Notifiers
|
223
208
|
|
224
|
-
If you rename the class of a notification object your existing queries can break. This is because
|
209
|
+
If you rename the class of a notification object your existing queries can break. This is because ActiveRecord serializes the class name and sets it to the `type` column on the Noticed records.
|
225
210
|
|
226
|
-
You can catch these errors at runtime by using `
|
211
|
+
You can catch these errors at runtime by using `YourNotifierClassName.name` instead of hardcoding the string when performing a query.
|
227
212
|
|
228
213
|
```ruby
|
229
|
-
|
230
|
-
|
214
|
+
Noticed::Event.where(type: YourNotifierClassName.name) # good
|
215
|
+
Noticed::Event.where(type: "YourNotifierClassName") # bad
|
231
216
|
```
|
232
217
|
|
233
218
|
When renaming a notification class you will need to backfill existing notifications to reference the new name.
|
234
219
|
|
235
220
|
```ruby
|
236
|
-
|
221
|
+
Noticed::Event.where(type: "OldNotifierClassName").update_all(type: NewNotifierClassName.name)
|
222
|
+
Noticed::Notification.where(type: "OldNotifierClassName::Notification").update_all(type: NewNotifierClassName::Notification.name)
|
237
223
|
```
|
238
224
|
|
239
225
|
## 🚛 Delivery Methods
|
240
226
|
|
241
|
-
The delivery methods are
|
227
|
+
The delivery methods are modular so you can customize the way each type gets delivered.
|
242
228
|
|
243
|
-
For example, emails will require a subject, body, and email address while an SMS requires a phone number and simple message. You can define the formats for each of these in your
|
244
|
-
|
245
|
-
* [Database](docs/delivery_methods/database.md)
|
246
|
-
* [Email](docs/delivery_methods/email.md)
|
247
|
-
* [ActionCable](docs/delivery_methods/action_cable.md)
|
248
|
-
* [iOS Apple Push Notifications](docs/delivery_methods/ios.md)
|
249
|
-
* [Microsoft Teams](docs/delivery_methods/microsoft_teams.md)
|
250
|
-
* [Slack](docs/delivery_methods/slack.md)
|
251
|
-
* [Test](docs/delivery_methods/test.md)
|
252
|
-
* [Twilio](docs/delivery_methods/twilio.md)
|
253
|
-
* [Vonage](docs/delivery_methods/vonage.md)
|
254
|
-
* [Firebase Cloud Messaging](docs/delivery_methods/fcm.md)
|
229
|
+
For example, emails will require a subject, body, and email address while an SMS requires a phone number and simple message. You can define the formats for each of these in your Notifier and the delivery method will handle the processing of it.
|
255
230
|
|
256
231
|
### Fallback Notifications
|
257
232
|
|
258
233
|
A common pattern is to deliver a notification via the database and then, after some time has passed, email the user if they have not yet read the notification. You can implement this functionality by combining multiple delivery methods, the `delay` option, and the conditional `if` / `unless` option.
|
259
234
|
|
260
235
|
```ruby
|
261
|
-
class
|
236
|
+
class CommentNotifier< Noticed::Base
|
262
237
|
deliver_by :database
|
263
238
|
deliver_by :email, mailer: 'CommentMailer', delay: 15.minutes, unless: :read?
|
264
239
|
end
|
@@ -269,7 +244,7 @@ Here a notification will be created immediately in the database (for display dir
|
|
269
244
|
You can also configure multiple fallback options:
|
270
245
|
|
271
246
|
```ruby
|
272
|
-
class
|
247
|
+
class CriticalSystemNotifier < Noticed::Base
|
273
248
|
deliver_by :database
|
274
249
|
deliver_by :slack
|
275
250
|
deliver_by :email, mailer: 'CriticalSystemMailer', delay: 10.minutes, if: :unread?
|
@@ -307,14 +282,14 @@ end
|
|
307
282
|
You can use the custom delivery method thus created by adding a `deliver_by` line with a unique name and `class` option in your notification class.
|
308
283
|
|
309
284
|
```ruby
|
310
|
-
class
|
285
|
+
class MyNotifier < Noticed::Base
|
311
286
|
deliver_by :discord, class: "DeliveryMethods::Discord"
|
312
287
|
end
|
313
288
|
```
|
314
289
|
|
315
290
|
Delivery methods have access to the following methods and attributes:
|
316
291
|
|
317
|
-
* `
|
292
|
+
* `record` - The instance of the Notification. You can call methods on the notification to let the user easily override formatting and other functionality of the delivery method.
|
318
293
|
* `options` - Any configuration options on the `deliver_by` line.
|
319
294
|
* `recipient` - The object who should receive the notification. This is typically a User, Account, or other ActiveRecord model.
|
320
295
|
* `params` - The params passed into the notification. This is details about the event that happened. For example, a user commenting on a post would have params of `{ user: User.first }`
|
@@ -343,7 +318,7 @@ class DeliveryMethods::Discord < Noticed::DeliveryMethods::Base
|
|
343
318
|
end
|
344
319
|
end
|
345
320
|
|
346
|
-
class
|
321
|
+
class CommentNotifier < Noticed::Base
|
347
322
|
deliver_by :discord, class: 'DeliveryMethods::Discord'
|
348
323
|
end
|
349
324
|
```
|
@@ -353,7 +328,7 @@ Now it will raise an error because a required argument is missing.
|
|
353
328
|
To fix the error, the argument has to be passed correctly. For example:
|
354
329
|
|
355
330
|
```ruby
|
356
|
-
class
|
331
|
+
class CommentNotifier < Noticed::Base
|
357
332
|
deliver_by :discord, class: 'DeliveryMethods::Discord', username: User.admin.username
|
358
333
|
end
|
359
334
|
```
|
@@ -399,10 +374,10 @@ user.notifications.mark_as_unread!
|
|
399
374
|
|
400
375
|
#### Instance methods
|
401
376
|
|
402
|
-
Convert back into a Noticed
|
377
|
+
Convert back into a Noticed notifier object:
|
403
378
|
|
404
379
|
```ruby
|
405
|
-
@notification.
|
380
|
+
@notification.to_notifier
|
406
381
|
```
|
407
382
|
|
408
383
|
Mark notification as read / unread:
|
@@ -445,11 +420,11 @@ class Post < ApplicationRecord
|
|
445
420
|
end
|
446
421
|
|
447
422
|
# Create a CommentNotification with a post param
|
448
|
-
|
423
|
+
CommentNotifier.with(post: @post).deliver(user)
|
449
424
|
# Lookup Notifications where params: {post: @post}
|
450
425
|
@post.notifications_as_post
|
451
426
|
|
452
|
-
|
427
|
+
CommentNotifier.with(parent: @post).deliver(user)
|
453
428
|
@post.notifications_as_parent
|
454
429
|
```
|
455
430
|
|
@@ -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,62 @@
|
|
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_unread
|
35
|
+
update(read_at: nil)
|
36
|
+
end
|
37
|
+
|
38
|
+
def mark_as_seen
|
39
|
+
update(seen_at: Time.current)
|
40
|
+
end
|
41
|
+
|
42
|
+
def mark_as_unseen
|
43
|
+
update(seen_at: nil)
|
44
|
+
end
|
45
|
+
|
46
|
+
def read?
|
47
|
+
read_at?
|
48
|
+
end
|
49
|
+
|
50
|
+
def unread?
|
51
|
+
!read_at?
|
52
|
+
end
|
53
|
+
|
54
|
+
def seen?
|
55
|
+
seen_at?
|
56
|
+
end
|
57
|
+
|
58
|
+
def unseen?
|
59
|
+
!seen_at?
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|