noticed 1.2.19 → 1.3.2

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: 0e9bdeaab06d2b6aa2022dad0ed115e8a64e829d25276be05ffaec64d17116f3
4
- data.tar.gz: ea1376e7faf08a81f2d691b0fec82f4a81647d3423f48260ac3d29b486f91a50
3
+ metadata.gz: 2362015405a07454aa743ec5709ede4af768866abf27499dce3754b37ac6f4f9
4
+ data.tar.gz: 05e36982fe6d3ed4d3cca6929271cc0382be5167fb4c6967396ae6969f3059fe
5
5
  SHA512:
6
- metadata.gz: 83514df616ee5334f50635cdf0be44a75919529dc2b35133175e49fc17f61fdf55f0a6d8401476262dbd9da4ae6f723361757df1cacea8f8091d10240459477f
7
- data.tar.gz: 887876dd5b12f140829cb518ed9ccbff733bdd2d54486e16d7f088bb6fc1b3d6467d0efdd20336bd49fd044c7622b28b728ad7280e1c29e3ef508c23feff6124
6
+ metadata.gz: ea23c339c18177f234c2d541ababb78cda5ec408100ff828e6a1cd5678dabbfabf6f1bc79c7a129edaf22286754e2e741e611c2b0fc319bcdac10e00fc6fd66f
7
+ data.tar.gz: 4f0f7b8c0552683e7e8014a36ebe1d724bfd97baa3056f8483a40a94881bbd4ecbe6527b15a9ce2875d24dba7a767ee88d474b986001bd9974c1eca249a2d6ff
data/README.md CHANGED
@@ -265,7 +265,7 @@ Sends a Teams notification via webhook.
265
265
 
266
266
  * `format: :format_for_teams` - *Optional*
267
267
 
268
- Use a custom method to define the payload sent to slack. Method should return a Hash.
268
+ Use a custom method to define the payload sent to Microsoft Teams. Method should return a Hash.
269
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
270
 
271
271
  ```ruby
@@ -354,6 +354,33 @@ Sends an SMS notification via Vonage / Nexmo.
354
354
  }
355
355
  ```
356
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
+
357
384
  ### 🚚 Custom Delivery Methods
358
385
 
359
386
  To generate a custom delivery method, simply run
@@ -464,10 +491,19 @@ Sorting notifications by newest first:
464
491
  user.notifications.newest_first
465
492
  ```
466
493
 
467
- Marking all notifications as read:
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:
468
503
 
469
504
  ```ruby
470
505
  user.notifications.mark_as_read!
506
+ user.notifications.mark_as_unread!
471
507
  ```
472
508
 
473
509
  #### Instance methods
@@ -498,53 +534,43 @@ Adding notification associations to your models makes querying and deleting noti
498
534
 
499
535
  For example, in most cases, you'll want to delete notifications for records that are destroyed.
500
536
 
501
- ##### JSON Columns
537
+ We'll need two associations for this:
502
538
 
503
- If you're using MySQL or Postgresql, the `params` column on the notifications table is in `json` or `jsonb` format and can be queried against directly.
539
+ 1. Notifications where the record is the recipient
540
+ 2. Notifications where the record is in the notification params
504
541
 
505
542
  For example, we can query the notifications and delete them on destroy like so:
506
543
 
507
544
  ```ruby
508
545
  class Post < ApplicationRecord
509
- def notifications
510
- # Exact match
511
- @notifications ||= Notification.where(params: { post: self })
512
-
513
- # Or Postgres syntax to query the post key in the JSON column
514
- # @notifications ||= Notification.where("params->'post' = ?", Noticed::Coder.dump(self).to_json)
515
- end
546
+ # Standard association for deleting notifications when you're the recipient
547
+ has_many :notifications, as: :recipient, dependent: :destroy
516
548
 
517
- before_destroy :destroy_notifications
549
+ # Helper for associating and destroying Notification records where(params: {post: self})
550
+ has_noticed_notifications
518
551
 
519
- def destroy_notifications
520
- notifications.destroy_all
521
- 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"
522
554
  end
523
- ```
524
-
525
- ##### Polymorphic Association
526
555
 
527
- If your notification is only associated with one model or you're using a `text` column for your params column , then a polymorphic association is what you'll want to use.
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
528
560
 
529
- 1. Add a polymorphic association to the Notification model. `rails g migration AddNotifiableToNotifications notifiable:belongs_to{polymorphic}`
530
-
531
- 2. Add `has_many :notifications, as: :notifiable, dependent: :destroy` to each model
561
+ CommentNotification.with(parent: @post).deliver(user)
562
+ @post.notifications_as_parent
563
+ ```
532
564
 
533
- 3. Customize database `format: ` option to write the `notifiable` attribute(s) when saving the notification
565
+ #### Handling Deleted Records
534
566
 
535
- ```ruby
536
- class ExampleNotification < Noticed::Base
537
- 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`:
538
568
 
539
- def format_for_database
540
- {
541
- notifiable: params.delete(:post),
542
- type: self.class.name,
543
- params: params
544
- }
545
- end
546
- end
547
- ```
569
+ ```ruby
570
+ class ApplicationJob < ActiveJob::Base
571
+ discard_on ActiveJob::DeserializationError
572
+ end
573
+ ```
548
574
 
549
575
  ## 🙏 Contributing
550
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
- "params:text"
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)
@@ -96,11 +98,14 @@ module Noticed
96
98
  run_callbacks delivery_method[:name] do
97
99
  method = delivery_method_for(delivery_method[:name], delivery_method[:options])
98
100
 
101
+ # If the queue is `nil`, ActiveJob will use a default queue name.
102
+ queue = delivery_method.dig(:options, :queue)
103
+
99
104
  # Always perfrom later if a delay is present
100
105
  if (delay = delivery_method.dig(:options, :delay))
101
- method.set(wait: delay).perform_later(args)
106
+ method.set(wait: delay, queue: queue).perform_later(args)
102
107
  elsif enqueue
103
- method.perform_later(args)
108
+ method.set(queue: queue).perform_later(args)
104
109
  else
105
110
  method.perform_now(args)
106
111
  end
@@ -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.merge(
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
@@ -1,4 +1,9 @@
1
1
  module Noticed
2
2
  class Engine < ::Rails::Engine
3
+ initializer "noticed.has_notifications" do
4
+ ActiveSupport.on_load(:active_record) do
5
+ include Noticed::HasNotifications
6
+ end
7
+ end
3
8
  end
4
9
  end
@@ -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,18 +14,26 @@ module Noticed
14
14
  scope :read, -> { where.not(read_at: nil) }
15
15
  end
16
16
 
17
- module ClassMethods
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
27
+ return Noticed::TextCoder unless table_exists?
28
+
23
29
  case attribute_types["params"].type
24
30
  when :json, :jsonb
25
31
  Noticed::Coder
26
32
  else
27
33
  Noticed::TextCoder
28
34
  end
35
+ rescue ActiveRecord::NoDatabaseError
36
+ Noticed::TextCoder
29
37
  end
30
38
  end
31
39
 
@@ -1,3 +1,3 @@
1
1
  module Noticed
2
- VERSION = "1.2.19"
2
+ VERSION = "1.3.2"
3
3
  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.2.19
4
+ version: 1.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Oliver
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-11-11 00:00:00.000000000 Z
11
+ date: 2021-03-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -80,6 +80,34 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
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
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
83
111
  description: Database, browser, realtime ActionCable, Email, SMS, Slack notifications,
84
112
  and more for Rails apps
85
113
  email:
@@ -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.1.4
167
+ rubygems_version: 3.2.3
139
168
  signing_key:
140
169
  specification_version: 4
141
170
  summary: Notifications for Ruby on Rails applications