noticed 1.2.2 → 1.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 +270 -42
- data/Rakefile +4 -0
- data/lib/generators/noticed/delivery_method_generator.rb +19 -0
- data/lib/generators/noticed/model_generator.rb +20 -1
- data/lib/generators/noticed/notification_generator.rb +1 -1
- data/lib/generators/noticed/templates/delivery_method.rb.tt +12 -0
- data/lib/generators/noticed/templates/notification.rb.tt +1 -1
- data/lib/noticed/base.rb +35 -12
- data/lib/noticed/coder.rb +2 -0
- data/lib/noticed/delivery_methods/action_cable.rb +12 -4
- data/lib/noticed/delivery_methods/base.rb +66 -7
- data/lib/noticed/delivery_methods/database.rb +7 -0
- data/lib/noticed/delivery_methods/email.rb +12 -1
- data/lib/noticed/delivery_methods/microsoft_teams.rb +32 -0
- data/lib/noticed/delivery_methods/slack.rb +2 -2
- data/lib/noticed/delivery_methods/twilio.rb +1 -1
- data/lib/noticed/delivery_methods/vonage.rb +7 -1
- data/lib/noticed/engine.rb +5 -0
- data/lib/noticed/has_notifications.rb +32 -0
- data/lib/noticed/model.rb +25 -6
- data/lib/noticed/text_coder.rb +16 -0
- data/lib/noticed/translation.rb +17 -15
- data/lib/noticed/version.rb +1 -1
- data/lib/noticed.rb +13 -0
- metadata +41 -8
- /data/{app/channels → lib}/noticed/notification_channel.rb +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 83635d722a21df622c90c4e8ded293206242be768b30b2139c41f13bc4d6712c
|
|
4
|
+
data.tar.gz: 707ff4e0e8d09ed4c1fa1b0a7228353de637b070e849e7eb234b58b6fb491e40
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 85ddb6e11858df2b21b4dc098096c78465f7ac3dcb7a804037f3cf1855735b954cccb1cb0ad3227d1ebd89cb98e2e6e5d0138af27e73e52d2feeb35ac85ccdec
|
|
7
|
+
data.tar.gz: 80d3485a086e8203397f83bf3269e057ec63c9da89b3016d29a8395499447419336d89e7d01daff911a838159b963423dd45f6161b80c0dfab608f2f4e11c662
|
data/README.md
CHANGED
|
@@ -4,18 +4,28 @@
|
|
|
4
4
|
|
|
5
5
|
### 🎉 Notifications for your Ruby on Rails app.
|
|
6
6
|
|
|
7
|
-
[](https://github.com/excid3/noticed/actions)
|
|
7
|
+
[](https://github.com/excid3/noticed/actions) [](https://badge.fury.io/rb/noticed)
|
|
8
8
|
|
|
9
9
|
Currently, we support these notification delivery methods out of the box:
|
|
10
10
|
|
|
11
11
|
* Database
|
|
12
12
|
* Email
|
|
13
13
|
* ActionCable channels
|
|
14
|
+
* Slack
|
|
15
|
+
* Microsoft Teams
|
|
14
16
|
* Twilio (SMS)
|
|
15
17
|
* Vonage / Nexmo (SMS)
|
|
16
18
|
|
|
17
19
|
And you can easily add new notification types for any other delivery methods.
|
|
18
20
|
|
|
21
|
+
## 🎬 Screencast
|
|
22
|
+
|
|
23
|
+
<div style="width:50%">
|
|
24
|
+
<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>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
[Watch Screencast](https://www.youtube.com/watch?v=Scffi4otlFc)
|
|
28
|
+
|
|
19
29
|
## 🚀 Installation
|
|
20
30
|
Run the following command to add Noticed to your Gemfile
|
|
21
31
|
|
|
@@ -37,6 +47,28 @@ To generate a notification object, simply run:
|
|
|
37
47
|
|
|
38
48
|
`rails generate noticed:notification CommentNotification`
|
|
39
49
|
|
|
50
|
+
#### Sending Notifications
|
|
51
|
+
|
|
52
|
+
To send a notification to a user:
|
|
53
|
+
|
|
54
|
+
```ruby
|
|
55
|
+
# Instantiate a new notification
|
|
56
|
+
notification = CommentNotification.with(comment: @comment)
|
|
57
|
+
|
|
58
|
+
# Deliver notification in background job
|
|
59
|
+
notification.deliver_later(@comment.post.author)
|
|
60
|
+
|
|
61
|
+
# Deliver notification immediately
|
|
62
|
+
notification.deliver(@comment.post.author)
|
|
63
|
+
|
|
64
|
+
# Deliver notification to multiple recipients
|
|
65
|
+
notification.deliver_later(User.all)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
This will instantiate a new notification with the `comment` stored in the notification's params.
|
|
69
|
+
|
|
70
|
+
Each delivery method is able to transform this metadata that's best for the format. For example, the database may simply store the comment so it can be linked when rendering in the navbar. The websocket mechanism may transform this into a browser notification or insert it into the navbar.
|
|
71
|
+
|
|
40
72
|
#### Notification Objects
|
|
41
73
|
|
|
42
74
|
Notifications inherit from `Noticed::Base`. This provides all their functionality and allows them to be delivered.
|
|
@@ -47,11 +79,7 @@ To add delivery methods, simply `include` the module for the delivery methods yo
|
|
|
47
79
|
class CommentNotification < Noticed::Base
|
|
48
80
|
deliver_by :database
|
|
49
81
|
deliver_by :action_cable
|
|
50
|
-
deliver_by :email, if: :email_notifications?
|
|
51
|
-
|
|
52
|
-
def email_notifications?
|
|
53
|
-
!!recipient.preferences[:email]
|
|
54
|
-
end
|
|
82
|
+
deliver_by :email, mailer: 'CommentMailer', if: :email_notifications?
|
|
55
83
|
|
|
56
84
|
# I18n helpers
|
|
57
85
|
def message
|
|
@@ -59,41 +87,26 @@ class CommentNotification < Noticed::Base
|
|
|
59
87
|
end
|
|
60
88
|
|
|
61
89
|
# URL helpers are accessible in notifications
|
|
90
|
+
# Don't forget to set your default_url_options so Rails knows how to generate urls
|
|
62
91
|
def url
|
|
63
92
|
post_path(params[:post])
|
|
64
93
|
end
|
|
65
94
|
|
|
95
|
+
def email_notifications?
|
|
96
|
+
!!recipient.preferences[:email]
|
|
97
|
+
end
|
|
98
|
+
|
|
66
99
|
after_deliver do
|
|
67
100
|
# Anything you want
|
|
68
101
|
end
|
|
69
102
|
end
|
|
70
103
|
```
|
|
71
104
|
|
|
72
|
-
#### Sending Notifications
|
|
73
|
-
|
|
74
|
-
To send a notification to a user:
|
|
75
|
-
|
|
76
|
-
```ruby
|
|
77
|
-
notification = CommentNotification.with(comment: @comment.to_gid)
|
|
78
|
-
|
|
79
|
-
# Deliver notification in background job
|
|
80
|
-
notification.deliver_later(@comment.post.author)
|
|
81
|
-
|
|
82
|
-
# Deliver notification immediately
|
|
83
|
-
notification.deliver(@comment.post.author)
|
|
84
|
-
|
|
85
|
-
# Deliver notification to multiple recipients
|
|
86
|
-
notification.deliver_later(User.all)
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
This will instantiate a new notification with the `comment` stored in the notification's params.
|
|
90
|
-
|
|
91
|
-
Each delivery method is able to transform this metadata that's best for the format. For example, the database may simply store the comment so it can be linked when rendering in the navbar. The websocket mechanism may transform this into a browser notification or insert it into the navbar.
|
|
92
|
-
|
|
93
105
|
**Shared Options**
|
|
94
106
|
|
|
95
107
|
* `if: :method_name` - Calls `method_name`and cancels delivery method if `false` is returned
|
|
96
108
|
* `unless: :method_name` - Calls `method_name`and cancels delivery method if `true` is returned
|
|
109
|
+
* `delay: ActiveSupport::Duration` - Delays the delivery for the given duration of time
|
|
97
110
|
|
|
98
111
|
##### Helper Methods
|
|
99
112
|
|
|
@@ -103,6 +116,12 @@ You can define helper methods inside your Notification object to make it easier
|
|
|
103
116
|
|
|
104
117
|
Rails url helpers are included in notification classes by default so you have full access to them just like you would in your controllers and views.
|
|
105
118
|
|
|
119
|
+
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.
|
|
120
|
+
|
|
121
|
+
```ruby
|
|
122
|
+
Rails.application.routes.default_url_options[:host] = 'localhost:3000'
|
|
123
|
+
```
|
|
124
|
+
|
|
106
125
|
**Callbacks**
|
|
107
126
|
|
|
108
127
|
Like ActiveRecord, notifications have several different types of callbacks.
|
|
@@ -110,7 +129,7 @@ Like ActiveRecord, notifications have several different types of callbacks.
|
|
|
110
129
|
```ruby
|
|
111
130
|
class CommentNotification < Noticed::Base
|
|
112
131
|
deliver_by :database
|
|
113
|
-
deliver_by :email
|
|
132
|
+
deliver_by :email, mailer: 'CommentMailer'
|
|
114
133
|
|
|
115
134
|
# Callbacks for the entire delivery
|
|
116
135
|
before_deliver :whatever
|
|
@@ -134,7 +153,7 @@ Defining custom delivery methods allows you to add callbacks that run inside the
|
|
|
134
153
|
|
|
135
154
|
##### Translations
|
|
136
155
|
|
|
137
|
-
We've added `translate` and
|
|
156
|
+
We've added `translate` and `t` helpers like Rails has to provide an easy way of scoping translations. If the key starts with a period, it will automatically scope the key under `notifications` and the underscored name of the notification class it is used in.
|
|
138
157
|
|
|
139
158
|
For example:
|
|
140
159
|
|
|
@@ -148,7 +167,7 @@ For example:
|
|
|
148
167
|
|
|
149
168
|
```ruby
|
|
150
169
|
class CommentNotification < Noticed::Base
|
|
151
|
-
deliver_by :email, if: :email_notifications?
|
|
170
|
+
deliver_by :email, mailer: 'CommentMailer', if: :email_notifications?
|
|
152
171
|
|
|
153
172
|
def email_notifications?
|
|
154
173
|
recipient.email_notifications?
|
|
@@ -168,7 +187,7 @@ Writes notification to the database.
|
|
|
168
187
|
|
|
169
188
|
`deliver_by :database`
|
|
170
189
|
|
|
171
|
-
**Note:** Database notifications are special in that they will run before the other delivery methods. We do this so you can reference the database record ID in other delivery methods.
|
|
190
|
+
**Note:** Database notifications are special in that they will run before the other delivery methods. We do this so you can reference the database record ID in other delivery methods. For that same reason, the delivery can't be delayed (via the `delay` option) or an error will be raised.
|
|
172
191
|
|
|
173
192
|
##### Options
|
|
174
193
|
|
|
@@ -176,6 +195,10 @@ Writes notification to the database.
|
|
|
176
195
|
|
|
177
196
|
The name of the database association to use. Defaults to `:notifications`
|
|
178
197
|
|
|
198
|
+
* `format: :format_for_database` - *Optional*
|
|
199
|
+
|
|
200
|
+
Use a custom method to define the attributes saved to the database
|
|
201
|
+
|
|
179
202
|
### Email
|
|
180
203
|
|
|
181
204
|
Sends an email notification. Emails will always be sent with `deliver_later`
|
|
@@ -192,6 +215,10 @@ Sends an email notification. Emails will always be sent with `deliver_later`
|
|
|
192
215
|
|
|
193
216
|
Used to customize the method on the mailer that is called
|
|
194
217
|
|
|
218
|
+
* `format: :format_for_email` - *Optional*
|
|
219
|
+
|
|
220
|
+
Use a custom method to define the params sent to the mailer. `recipient` will be merged into the params.
|
|
221
|
+
|
|
195
222
|
### ActionCable
|
|
196
223
|
|
|
197
224
|
Sends a notification to the browser via websockets (ActionCable channel by default).
|
|
@@ -228,6 +255,42 @@ Sends a Slack notification via webhook.
|
|
|
228
255
|
|
|
229
256
|
Defaults to `Rails.application.credentials.slack[:notification_url]`
|
|
230
257
|
|
|
258
|
+
### Microsoft Teams
|
|
259
|
+
|
|
260
|
+
Sends a Teams notification via webhook.
|
|
261
|
+
|
|
262
|
+
`deliver_by :microsoft_teams`
|
|
263
|
+
|
|
264
|
+
#### Options
|
|
265
|
+
|
|
266
|
+
* `format: :format_for_teams` - *Optional*
|
|
267
|
+
|
|
268
|
+
Use a custom method to define the payload sent to Microsoft Teams. Method should return a Hash.
|
|
269
|
+
Documentation for posting via Webhooks available at: https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/add-incoming-webhook
|
|
270
|
+
|
|
271
|
+
```ruby
|
|
272
|
+
{
|
|
273
|
+
title: "This is the title for the card",
|
|
274
|
+
text: "This is the body text for the card",
|
|
275
|
+
sections: [{activityTitle: "Section Title", activityText: "Section Text"}],
|
|
276
|
+
"potentialAction": [{
|
|
277
|
+
"@type": "OpenUri",
|
|
278
|
+
name: "Button Text",
|
|
279
|
+
targets: [{
|
|
280
|
+
os: "default",
|
|
281
|
+
uri: "https://example.com/foo/action"
|
|
282
|
+
}]
|
|
283
|
+
}]
|
|
284
|
+
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
* `url: :url_for_teams_channel`: - *Optional*
|
|
289
|
+
|
|
290
|
+
Use a custom method to retrieve the MS Teams Webhook URL. Method should return a string.
|
|
291
|
+
|
|
292
|
+
Defaults to `Rails.application.credentials.microsoft_teams[:notification_url]`
|
|
293
|
+
|
|
231
294
|
### Twilio SMS
|
|
232
295
|
|
|
233
296
|
Sends an SMS notification via Twilio.
|
|
@@ -238,7 +301,7 @@ Sends an SMS notification via Twilio.
|
|
|
238
301
|
|
|
239
302
|
* `credentials: :get_twilio_credentials` - *Optional*
|
|
240
303
|
|
|
241
|
-
Use a custom method to retrieve the credentials for Twilio. Method should return a Hash with `:account_sid`, `:auth_token` and `:
|
|
304
|
+
Use a custom method to retrieve the credentials for Twilio. Method should return a Hash with `:account_sid`, `:auth_token` and `:phone_number` keys.
|
|
242
305
|
|
|
243
306
|
Defaults to `Rails.application.credentials.twilio[:account_sid]` and `Rails.application.credentials.twilio[:auth_token]`
|
|
244
307
|
|
|
@@ -264,7 +327,7 @@ Sends an SMS notification via Twilio.
|
|
|
264
327
|
|
|
265
328
|
### Vonage SMS
|
|
266
329
|
|
|
267
|
-
Sends an SMS notification
|
|
330
|
+
Sends an SMS notification via Vonage / Nexmo.
|
|
268
331
|
|
|
269
332
|
`deliver_by :vonage`
|
|
270
333
|
|
|
@@ -291,24 +354,57 @@ Sends an SMS notification vai Vonage / Nexmo.
|
|
|
291
354
|
}
|
|
292
355
|
```
|
|
293
356
|
|
|
294
|
-
###
|
|
357
|
+
### Fallback Notifications
|
|
295
358
|
|
|
296
|
-
|
|
359
|
+
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.
|
|
297
360
|
|
|
298
361
|
```ruby
|
|
299
|
-
class
|
|
300
|
-
deliver_by :
|
|
362
|
+
class CommentNotification < Noticed::Base
|
|
363
|
+
deliver_by :database
|
|
364
|
+
deliver_by :email, mailer: 'CommentMailer', delay: 15.minutes, unless: :read?
|
|
365
|
+
end
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
Here a notification will be created immediately in the database (for display directly in your app). If the notification has not been read after 15 minutes, the email notification will be sent. If the notification has already been read in the app, the email will be skipped.
|
|
369
|
+
|
|
370
|
+
You can also configure multiple fallback options:
|
|
371
|
+
|
|
372
|
+
```ruby
|
|
373
|
+
class CriticalSystemNotification < Noticed::Base
|
|
374
|
+
deliver_by :slack
|
|
375
|
+
deliver_by :email, mailer: 'CriticalSystemMailer', delay: 10.minutes, unless: :read?
|
|
376
|
+
deliver_by :twilio, delay: 20.minutes, unless: :read?
|
|
301
377
|
end
|
|
302
378
|
```
|
|
303
379
|
|
|
380
|
+
In this scenario, you can create an escalating notification that starts with a ping in Slack, then emails the team, and then finally sends an SMS to the on-call phone.
|
|
381
|
+
|
|
382
|
+
You can mix and match the options and delivery methods to suit your application specific needs.
|
|
383
|
+
|
|
384
|
+
### 🚚 Custom Delivery Methods
|
|
385
|
+
|
|
386
|
+
To generate a custom delivery method, simply run
|
|
387
|
+
|
|
388
|
+
`rails generate noticed:delivery_method Discord`
|
|
389
|
+
|
|
390
|
+
This will generate a new `DeliveryMethods::Discord` class inside the `app/notifications/delivery_methods` folder, which can be used to deliver notifications to Discord.
|
|
391
|
+
|
|
304
392
|
```ruby
|
|
305
|
-
class
|
|
393
|
+
class DeliveryMethods::Discord < Noticed::DeliveryMethods::Base
|
|
306
394
|
def deliver
|
|
307
395
|
# Logic for sending a Discord notification
|
|
308
396
|
end
|
|
309
397
|
end
|
|
310
398
|
```
|
|
311
399
|
|
|
400
|
+
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.
|
|
401
|
+
|
|
402
|
+
```ruby
|
|
403
|
+
class MyNotification < Noticed::Base
|
|
404
|
+
deliver_by :discord, class: "DeliveryMethods::Discord"
|
|
405
|
+
end
|
|
406
|
+
```
|
|
407
|
+
|
|
312
408
|
Delivery methods have access to the following methods and attributes:
|
|
313
409
|
|
|
314
410
|
* `notification` - 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.
|
|
@@ -316,12 +412,51 @@ Delivery methods have access to the following methods and attributes:
|
|
|
316
412
|
* `recipient` - The object who should receive the notification. This is typically a User, Account, or other ActiveRecord model.
|
|
317
413
|
* `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 }`
|
|
318
414
|
|
|
415
|
+
#### Validating options passed to Custom Delivery methods
|
|
416
|
+
|
|
417
|
+
The presence of the delivery method options is automatically validated if using the `option(s)` method.
|
|
418
|
+
|
|
419
|
+
If you want to validate that the passed options contain valid values, or to add any custom validations, override the `self.validate!(delivery_method_options)` method from the `Noticed::DeliveryMethods::Base` class.
|
|
420
|
+
|
|
421
|
+
```ruby
|
|
422
|
+
class DeliveryMethods::Discord < Noticed::DeliveryMethods::Base
|
|
423
|
+
option :username # Requires the username option to be passed
|
|
424
|
+
|
|
425
|
+
def deliver
|
|
426
|
+
# Logic for sending a Discord notification
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
def self.validate!(delivery_method_options)
|
|
430
|
+
super # Don't forget to call super, otherwise option presence won't be validated
|
|
431
|
+
|
|
432
|
+
# Custom validations
|
|
433
|
+
if delivery_method_options[:username].blank?
|
|
434
|
+
raise Noticed::ValidationError, 'the `username` option must be present'
|
|
435
|
+
end
|
|
436
|
+
end
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
class CommentNotification < Noticed::Base
|
|
440
|
+
deliver_by :discord, class: 'DeliveryMethods::Discord'
|
|
441
|
+
end
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
Now it will raise an error because a required argument is missing.
|
|
445
|
+
|
|
446
|
+
To fix the error, the argument has to be passed correctly. For example:
|
|
447
|
+
|
|
448
|
+
```ruby
|
|
449
|
+
class CommentNotification < Noticed::Base
|
|
450
|
+
deliver_by :discord, class: 'DeliveryMethods::Discord', username: User.admin.username
|
|
451
|
+
end
|
|
452
|
+
```
|
|
453
|
+
|
|
319
454
|
#### Callbacks
|
|
320
455
|
|
|
321
456
|
Callbacks for delivery methods wrap the *actual* delivery of the notification. You can use `before_deliver`, `around_deliver` and `after_deliver` in your custom delivery methods.
|
|
322
457
|
|
|
323
458
|
```ruby
|
|
324
|
-
class
|
|
459
|
+
class DeliveryMethods::Discord < Noticed::DeliveryMethods::Base
|
|
325
460
|
after_deliver do
|
|
326
461
|
# Do whatever you want
|
|
327
462
|
end
|
|
@@ -333,17 +468,110 @@ end
|
|
|
333
468
|
Rails 6.1+ can serialize Class and Module objects as arguments to ActiveJob. The following syntax should work for Rails 6.1+:
|
|
334
469
|
|
|
335
470
|
```ruby
|
|
336
|
-
deliver_by
|
|
471
|
+
deliver_by DeliveryMethods::Discord
|
|
337
472
|
```
|
|
338
473
|
|
|
339
|
-
For Rails 6.0
|
|
474
|
+
For Rails 6.0, you must pass strings of the class names in the `deliver_by` options.
|
|
340
475
|
|
|
341
476
|
```ruby
|
|
342
|
-
deliver_by :discord, class: "
|
|
477
|
+
deliver_by :discord, class: "DeliveryMethods::Discord"
|
|
343
478
|
```
|
|
344
479
|
|
|
345
480
|
We recommend the Rails 6.0 compatible options to prevent confusion.
|
|
346
481
|
|
|
482
|
+
### 📦 Database Model
|
|
483
|
+
|
|
484
|
+
The Notification database model includes several helpful features to make working with database notifications easier.
|
|
485
|
+
|
|
486
|
+
#### Class methods
|
|
487
|
+
|
|
488
|
+
Sorting notifications by newest first:
|
|
489
|
+
|
|
490
|
+
```ruby
|
|
491
|
+
user.notifications.newest_first
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
Query for read or unread notifications:
|
|
495
|
+
|
|
496
|
+
```ruby
|
|
497
|
+
user.notifications.read
|
|
498
|
+
user.notifications.unread
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
Marking all notifications as read or unread:
|
|
503
|
+
|
|
504
|
+
```ruby
|
|
505
|
+
user.notifications.mark_as_read!
|
|
506
|
+
user.notifications.mark_as_unread!
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
#### Instance methods
|
|
510
|
+
|
|
511
|
+
Convert back into a Noticed notification object:
|
|
512
|
+
|
|
513
|
+
```ruby
|
|
514
|
+
@notification.to_notification
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
Mark notification as read / unread:
|
|
518
|
+
|
|
519
|
+
```ruby
|
|
520
|
+
@notification.mark_as_read!
|
|
521
|
+
@notification.mark_as_unread!
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
Check if read / unread:
|
|
525
|
+
|
|
526
|
+
```ruby
|
|
527
|
+
@notification.read?
|
|
528
|
+
@notification.unread?
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
#### Associating Notifications
|
|
532
|
+
|
|
533
|
+
Adding notification associations to your models makes querying and deleting notifications easy and is a pretty critical feature of most applications.
|
|
534
|
+
|
|
535
|
+
For example, in most cases, you'll want to delete notifications for records that are destroyed.
|
|
536
|
+
|
|
537
|
+
We'll need two associations for this:
|
|
538
|
+
|
|
539
|
+
1. Notifications where the record is the recipient
|
|
540
|
+
2. Notifications where the record is in the notification params
|
|
541
|
+
|
|
542
|
+
For example, we can query the notifications and delete them on destroy like so:
|
|
543
|
+
|
|
544
|
+
```ruby
|
|
545
|
+
class Post < ApplicationRecord
|
|
546
|
+
# Standard association for deleting notifications when you're the recipient
|
|
547
|
+
has_many :notifications, as: :recipient, dependent: :destroy
|
|
548
|
+
|
|
549
|
+
# Helper for associating and destroying Notification records where(params: {post: self})
|
|
550
|
+
has_noticed_notifications
|
|
551
|
+
|
|
552
|
+
# You can override the param_name, the notification model name, or disable the before_destroy callback
|
|
553
|
+
has_noticed_notifications param_name: :parent, destroy: false, model: "Notification"
|
|
554
|
+
end
|
|
555
|
+
|
|
556
|
+
# Create a CommentNotification with a post param
|
|
557
|
+
CommentNotification.with(post: @post).deliver(user)
|
|
558
|
+
# Lookup Notifications where params: {post: @post}
|
|
559
|
+
@post.notifications_as_post
|
|
560
|
+
|
|
561
|
+
CommentNotification.with(parent: @post).deliver(user)
|
|
562
|
+
@post.notifications_as_parent
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
#### Handling Deleted Records
|
|
566
|
+
|
|
567
|
+
If you create a notification but delete the associated record and forgot `has_noticed_notifications` on the model, the jobs for sending the notification will not be able to find the record when ActiveJob deserializes. You can discord the job on these errors by adding the following to `ApplicationJob`:
|
|
568
|
+
|
|
569
|
+
```ruby
|
|
570
|
+
class ApplicationJob < ActiveJob::Base
|
|
571
|
+
discard_on ActiveJob::DeserializationError
|
|
572
|
+
end
|
|
573
|
+
```
|
|
574
|
+
|
|
347
575
|
## 🙏 Contributing
|
|
348
576
|
|
|
349
577
|
This project uses [Standard](https://github.com/testdouble/standard) for formatting Ruby code. Please make sure to run `standardrb` before submitting pull requests.
|
data/Rakefile
CHANGED
|
@@ -18,6 +18,10 @@ require "bundler/gem_tasks"
|
|
|
18
18
|
|
|
19
19
|
require "rake/testtask"
|
|
20
20
|
|
|
21
|
+
APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
|
|
22
|
+
load "rails/tasks/engine.rake"
|
|
23
|
+
load "rails/tasks/statistics.rake"
|
|
24
|
+
|
|
21
25
|
Rake::TestTask.new(:test) do |t|
|
|
22
26
|
t.libs << "test"
|
|
23
27
|
t.pattern = "test/**/*_test.rb"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators/named_base"
|
|
4
|
+
|
|
5
|
+
module Noticed
|
|
6
|
+
module Generators
|
|
7
|
+
class DeliveryMethodGenerator < Rails::Generators::NamedBase
|
|
8
|
+
include Rails::Generators::ResourceHelpers
|
|
9
|
+
|
|
10
|
+
source_root File.expand_path("../templates", __FILE__)
|
|
11
|
+
|
|
12
|
+
desc "Generates a class for a custom delivery method with the given NAME."
|
|
13
|
+
|
|
14
|
+
def generate_notification
|
|
15
|
+
template "delivery_method.rb", "app/notifications/delivery_methods/#{singular_name}.rb"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -15,13 +15,22 @@ module Noticed
|
|
|
15
15
|
argument :attributes, type: :array, default: [], banner: "field:type field:type"
|
|
16
16
|
|
|
17
17
|
def generate_notification
|
|
18
|
-
generate :model, name, "recipient:references{polymorphic}", "type",
|
|
18
|
+
generate :model, name, "recipient:references{polymorphic}", "type", params_column, "read_at:datetime:index", *attributes
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def add_noticed_model
|
|
22
22
|
inject_into_class model_path, class_name, " include Noticed::Model\n"
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
+
def add_not_nullable
|
|
26
|
+
migration_path = Dir.glob(Rails.root.join("db/migrate/*")).max_by { |f| File.mtime(f) }
|
|
27
|
+
|
|
28
|
+
# Force is required because null: false already exists in the file and Thor isn't smart enough to tell the difference
|
|
29
|
+
insert_into_file migration_path, after: "t.string :type", force: true do
|
|
30
|
+
", null: false"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
25
34
|
def done
|
|
26
35
|
readme "README" if behavior == :invoke
|
|
27
36
|
end
|
|
@@ -31,6 +40,16 @@ module Noticed
|
|
|
31
40
|
def model_path
|
|
32
41
|
@model_path ||= File.join("app", "models", "#{file_path}.rb")
|
|
33
42
|
end
|
|
43
|
+
|
|
44
|
+
def params_column
|
|
45
|
+
case ActiveRecord::Base.configurations.configs_for(spec_name: "primary").config["adapter"]
|
|
46
|
+
when "postgresql"
|
|
47
|
+
"params:jsonb"
|
|
48
|
+
else
|
|
49
|
+
# MySQL and SQLite both support json
|
|
50
|
+
"params:json"
|
|
51
|
+
end
|
|
52
|
+
end
|
|
34
53
|
end
|
|
35
54
|
end
|
|
36
55
|
end
|
|
@@ -12,7 +12,7 @@ module Noticed
|
|
|
12
12
|
desc "Generates a notification with the given NAME."
|
|
13
13
|
|
|
14
14
|
def generate_notification
|
|
15
|
-
template "notification.rb", "app/notifications/#{
|
|
15
|
+
template "notification.rb", "app/notifications/#{file_path}.rb"
|
|
16
16
|
end
|
|
17
17
|
end
|
|
18
18
|
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
class DeliveryMethods::<%= class_name %> < Noticed::DeliveryMethods::Base
|
|
2
|
+
def deliver
|
|
3
|
+
# Logic for sending the notification
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
# You may override this method to validate options for the delivery method
|
|
7
|
+
# Invalid options should raise a ValidationError
|
|
8
|
+
#
|
|
9
|
+
# def self.validate!(options)
|
|
10
|
+
# raise ValidationError, "required_option missing" unless options[:required_option]
|
|
11
|
+
# end
|
|
12
|
+
end
|
data/lib/noticed/base.rb
CHANGED
|
@@ -9,7 +9,10 @@ module Noticed
|
|
|
9
9
|
class_attribute :delivery_methods, instance_writer: false, default: []
|
|
10
10
|
class_attribute :param_names, instance_writer: false, default: []
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
# Gives notifications access to the record and recipient when formatting for delivery
|
|
13
|
+
attr_accessor :record, :recipient
|
|
14
|
+
|
|
15
|
+
delegate :read?, :unread?, to: :record
|
|
13
16
|
|
|
14
17
|
class << self
|
|
15
18
|
def deliver_by(name, options = {})
|
|
@@ -28,9 +31,10 @@ module Noticed
|
|
|
28
31
|
new(params)
|
|
29
32
|
end
|
|
30
33
|
|
|
31
|
-
def
|
|
32
|
-
param_names.
|
|
34
|
+
def params(*names)
|
|
35
|
+
param_names.concat Array.wrap(names)
|
|
33
36
|
end
|
|
37
|
+
alias_method :param, :params
|
|
34
38
|
end
|
|
35
39
|
|
|
36
40
|
def initialize(params = {})
|
|
@@ -67,6 +71,9 @@ module Noticed
|
|
|
67
71
|
def run_delivery(recipient, enqueue: true)
|
|
68
72
|
delivery_methods = self.class.delivery_methods.dup
|
|
69
73
|
|
|
74
|
+
# Set recipient to instance var so it is available to Notification class
|
|
75
|
+
@recipient = recipient
|
|
76
|
+
|
|
70
77
|
# Run database delivery inline first if it exists so other methods have access to the record
|
|
71
78
|
if (index = delivery_methods.find_index { |m| m[:name] == :database })
|
|
72
79
|
delivery_method = delivery_methods.delete_at(index)
|
|
@@ -80,9 +87,6 @@ module Noticed
|
|
|
80
87
|
|
|
81
88
|
# Actually runs an individual delivery
|
|
82
89
|
def run_delivery_method(delivery_method, recipient:, enqueue:)
|
|
83
|
-
return if (delivery_method_name = delivery_method.dig(:options, :if)) && !send(delivery_method_name)
|
|
84
|
-
return if (delivery_method_name = delivery_method.dig(:options, :unless)) && send(delivery_method_name)
|
|
85
|
-
|
|
86
90
|
args = {
|
|
87
91
|
notification_class: self.class.name,
|
|
88
92
|
options: delivery_method[:options],
|
|
@@ -92,27 +96,46 @@ module Noticed
|
|
|
92
96
|
}
|
|
93
97
|
|
|
94
98
|
run_callbacks delivery_method[:name] do
|
|
95
|
-
|
|
96
|
-
|
|
99
|
+
method = delivery_method_for(delivery_method[:name], delivery_method[:options])
|
|
100
|
+
|
|
101
|
+
# Always perfrom later if a delay is present
|
|
102
|
+
if (delay = delivery_method.dig(:options, :delay))
|
|
103
|
+
method.set(wait: delay).perform_later(args)
|
|
104
|
+
elsif enqueue
|
|
105
|
+
method.perform_later(args)
|
|
106
|
+
else
|
|
107
|
+
method.perform_now(args)
|
|
108
|
+
end
|
|
97
109
|
end
|
|
98
110
|
end
|
|
99
111
|
|
|
100
|
-
|
|
101
|
-
def get_class(name, options)
|
|
112
|
+
def delivery_method_for(name, options)
|
|
102
113
|
if options[:class]
|
|
103
114
|
options[:class].constantize
|
|
104
115
|
else
|
|
105
|
-
"Noticed::DeliveryMethods::#{name.to_s.
|
|
116
|
+
"Noticed::DeliveryMethods::#{name.to_s.camelize}".constantize
|
|
106
117
|
end
|
|
107
118
|
end
|
|
108
119
|
|
|
109
|
-
# Validates that all params are present
|
|
110
120
|
def validate!
|
|
121
|
+
validate_params_present!
|
|
122
|
+
validate_options_of_delivery_methods!
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Validates that all params are present
|
|
126
|
+
def validate_params_present!
|
|
111
127
|
self.class.param_names.each do |param_name|
|
|
112
128
|
if params[param_name].nil?
|
|
113
129
|
raise ValidationError, "#{param_name} is missing."
|
|
114
130
|
end
|
|
115
131
|
end
|
|
116
132
|
end
|
|
133
|
+
|
|
134
|
+
def validate_options_of_delivery_methods!
|
|
135
|
+
delivery_methods.each do |delivery_method|
|
|
136
|
+
method = delivery_method_for(delivery_method[:name], delivery_method[:options])
|
|
137
|
+
method.validate!(delivery_method[:options])
|
|
138
|
+
end
|
|
139
|
+
end
|
|
117
140
|
end
|
|
118
141
|
end
|
data/lib/noticed/coder.rb
CHANGED
|
@@ -16,10 +16,18 @@ module Noticed
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def channel
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
@channel ||= begin
|
|
20
|
+
value = options[:channel]
|
|
21
|
+
case value
|
|
22
|
+
when String
|
|
23
|
+
value.constantize
|
|
24
|
+
when Symbol
|
|
25
|
+
notification.send(value)
|
|
26
|
+
when Class
|
|
27
|
+
value
|
|
28
|
+
else
|
|
29
|
+
Noticed::NotificationChannel
|
|
30
|
+
end
|
|
23
31
|
end
|
|
24
32
|
end
|
|
25
33
|
end
|
|
@@ -4,15 +4,44 @@ module Noticed
|
|
|
4
4
|
extend ActiveModel::Callbacks
|
|
5
5
|
define_model_callbacks :deliver
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
class_attribute :option_names, instance_writer: false, default: []
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
@notification = notification_class.constantize.new(params)
|
|
11
|
-
@options = options
|
|
12
|
-
@recipient = recipient
|
|
9
|
+
attr_reader :notification, :options, :params, :recipient, :record
|
|
13
10
|
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
class << self
|
|
12
|
+
# Copy option names from parent
|
|
13
|
+
def inherited(base) #:nodoc:
|
|
14
|
+
base.option_names = option_names.dup
|
|
15
|
+
super
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def options(*names)
|
|
19
|
+
option_names.concat Array.wrap(names)
|
|
20
|
+
end
|
|
21
|
+
alias_method :option, :options
|
|
22
|
+
|
|
23
|
+
def validate!(delivery_method_options)
|
|
24
|
+
option_names.each do |option_name|
|
|
25
|
+
unless delivery_method_options.key? option_name
|
|
26
|
+
raise ValidationError, "option `#{option_name}` must be set for #{name}"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def perform(args)
|
|
33
|
+
@notification = args[:notification_class].constantize.new(args[:params])
|
|
34
|
+
@options = args[:options]
|
|
35
|
+
@params = args[:params]
|
|
36
|
+
@recipient = args[:recipient]
|
|
37
|
+
@record = args[:record]
|
|
38
|
+
|
|
39
|
+
# Make notification aware of database record and recipient during delivery
|
|
40
|
+
@notification.record = args[:record]
|
|
41
|
+
@notification.recipient = args[:recipient]
|
|
42
|
+
|
|
43
|
+
return if (condition = @options[:if]) && !@notification.send(condition)
|
|
44
|
+
return if (condition = @options[:unless]) && @notification.send(condition)
|
|
16
45
|
|
|
17
46
|
run_callbacks :deliver do
|
|
18
47
|
deliver
|
|
@@ -22,6 +51,36 @@ module Noticed
|
|
|
22
51
|
def deliver
|
|
23
52
|
raise NotImplementedError, "Delivery methods must implement a deliver method"
|
|
24
53
|
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
# Helper method for making POST requests from delivery methods
|
|
58
|
+
#
|
|
59
|
+
# Usage:
|
|
60
|
+
# post("http://example.com", basic_auth: {user:, pass:}, json: {}, form: {})
|
|
61
|
+
#
|
|
62
|
+
def post(url, args = {})
|
|
63
|
+
basic_auth = args.delete(:basic_auth)
|
|
64
|
+
|
|
65
|
+
request = if basic_auth
|
|
66
|
+
HTTP.basic_auth(user: basic_auth[:user], pass: basic_auth[:pass])
|
|
67
|
+
else
|
|
68
|
+
HTTP
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
response = request.post(url, args)
|
|
72
|
+
|
|
73
|
+
if options[:debug]
|
|
74
|
+
Rails.logger.debug("POST #{url}")
|
|
75
|
+
Rails.logger.debug("Response: #{response.code}: #{response}")
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
if !options[:ignore_failure] && !response.status.success?
|
|
79
|
+
raise ResponseUnsuccessful.new(response)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
response
|
|
83
|
+
end
|
|
25
84
|
end
|
|
26
85
|
end
|
|
27
86
|
end
|
|
@@ -6,6 +6,13 @@ module Noticed
|
|
|
6
6
|
recipient.send(association_name).create!(attributes)
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
+
def self.validate!(options)
|
|
10
|
+
super
|
|
11
|
+
|
|
12
|
+
# Must be executed right away so the other deliveries can access the db record
|
|
13
|
+
raise ArgumentError, "database delivery cannot be delayed" if options.key?(:delay)
|
|
14
|
+
end
|
|
15
|
+
|
|
9
16
|
private
|
|
10
17
|
|
|
11
18
|
def association_name
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
module Noticed
|
|
2
2
|
module DeliveryMethods
|
|
3
3
|
class Email < Base
|
|
4
|
+
option :mailer
|
|
5
|
+
|
|
4
6
|
def deliver
|
|
5
|
-
mailer.with(
|
|
7
|
+
mailer.with(format).send(method.to_sym).deliver_now
|
|
6
8
|
end
|
|
7
9
|
|
|
8
10
|
private
|
|
@@ -14,6 +16,15 @@ module Noticed
|
|
|
14
16
|
def method
|
|
15
17
|
options[:method] || notification.class.name.underscore
|
|
16
18
|
end
|
|
19
|
+
|
|
20
|
+
def format
|
|
21
|
+
params = if (method = options[:format])
|
|
22
|
+
notification.send(method)
|
|
23
|
+
else
|
|
24
|
+
notification.params
|
|
25
|
+
end
|
|
26
|
+
params.merge(recipient: recipient, record: record)
|
|
27
|
+
end
|
|
17
28
|
end
|
|
18
29
|
end
|
|
19
30
|
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module Noticed
|
|
2
|
+
module DeliveryMethods
|
|
3
|
+
class MicrosoftTeams < Base
|
|
4
|
+
def deliver
|
|
5
|
+
post(url, json: format)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def format
|
|
11
|
+
if (method = options[:format])
|
|
12
|
+
notification.send(method)
|
|
13
|
+
else
|
|
14
|
+
{
|
|
15
|
+
title: notification.params[:title],
|
|
16
|
+
text: notification.params[:text],
|
|
17
|
+
sections: notification.params[:sections],
|
|
18
|
+
potentialAction: notification.params[:notification_action]
|
|
19
|
+
}
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def url
|
|
24
|
+
if (method = options[:url])
|
|
25
|
+
notification.send(method)
|
|
26
|
+
else
|
|
27
|
+
Rails.application.credentials.microsoft_teams[:notification_url]
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -2,7 +2,7 @@ module Noticed
|
|
|
2
2
|
module DeliveryMethods
|
|
3
3
|
class Slack < Base
|
|
4
4
|
def deliver
|
|
5
|
-
|
|
5
|
+
post(url, json: format)
|
|
6
6
|
end
|
|
7
7
|
|
|
8
8
|
private
|
|
@@ -11,7 +11,7 @@ module Noticed
|
|
|
11
11
|
if (method = options[:format])
|
|
12
12
|
notification.send(method)
|
|
13
13
|
else
|
|
14
|
-
params
|
|
14
|
+
notification.params
|
|
15
15
|
end
|
|
16
16
|
end
|
|
17
17
|
|
|
@@ -2,7 +2,13 @@ module Noticed
|
|
|
2
2
|
module DeliveryMethods
|
|
3
3
|
class Vonage < Base
|
|
4
4
|
def deliver
|
|
5
|
-
|
|
5
|
+
response = post("https://rest.nexmo.com/sms/json", json: format)
|
|
6
|
+
status = response.parse.dig("messages", 0, "status")
|
|
7
|
+
if !options[:ignore_failure] && status != "0"
|
|
8
|
+
raise ResponseUnsuccessful.new(response)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
response
|
|
6
12
|
end
|
|
7
13
|
|
|
8
14
|
private
|
data/lib/noticed/engine.rb
CHANGED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module Noticed
|
|
2
|
+
module HasNotifications
|
|
3
|
+
# Defines a method for the association and a before_destory callback to remove notifications
|
|
4
|
+
# where this record is a param
|
|
5
|
+
#
|
|
6
|
+
# class User < ApplicationRecord
|
|
7
|
+
# has_noticed_notifications
|
|
8
|
+
# has_noticed_notifications param_name: :owner, destroy: false, model: "Notification"
|
|
9
|
+
# end
|
|
10
|
+
#
|
|
11
|
+
# @user.notifications_as_user
|
|
12
|
+
# @user.notifications_as_owner
|
|
13
|
+
|
|
14
|
+
extend ActiveSupport::Concern
|
|
15
|
+
|
|
16
|
+
class_methods do
|
|
17
|
+
def has_noticed_notifications(param_name: model_name.singular, **options)
|
|
18
|
+
model = options.fetch(:model_name, "Notification").constantize
|
|
19
|
+
|
|
20
|
+
define_method "notifications_as_#{param_name}" do
|
|
21
|
+
model.where(params: {param_name.to_sym => self})
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
if options.fetch(:destroy, true)
|
|
25
|
+
before_destroy do
|
|
26
|
+
send("notifications_as_#{param_name}").destroy_all
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
data/lib/noticed/model.rb
CHANGED
|
@@ -5,32 +5,51 @@ module Noticed
|
|
|
5
5
|
included do
|
|
6
6
|
self.inheritance_column = nil
|
|
7
7
|
|
|
8
|
-
serialize :params,
|
|
8
|
+
serialize :params, noticed_coder
|
|
9
9
|
|
|
10
10
|
belongs_to :recipient, polymorphic: true
|
|
11
11
|
|
|
12
12
|
scope :newest_first, -> { order(created_at: :desc) }
|
|
13
|
+
scope :unread, -> { where(read_at: nil) }
|
|
14
|
+
scope :read, -> { where.not(read_at: nil) }
|
|
13
15
|
end
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
class_methods do
|
|
16
18
|
def mark_as_read!
|
|
17
19
|
update_all(read_at: Time.current, updated_at: Time.current)
|
|
18
20
|
end
|
|
21
|
+
|
|
22
|
+
def mark_as_unread!
|
|
23
|
+
update_all(read_at: nil, updated_at: Time.current)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def noticed_coder
|
|
27
|
+
case attribute_types["params"].type
|
|
28
|
+
when :json, :jsonb
|
|
29
|
+
Noticed::Coder
|
|
30
|
+
else
|
|
31
|
+
Noticed::TextCoder
|
|
32
|
+
end
|
|
33
|
+
end
|
|
19
34
|
end
|
|
20
35
|
|
|
21
36
|
# Rehydrate the database notification into the Notification object for rendering
|
|
22
37
|
def to_notification
|
|
23
38
|
@_notification ||= begin
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
39
|
+
instance = type.constantize.with(params)
|
|
40
|
+
instance.record = self
|
|
41
|
+
instance
|
|
42
|
+
end
|
|
28
43
|
end
|
|
29
44
|
|
|
30
45
|
def mark_as_read!
|
|
31
46
|
update(read_at: Time.current)
|
|
32
47
|
end
|
|
33
48
|
|
|
49
|
+
def mark_as_unread!
|
|
50
|
+
update(read_at: nil)
|
|
51
|
+
end
|
|
52
|
+
|
|
34
53
|
def unread?
|
|
35
54
|
!read?
|
|
36
55
|
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Noticed
|
|
2
|
+
class TextCoder
|
|
3
|
+
def self.load(data)
|
|
4
|
+
return if data.nil?
|
|
5
|
+
|
|
6
|
+
# Text columns need JSON parsing
|
|
7
|
+
data = JSON.parse(data)
|
|
8
|
+
ActiveJob::Arguments.send(:deserialize_argument, data)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.dump(data)
|
|
12
|
+
return if data.nil?
|
|
13
|
+
ActiveJob::Arguments.send(:serialize_argument, data).to_json
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
data/lib/noticed/translation.rb
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
|
-
module
|
|
2
|
-
|
|
1
|
+
module Noticed
|
|
2
|
+
module Translation
|
|
3
|
+
extend ActiveSupport::Concern
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
# Returns the +i18n_scope+ for the class. Overwrite if you want custom lookup.
|
|
6
|
+
def i18n_scope
|
|
7
|
+
:notifications
|
|
8
|
+
end
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
def translate(key, **options)
|
|
11
|
+
I18n.translate(scope_translation_key(key), **options)
|
|
12
|
+
end
|
|
13
|
+
alias_method :t, :translate
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
def scope_translation_key(key)
|
|
16
|
+
if key.to_s.start_with?(".")
|
|
17
|
+
"#{i18n_scope}.#{self.class.name.underscore}#{key}"
|
|
18
|
+
else
|
|
19
|
+
key
|
|
20
|
+
end
|
|
19
21
|
end
|
|
20
22
|
end
|
|
21
23
|
end
|
data/lib/noticed/version.rb
CHANGED
data/lib/noticed.rb
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
|
+
require "active_job/arguments"
|
|
1
2
|
require "http"
|
|
2
3
|
require "noticed/engine"
|
|
3
4
|
|
|
4
5
|
module Noticed
|
|
5
6
|
autoload :Base, "noticed/base"
|
|
6
7
|
autoload :Coder, "noticed/coder"
|
|
8
|
+
autoload :HasNotifications, "noticed/has_notifications"
|
|
7
9
|
autoload :Model, "noticed/model"
|
|
10
|
+
autoload :TextCoder, "noticed/text_coder"
|
|
8
11
|
autoload :Translation, "noticed/translation"
|
|
12
|
+
autoload :NotificationChannel, "noticed/notification_channel"
|
|
9
13
|
|
|
10
14
|
module DeliveryMethods
|
|
11
15
|
autoload :Base, "noticed/delivery_methods/base"
|
|
@@ -13,6 +17,7 @@ module Noticed
|
|
|
13
17
|
autoload :Database, "noticed/delivery_methods/database"
|
|
14
18
|
autoload :Email, "noticed/delivery_methods/email"
|
|
15
19
|
autoload :Slack, "noticed/delivery_methods/slack"
|
|
20
|
+
autoload :MicrosoftTeams, "noticed/delivery_methods/microsoft_teams"
|
|
16
21
|
autoload :Test, "noticed/delivery_methods/test"
|
|
17
22
|
autoload :Twilio, "noticed/delivery_methods/twilio"
|
|
18
23
|
autoload :Vonage, "noticed/delivery_methods/vonage"
|
|
@@ -32,4 +37,12 @@ module Noticed
|
|
|
32
37
|
|
|
33
38
|
class ValidationError < StandardError
|
|
34
39
|
end
|
|
40
|
+
|
|
41
|
+
class ResponseUnsuccessful < StandardError
|
|
42
|
+
attr_reader :response
|
|
43
|
+
|
|
44
|
+
def initialize(response)
|
|
45
|
+
@response = response
|
|
46
|
+
end
|
|
47
|
+
end
|
|
35
48
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: noticed
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Chris Oliver
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2021-03-13 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|
|
@@ -67,7 +67,35 @@ dependencies:
|
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
68
|
version: '0'
|
|
69
69
|
- !ruby/object:Gem::Dependency
|
|
70
|
-
name:
|
|
70
|
+
name: webmock
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: mysql2
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - ">="
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '0'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: sqlite3
|
|
71
99
|
requirement: !ruby/object:Gem::Requirement
|
|
72
100
|
requirements:
|
|
73
101
|
- - ">="
|
|
@@ -91,10 +119,11 @@ files:
|
|
|
91
119
|
- MIT-LICENSE
|
|
92
120
|
- README.md
|
|
93
121
|
- Rakefile
|
|
94
|
-
-
|
|
122
|
+
- lib/generators/noticed/delivery_method_generator.rb
|
|
95
123
|
- lib/generators/noticed/model_generator.rb
|
|
96
124
|
- lib/generators/noticed/notification_generator.rb
|
|
97
125
|
- lib/generators/noticed/templates/README
|
|
126
|
+
- lib/generators/noticed/templates/delivery_method.rb.tt
|
|
98
127
|
- lib/generators/noticed/templates/notification.rb.tt
|
|
99
128
|
- lib/noticed.rb
|
|
100
129
|
- lib/noticed/base.rb
|
|
@@ -103,12 +132,16 @@ files:
|
|
|
103
132
|
- lib/noticed/delivery_methods/base.rb
|
|
104
133
|
- lib/noticed/delivery_methods/database.rb
|
|
105
134
|
- lib/noticed/delivery_methods/email.rb
|
|
135
|
+
- lib/noticed/delivery_methods/microsoft_teams.rb
|
|
106
136
|
- lib/noticed/delivery_methods/slack.rb
|
|
107
137
|
- lib/noticed/delivery_methods/test.rb
|
|
108
138
|
- lib/noticed/delivery_methods/twilio.rb
|
|
109
139
|
- lib/noticed/delivery_methods/vonage.rb
|
|
110
140
|
- lib/noticed/engine.rb
|
|
141
|
+
- lib/noticed/has_notifications.rb
|
|
111
142
|
- lib/noticed/model.rb
|
|
143
|
+
- lib/noticed/notification_channel.rb
|
|
144
|
+
- lib/noticed/text_coder.rb
|
|
112
145
|
- lib/noticed/translation.rb
|
|
113
146
|
- lib/noticed/version.rb
|
|
114
147
|
- lib/tasks/noticed_tasks.rake
|
|
@@ -116,7 +149,7 @@ homepage: https://github.com/excid3/noticed
|
|
|
116
149
|
licenses:
|
|
117
150
|
- MIT
|
|
118
151
|
metadata: {}
|
|
119
|
-
post_install_message:
|
|
152
|
+
post_install_message:
|
|
120
153
|
rdoc_options: []
|
|
121
154
|
require_paths:
|
|
122
155
|
- lib
|
|
@@ -131,8 +164,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
131
164
|
- !ruby/object:Gem::Version
|
|
132
165
|
version: '0'
|
|
133
166
|
requirements: []
|
|
134
|
-
rubygems_version: 3.
|
|
135
|
-
signing_key:
|
|
167
|
+
rubygems_version: 3.2.3
|
|
168
|
+
signing_key:
|
|
136
169
|
specification_version: 4
|
|
137
170
|
summary: Notifications for Ruby on Rails applications
|
|
138
171
|
test_files: []
|
|
File without changes
|