noticed 1.2.17 → 1.3.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 +63 -36
- data/lib/generators/noticed/model_generator.rb +2 -3
- data/lib/noticed.rb +1 -0
- data/lib/noticed/base.rb +11 -4
- data/lib/noticed/delivery_methods/base.rb +3 -0
- data/lib/noticed/delivery_methods/database.rb +7 -0
- data/lib/noticed/delivery_methods/email.rb +4 -6
- data/lib/noticed/delivery_methods/vonage.rb +2 -0
- data/lib/noticed/engine.rb +5 -0
- data/lib/noticed/has_notifications.rb +32 -0
- data/lib/noticed/model.rb +5 -1
- data/lib/noticed/version.rb +1 -1
- metadata +33 -4
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
@@ -106,6 +106,7 @@ end
|
|
106
106
|
|
107
107
|
* `if: :method_name` - Calls `method_name`and cancels delivery method if `false` is returned
|
108
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
|
109
110
|
|
110
111
|
##### Helper Methods
|
111
112
|
|
@@ -186,7 +187,7 @@ Writes notification to the database.
|
|
186
187
|
|
187
188
|
`deliver_by :database`
|
188
189
|
|
189
|
-
**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.
|
190
191
|
|
191
192
|
##### Options
|
192
193
|
|
@@ -264,7 +265,7 @@ Sends a Teams notification via webhook.
|
|
264
265
|
|
265
266
|
* `format: :format_for_teams` - *Optional*
|
266
267
|
|
267
|
-
Use a custom method to define the payload sent to
|
268
|
+
Use a custom method to define the payload sent to Microsoft Teams. Method should return a Hash.
|
268
269
|
Documentation for posting via Webhooks available at: https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/add-incoming-webhook
|
269
270
|
|
270
271
|
```ruby
|
@@ -353,6 +354,33 @@ Sends an SMS notification via Vonage / Nexmo.
|
|
353
354
|
}
|
354
355
|
```
|
355
356
|
|
357
|
+
### Fallback Notifications
|
358
|
+
|
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.
|
360
|
+
|
361
|
+
```ruby
|
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?
|
377
|
+
end
|
378
|
+
```
|
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
|
+
|
356
384
|
### 🚚 Custom Delivery Methods
|
357
385
|
|
358
386
|
To generate a custom delivery method, simply run
|
@@ -463,10 +491,19 @@ Sorting notifications by newest first:
|
|
463
491
|
user.notifications.newest_first
|
464
492
|
```
|
465
493
|
|
466
|
-
|
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:
|
467
503
|
|
468
504
|
```ruby
|
469
505
|
user.notifications.mark_as_read!
|
506
|
+
user.notifications.mark_as_unread!
|
470
507
|
```
|
471
508
|
|
472
509
|
#### Instance methods
|
@@ -497,53 +534,43 @@ Adding notification associations to your models makes querying and deleting noti
|
|
497
534
|
|
498
535
|
For example, in most cases, you'll want to delete notifications for records that are destroyed.
|
499
536
|
|
500
|
-
|
537
|
+
We'll need two associations for this:
|
501
538
|
|
502
|
-
|
539
|
+
1. Notifications where the record is the recipient
|
540
|
+
2. Notifications where the record is in the notification params
|
503
541
|
|
504
542
|
For example, we can query the notifications and delete them on destroy like so:
|
505
543
|
|
506
544
|
```ruby
|
507
545
|
class Post < ApplicationRecord
|
508
|
-
|
509
|
-
|
510
|
-
@notifications ||= Notification.where(params: { post: self })
|
511
|
-
|
512
|
-
# Or Postgres syntax to query the post key in the JSON column
|
513
|
-
# @notifications ||= Notification.where("params->'post' = ?", Noticed::Coder.dump(self).to_json)
|
514
|
-
end
|
546
|
+
# Standard association for deleting notifications when you're the recipient
|
547
|
+
has_many :notifications, as: :recipient, dependent: :destroy
|
515
548
|
|
516
|
-
|
549
|
+
# Helper for associating and destroying Notification records where(params: {post: self})
|
550
|
+
has_noticed_notifications
|
517
551
|
|
518
|
-
|
519
|
-
|
520
|
-
end
|
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"
|
521
554
|
end
|
522
|
-
```
|
523
|
-
|
524
|
-
##### Polymorphic Assocation
|
525
555
|
|
526
|
-
|
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
|
527
560
|
|
528
|
-
|
529
|
-
|
530
|
-
|
561
|
+
CommentNotification.with(parent: @post).deliver(user)
|
562
|
+
@post.notifications_as_parent
|
563
|
+
```
|
531
564
|
|
532
|
-
|
565
|
+
#### Handling Deleted Records
|
533
566
|
|
534
|
-
|
535
|
-
class ExampleNotification < Noticed::Base
|
536
|
-
deliver_by :database, format: :format_for_database
|
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`:
|
537
568
|
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
}
|
544
|
-
end
|
545
|
-
end
|
546
|
-
```
|
569
|
+
```ruby
|
570
|
+
class ApplicationJob < ActiveJob::Base
|
571
|
+
discard_on ActiveJob::DeserializationError
|
572
|
+
end
|
573
|
+
```
|
547
574
|
|
548
575
|
## 🙏 Contributing
|
549
576
|
|
@@ -43,12 +43,11 @@ module Noticed
|
|
43
43
|
|
44
44
|
def params_column
|
45
45
|
case ActiveRecord::Base.configurations.configs_for(spec_name: "primary").config["adapter"]
|
46
|
-
when "mysql2"
|
47
|
-
"params:json"
|
48
46
|
when "postgresql"
|
49
47
|
"params:jsonb"
|
50
48
|
else
|
51
|
-
|
49
|
+
# MySQL and SQLite both support json
|
50
|
+
"params:json"
|
52
51
|
end
|
53
52
|
end
|
54
53
|
end
|
data/lib/noticed.rb
CHANGED
@@ -5,6 +5,7 @@ require "noticed/engine"
|
|
5
5
|
module Noticed
|
6
6
|
autoload :Base, "noticed/base"
|
7
7
|
autoload :Coder, "noticed/coder"
|
8
|
+
autoload :HasNotifications, "noticed/has_notifications"
|
8
9
|
autoload :Model, "noticed/model"
|
9
10
|
autoload :TextCoder, "noticed/text_coder"
|
10
11
|
autoload :Translation, "noticed/translation"
|
data/lib/noticed/base.rb
CHANGED
@@ -12,6 +12,8 @@ module Noticed
|
|
12
12
|
# Gives notifications access to the record and recipient when formatting for delivery
|
13
13
|
attr_accessor :record, :recipient
|
14
14
|
|
15
|
+
delegate :read?, :unread?, to: :record
|
16
|
+
|
15
17
|
class << self
|
16
18
|
def deliver_by(name, options = {})
|
17
19
|
delivery_methods.push(name: name, options: options)
|
@@ -85,9 +87,6 @@ module Noticed
|
|
85
87
|
|
86
88
|
# Actually runs an individual delivery
|
87
89
|
def run_delivery_method(delivery_method, recipient:, enqueue:)
|
88
|
-
return if (delivery_method_name = delivery_method.dig(:options, :if)) && !send(delivery_method_name)
|
89
|
-
return if (delivery_method_name = delivery_method.dig(:options, :unless)) && send(delivery_method_name)
|
90
|
-
|
91
90
|
args = {
|
92
91
|
notification_class: self.class.name,
|
93
92
|
options: delivery_method[:options],
|
@@ -98,7 +97,15 @@ module Noticed
|
|
98
97
|
|
99
98
|
run_callbacks delivery_method[:name] do
|
100
99
|
method = delivery_method_for(delivery_method[:name], delivery_method[:options])
|
101
|
-
|
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
|
102
109
|
end
|
103
110
|
end
|
104
111
|
|
@@ -40,6 +40,9 @@ module Noticed
|
|
40
40
|
@notification.record = args[:record]
|
41
41
|
@notification.recipient = args[:recipient]
|
42
42
|
|
43
|
+
return if (condition = @options[:if]) && !@notification.send(condition)
|
44
|
+
return if (condition = @options[:unless]) && @notification.send(condition)
|
45
|
+
|
43
46
|
run_callbacks :deliver do
|
44
47
|
deliver
|
45
48
|
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
|
@@ -4,7 +4,7 @@ module Noticed
|
|
4
4
|
option :mailer
|
5
5
|
|
6
6
|
def deliver
|
7
|
-
mailer.with(format).send(method.to_sym).
|
7
|
+
mailer.with(format).send(method.to_sym).deliver_now
|
8
8
|
end
|
9
9
|
|
10
10
|
private
|
@@ -18,14 +18,12 @@ module Noticed
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def format
|
21
|
-
if (method = options[:format])
|
21
|
+
params = if (method = options[:format])
|
22
22
|
notification.send(method)
|
23
23
|
else
|
24
|
-
notification.params
|
25
|
-
recipient: recipient,
|
26
|
-
record: record
|
27
|
-
)
|
24
|
+
notification.params
|
28
25
|
end
|
26
|
+
params.merge(recipient: recipient, record: record)
|
29
27
|
end
|
30
28
|
end
|
31
29
|
end
|
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
@@ -14,11 +14,15 @@ module Noticed
|
|
14
14
|
scope :read, -> { where.not(read_at: nil) }
|
15
15
|
end
|
16
16
|
|
17
|
-
|
17
|
+
class_methods do
|
18
18
|
def mark_as_read!
|
19
19
|
update_all(read_at: Time.current, updated_at: Time.current)
|
20
20
|
end
|
21
21
|
|
22
|
+
def mark_as_unread!
|
23
|
+
update_all(read_at: nil, updated_at: Time.current)
|
24
|
+
end
|
25
|
+
|
22
26
|
def noticed_coder
|
23
27
|
case attribute_types["params"].type
|
24
28
|
when :json, :jsonb
|
data/lib/noticed/version.rb
CHANGED
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
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
|
- - ">="
|
@@ -110,6 +138,7 @@ files:
|
|
110
138
|
- lib/noticed/delivery_methods/twilio.rb
|
111
139
|
- lib/noticed/delivery_methods/vonage.rb
|
112
140
|
- lib/noticed/engine.rb
|
141
|
+
- lib/noticed/has_notifications.rb
|
113
142
|
- lib/noticed/model.rb
|
114
143
|
- lib/noticed/notification_channel.rb
|
115
144
|
- lib/noticed/text_coder.rb
|
@@ -135,7 +164,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
135
164
|
- !ruby/object:Gem::Version
|
136
165
|
version: '0'
|
137
166
|
requirements: []
|
138
|
-
rubygems_version: 3.
|
167
|
+
rubygems_version: 3.2.3
|
139
168
|
signing_key:
|
140
169
|
specification_version: 4
|
141
170
|
summary: Notifications for Ruby on Rails applications
|