noticed 2.0.6 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 69e1c606f6f2cdd2dbc826af26fb364fe32401a4ad5cefbbb1aba4cf2e92759f
4
- data.tar.gz: 47a3d8a64032e4190e6370020f12b25db30b0f48340c1805f443f1b9a1b6763c
3
+ metadata.gz: e64d58c0ef63c62be08a469e1ba9177d3a06641db4dadcaa0dbac3155cb0fee3
4
+ data.tar.gz: 50b3cc355e8d1a1cb228f2b5950dac4be5e505b021810fab0b9076a21b1719bd
5
5
  SHA512:
6
- metadata.gz: 1d0ee032b3467924e1726c2cf6658260d6c9fb2b81464769b21e4ea0c4692d7675ff74600964ff47ce63dbac980b4820808fb85e1ed4cfa4a4e6a740e3eb4df6
7
- data.tar.gz: 19b8bae7b3d0ae713a42fb446d1cea8e8add561f11bd46cc0b42fe832b5fa971a3e17f58c5b22c2f3acdbaa6eaaa0a8116f16ea6e10852acb1f9d03444b36720
6
+ metadata.gz: 92b9badbf82f4c5fb3cde41a88bae7cac8f0354e679f9c9b62b153d962de92e212207372264f87ce8f71289139cac462270e253e284fc1e1dfa1e1d2e34f58cd
7
+ data.tar.gz: 419eef948c15ee9fd46664619de5ca5365e6adc0cdf0563155181bd09223279dfc4fb6a2e396bd162c0896448700abaa4de76e4c168658bbcbf3c32dc2ef5c34
data/README.md CHANGED
@@ -1,24 +1,31 @@
1
1
  # Noticed
2
2
 
3
- ### 🎉 Notifications for your Ruby on Rails app.
3
+ ## 🎉 Notifications for your Ruby on Rails app.
4
4
 
5
5
  [![Build Status](https://github.com/excid3/noticed/workflows/Tests/badge.svg)](https://github.com/excid3/noticed/actions) [![Gem Version](https://badge.fury.io/rb/noticed.svg)](https://badge.fury.io/rb/noticed)
6
6
 
7
- **⚠️⚠️ Upgrading from V1? Read the [Upgrade Guide](https://github.com/excid3/noticed/blob/main/UPGRADE.md)!**
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: Where each recipient gets their own notification
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 - one notification for all recipients. This is useful for sending a notification to your Slack team, for example.
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
 
@@ -68,6 +75,19 @@ To start, generate a Notifier:
68
75
  rails generate noticed:notifier NewCommentNotifier
69
76
  ```
70
77
 
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
+
71
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.
@@ -123,9 +143,14 @@ Each delivery method can be configured with a block that yields a `config` objec
123
143
 
124
144
  Procs/Lambdas will be evaluated when needed and symbols can be used to call a method.
125
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
+
126
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.
127
149
  Your method must accept a single argument. If you don't need to use the object you can just use `(*)`.
128
150
 
151
+ <details>
152
+ <summary> Show Example </summary>
153
+
129
154
  ```ruby
130
155
  class CommentNotifier < Noticed::Event
131
156
  deliver_by :ios do |config|
@@ -135,7 +160,7 @@ class CommentNotifier < Noticed::Event
135
160
  config.team_id = :ios_team_id
136
161
  config.bundle_identifier = Rails.application.credentials.dig(:ios, :bundle_identifier)
137
162
  config.device_tokens = :ios_device_tokens
138
- config.if = ->(notification) { recipient.send_ios_notification? }
163
+ config.if = -> { recipient.send_ios_notification? }
139
164
  end
140
165
 
141
166
  def ios_format(apn)
@@ -172,18 +197,22 @@ class CommentNotifier < Noticed::Event
172
197
  notification.recipient.ios_device_tokens
173
198
  end
174
199
 
175
- def send_ios_notification?(notification)
176
- recipient = notification.recipient
177
- return false unless recipient.is_a?(User)
178
-
179
- recipient.send_notifications?
180
- end
181
-
182
200
  def url
183
201
  comment_thread_path(record.thread)
184
202
  end
185
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
186
212
  ```
213
+ </details>
214
+
215
+ More examples are in the docs for each delivery method.
187
216
 
188
217
  #### Required Params
189
218
 
@@ -211,7 +240,6 @@ CarSaleNotifier.with(record: Car.last, branch: Branch.last).deliver(Branch.hq)
211
240
  #=> OK
212
241
  ```
213
242
 
214
-
215
243
  #### Helper Methods
216
244
 
217
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:
@@ -226,7 +254,7 @@ Notifiers can implement various helper methods, within a `notification_methods`
226
254
 
227
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.
228
256
 
229
- #### URL Helpers
257
+ #### URL Helpers
230
258
 
231
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.
232
260
 
@@ -336,7 +364,7 @@ Each of these options are available for every delivery method (individual or bul
336
364
  * `config.wait_until` — (Should yield a specific time object) Delays the job that runs this delivery method until the specific time specified
337
365
  * `config.queue` — Sets the ActiveJob queue name to be used for the job that runs this delivery method
338
366
 
339
- ### Sending Notifications
367
+ ### 📨 Sending Notifications
340
368
 
341
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:
342
370
 
@@ -357,6 +385,51 @@ This invocation will create a single `Noticed::Event` record and a `Noticed::Not
357
385
  - An individual delivery job for `:email` method to the second thread author
358
386
  - Etc...
359
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
+
360
433
  ## ✅ Best Practices
361
434
 
362
435
  ### Renaming Notifiers
@@ -441,7 +514,7 @@ If you want to build your own delivery method to deliver notifications to a spec
441
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.
442
515
 
443
516
  ```ruby
444
- class DeliveryMethods::Discord < Noticed::DeliveryMethod
517
+ class DeliveryMethods::Discord < ApplicationDeliveryMethod
445
518
  # Specify the config options your delivery method requires in its config block
446
519
  required_options # :foo, :bar
447
520
 
@@ -460,6 +533,63 @@ class MyNotifier < Noticed::Event
460
533
  end
461
534
  ```
462
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
+
463
593
  Delivery methods have access to the following methods and attributes:
464
594
 
465
595
  * `event` — The `Noticed::Event` record that spawned the notification object currently being delivered
@@ -516,13 +646,13 @@ class DeliveryMethods::Discord < Noticed::DeliveryMethod
516
646
  end
517
647
  ```
518
648
 
519
- ### 📦 Database Model
649
+ ## 📦 Database Model
520
650
 
521
651
  The Noticed database models include several helpful features to make working with notifications easier.
522
652
 
523
- #### Notification
653
+ ### Notification
524
654
 
525
- ##### Class methods/scopes
655
+ #### Class methods/scopes
526
656
 
527
657
  (Assuming your user `has_many :notifications, as: :recipient, class_name: "Noticed::Notification"`)
528
658
 
@@ -539,7 +669,6 @@ user.notifications.read
539
669
  user.notifications.unread
540
670
  ```
541
671
 
542
-
543
672
  Marking all notifications as read or unread:
544
673
 
545
674
  ```ruby
@@ -594,10 +723,11 @@ end
594
723
 
595
724
  class Post < ApplicationRecord
596
725
  has_many :noticed_events, as: :record, dependent: :destroy, class_name: "Noticed::Event"
726
+ has_many :notifications, through: :noticed_events, class_name: "Noticed::Notification"
597
727
  end
598
728
 
599
729
  # All of the notification events this post generated
600
- # @post.noticed_events.each { |ne| ne.notifications... }
730
+ # @post.notifications
601
731
  ```
602
732
 
603
733
  #### ActiveJob Parent Class
@@ -618,6 +748,48 @@ class ApplicationJob < ActiveJob::Base
618
748
  end
619
749
  ```
620
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
+
621
793
  ## 🙏 Contributing
622
794
 
623
795
  This project uses [Standard](https://github.com/testdouble/standard) for formatting Ruby code. Please make sure to run `standardrb` before submitting pull requests.
@@ -9,10 +9,13 @@ module Noticed
9
9
 
10
10
  attribute :params, default: {}
11
11
 
12
- if Rails.gem_version >= Gem::Version.new("7.1.0.alpha")
13
- serialize :params, coder: Coder
14
- else
15
- serialize :params, Coder
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
 
@@ -71,8 +74,8 @@ module Noticed
71
74
  new(params: params, record: record)
72
75
  end
73
76
 
74
- def deliver(recipients = nil, options = {})
75
- new.deliver(recipients, options)
77
+ def deliver(recipients = nil, **options)
78
+ new.deliver(recipients, **options)
76
79
  end
77
80
  alias_method :deliver_later, :deliver
78
81
  end
@@ -82,16 +85,17 @@ module Noticed
82
85
  # CommentNotifier.deliver(User.all, queue: :low_priority)
83
86
  # CommentNotifier.deliver(User.all, wait: 5.minutes)
84
87
  # CommentNotifier.deliver(User.all, wait_until: 1.hour.from_now)
85
- def deliver(recipients = nil, options = {})
88
+ def deliver(recipients = nil, enqueue_job: true, **options)
86
89
  validate!
87
90
 
88
91
  transaction do
89
- save!
90
-
91
92
  recipients_attributes = Array.wrap(recipients).map do |recipient|
92
93
  recipient_attributes_for(recipient)
93
94
  end
94
95
 
96
+ self.notifications_count = recipients_attributes.size
97
+ save!
98
+
95
99
  if Rails.gem_version >= Gem::Version.new("7.0.0.alpha1")
96
100
  notifications.insert_all!(recipients_attributes, record_timestamps: true) if recipients_attributes.any?
97
101
  else
@@ -105,7 +109,7 @@ module Noticed
105
109
  end
106
110
 
107
111
  # Enqueue delivery job
108
- EventJob.set(options).perform_later(self)
112
+ EventJob.set(options).perform_later(self) if enqueue_job
109
113
 
110
114
  self
111
115
  end
@@ -19,14 +19,23 @@ module Noticed
19
19
  end
20
20
 
21
21
  def perform_later(event_or_notification, options = {})
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)
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
@@ -4,7 +4,7 @@ module Noticed
4
4
  include Readable
5
5
  include Translation
6
6
 
7
- belongs_to :event
7
+ belongs_to :event, counter_cache: true
8
8
  belongs_to :recipient, polymorphic: true
9
9
 
10
10
  scope :newest_first, -> { order(created_at: :desc) }
@@ -0,0 +1,5 @@
1
+ class AddNotificationsCountToNoticedEvent < ActiveRecord::Migration[7.1]
2
+ def change
3
+ add_column :noticed_events, :notifications_count, :integer
4
+ end
5
+ 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
- @event = event
12
- @config = event.bulk_delivery_methods.fetch(delivery_method_name).config
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)
@@ -0,0 +1,11 @@
1
+ module Noticed
2
+ module BulkDeliveryMethods
3
+ class Test < DeliveryMethod
4
+ class_attribute :delivered, default: []
5
+
6
+ def deliver
7
+ delivered << notification
8
+ end
9
+ end
10
+ end
11
+ end
@@ -12,9 +12,15 @@ module Noticed
12
12
  delegate :recipient, to: :notification
13
13
  delegate :record, :params, to: :event
14
14
 
15
- def perform(delivery_method_name, notification, overrides: {})
16
- @notification = notification
17
- @event = notification.event
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
18
24
 
19
25
  # Look up config from Notifier and merge overrides
20
26
  @config = event.delivery_methods.fetch(delivery_method_name).config.merge(overrides)
@@ -32,8 +32,12 @@ module Noticed
32
32
  def format_notification(apn)
33
33
  apn.topic = evaluate_option(:bundle_identifier)
34
34
 
35
- if (method = config[:format])
36
- method = event.send(method, apn) if method.is_a?(Symbol) && event.respond_to?(method)
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)
37
41
  notification.instance_exec(apn, &method)
38
42
  elsif notification.params.try(:has_key?, :message)
39
43
  apn.alert = notification.params[:message]
@@ -1,5 +1,11 @@
1
1
  module Noticed
2
2
  class Engine < ::Rails::Engine
3
3
  isolate_namespace Noticed
4
+
5
+ initializer "noticed.has_notifications" do
6
+ ActiveSupport.on_load(:active_record) do
7
+ include Noticed::HasNotifications
8
+ end
9
+ end
4
10
  end
5
11
  end
@@ -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
@@ -1,5 +1,5 @@
1
1
  module Noticed
2
- class NotificationChannel < ::ApplicationCable::Channel
2
+ class NotificationChannel < ApplicationCable::Channel
3
3
  def subscribed
4
4
  stream_for current_user
5
5
  end
@@ -1,3 +1,3 @@
1
1
  module Noticed
2
- VERSION = "2.0.6"
2
+ VERSION = "2.1.1"
3
3
  end
data/lib/noticed.rb CHANGED
@@ -14,12 +14,15 @@ 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"
18
+ autoload :NotificationChannel, "noticed/notification_channel"
17
19
  autoload :RequiredOptions, "noticed/required_options"
18
20
  autoload :Translation, "noticed/translation"
19
21
 
20
22
  module BulkDeliveryMethods
21
23
  autoload :Discord, "noticed/bulk_delivery_methods/discord"
22
24
  autoload :Slack, "noticed/bulk_delivery_methods/slack"
25
+ autoload :Test, "noticed/bulk_delivery_methods/test"
23
26
  autoload :Webhook, "noticed/bulk_delivery_methods/webhook"
24
27
  end
25
28
 
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.6
4
+ version: 2.1.1
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-26 00:00:00.000000000 Z
11
+ date: 2024-01-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -35,7 +35,6 @@ files:
35
35
  - MIT-LICENSE
36
36
  - README.md
37
37
  - Rakefile
38
- - app/channels/noticed/notification_channel.rb
39
38
  - app/jobs/noticed/application_job.rb
40
39
  - app/jobs/noticed/event_job.rb
41
40
  - app/models/concerns/noticed/deliverable.rb
@@ -43,9 +42,11 @@ files:
43
42
  - app/models/concerns/noticed/readable.rb
44
43
  - app/models/noticed/application_record.rb
45
44
  - app/models/noticed/deliverable/deliver_by.rb
45
+ - app/models/noticed/ephemeral.rb
46
46
  - app/models/noticed/event.rb
47
47
  - app/models/noticed/notification.rb
48
48
  - db/migrate/20231215190233_create_noticed_tables.rb
49
+ - db/migrate/20240129184740_add_notifications_count_to_noticed_event.rb
49
50
  - lib/generators/noticed/delivery_method_generator.rb
50
51
  - lib/generators/noticed/install_generator.rb
51
52
  - lib/generators/noticed/notifier_generator.rb
@@ -59,6 +60,7 @@ files:
59
60
  - lib/noticed/bulk_delivery_method.rb
60
61
  - lib/noticed/bulk_delivery_methods/discord.rb
61
62
  - lib/noticed/bulk_delivery_methods/slack.rb
63
+ - lib/noticed/bulk_delivery_methods/test.rb
62
64
  - lib/noticed/bulk_delivery_methods/webhook.rb
63
65
  - lib/noticed/coder.rb
64
66
  - lib/noticed/delivery_method.rb
@@ -74,6 +76,8 @@ files:
74
76
  - lib/noticed/delivery_methods/vonage_sms.rb
75
77
  - lib/noticed/delivery_methods/webhook.rb
76
78
  - lib/noticed/engine.rb
79
+ - lib/noticed/has_notifications.rb
80
+ - lib/noticed/notification_channel.rb
77
81
  - lib/noticed/required_options.rb
78
82
  - lib/noticed/translation.rb
79
83
  - lib/noticed/version.rb
@@ -96,7 +100,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
96
100
  - !ruby/object:Gem::Version
97
101
  version: '0'
98
102
  requirements: []
99
- rubygems_version: 3.5.3
103
+ rubygems_version: 3.5.4
100
104
  signing_key:
101
105
  specification_version: 4
102
106
  summary: Notifications for Ruby on Rails applications