noticed 2.0.3 → 2.1.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 +322 -27
- data/app/channels/noticed/notification_channel.rb +1 -1
- data/app/models/concerns/noticed/deliverable.rb +39 -12
- data/app/models/concerns/noticed/readable.rb +4 -4
- data/app/models/noticed/deliverable/deliver_by.rb +13 -4
- data/app/models/noticed/ephemeral.rb +53 -0
- data/app/models/noticed/notification.rb +1 -1
- data/db/migrate/20231215190233_create_noticed_tables.rb +1 -0
- data/db/migrate/20240129184740_add_notifications_count_to_noticed_event.rb +5 -0
- data/lib/generators/noticed/delivery_method_generator.rb +1 -0
- data/lib/generators/noticed/notifier_generator.rb +1 -0
- data/lib/generators/noticed/templates/application_delivery_method.rb.tt +2 -0
- data/lib/generators/noticed/templates/application_notifier.rb.tt +2 -0
- data/lib/generators/noticed/templates/delivery_method.rb.tt +1 -1
- data/lib/generators/noticed/templates/notifier.rb.tt +1 -1
- data/lib/noticed/bulk_delivery_method.rb +9 -3
- data/lib/noticed/bulk_delivery_methods/test.rb +11 -0
- data/lib/noticed/delivery_method.rb +16 -5
- data/lib/noticed/delivery_methods/fcm.rb +10 -1
- data/lib/noticed/delivery_methods/ios.rb +9 -4
- data/lib/noticed/engine.rb +6 -0
- data/lib/noticed/has_notifications.rb +49 -0
- data/lib/noticed/version.rb +1 -1
- data/lib/noticed.rb +5 -0
- metadata +9 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 457449f2026b5ae977841ac9c0c0d30373fe1f8715670290ff49b75e021fea9a
|
|
4
|
+
data.tar.gz: 51adcf9c865b36de52eed8066a8d622867a897caad615879632c63939b5aebf2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2d57c184f5c1b72848795ab0350e3fe2f6a05a4dc7698e26acb8bda186ac7f8e9ef5c1451fd9bba77e6afa5b118288a1bdc39a293b269d8645491fb9d026ea23
|
|
7
|
+
data.tar.gz: daf3bb3225f9273821d9918da31700e7c0c65e89c441e8340847d085b9f76bdfde347e920f861dff3eb697e3083fbbfe0fb7a3ee6e0759172d89988b90c68381
|
data/README.md
CHANGED
|
@@ -1,24 +1,31 @@
|
|
|
1
1
|
# Noticed
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## 🎉 Notifications for your Ruby on Rails app.
|
|
4
4
|
|
|
5
5
|
[](https://github.com/excid3/noticed/actions) [](https://badge.fury.io/rb/noticed)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
> [!IMPORTANT]
|
|
8
|
+
> **⚠️ Upgrading from V1? Read the [Upgrade Guide](https://github.com/excid3/noticed/blob/main/UPGRADE.md)!**
|
|
8
9
|
|
|
9
10
|
Noticed is a gem that allows your application to send notifications of varying types, over various mediums, to various recipients. Be it a Slack notification to your own team when some internal event occurs or a notification to your user, sent as a text message, email, and real-time UI element in the browser, Noticed supports all of the above (at the same time)!
|
|
10
11
|
|
|
11
12
|
Noticed implements two top-level types of delivery methods:
|
|
12
13
|
|
|
13
|
-
1. Individual Deliveries
|
|
14
|
+
1. **Individual Deliveries**: Where each recipient gets their own notification
|
|
15
|
+
<details>
|
|
16
|
+
<summary> Show Example </summary>
|
|
14
17
|
|
|
15
18
|
Let’s use a car dealership as an example here. When someone purchases a car, a notification will be sent to the buyer with some contract details (“Congrats on your new 2024 XYZ Model...”), another to the car sales-person with different details (“You closed X deal; your commission is Y”), and another to the bank handling the loan with financial details (“New loan issued; amount $20,000...”). The event (the car being sold) necessitates multiple notifications being sent out to different recipients, but each contains its own unique information and should be separate from the others. These are individual deliveries.
|
|
19
|
+
</details>
|
|
16
20
|
|
|
17
|
-
2. Bulk Deliveries
|
|
21
|
+
2. **Bulk Deliveries**: One notification for all recipients. This is useful for sending a notification to your Slack team, for example.
|
|
18
22
|
|
|
23
|
+
<details>
|
|
24
|
+
<summary> Show Example </summary>
|
|
19
25
|
Let’s continue with the car-sale example here. Consider that your development team created the car-sales application that processed the deal above and sent out the notifications to the three parties. For the sake of team morale and feeling the ‘wins’, you may want to implement a notification that notifies your internal development team whenever a car sells through your platform. In this case, you’ll be notifying many people (your development team, maybe others at your company) but with the same content (“someone just bought a car through our platform!”). This is a bulk delivery. It’s generally a single notification that many people just need to be made aware of.
|
|
20
26
|
|
|
21
27
|
Bulk deliveries are typically used to push notifications to other platforms where users are managed (Slack, Discord, etc.) instead of your own.
|
|
28
|
+
</details>
|
|
22
29
|
|
|
23
30
|
Delivery methods we officially support:
|
|
24
31
|
|
|
@@ -62,13 +69,26 @@ rails db:migrate
|
|
|
62
69
|
|
|
63
70
|
Noticed operates with a few constructs: Notifiers, delivery methods, and Notification records.
|
|
64
71
|
|
|
65
|
-
To start, generate a Notifier:
|
|
72
|
+
To start, generate a Notifier:
|
|
66
73
|
|
|
67
74
|
```sh
|
|
68
75
|
rails generate noticed:notifier NewCommentNotifier
|
|
69
76
|
```
|
|
70
77
|
|
|
71
|
-
|
|
78
|
+
### Usage Contents
|
|
79
|
+
- [Notifier Objects](#notifier-objects)
|
|
80
|
+
- [Delivery Method Configuration](#delivery-method-configuration)
|
|
81
|
+
- [Required Params](#required-params)
|
|
82
|
+
- [Helper Methods](#helper-methods)
|
|
83
|
+
- [URL Helpers](#url-helpers)
|
|
84
|
+
- [Translations](#translations)
|
|
85
|
+
- [Tip: Capture User Preferences](#tip-capture-user-preferences)
|
|
86
|
+
- [Tip: Extracting Delivery Method Configurations](#tip-extracting-delivery-method-configurations)
|
|
87
|
+
- [Shared Delivery Method Options](#shared-delivery-method-options)
|
|
88
|
+
- [Sending Notifications](#sending-notifications)
|
|
89
|
+
- [Custom Noticed Model Methods](#custom-noticed-model-methods)
|
|
90
|
+
|
|
91
|
+
### Notifier Objects
|
|
72
92
|
|
|
73
93
|
Notifiers are essentially the controllers of the Noticed ecosystem and represent an Event. As such, we recommend naming them with the event they model in mind — be it a `NewSaleNotifier,` `ChargeFailureNotifier`, etc.
|
|
74
94
|
|
|
@@ -117,7 +137,84 @@ end
|
|
|
117
137
|
|
|
118
138
|
For deeper specifics on setting up the `:action_cable`, `:email`, and `:discord` (bulk) delivery methods, refer to their docs: [`action_cable`](docs/delivery_methods/action_cable.md), [`email`](docs/delivery_methods/email.md), and [`discord` (bulk)](docs/bulk_delivery_methods/discord.md).
|
|
119
139
|
|
|
120
|
-
|
|
140
|
+
#### Delivery Method Configuration
|
|
141
|
+
|
|
142
|
+
Each delivery method can be configured with a block that yields a `config` object.
|
|
143
|
+
|
|
144
|
+
Procs/Lambdas will be evaluated when needed and symbols can be used to call a method.
|
|
145
|
+
|
|
146
|
+
When a lambda is passed, it will not pass any arguments and evaluates the Proc in the context of the Noticed::Notification
|
|
147
|
+
|
|
148
|
+
If you are using a symbol to call a method, we pass the notification object as an argument to the method. This allows you to access the notification object within the method.
|
|
149
|
+
Your method must accept a single argument. If you don't need to use the object you can just use `(*)`.
|
|
150
|
+
|
|
151
|
+
<details>
|
|
152
|
+
<summary> Show Example </summary>
|
|
153
|
+
|
|
154
|
+
```ruby
|
|
155
|
+
class CommentNotifier < Noticed::Event
|
|
156
|
+
deliver_by :ios do |config|
|
|
157
|
+
config.format = :ios_format
|
|
158
|
+
config.apns_key = :ios_cert
|
|
159
|
+
config.key_id = :ios_key_id
|
|
160
|
+
config.team_id = :ios_team_id
|
|
161
|
+
config.bundle_identifier = Rails.application.credentials.dig(:ios, :bundle_identifier)
|
|
162
|
+
config.device_tokens = :ios_device_tokens
|
|
163
|
+
config.if = -> { recipient.send_ios_notification? }
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def ios_format(apn)
|
|
167
|
+
apn.alert = { title:, body: }
|
|
168
|
+
apn.mutable_content = true
|
|
169
|
+
apn.content_available = true
|
|
170
|
+
apn.sound = "notification.m4r"
|
|
171
|
+
apn.custom_payload = {
|
|
172
|
+
url:,
|
|
173
|
+
type: self.class.name,
|
|
174
|
+
id: record.id,
|
|
175
|
+
image_url: "" || image_url,
|
|
176
|
+
params: params.to_json
|
|
177
|
+
}
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def ios_cert(*)
|
|
181
|
+
Rails.application.credentials.dig(:ios, Rails.env.to_sym, :apns_token_cert)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def ios_key_id(*)
|
|
185
|
+
Rails.application.credentials.dig(:ios, Rails.env.to_sym, :key_id)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def ios_team_id(*)
|
|
189
|
+
Rails.application.credentials.dig(:ios, Rails.env.to_sym, :team_id)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def ios_bundle_id(*)
|
|
193
|
+
Rails.application.credentials.dig(:ios, Rails.env.to_sym, :bundle_identifier)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def ios_device_tokens(notification)
|
|
197
|
+
notification.recipient.ios_device_tokens
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def url
|
|
201
|
+
comment_thread_path(record.thread)
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
class Recipent < ApplicationRecord # or whatever your recipient model is
|
|
206
|
+
has_many :ios_device_tokens
|
|
207
|
+
|
|
208
|
+
def send_ios_notification?
|
|
209
|
+
# some logic
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
```
|
|
213
|
+
</details>
|
|
214
|
+
|
|
215
|
+
More examples are in the docs for each delivery method.
|
|
216
|
+
|
|
217
|
+
#### Required Params
|
|
121
218
|
|
|
122
219
|
While explicit / required parameters are completely optional, Notifiers are able to opt in to required parameters via the `required_params` method:
|
|
123
220
|
|
|
@@ -126,7 +223,10 @@ class CarSaleNotifier < Noticed::Event
|
|
|
126
223
|
deliver_by :email { |c| c.mailer = "BranchMailer" }
|
|
127
224
|
|
|
128
225
|
# `record` is the Car record, `Branch` is the dealership
|
|
129
|
-
required_params :
|
|
226
|
+
required_params :branch
|
|
227
|
+
|
|
228
|
+
# To validate the `:record` param, add a validation since it is an association on the Noticed::Event
|
|
229
|
+
validates :record, presence: true
|
|
130
230
|
end
|
|
131
231
|
```
|
|
132
232
|
|
|
@@ -140,9 +240,7 @@ CarSaleNotifier.with(record: Car.last, branch: Branch.last).deliver(Branch.hq)
|
|
|
140
240
|
#=> OK
|
|
141
241
|
```
|
|
142
242
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
##### Helper Methods
|
|
243
|
+
#### Helper Methods
|
|
146
244
|
|
|
147
245
|
Notifiers can implement various helper methods, within a `notification_methods` block, that make it easier to render the resulting notification directly. These helpers can be helpful depending on where and how you choose to render notifications. A common use is rendering a user’s notifications in your web UI as standard ERB. These notification helper methods make that rendering much simpler:
|
|
148
246
|
|
|
@@ -156,7 +254,7 @@ Notifiers can implement various helper methods, within a `notification_methods`
|
|
|
156
254
|
|
|
157
255
|
On the other hand, if you’re using email delivery, ActionMailer has its own full stack for setting up objects and rendering. Your notification helper methods will always be available from the notification object, but using ActionMailer’s own paradigms may fit better for that particular delivery method. YMMV.
|
|
158
256
|
|
|
159
|
-
|
|
257
|
+
#### URL Helpers
|
|
160
258
|
|
|
161
259
|
Rails url helpers are included in Notifiers by default so you have full access to them in your notification helper methods, just like you would in your controllers and views.
|
|
162
260
|
|
|
@@ -166,7 +264,7 @@ _But don't forget_, you'll need to configure `default_url_options` in order for
|
|
|
166
264
|
Rails.application.routes.default_url_options[:host] = 'localhost:3000'
|
|
167
265
|
```
|
|
168
266
|
|
|
169
|
-
|
|
267
|
+
#### Translations
|
|
170
268
|
|
|
171
269
|
We've also included Rails’ `translate` and `t` helpers for you to use in your notification helper methods. This also provides an easy way of scoping translations. If the key starts with a period, it will automatically scope the key under `notifiers`, the underscored name of the notifier class, and `notification`. For example:
|
|
172
270
|
|
|
@@ -175,13 +273,13 @@ From the above Notifier...
|
|
|
175
273
|
```ruby
|
|
176
274
|
class NewCommentNotifier < Noticed::Event
|
|
177
275
|
# ...
|
|
178
|
-
|
|
276
|
+
|
|
179
277
|
notification_methods do
|
|
180
278
|
def message
|
|
181
279
|
t(".message")
|
|
182
280
|
end
|
|
183
281
|
end
|
|
184
|
-
|
|
282
|
+
|
|
185
283
|
# ...
|
|
186
284
|
end
|
|
187
285
|
```
|
|
@@ -206,7 +304,7 @@ en:
|
|
|
206
304
|
|
|
207
305
|
Or, if you have your Notifier within another module, such as `Admin::NewCommentNotifier`, the resulting lookup path will be `en.notifiers.admin.new_comment_notifier.notification.message` (modules become nesting steps).
|
|
208
306
|
|
|
209
|
-
|
|
307
|
+
#### Tip: Capture User Preferences
|
|
210
308
|
|
|
211
309
|
You can use the `if:` and `unless: ` options on your delivery methods to check the user's preferences and skip processing if they have disabled that type of notification.
|
|
212
310
|
|
|
@@ -221,8 +319,42 @@ class CommentNotifier < Noticed::Event
|
|
|
221
319
|
end
|
|
222
320
|
end
|
|
223
321
|
```
|
|
322
|
+
#### Tip: Extracting Delivery Method Configurations
|
|
323
|
+
|
|
324
|
+
If you want to reuse delivery method configurations across multiple Notifiers, you can extract them into a module and include them in your Notifiers.
|
|
325
|
+
|
|
326
|
+
```ruby
|
|
327
|
+
# /app/notifiers/notifiers/comment_notifier.rb
|
|
328
|
+
class CommentNotifier < Noticed::Event
|
|
329
|
+
include IosNotifier
|
|
330
|
+
include AndriodNotifer
|
|
331
|
+
include EmailNotifier
|
|
332
|
+
|
|
333
|
+
validates :record, presence: true
|
|
334
|
+
end
|
|
224
335
|
|
|
225
|
-
|
|
336
|
+
# /app/notifiers/concerns/ios_notifier.rb
|
|
337
|
+
module IosNotifier
|
|
338
|
+
extend ActiveSupport::Concern
|
|
339
|
+
|
|
340
|
+
included do
|
|
341
|
+
deliver_by :ios do |config|
|
|
342
|
+
config.device_tokens = ->(recipient) { recipient.notification_tokens.where(platform: :iOS).pluck(:token) }
|
|
343
|
+
config.format = ->(apn) {
|
|
344
|
+
apn.alert = "Hello world"
|
|
345
|
+
apn.custom_payload = {url: root_url(host: "example.org")}
|
|
346
|
+
}
|
|
347
|
+
config.bundle_identifier = Rails.application.credentials.dig(:ios, :bundle_id)
|
|
348
|
+
config.key_id = Rails.application.credentials.dig(:ios, :key_id)
|
|
349
|
+
config.team_id = Rails.application.credentials.dig(:ios, :team_id)
|
|
350
|
+
config.apns_key = Rails.application.credentials.dig(:ios, :apns_key)
|
|
351
|
+
config.if = ->(recipient) { recipient.ios_notifications? }
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
#### Shared Delivery Method Options
|
|
226
358
|
|
|
227
359
|
Each of these options are available for every delivery method (individual or bulk). The value passed may be a lambda, a symbol that represents a callable method, a symbol value, or a string value.
|
|
228
360
|
|
|
@@ -232,7 +364,7 @@ Each of these options are available for every delivery method (individual or bul
|
|
|
232
364
|
* `config.wait_until` — (Should yield a specific time object) Delays the job that runs this delivery method until the specific time specified
|
|
233
365
|
* `config.queue` — Sets the ActiveJob queue name to be used for the job that runs this delivery method
|
|
234
366
|
|
|
235
|
-
|
|
367
|
+
### 📨 Sending Notifications
|
|
236
368
|
|
|
237
369
|
Following the `NewCommentNotifier` example above, here’s how we might invoke the Notifier to send notifications to every author in the thread about a new comment being added:
|
|
238
370
|
|
|
@@ -253,6 +385,51 @@ This invocation will create a single `Noticed::Event` record and a `Noticed::Not
|
|
|
253
385
|
- An individual delivery job for `:email` method to the second thread author
|
|
254
386
|
- Etc...
|
|
255
387
|
|
|
388
|
+
### Custom Noticed Model Methods
|
|
389
|
+
|
|
390
|
+
In order to extend the Noticed models you'll need to use a concern an a to_prepare block:
|
|
391
|
+
|
|
392
|
+
```ruby
|
|
393
|
+
# config/initializers/noticed.rb
|
|
394
|
+
module NotificationExtensions
|
|
395
|
+
extend ActiveSupport::Concern
|
|
396
|
+
|
|
397
|
+
included do
|
|
398
|
+
belongs_to :organisation
|
|
399
|
+
|
|
400
|
+
scope :filter_by_type, ->(type) { where(type:) }
|
|
401
|
+
scope :exclude_type, ->(type) { where.not(type:) }
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
# You can also add instance methods here
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
Rails.application.config.to_prepare do
|
|
408
|
+
# You can extend Noticed::Event or Noticed::Notification here
|
|
409
|
+
Noticed::Event.include EventExtensions
|
|
410
|
+
Noticed::Notification.include NotificationExtensions
|
|
411
|
+
end
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
The `NotificationExtensions` class could be separated into it's own file and live somewhere like `app/models/concerns/notification_extensions.rb`.
|
|
415
|
+
|
|
416
|
+
If you do this, the `to_prepare` block will need to be in `application.rb` instead of an initializer.
|
|
417
|
+
|
|
418
|
+
```ruby
|
|
419
|
+
# config/application.rb
|
|
420
|
+
module MyApp
|
|
421
|
+
class Application < Rails::Application
|
|
422
|
+
|
|
423
|
+
# ...
|
|
424
|
+
|
|
425
|
+
config.to_prepare do
|
|
426
|
+
Noticed::Event.include Noticed::EventExtensions
|
|
427
|
+
Noticed::Notification.include Noticed::NotificationExtensions
|
|
428
|
+
end
|
|
429
|
+
end
|
|
430
|
+
end
|
|
431
|
+
```
|
|
432
|
+
|
|
256
433
|
## ✅ Best Practices
|
|
257
434
|
|
|
258
435
|
### Renaming Notifiers
|
|
@@ -337,7 +514,7 @@ If you want to build your own delivery method to deliver notifications to a spec
|
|
|
337
514
|
This will generate a new `DeliveryMethods::Discord` class inside the `app/notifiers/delivery_methods` folder, which can be used to deliver notifications to Discord.
|
|
338
515
|
|
|
339
516
|
```ruby
|
|
340
|
-
class DeliveryMethods::Discord <
|
|
517
|
+
class DeliveryMethods::Discord < ApplicationDeliveryMethod
|
|
341
518
|
# Specify the config options your delivery method requires in its config block
|
|
342
519
|
required_options # :foo, :bar
|
|
343
520
|
|
|
@@ -356,6 +533,63 @@ class MyNotifier < Noticed::Event
|
|
|
356
533
|
end
|
|
357
534
|
```
|
|
358
535
|
|
|
536
|
+
<details>
|
|
537
|
+
<summary>Turbo Stream Custom Delivery Method Example</summary>
|
|
538
|
+
|
|
539
|
+
A common custom delivery method in the Rails world might be to Delivery to the web via turbo stream.
|
|
540
|
+
|
|
541
|
+
Note: This example users custom methods that extend the `Noticed::Notification` class.
|
|
542
|
+
|
|
543
|
+
See the [Custom Noticed Model Methods](#custom-noticed-model-methods) section for more information.
|
|
544
|
+
|
|
545
|
+
```ruby
|
|
546
|
+
# app/notifiers/delivery_methods/turbo_stream.rb
|
|
547
|
+
class DeliveryMethods::TurboStream < ApplicationDeliveryMethod
|
|
548
|
+
def deliver
|
|
549
|
+
return unless recipient.is_a?(User)
|
|
550
|
+
|
|
551
|
+
notification.broadcast_update_to_bell
|
|
552
|
+
notification.broadcast_replace_to_index_count
|
|
553
|
+
notification.broadcast_prepend_to_index_list
|
|
554
|
+
end
|
|
555
|
+
end
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
```ruby
|
|
559
|
+
# app/models/concerns/noticed/notification_extensions.rb
|
|
560
|
+
module Noticed::NotificationExtensions
|
|
561
|
+
extend ActiveSupport::Concern
|
|
562
|
+
|
|
563
|
+
def broadcast_update_to_bell
|
|
564
|
+
broadcast_update_to(
|
|
565
|
+
"notifications_#{recipient.id}",
|
|
566
|
+
target: "notification_bell",
|
|
567
|
+
partial: "navbar/notifications/bell",
|
|
568
|
+
locals: { user: recipient }
|
|
569
|
+
)
|
|
570
|
+
end
|
|
571
|
+
|
|
572
|
+
def broadcast_replace_to_index_count
|
|
573
|
+
broadcast_replace_to(
|
|
574
|
+
"notifications_index_#{recipient.id}",
|
|
575
|
+
target: "notification_index_count",
|
|
576
|
+
partial: "notifications/notifications_count",
|
|
577
|
+
locals: { count: recipient.reload.notifications_count, unread: recipient.reload.unread_notifications_count }
|
|
578
|
+
)
|
|
579
|
+
end
|
|
580
|
+
|
|
581
|
+
def broadcast_prepend_to_index_list
|
|
582
|
+
broadcast_prepend_to(
|
|
583
|
+
"notifications_index_list_#{recipient.id}",
|
|
584
|
+
target: "notifications",
|
|
585
|
+
partial: "notifications/notification",
|
|
586
|
+
locals: { notification: self }
|
|
587
|
+
)
|
|
588
|
+
end
|
|
589
|
+
end
|
|
590
|
+
```
|
|
591
|
+
</details>
|
|
592
|
+
|
|
359
593
|
Delivery methods have access to the following methods and attributes:
|
|
360
594
|
|
|
361
595
|
* `event` — The `Noticed::Event` record that spawned the notification object currently being delivered
|
|
@@ -400,13 +634,25 @@ class DeliveryMethods::WhatsApp < Noticed::DeliveryMethod
|
|
|
400
634
|
end
|
|
401
635
|
```
|
|
402
636
|
|
|
403
|
-
|
|
637
|
+
#### Callbacks
|
|
638
|
+
|
|
639
|
+
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.
|
|
640
|
+
|
|
641
|
+
```ruby
|
|
642
|
+
class DeliveryMethods::Discord < Noticed::DeliveryMethod
|
|
643
|
+
after_deliver do
|
|
644
|
+
# Do whatever you want
|
|
645
|
+
end
|
|
646
|
+
end
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
## 📦 Database Model
|
|
404
650
|
|
|
405
651
|
The Noticed database models include several helpful features to make working with notifications easier.
|
|
406
652
|
|
|
407
|
-
|
|
653
|
+
### Notification
|
|
408
654
|
|
|
409
|
-
|
|
655
|
+
#### Class methods/scopes
|
|
410
656
|
|
|
411
657
|
(Assuming your user `has_many :notifications, as: :recipient, class_name: "Noticed::Notification"`)
|
|
412
658
|
|
|
@@ -423,12 +669,11 @@ user.notifications.read
|
|
|
423
669
|
user.notifications.unread
|
|
424
670
|
```
|
|
425
671
|
|
|
426
|
-
|
|
427
672
|
Marking all notifications as read or unread:
|
|
428
673
|
|
|
429
674
|
```ruby
|
|
430
|
-
user.notifications.mark_as_read
|
|
431
|
-
user.notifications.mark_as_unread
|
|
675
|
+
user.notifications.mark_as_read
|
|
676
|
+
user.notifications.mark_as_unread
|
|
432
677
|
```
|
|
433
678
|
|
|
434
679
|
#### Instance methods
|
|
@@ -478,10 +723,19 @@ end
|
|
|
478
723
|
|
|
479
724
|
class Post < ApplicationRecord
|
|
480
725
|
has_many :noticed_events, as: :record, dependent: :destroy, class_name: "Noticed::Event"
|
|
726
|
+
has_many :notifications, through: :noticed_events, class_name: "Noticed::Notification"
|
|
481
727
|
end
|
|
482
728
|
|
|
483
729
|
# All of the notification events this post generated
|
|
484
|
-
# @post.
|
|
730
|
+
# @post.notifications
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
#### ActiveJob Parent Class
|
|
734
|
+
|
|
735
|
+
Noticed uses its own `Noticed::ApplicationJob` as the base job for all notifications. In the event that you would like to customize the parent job class, there is a `parent_class` attribute that can be overridden with your own class. This should be done in a `noticed.rb` initializer.
|
|
736
|
+
|
|
737
|
+
```ruby
|
|
738
|
+
Noticed.parent_class = "ApplicationJob"
|
|
485
739
|
```
|
|
486
740
|
|
|
487
741
|
#### Handling Deleted Records
|
|
@@ -494,6 +748,48 @@ class ApplicationJob < ActiveJob::Base
|
|
|
494
748
|
end
|
|
495
749
|
```
|
|
496
750
|
|
|
751
|
+
### Customizing the Database Models
|
|
752
|
+
|
|
753
|
+
You can modify the database models by editing the generated migrations.
|
|
754
|
+
|
|
755
|
+
One common adjustment is to change the IDs to UUIDs (if you're using UUIDs in your app).
|
|
756
|
+
|
|
757
|
+
You can also add additional columns to the `Noticed::Event` and `Noticed::Notification` models.
|
|
758
|
+
|
|
759
|
+
```ruby
|
|
760
|
+
# This migration comes from noticed (originally 20231215190233)
|
|
761
|
+
class CreateNoticedTables < ActiveRecord::Migration[7.1]
|
|
762
|
+
def change
|
|
763
|
+
create_table :noticed_events, id: :uuid do |t|
|
|
764
|
+
t.string :type
|
|
765
|
+
t.belongs_to :record, polymorphic: true, type: :uuid
|
|
766
|
+
t.jsonb :params
|
|
767
|
+
|
|
768
|
+
# Custom Fields
|
|
769
|
+
t.string :organisation_id, type: :uuid, as: "((params ->> 'organisation_id')::uuid)", stored: true
|
|
770
|
+
t.virtual :action_type, type: :string, as: "((params ->> 'action_type'))", stored: true
|
|
771
|
+
t.virtual :url, type: :string, as: "((params ->> 'url'))", stored: true
|
|
772
|
+
|
|
773
|
+
t.timestamps
|
|
774
|
+
end
|
|
775
|
+
|
|
776
|
+
create_table :noticed_notifications, id: :uuid do |t|
|
|
777
|
+
t.string :type
|
|
778
|
+
t.belongs_to :event, null: false, type: :uuid
|
|
779
|
+
t.belongs_to :recipient, polymorphic: true, null: false, type: :uuid
|
|
780
|
+
t.datetime :read_at
|
|
781
|
+
t.datetime :seen_at
|
|
782
|
+
|
|
783
|
+
t.timestamps
|
|
784
|
+
end
|
|
785
|
+
|
|
786
|
+
add_index :noticed_notifications, :read_at
|
|
787
|
+
end
|
|
788
|
+
end
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
The custom fields in the above example are stored as virtual columns. These are populated from values passed in the `params` hash when creating the notifier.
|
|
792
|
+
|
|
497
793
|
## 🙏 Contributing
|
|
498
794
|
|
|
499
795
|
This project uses [Standard](https://github.com/testdouble/standard) for formatting Ruby code. Please make sure to run `standardrb` before submitting pull requests.
|
|
@@ -508,4 +804,3 @@ DATABASE_URL=postgres://127.0.0.1/noticed_test rails test
|
|
|
508
804
|
|
|
509
805
|
## 📝 License
|
|
510
806
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
511
|
-
|
|
@@ -9,10 +9,13 @@ module Noticed
|
|
|
9
9
|
|
|
10
10
|
attribute :params, default: {}
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
# Ephemeral notifiers cannot serialize params since they aren't ActiveRecord backed
|
|
13
|
+
if respond_to? :serialize
|
|
14
|
+
if Rails.gem_version >= Gem::Version.new("7.1.0.alpha")
|
|
15
|
+
serialize :params, coder: Coder
|
|
16
|
+
else
|
|
17
|
+
serialize :params, Coder
|
|
18
|
+
end
|
|
16
19
|
end
|
|
17
20
|
end
|
|
18
21
|
|
|
@@ -35,6 +38,13 @@ module Noticed
|
|
|
35
38
|
def deliver_by(name, options = {})
|
|
36
39
|
raise NameError, "#{name} has already been used for this Notifier." if delivery_methods.has_key?(name)
|
|
37
40
|
|
|
41
|
+
if name == :database
|
|
42
|
+
Noticed.deprecator.warn <<-WARNING.squish
|
|
43
|
+
The :database delivery method has been deprecated and does nothing. Notifiers automatically save to the database now.
|
|
44
|
+
WARNING
|
|
45
|
+
return
|
|
46
|
+
end
|
|
47
|
+
|
|
38
48
|
config = ActiveSupport::OrderedOptions.new.merge(options)
|
|
39
49
|
yield config if block_given?
|
|
40
50
|
delivery_methods[name] = DeliverBy.new(name, config)
|
|
@@ -45,14 +55,29 @@ module Noticed
|
|
|
45
55
|
end
|
|
46
56
|
alias_method :required_param, :required_params
|
|
47
57
|
|
|
58
|
+
def params(*names)
|
|
59
|
+
Noticed.deprecator.warn <<-WARNING.squish
|
|
60
|
+
`params` is deprecated and has been renamed to `required_params`
|
|
61
|
+
WARNING
|
|
62
|
+
required_params(*names)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def param(*names)
|
|
66
|
+
Noticed.deprecator.warn <<-WARNING.squish
|
|
67
|
+
`param :name` is deprecated and has been renamed to `required_param :name`
|
|
68
|
+
WARNING
|
|
69
|
+
required_params(*names)
|
|
70
|
+
end
|
|
71
|
+
|
|
48
72
|
def with(params)
|
|
49
73
|
record = params.delete(:record)
|
|
50
74
|
new(params: params, record: record)
|
|
51
75
|
end
|
|
52
76
|
|
|
53
|
-
def deliver(recipients = nil, options
|
|
54
|
-
new.deliver(recipients, options)
|
|
77
|
+
def deliver(recipients = nil, **options)
|
|
78
|
+
new.deliver(recipients, **options)
|
|
55
79
|
end
|
|
80
|
+
alias_method :deliver_later, :deliver
|
|
56
81
|
end
|
|
57
82
|
|
|
58
83
|
# CommentNotifier.deliver(User.all)
|
|
@@ -60,16 +85,17 @@ module Noticed
|
|
|
60
85
|
# CommentNotifier.deliver(User.all, queue: :low_priority)
|
|
61
86
|
# CommentNotifier.deliver(User.all, wait: 5.minutes)
|
|
62
87
|
# CommentNotifier.deliver(User.all, wait_until: 1.hour.from_now)
|
|
63
|
-
def deliver(recipients = nil,
|
|
88
|
+
def deliver(recipients = nil, enqueue_job: true, **options)
|
|
64
89
|
validate!
|
|
65
90
|
|
|
66
91
|
transaction do
|
|
67
|
-
save!
|
|
68
|
-
|
|
69
92
|
recipients_attributes = Array.wrap(recipients).map do |recipient|
|
|
70
93
|
recipient_attributes_for(recipient)
|
|
71
94
|
end
|
|
72
95
|
|
|
96
|
+
self.notifications_count = recipients_attributes.size
|
|
97
|
+
save!
|
|
98
|
+
|
|
73
99
|
if Rails.gem_version >= Gem::Version.new("7.0.0.alpha1")
|
|
74
100
|
notifications.insert_all!(recipients_attributes, record_timestamps: true) if recipients_attributes.any?
|
|
75
101
|
else
|
|
@@ -83,15 +109,16 @@ module Noticed
|
|
|
83
109
|
end
|
|
84
110
|
|
|
85
111
|
# Enqueue delivery job
|
|
86
|
-
EventJob.set(options).perform_later(self)
|
|
112
|
+
EventJob.set(options).perform_later(self) if enqueue_job
|
|
87
113
|
|
|
88
114
|
self
|
|
89
115
|
end
|
|
116
|
+
alias_method :deliver_later, :deliver
|
|
90
117
|
|
|
91
118
|
def recipient_attributes_for(recipient)
|
|
92
119
|
{
|
|
93
120
|
type: "#{self.class.name}::Notification",
|
|
94
|
-
recipient_type: recipient.class.name,
|
|
121
|
+
recipient_type: recipient.class.base_class.name,
|
|
95
122
|
recipient_id: recipient.id
|
|
96
123
|
}
|
|
97
124
|
end
|
|
@@ -103,7 +130,7 @@ module Noticed
|
|
|
103
130
|
|
|
104
131
|
def validate_params!
|
|
105
132
|
required_param_names.each do |param_name|
|
|
106
|
-
raise ValidationError, "Param `#{param_name}` is required for #{self.class.name}." unless params
|
|
133
|
+
raise ValidationError, "Param `#{param_name}` is required for #{self.class.name}." unless params.has_key?(param_name)
|
|
107
134
|
end
|
|
108
135
|
end
|
|
109
136
|
|
|
@@ -11,19 +11,19 @@ module Noticed
|
|
|
11
11
|
|
|
12
12
|
class_methods do
|
|
13
13
|
def mark_as_read
|
|
14
|
-
update_all(read_at: Time.current)
|
|
14
|
+
update_all(read_at: Time.current, updated_at: Time.current)
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def mark_as_unread
|
|
18
|
-
update_all(read_at: nil)
|
|
18
|
+
update_all(read_at: nil, updated_at: Time.current)
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def mark_as_seen
|
|
22
|
-
update_all(seen_at: Time.current)
|
|
22
|
+
update_all(seen_at: Time.current, updated_at: Time.current)
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def mark_as_unseen
|
|
26
|
-
update_all(seen_at: nil)
|
|
26
|
+
update_all(seen_at: nil, updated_at: Time.current)
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
29
|
|
|
@@ -19,14 +19,23 @@ module Noticed
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def perform_later(event_or_notification, options = {})
|
|
22
|
-
options[:wait]
|
|
23
|
-
options[:wait_until]
|
|
24
|
-
options[:queue]
|
|
25
|
-
options[:priority]
|
|
22
|
+
options[:wait] ||= evaluate_option(:wait, event_or_notification) if config.has_key?(:wait)
|
|
23
|
+
options[:wait_until] ||= evaluate_option(:wait_until, event_or_notification) if config.has_key?(:wait_until)
|
|
24
|
+
options[:queue] ||= evaluate_option(:queue, event_or_notification) if config.has_key?(:queue)
|
|
25
|
+
options[:priority] ||= evaluate_option(:priority, event_or_notification) if config.has_key?(:priority)
|
|
26
26
|
|
|
27
27
|
constant.set(options).perform_later(name, event_or_notification)
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
def ephemeral_perform_later(notifier, recipient, params, options = {})
|
|
31
|
+
options[:wait] ||= evaluate_option(:wait, recipient) if config.has_key?(:wait)
|
|
32
|
+
options[:wait_until] ||= evaluate_option(:wait_until, recipient) if config.has_key?(:wait_until)
|
|
33
|
+
options[:queue] ||= evaluate_option(:queue, recipient) if config.has_key?(:queue)
|
|
34
|
+
options[:priority] ||= evaluate_option(:priority, recipient) if config.has_key?(:priority)
|
|
35
|
+
|
|
36
|
+
constant.set(options).perform_later(name, "#{notifier}::Notification", recipient: recipient, params: params)
|
|
37
|
+
end
|
|
38
|
+
|
|
30
39
|
def evaluate_option(name, context)
|
|
31
40
|
option = config[name]
|
|
32
41
|
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module Noticed
|
|
2
|
+
class Ephemeral
|
|
3
|
+
include ActiveModel::Model
|
|
4
|
+
include ActiveModel::Attributes
|
|
5
|
+
include Noticed::Deliverable
|
|
6
|
+
|
|
7
|
+
attribute :record
|
|
8
|
+
attribute :params, default: {}
|
|
9
|
+
|
|
10
|
+
class Notification
|
|
11
|
+
include ActiveModel::Model
|
|
12
|
+
include ActiveModel::Attributes
|
|
13
|
+
|
|
14
|
+
attribute :recipient
|
|
15
|
+
attribute :event
|
|
16
|
+
|
|
17
|
+
delegate :params, :record, to: :event
|
|
18
|
+
|
|
19
|
+
def self.new_with_params(recipient, params)
|
|
20
|
+
instance = new(recipient: recipient)
|
|
21
|
+
instance.event = module_parent.new(params: params)
|
|
22
|
+
instance
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Dynamically define Notification on each Ephemeral Notifier
|
|
27
|
+
def self.inherited(notifier)
|
|
28
|
+
super
|
|
29
|
+
notifier.const_set :Notification, Class.new(Noticed::Ephemeral::Notification)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def deliver(recipients)
|
|
33
|
+
recipients = Array.wrap(recipients)
|
|
34
|
+
bulk_delivery_methods.each do |_, deliver_by|
|
|
35
|
+
deliver_by.ephemeral_perform_later(self.class.name, recipients, params)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
recipients.each do |recipient|
|
|
39
|
+
delivery_methods.each do |_, deliver_by|
|
|
40
|
+
deliver_by.ephemeral_perform_later(self.class.name, recipient, params)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def record
|
|
46
|
+
params[:record]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def notification_methods(&block)
|
|
50
|
+
const_get(:Notification).class_eval(&block)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -12,6 +12,7 @@ module Noticed
|
|
|
12
12
|
desc "Generates a class for a custom delivery method with the given NAME."
|
|
13
13
|
|
|
14
14
|
def generate_notification
|
|
15
|
+
template "application_delivery_method.rb", "app/notifiers/application_delivery_method.rb"
|
|
15
16
|
template "delivery_method.rb", "app/notifiers/delivery_methods/#{singular_name}.rb"
|
|
16
17
|
end
|
|
17
18
|
end
|
|
@@ -7,9 +7,15 @@ module Noticed
|
|
|
7
7
|
|
|
8
8
|
attr_reader :config, :event
|
|
9
9
|
|
|
10
|
-
def perform(delivery_method_name, event)
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
def perform(delivery_method_name, event, recipients: nil, params: {}, overrides: {})
|
|
11
|
+
# Ephemeral notifications
|
|
12
|
+
if event.is_a? String
|
|
13
|
+
@event = @notification.event
|
|
14
|
+
@config = overrides
|
|
15
|
+
else
|
|
16
|
+
@event = event
|
|
17
|
+
@config = event.bulk_delivery_methods.fetch(delivery_method_name).config.merge(overrides)
|
|
18
|
+
end
|
|
13
19
|
|
|
14
20
|
return false if config.has_key?(:if) && !evaluate_option(:if)
|
|
15
21
|
return false if config.has_key?(:unless) && evaluate_option(:unless)
|
|
@@ -1,17 +1,26 @@
|
|
|
1
1
|
module Noticed
|
|
2
|
-
class DeliveryMethod <
|
|
2
|
+
class DeliveryMethod < Noticed.parent_class.constantize
|
|
3
3
|
include ApiClient
|
|
4
4
|
include RequiredOptions
|
|
5
5
|
|
|
6
|
+
extend ActiveModel::Callbacks
|
|
7
|
+
define_model_callbacks :deliver
|
|
8
|
+
|
|
6
9
|
class_attribute :logger, default: Rails.logger
|
|
7
10
|
|
|
8
11
|
attr_reader :config, :event, :notification
|
|
9
12
|
delegate :recipient, to: :notification
|
|
10
13
|
delegate :record, :params, to: :event
|
|
11
14
|
|
|
12
|
-
def perform(delivery_method_name, notification, overrides: {})
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
def perform(delivery_method_name, notification, recipient: nil, params: {}, overrides: {})
|
|
16
|
+
# Ephemeral notifications
|
|
17
|
+
if notification.is_a? String
|
|
18
|
+
@notification = notification.constantize.new_with_params(recipient, params)
|
|
19
|
+
@event = @notification.event
|
|
20
|
+
else
|
|
21
|
+
@notification = notification
|
|
22
|
+
@event = notification.event
|
|
23
|
+
end
|
|
15
24
|
|
|
16
25
|
# Look up config from Notifier and merge overrides
|
|
17
26
|
@config = event.delivery_methods.fetch(delivery_method_name).config.merge(overrides)
|
|
@@ -19,7 +28,9 @@ module Noticed
|
|
|
19
28
|
return false if config.has_key?(:if) && !evaluate_option(:if)
|
|
20
29
|
return false if config.has_key?(:unless) && evaluate_option(:unless)
|
|
21
30
|
|
|
22
|
-
deliver
|
|
31
|
+
run_callbacks :deliver do
|
|
32
|
+
deliver
|
|
33
|
+
end
|
|
23
34
|
end
|
|
24
35
|
|
|
25
36
|
def deliver
|
|
@@ -14,7 +14,7 @@ module Noticed
|
|
|
14
14
|
def send_notification(device_token)
|
|
15
15
|
post_request("https://fcm.googleapis.com/v1/projects/#{credentials[:project_id]}/messages:send",
|
|
16
16
|
headers: {authorization: "Bearer #{access_token}"},
|
|
17
|
-
json:
|
|
17
|
+
json: format_notification(device_token))
|
|
18
18
|
rescue Noticed::ResponseUnsuccessful => exception
|
|
19
19
|
if exception.response.code == "404" && config[:invalid_token]
|
|
20
20
|
notification.instance_exec(device_token, &config[:invalid_token])
|
|
@@ -23,6 +23,15 @@ module Noticed
|
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
+
def format_notification(device_token)
|
|
27
|
+
method = config[:json]
|
|
28
|
+
if method.is_a?(Symbol) && event.respond_to?(method)
|
|
29
|
+
event.send(method, device_token)
|
|
30
|
+
else
|
|
31
|
+
notification.instance_exec(device_token, &method)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
26
35
|
def credentials
|
|
27
36
|
@credentials ||= begin
|
|
28
37
|
value = evaluate_option(:credentials)
|
|
@@ -32,7 +32,12 @@ module Noticed
|
|
|
32
32
|
def format_notification(apn)
|
|
33
33
|
apn.topic = evaluate_option(:bundle_identifier)
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
method = config[:format]
|
|
36
|
+
# Call method on Notifier if defined
|
|
37
|
+
if method&.is_a?(Symbol) && event.respond_to?(method)
|
|
38
|
+
event.send(method, apn)
|
|
39
|
+
# If Proc, evaluate it on the Notification
|
|
40
|
+
elsif method&.respond_to?(:call)
|
|
36
41
|
notification.instance_exec(apn, &method)
|
|
37
42
|
elsif notification.params.try(:has_key?, :message)
|
|
38
43
|
apn.alert = notification.params[:message]
|
|
@@ -70,9 +75,9 @@ module Noticed
|
|
|
70
75
|
def connection_pool_options
|
|
71
76
|
{
|
|
72
77
|
auth_method: :token,
|
|
73
|
-
cert_path: StringIO.new(
|
|
74
|
-
key_id:
|
|
75
|
-
team_id:
|
|
78
|
+
cert_path: StringIO.new(evaluate_option(:apns_key)),
|
|
79
|
+
key_id: evaluate_option(:key_id),
|
|
80
|
+
team_id: evaluate_option(:team_id)
|
|
76
81
|
}
|
|
77
82
|
end
|
|
78
83
|
|
data/lib/noticed/engine.rb
CHANGED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module Noticed
|
|
2
|
+
module HasNotifications
|
|
3
|
+
# Defines a method for the association and a before_destroy 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
|
+
define_method :"notifications_as_#{param_name}" do
|
|
19
|
+
model = options.fetch(:model_name, "Noticed::Event").constantize
|
|
20
|
+
case current_adapter
|
|
21
|
+
when "postgresql", "postgis"
|
|
22
|
+
model.where("params @> ?", Noticed::Coder.dump(param_name.to_sym => self).to_json)
|
|
23
|
+
when "mysql2"
|
|
24
|
+
model.where("JSON_CONTAINS(params, ?)", Noticed::Coder.dump(param_name.to_sym => self).to_json)
|
|
25
|
+
when "sqlite3"
|
|
26
|
+
model.where("json_extract(params, ?) = ?", "$.#{param_name}", Noticed::Coder.dump(self).to_json)
|
|
27
|
+
else
|
|
28
|
+
# This will perform an exact match which isn't ideal
|
|
29
|
+
model.where(params: {param_name.to_sym => self})
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
if options.fetch(:destroy, true)
|
|
34
|
+
before_destroy do
|
|
35
|
+
send(:"notifications_as_#{param_name}").destroy_all
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def current_adapter
|
|
42
|
+
if ActiveRecord::Base.respond_to?(:connection_db_config)
|
|
43
|
+
ActiveRecord::Base.connection_db_config.adapter
|
|
44
|
+
else
|
|
45
|
+
ActiveRecord::Base.connection_config[:adapter]
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
data/lib/noticed/version.rb
CHANGED
data/lib/noticed.rb
CHANGED
|
@@ -14,12 +14,14 @@ module Noticed
|
|
|
14
14
|
autoload :BulkDeliveryMethod, "noticed/bulk_delivery_method"
|
|
15
15
|
autoload :Coder, "noticed/coder"
|
|
16
16
|
autoload :DeliveryMethod, "noticed/delivery_method"
|
|
17
|
+
autoload :HasNotifications, "noticed/has_notifications"
|
|
17
18
|
autoload :RequiredOptions, "noticed/required_options"
|
|
18
19
|
autoload :Translation, "noticed/translation"
|
|
19
20
|
|
|
20
21
|
module BulkDeliveryMethods
|
|
21
22
|
autoload :Discord, "noticed/bulk_delivery_methods/discord"
|
|
22
23
|
autoload :Slack, "noticed/bulk_delivery_methods/slack"
|
|
24
|
+
autoload :Test, "noticed/bulk_delivery_methods/test"
|
|
23
25
|
autoload :Webhook, "noticed/bulk_delivery_methods/webhook"
|
|
24
26
|
end
|
|
25
27
|
|
|
@@ -39,6 +41,9 @@ module Noticed
|
|
|
39
41
|
autoload :Webhook, "noticed/delivery_methods/webhook"
|
|
40
42
|
end
|
|
41
43
|
|
|
44
|
+
mattr_accessor :parent_class
|
|
45
|
+
@@parent_class = "Noticed::ApplicationJob"
|
|
46
|
+
|
|
42
47
|
class ValidationError < StandardError
|
|
43
48
|
end
|
|
44
49
|
|
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: 2.0
|
|
4
|
+
version: 2.1.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: 2024-01-
|
|
11
|
+
date: 2024-01-29 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|
|
@@ -43,13 +43,17 @@ files:
|
|
|
43
43
|
- app/models/concerns/noticed/readable.rb
|
|
44
44
|
- app/models/noticed/application_record.rb
|
|
45
45
|
- app/models/noticed/deliverable/deliver_by.rb
|
|
46
|
+
- app/models/noticed/ephemeral.rb
|
|
46
47
|
- app/models/noticed/event.rb
|
|
47
48
|
- app/models/noticed/notification.rb
|
|
48
49
|
- db/migrate/20231215190233_create_noticed_tables.rb
|
|
50
|
+
- db/migrate/20240129184740_add_notifications_count_to_noticed_event.rb
|
|
49
51
|
- lib/generators/noticed/delivery_method_generator.rb
|
|
50
52
|
- lib/generators/noticed/install_generator.rb
|
|
51
53
|
- lib/generators/noticed/notifier_generator.rb
|
|
52
54
|
- lib/generators/noticed/templates/README
|
|
55
|
+
- lib/generators/noticed/templates/application_delivery_method.rb.tt
|
|
56
|
+
- lib/generators/noticed/templates/application_notifier.rb.tt
|
|
53
57
|
- lib/generators/noticed/templates/delivery_method.rb.tt
|
|
54
58
|
- lib/generators/noticed/templates/notifier.rb.tt
|
|
55
59
|
- lib/noticed.rb
|
|
@@ -57,6 +61,7 @@ files:
|
|
|
57
61
|
- lib/noticed/bulk_delivery_method.rb
|
|
58
62
|
- lib/noticed/bulk_delivery_methods/discord.rb
|
|
59
63
|
- lib/noticed/bulk_delivery_methods/slack.rb
|
|
64
|
+
- lib/noticed/bulk_delivery_methods/test.rb
|
|
60
65
|
- lib/noticed/bulk_delivery_methods/webhook.rb
|
|
61
66
|
- lib/noticed/coder.rb
|
|
62
67
|
- lib/noticed/delivery_method.rb
|
|
@@ -72,6 +77,7 @@ files:
|
|
|
72
77
|
- lib/noticed/delivery_methods/vonage_sms.rb
|
|
73
78
|
- lib/noticed/delivery_methods/webhook.rb
|
|
74
79
|
- lib/noticed/engine.rb
|
|
80
|
+
- lib/noticed/has_notifications.rb
|
|
75
81
|
- lib/noticed/required_options.rb
|
|
76
82
|
- lib/noticed/translation.rb
|
|
77
83
|
- lib/noticed/version.rb
|
|
@@ -94,7 +100,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
94
100
|
- !ruby/object:Gem::Version
|
|
95
101
|
version: '0'
|
|
96
102
|
requirements: []
|
|
97
|
-
rubygems_version: 3.5.
|
|
103
|
+
rubygems_version: 3.5.4
|
|
98
104
|
signing_key:
|
|
99
105
|
specification_version: 4
|
|
100
106
|
summary: Notifications for Ruby on Rails applications
|