noticed 1.2.7 → 1.2.12

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: fcd5afdca1ab2862bca9259b3015f86d93ab323eb64b6f8de666f9067216b202
4
- data.tar.gz: 8c6a53d533eb2667c44804b4a7a5e518b4ab7938b345c6105490145bb4fdb75a
3
+ metadata.gz: 6fca882449d4696c1551badc82383f789610f19898dec345a64f33a225ca9f81
4
+ data.tar.gz: 8751afe5063970449573719d7bedb957d7c88e52f1efef96357d8754d48cec21
5
5
  SHA512:
6
- metadata.gz: 21ea1e549dcbd0857fea6538a0fc2d17ba5f0d249b7f39d1e1d33a56cbece35a3413605d8c1a6e71a381a424b6db93c82bfe7de19d87e3e669c569926fa4a42b
7
- data.tar.gz: 1e85eb2edfbbac61fa9bcacf91a4fe041adc945c54ad029a6e8ab75e3d4fc64d52fbcf8527e43da9d0fd3c30e267c2c1e13bafbdf10662f6f826758a1a4f3f41
6
+ metadata.gz: 8ab4f29e4611ee0e5c3f548562e0265dc991ca45cb4789fbf7eb780430b21eddaef3407fbc7946f21bbff411149dd8e796197d9d0b334d85d8085aaea7beb54a
7
+ data.tar.gz: 7c6ce9b53092a182e405fc26274daae30e8ec6e2149c5f5794d3fa5c3728210f013f632c844dd00f9ad5f1d64cba12b09de6c311b9d367b580d91f899affb4c4
data/README.md CHANGED
@@ -256,7 +256,7 @@ Sends an SMS notification via Twilio.
256
256
 
257
257
  * `credentials: :get_twilio_credentials` - *Optional*
258
258
 
259
- Use a custom method to retrieve the credentials for Twilio. Method should return a Hash with `:account_sid`, `:auth_token` and `:phone_ number` keys.
259
+ Use a custom method to retrieve the credentials for Twilio. Method should return a Hash with `:account_sid`, `:auth_token` and `:phone_number` keys.
260
260
 
261
261
  Defaults to `Rails.application.credentials.twilio[:account_sid]` and `Rails.application.credentials.twilio[:auth_token]`
262
262
 
@@ -311,22 +311,28 @@ Sends an SMS notification via Vonage / Nexmo.
311
311
 
312
312
  ### 🚚 Custom Delivery Methods
313
313
 
314
- You can define a custom delivery method easily by adding a `deliver_by` line with a unique name and class option. The class will be instantiated and should inherit from `Noticed::DeliveryMethods::Base`.
314
+ To generate a custom delivery method, simply run
315
315
 
316
- ```ruby
317
- class MyNotification < Noticed::Base
318
- deliver_by :discord, class: "DiscordNotification"
319
- end
320
- ```
316
+ `rails generate noticed:delivery_method Discord`
317
+
318
+ This will generate a new `DeliveryMethods::Discord` class inside the `app/notifications/delivery_methods` folder, which can be used to deliver notifications to Discord.
321
319
 
322
320
  ```ruby
323
- class DiscordNotification < Noticed::DeliveryMethods::Base
321
+ class DeliveryMethods::Discord < Noticed::DeliveryMethods::Base
324
322
  def deliver
325
323
  # Logic for sending a Discord notification
326
324
  end
327
325
  end
328
326
  ```
329
327
 
328
+ You can use the custom delivery method thus created by adding a `deliver_by` line with a unique name and `class` option in your notification class.
329
+
330
+ ```ruby
331
+ class MyNotification < Noticed::Base
332
+ deliver_by :discord, class: "DeliveryMethods::Discord"
333
+ end
334
+ ```
335
+
330
336
  Delivery methods have access to the following methods and attributes:
331
337
 
332
338
  * `notification` - The instance of the Notification. You can call methods on the notification to let the user easily override formatting and other functionality of the delivery method.
@@ -339,7 +345,7 @@ Delivery methods have access to the following methods and attributes:
339
345
  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.
340
346
 
341
347
  ```ruby
342
- class DiscordNotification < Noticed::DeliveryMethods::Base
348
+ class DeliveryMethods::Discord < Noticed::DeliveryMethods::Base
343
349
  after_deliver do
344
350
  # Do whatever you want
345
351
  end
@@ -351,13 +357,13 @@ end
351
357
  Rails 6.1+ can serialize Class and Module objects as arguments to ActiveJob. The following syntax should work for Rails 6.1+:
352
358
 
353
359
  ```ruby
354
- deliver_by DiscordNotification
360
+ deliver_by DeliveryMethods::Discord
355
361
  ```
356
362
 
357
363
  For Rails 6.0 and earlier, you must pass strings of the class names in the `deliver_by` options.
358
364
 
359
365
  ```ruby
360
- deliver_by :discord, class: "DiscordNotification"
366
+ deliver_by :discord, class: "DeliveryMethods::Discord"
361
367
  ```
362
368
 
363
369
  We recommend the Rails 6.0 compatible options to prevent confusion.
@@ -401,6 +407,60 @@ Check if read / unread:
401
407
  @notification.unread?
402
408
  ```
403
409
 
410
+ #### Associating Notifications
411
+
412
+ Adding notification associations to your models makes querying and deleting notifications easy and is a pretty critical feature of most applications.
413
+
414
+ For example, in most cases, you'll want to delete notifications for records that are destroyed.
415
+
416
+ ##### JSON Columns
417
+
418
+ 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.
419
+
420
+ For example, we can query the notifications and delete them on destroy like so:
421
+
422
+ ```ruby
423
+ class Post < ApplicationRecord
424
+ def notifications
425
+ # Exact match
426
+ @notifications ||= Notification.where(params: { post: self })
427
+
428
+ # Or Postgres syntax to query the post key in the JSON column
429
+ # @notifications ||= Notification.where("params->'post' = ?", Noticed::Coder.dump(self).to_json)
430
+ end
431
+
432
+ before_destroy :destroy_notifications
433
+
434
+ def destroy_notifications
435
+ notifications.destroy_all
436
+ end
437
+ end
438
+ ```
439
+
440
+ ##### Polymorphic Assocation
441
+
442
+ 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.
443
+
444
+ 1. Add a polymorphic association to the Notification model. `rails g migration AddNotifiableToNotifications notifiable:belongs_to{polymorphic}`
445
+
446
+ 2. Add `has_many :notifications, as: :notifiable, dependent: :destroy` to each model
447
+
448
+ 3. Customize database `format: ` option to write the `notifiable` attribute(s) when saving the notification
449
+
450
+ ```ruby
451
+ class ExampleNotification < Noticed::Base
452
+ deliver_by :database, format: :format_for_database
453
+
454
+ def format_for_database
455
+ {
456
+ notifiable: params.delete(:post),
457
+ type: self.class.name,
458
+ params: params
459
+ }
460
+ end
461
+ end
462
+ ```
463
+
404
464
  ## 🙏 Contributing
405
465
 
406
466
  This project uses [Standard](https://github.com/testdouble/standard) for formatting Ruby code. Please make sure to run `standardrb` before submitting pull requests.
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators/named_base"
4
+
5
+ module Noticed
6
+ module Generators
7
+ class DeliveryMethodGenerator < Rails::Generators::NamedBase
8
+ include Rails::Generators::ResourceHelpers
9
+
10
+ source_root File.expand_path("../templates", __FILE__)
11
+
12
+ desc "Generates a class for a custom delivery method with the given NAME."
13
+
14
+ def generate_notification
15
+ template "delivery_method.rb", "app/notifications/delivery_methods/#{singular_name}.rb"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -15,7 +15,7 @@ module Noticed
15
15
  argument :attributes, type: :array, default: [], banner: "field:type field:type"
16
16
 
17
17
  def generate_notification
18
- generate :model, name, "recipient:references{polymorphic}", "type", "params:text", "read_at:datetime", *attributes
18
+ generate :model, name, "recipient:references{polymorphic}", "type", params_column, "read_at:datetime", *attributes
19
19
  end
20
20
 
21
21
  def add_noticed_model
@@ -31,6 +31,17 @@ module Noticed
31
31
  def model_path
32
32
  @model_path ||= File.join("app", "models", "#{file_path}.rb")
33
33
  end
34
+
35
+ def params_column
36
+ case ActiveRecord::Base.configurations.configs_for(spec_name: "primary").config["adapter"]
37
+ when "mysql"
38
+ "params:json"
39
+ when "postgresql"
40
+ "params:jsonb"
41
+ else
42
+ "params:text"
43
+ end
44
+ end
34
45
  end
35
46
  end
36
47
  end
@@ -12,7 +12,7 @@ module Noticed
12
12
  desc "Generates a notification with the given NAME."
13
13
 
14
14
  def generate_notification
15
- template "notification.rb", "app/notifications/#{singular_name}.rb"
15
+ template "notification.rb", "app/notifications/#{file_path}.rb"
16
16
  end
17
17
  end
18
18
  end
@@ -0,0 +1,5 @@
1
+ class DeliveryMethods::<%= class_name %> < Noticed::DeliveryMethods::Base
2
+ def deliver
3
+ # Logic for sending the notification
4
+ end
5
+ end
@@ -6,6 +6,7 @@ module Noticed
6
6
  autoload :Base, "noticed/base"
7
7
  autoload :Coder, "noticed/coder"
8
8
  autoload :Model, "noticed/model"
9
+ autoload :TextCoder, "noticed/text_coder"
9
10
  autoload :Translation, "noticed/translation"
10
11
 
11
12
  module DeliveryMethods
@@ -33,4 +34,12 @@ module Noticed
33
34
 
34
35
  class ValidationError < StandardError
35
36
  end
37
+
38
+ class ResponseUnsuccessful < StandardError
39
+ attr_reader :response
40
+
41
+ def initialize(response)
42
+ @response = response
43
+ end
44
+ end
36
45
  end
@@ -69,6 +69,9 @@ module Noticed
69
69
  def run_delivery(recipient, enqueue: true)
70
70
  delivery_methods = self.class.delivery_methods.dup
71
71
 
72
+ # Set recipient to instance var so it is available to Notification class
73
+ @recipient = recipient
74
+
72
75
  # Run database delivery inline first if it exists so other methods have access to the record
73
76
  if (index = delivery_methods.find_index { |m| m[:name] == :database })
74
77
  delivery_method = delivery_methods.delete_at(index)
@@ -2,18 +2,12 @@ module Noticed
2
2
  class Coder
3
3
  def self.load(data)
4
4
  return if data.nil?
5
-
6
- # Text columns need JSON parsing
7
- if data.is_a?(String)
8
- data = JSON.parse(data)
9
- end
10
-
11
5
  ActiveJob::Arguments.send(:deserialize_argument, data)
12
6
  end
13
7
 
14
8
  def self.dump(data)
15
9
  return if data.nil?
16
- ActiveJob::Arguments.send(:serialize_argument, data).to_json
10
+ ActiveJob::Arguments.send(:serialize_argument, data)
17
11
  end
18
12
  end
19
13
  end
@@ -17,16 +17,16 @@ module Noticed
17
17
 
18
18
  def channel
19
19
  @channel ||= begin
20
- value = options[:channel]
21
- case value
22
- when String
23
- value.constantize
24
- when Symbol
25
- notification.send(value)
26
- when Class
27
- value
28
- else
29
- Noticed::NotificationChannel
20
+ value = options[:channel]
21
+ case value
22
+ when String
23
+ value.constantize
24
+ when Symbol
25
+ notification.send(value)
26
+ when Class
27
+ value
28
+ else
29
+ Noticed::NotificationChannel
30
30
  end
31
31
  end
32
32
  end
@@ -6,15 +6,15 @@ module Noticed
6
6
 
7
7
  attr_reader :notification, :options, :recipient, :record
8
8
 
9
- def perform(notification_class:, options:, params:, recipient:, record:)
10
- @notification = notification_class.constantize.new(params)
11
- @options = options
12
- @recipient = recipient
13
- @record = record
9
+ def perform(args)
10
+ @notification = args[:notification_class].constantize.new(args[:params])
11
+ @options = args[:options]
12
+ @recipient = args[:recipient]
13
+ @record = args[:record]
14
14
 
15
15
  # Make notification aware of database record and recipient during delivery
16
- @notification.record = record
17
- @notification.recipient = recipient
16
+ @notification.record = args[:record]
17
+ @notification.recipient = args[:recipient]
18
18
 
19
19
  run_callbacks :deliver do
20
20
  deliver
@@ -24,6 +24,36 @@ module Noticed
24
24
  def deliver
25
25
  raise NotImplementedError, "Delivery methods must implement a deliver method"
26
26
  end
27
+
28
+ private
29
+
30
+ # Helper method for making POST requests from delivery methods
31
+ #
32
+ # Usage:
33
+ # post("http://example.com", basic_auth: {user:, pass:}, json: {}, form: {})
34
+ #
35
+ def post(url, args = {})
36
+ basic_auth = args.delete(:basic_auth)
37
+
38
+ request = if basic_auth
39
+ HTTP.basic_auth(user: basic_auth[:user], pass: basic_auth[:pass])
40
+ else
41
+ HTTP
42
+ end
43
+
44
+ response = request.post(url, args)
45
+
46
+ if options[:debug]
47
+ Rails.logger.debug("POST #{url}")
48
+ Rails.logger.debug("Response: #{response.code}: #{response}")
49
+ end
50
+
51
+ if !options[:ignore_failure] && !response.status.success?
52
+ raise ResponseUnsuccessful.new(response)
53
+ end
54
+
55
+ response
56
+ end
27
57
  end
28
58
  end
29
59
  end
@@ -2,7 +2,7 @@ module Noticed
2
2
  module DeliveryMethods
3
3
  class Slack < Base
4
4
  def deliver
5
- HTTP.post(url, json: format)
5
+ post(url, json: format)
6
6
  end
7
7
 
8
8
  private
@@ -2,7 +2,7 @@ module Noticed
2
2
  module DeliveryMethods
3
3
  class Twilio < Base
4
4
  def deliver
5
- HTTP.basic_auth(user: account_sid, pass: auth_token).post(url, json: format)
5
+ post(url, basic_auth: {user: account_sid, pass: auth_token}, form: format)
6
6
  end
7
7
 
8
8
  private
@@ -2,7 +2,11 @@ module Noticed
2
2
  module DeliveryMethods
3
3
  class Vonage < Base
4
4
  def deliver
5
- HTTP.post("https://rest.nexmo.com/sms/json", json: format)
5
+ response = post("https://rest.nexmo.com/sms/json", json: format)
6
+ status = response.parse.dig("messages", 0, "status")
7
+ if !options[:ignore_failure] && status != "0"
8
+ raise ResponseUnsuccessful.new(response)
9
+ end
6
10
  end
7
11
 
8
12
  private
@@ -5,17 +5,28 @@ module Noticed
5
5
  included do
6
6
  self.inheritance_column = nil
7
7
 
8
- serialize :params, Noticed::Coder
8
+ serialize :params, noticed_coder
9
9
 
10
10
  belongs_to :recipient, polymorphic: true
11
11
 
12
12
  scope :newest_first, -> { order(created_at: :desc) }
13
+ scope :unread, -> { where(read_at: nil) }
14
+ scope :read, -> { where.not(read_at: nil) }
13
15
  end
14
16
 
15
17
  module ClassMethods
16
18
  def mark_as_read!
17
19
  update_all(read_at: Time.current, updated_at: Time.current)
18
20
  end
21
+
22
+ def noticed_coder
23
+ case attribute_types["params"].type
24
+ when :json, :jsonb
25
+ Noticed::Coder
26
+ else
27
+ Noticed::TextCoder
28
+ end
29
+ end
19
30
  end
20
31
 
21
32
  # Rehydrate the database notification into the Notification object for rendering
@@ -0,0 +1,16 @@
1
+ module Noticed
2
+ class TextCoder
3
+ def self.load(data)
4
+ return if data.nil?
5
+
6
+ # Text columns need JSON parsing
7
+ data = JSON.parse(data)
8
+ ActiveJob::Arguments.send(:deserialize_argument, data)
9
+ end
10
+
11
+ def self.dump(data)
12
+ return if data.nil?
13
+ ActiveJob::Arguments.send(:serialize_argument, data).to_json
14
+ end
15
+ end
16
+ end
@@ -1,21 +1,23 @@
1
- module Translation
2
- extend ActiveSupport::Concern
1
+ module Noticed
2
+ module Translation
3
+ extend ActiveSupport::Concern
3
4
 
4
- # Returns the +i18n_scope+ for the class. Overwrite if you want custom lookup.
5
- def i18n_scope
6
- :notifications
7
- end
5
+ # Returns the +i18n_scope+ for the class. Overwrite if you want custom lookup.
6
+ def i18n_scope
7
+ :notifications
8
+ end
8
9
 
9
- def translate(key, **options)
10
- I18n.translate(scope_translation_key(key), **options)
11
- end
12
- alias t translate
10
+ def translate(key, **options)
11
+ I18n.translate(scope_translation_key(key), **options)
12
+ end
13
+ alias t translate
13
14
 
14
- def scope_translation_key(key)
15
- if key.to_s.start_with?(".")
16
- "#{i18n_scope}.#{self.class.name.underscore}#{key}"
17
- else
18
- key
15
+ def scope_translation_key(key)
16
+ if key.to_s.start_with?(".")
17
+ "#{i18n_scope}.#{self.class.name.underscore}#{key}"
18
+ else
19
+ key
20
+ end
19
21
  end
20
22
  end
21
23
  end
@@ -1,3 +1,3 @@
1
1
  module Noticed
2
- VERSION = "1.2.7"
2
+ VERSION = "1.2.12"
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.7
4
+ version: 1.2.12
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-08-07 00:00:00.000000000 Z
11
+ date: 2020-08-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -92,9 +92,11 @@ files:
92
92
  - README.md
93
93
  - Rakefile
94
94
  - app/channels/noticed/notification_channel.rb
95
+ - lib/generators/noticed/delivery_method_generator.rb
95
96
  - lib/generators/noticed/model_generator.rb
96
97
  - lib/generators/noticed/notification_generator.rb
97
98
  - lib/generators/noticed/templates/README
99
+ - lib/generators/noticed/templates/delivery_method.rb.tt
98
100
  - lib/generators/noticed/templates/notification.rb.tt
99
101
  - lib/noticed.rb
100
102
  - lib/noticed/base.rb
@@ -109,6 +111,7 @@ files:
109
111
  - lib/noticed/delivery_methods/vonage.rb
110
112
  - lib/noticed/engine.rb
111
113
  - lib/noticed/model.rb
114
+ - lib/noticed/text_coder.rb
112
115
  - lib/noticed/translation.rb
113
116
  - lib/noticed/version.rb
114
117
  - lib/tasks/noticed_tasks.rake