noticed 3.0.0 → 3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 10b19f81b81ae5471493e73857b55fbb1086545cca11f58cd40935597a39b5a1
4
- data.tar.gz: 6a3e5502295f8152f0d05b4150a7c272ec48595adb986c81112aea3d09fce891
3
+ metadata.gz: 5a62b2258520a1d287a6ab2ff9b73288e37ebd7d4b18994c0b13bcfb591e9440
4
+ data.tar.gz: ecf9a251b05942f6b32c3730b923d59e16e4eb9de169e361b1ce0cb2989750f1
5
5
  SHA512:
6
- metadata.gz: ca14522f41fbbc56b6764fb818a7d283a49d94e31d1dfcddbe8042429612d6a7f41e42df53867bfc4175fd66fc7aa5079c116db3b62707b5f87ea884c5dd5a5f
7
- data.tar.gz: c0b7e78c47afa9df9a642f18717aa15c9c37a1823110b05c9c6b82987f6de0791ca82437f4b7e552ced05c38274b16d90478b9af29999791c2bc66ddd29fe37b
6
+ metadata.gz: 75edf989bdb4bc2fd7ee445f2852107fdfd629696affeae51d7e3d407ebdd97669b5d11bf8bbc79d92c4dc22dfde7bddcdd528c6f52c0230e1b169ef716d95cb
7
+ data.tar.gz: d34cdd0a3e905188d6efe9852b1b199148e9b4e02e0677d446fbf48ab94fb98d6bc87bf9b7917280649b6182f5e7ef9e9d64af0cd43ef1db4ea45b23d4517225
data/README.md CHANGED
@@ -51,6 +51,8 @@ Bulk delivery methods we support:
51
51
  [Watch Screencast](https://www.youtube.com/watch?v=SzX-aBEqnAc)
52
52
 
53
53
  ## 🚀 Installation
54
+ Noticed requires Rails 7.1 or newer.
55
+
54
56
  Run the following command to add Noticed to your Gemfile:
55
57
 
56
58
  ```ruby
@@ -374,6 +376,20 @@ Each of these options are available for every delivery method (individual or bul
374
376
  * `config.if` — Intended for a lambda or method; runs after the `wait` if configured; cancels the delivery method if returns falsey
375
377
  * `config.unless` — Intended for a lambda or method; runs after the `wait` if configured; cancels the delivery method if returns truthy
376
378
 
379
+ Lambdas for `if` and `unless` are evaluated in the context of the Notification. A symbol calls a method on the Notifier and passes the notification:
380
+
381
+ ```ruby
382
+ class MessageNotifier < Noticed::Event
383
+ deliver_by :email do |config|
384
+ config.if = :email_enabled?
385
+ end
386
+
387
+ def email_enabled?(notification)
388
+ notification.recipient.email_notifications?
389
+ end
390
+ end
391
+ ```
392
+
377
393
  The following are evaluated in the context of the Notification so it can be customize to the recipient:
378
394
 
379
395
  * `config.wait` — (Should yield an `ActiveSupport::Duration`) Delays the job that runs this delivery method for the given duration of time
@@ -6,11 +6,17 @@ module Noticed
6
6
  deliver_by.perform_later(event) if deliver_by.perform?(event)
7
7
  end
8
8
 
9
- # Enqueue individual deliveries
10
- event.notifications.each do |notification|
11
- event.delivery_methods.each_value do |deliver_by|
12
- deliver_by.perform_later(notification) if deliver_by.perform?(notification)
13
- end
9
+ # Enqueue individual deliveries in batches so large recipient lists don't load into memory all at once
10
+ event.notifications.find_in_batches do |notifications|
11
+ ActiveJob.perform_all_later notifications.flat_map { |notification| delivery_jobs_for(event, notification) }
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def delivery_jobs_for(event, notification)
18
+ event.delivery_methods.values.filter_map do |deliver_by|
19
+ deliver_by.job(notification) if deliver_by.perform?(notification)
14
20
  end
15
21
  end
16
22
  end
@@ -18,7 +18,7 @@ module Noticed
18
18
  end
19
19
 
20
20
  def bulk_deliver_by(name, options = {})
21
- raise NameError, "#{name} has already been used for this Notifier." if bulk_delivery_methods.has_key?(name)
21
+ raise ArgumentError, "#{name} has already been used for this Notifier." if bulk_delivery_methods.has_key?(name)
22
22
 
23
23
  config = ActiveSupport::OrderedOptions.new.merge(options)
24
24
  yield config if block_given?
@@ -26,7 +26,7 @@ module Noticed
26
26
  end
27
27
 
28
28
  def deliver_by(name, options = {})
29
- raise NameError, "#{name} has already been used for this Notifier." if delivery_methods.has_key?(name)
29
+ raise ArgumentError, "#{name} has already been used for this Notifier." if delivery_methods.has_key?(name)
30
30
 
31
31
  if name == :database
32
32
  Noticed.deprecator.warn <<-WARNING.squish
@@ -67,15 +67,17 @@ module Noticed
67
67
  if self < Ephemeral
68
68
  new(params: params)
69
69
  else
70
- record = params.delete(:record)
71
- new(params: params, record: record)
70
+ new(params: params.except(:record), record: params[:record])
72
71
  end
73
72
  end
74
73
 
75
74
  def deliver(recipients = nil, **options)
76
75
  new.deliver(recipients, **options)
77
76
  end
78
- alias_method :deliver_later, :deliver
77
+
78
+ def deliver_later(...)
79
+ deliver(...)
80
+ end
79
81
  end
80
82
 
81
83
  # CommentNotifier.deliver(User.all)
@@ -96,16 +98,7 @@ module Noticed
96
98
  self.notifications_count = recipients_attributes.size
97
99
  save!
98
100
 
99
- if Rails.gem_version >= Gem::Version.new("7.0.0.alpha1")
100
- notifications.insert_all!(recipients_attributes, record_timestamps: true) if recipients_attributes.any?
101
- else
102
- time = Time.current
103
- recipients_attributes.each do |attributes|
104
- attributes[:created_at] = time
105
- attributes[:updated_at] = time
106
- end
107
- notifications.insert_all!(recipients_attributes) if recipients_attributes.any?
108
- end
101
+ notifications.insert_all!(recipients_attributes, record_timestamps: true) if recipients_attributes.any?
109
102
  end
110
103
 
111
104
  # Enqueue delivery job
@@ -113,14 +106,17 @@ module Noticed
113
106
 
114
107
  self
115
108
  end
116
- alias_method :deliver_later, :deliver
109
+
110
+ def deliver_later(...)
111
+ deliver(...)
112
+ end
117
113
 
118
114
  def evaluate_recipients
119
115
  return unless _recipients
120
116
 
121
117
  if _recipients.respond_to?(:call, true)
122
118
  instance_exec(&_recipients)
123
- elsif _recipients.is_a?(Symbol) && respond_to?(_recipients, true)
119
+ else
124
120
  send(_recipients)
125
121
  end
126
122
  end
@@ -4,13 +4,15 @@ module Noticed
4
4
 
5
5
  class_methods do
6
6
  # Generate a Notification class each time a Notifier is defined
7
+ #
8
+ # Lookup must not inherit, otherwise a top-level ::Notification model in the host app would be used instead of Noticed::Notification
7
9
  def inherited(notifier)
8
10
  super
9
- notifier.const_set :Notification, Class.new(const_defined?(:Notification) ? const_get(:Notification) : Noticed::Notification)
11
+ notifier.const_set :Notification, Class.new(const_defined?(:Notification, false) ? const_get(:Notification, false) : Noticed::Notification)
10
12
  end
11
13
 
12
14
  def notification_methods(&block)
13
- const_get(:Notification).class_eval(&block)
15
+ const_get(:Notification, false).class_eval(&block)
14
16
  end
15
17
  end
16
18
  end
@@ -4,7 +4,7 @@ module Noticed
4
4
  attr_reader :name, :config, :bulk
5
5
 
6
6
  def initialize(name, config, bulk: false)
7
- @name, @config, @bulk, = name, config, bulk
7
+ @name, @config, @bulk = name, config, bulk
8
8
  end
9
9
 
10
10
  def constant
@@ -14,16 +14,23 @@ module Noticed
14
14
 
15
15
  def validate!
16
16
  constant.required_option_names.each do |option|
17
- raise ValidationError, "option `#{option}` must be set for `deliver_by :#{name}`" unless config[option].present?
17
+ raise ValidationError, "option `#{option}` must be set for `deliver_by :#{name}`" if config[option].nil?
18
18
  end
19
19
  end
20
20
 
21
+ # Builds the delivery job without enqueuing it, so jobs can be enqueued in bulk
22
+ def job(event_or_notification, options = {})
23
+ constant.new(name, event_or_notification).set(computed_options(options, event_or_notification))
24
+ end
25
+
21
26
  def perform_later(event_or_notification, options = {})
22
- constant.set(computed_options(options, event_or_notification)).perform_later(name, event_or_notification)
27
+ job(event_or_notification, options).enqueue
23
28
  end
24
29
 
25
- def ephemeral_perform_later(notifier, recipient, params, options = {})
26
- constant.set(computed_options(options, recipient))
30
+ # Ephemeral notifiers aren't persisted, so the notifier name, recipient(s), and params are passed to the job instead.
31
+ # Options like `wait` are evaluated against `context` (the ephemeral Event or Notification), matching the persisted path.
32
+ def ephemeral_perform_later(notifier, recipient, params, options, context:)
33
+ constant.set(computed_options(options, context))
27
34
  .perform_later(name, "#{notifier}::Notification", recipient: recipient, params: params)
28
35
  end
29
36
 
@@ -52,11 +59,12 @@ module Noticed
52
59
 
53
60
  private
54
61
 
55
- def computed_options(options, recipient)
56
- options[:wait] ||= evaluate_option(:wait, recipient) if config.has_key?(:wait)
57
- options[:wait_until] ||= evaluate_option(:wait_until, recipient) if config.has_key?(:wait_until)
58
- options[:queue] ||= evaluate_option(:queue, recipient) if config.has_key?(:queue)
59
- options[:priority] ||= evaluate_option(:priority, recipient) if config.has_key?(:priority)
62
+ def computed_options(options, context)
63
+ options = options.dup
64
+ options[:wait] ||= evaluate_option(:wait, context) if config.has_key?(:wait)
65
+ options[:wait_until] ||= evaluate_option(:wait_until, context) if config.has_key?(:wait_until)
66
+ options[:queue] ||= evaluate_option(:queue, context) if config.has_key?(:queue)
67
+ options[:priority] ||= evaluate_option(:priority, context) if config.has_key?(:priority)
60
68
  options
61
69
  end
62
70
  end
@@ -6,7 +6,6 @@ module Noticed
6
6
  include Noticed::Translation
7
7
  include Rails.application.routes.url_helpers
8
8
 
9
- attribute :record
10
9
  attribute :params, default: {}
11
10
 
12
11
  class Notification
@@ -27,27 +26,32 @@ module Noticed
27
26
  end
28
27
  end
29
28
 
30
- # Dynamically define Notification on each Ephemeral Notifier
29
+ # Dynamically define Notification on each Ephemeral Notifier, inheriting from the parent Notifier's Notification
31
30
  def self.inherited(notifier)
32
31
  super
33
- notifier.const_set :Notification, Class.new(Noticed::Ephemeral::Notification)
32
+ notifier.const_set :Notification, Class.new(const_defined?(:Notification, false) ? const_get(:Notification, false) : Noticed::Ephemeral::Notification)
34
33
  end
35
34
 
36
35
  def self.notification_methods(&block)
37
- const_get(:Notification).class_eval(&block)
36
+ const_get(:Notification, false).class_eval(&block)
38
37
  end
39
38
 
40
- def deliver(recipients = nil)
39
+ # EphemeralNotifier.with(message: "test").deliver(User.all, wait: 5.minutes)
40
+ def deliver(recipients = nil, **options)
41
41
  recipients ||= evaluate_recipients
42
42
  recipients = Array.wrap(recipients)
43
43
 
44
- bulk_delivery_methods.each do |_, deliver_by|
45
- deliver_by.ephemeral_perform_later(self.class.name, recipients, params)
44
+ validate!
45
+
46
+ bulk_delivery_methods.each_value do |deliver_by|
47
+ deliver_by.ephemeral_perform_later(self.class.name, recipients, params, options, context: self) if deliver_by.perform?(self)
46
48
  end
47
49
 
48
50
  recipients.each do |recipient|
49
- delivery_methods.each do |_, deliver_by|
50
- deliver_by.ephemeral_perform_later(self.class.name, recipient, params)
51
+ notification = self.class::Notification.new(recipient: recipient, event: self)
52
+
53
+ delivery_methods.each_value do |deliver_by|
54
+ deliver_by.ephemeral_perform_later(self.class.name, recipient, params, options, context: notification) if deliver_by.perform?(notification)
51
55
  end
52
56
  end
53
57
 
@@ -8,20 +8,11 @@ module Noticed
8
8
  belongs_to :record, polymorphic: true, optional: true
9
9
  has_many :notifications, dependent: :delete_all
10
10
 
11
- accepts_nested_attributes_for :notifications
12
-
13
11
  scope :newest_first, -> { order(created_at: :desc) }
14
12
 
15
13
  attribute :params, :json, default: {}
16
14
 
17
- # Ephemeral notifiers cannot serialize params since they aren't ActiveRecord backed
18
- if respond_to? :serialize
19
- if Rails.gem_version >= Gem::Version.new("7.1.0.alpha")
20
- serialize :params, coder: Coder
21
- else
22
- serialize :params, Coder
23
- end
24
- end
15
+ serialize :params, coder: Coder
25
16
  end
26
17
  end
27
18
 
@@ -2,11 +2,11 @@
2
2
 
3
3
  module Noticed
4
4
  module Generators
5
- class ModelGenerator < Rails::Generators::Base
6
- include Rails::Generators::ResourceHelpers
7
-
5
+ class InstallGenerator < Rails::Generators::Base
8
6
  source_root File.expand_path("../templates", __FILE__)
9
7
 
8
+ desc "Copies the Noticed migrations into your application."
9
+
10
10
  def create_migrations
11
11
  rails_command "railties:install:migrations FROM=noticed", inline: true
12
12
  end
@@ -4,5 +4,5 @@
4
4
  Next steps:
5
5
  1. Run `rails db:migrate`
6
6
  2. Add `has_many :notifications, as: :recipient, dependent: :destroy, class_name: "Noticed::Notification"` to your User model(s).
7
- 2. Add `has_many :notifications, as: :record, dependent: :destroy, class_name: "Noticed::Event"` to your model(s) that notifications reference.
8
- 3. Generate notifiers with "rails g noticed:notifier"
7
+ 3. Add `has_many :notifications, as: :record, dependent: :destroy, class_name: "Noticed::Event"` to your model(s) that notifications reference.
8
+ 4. Generate notifiers with "rails g noticed:notifier"
@@ -2,7 +2,7 @@ class BulkDeliveryMethods::<%= class_name %> < ApplicationBulkDeliveryMethod
2
2
  # To use this delivery method, specify the class option in your notifier.
3
3
  #
4
4
  # class MyNotifer < ApplicationNotifier
5
- # bulk_deliver_by :<%= file_path %>, class: "<%= class_name %>"
5
+ # bulk_deliver_by :<%= file_name %>, class: "BulkDeliveryMethods::<%= class_name %>"
6
6
  # end
7
7
 
8
8
  # Specify required options for the deliver_by config block
@@ -2,7 +2,7 @@ class DeliveryMethods::<%= class_name %> < ApplicationDeliveryMethod
2
2
  # To use this delivery method, specify the class option in your notifier.
3
3
  #
4
4
  # class MyNotifer < ApplicationNotifier
5
- # deliver_by :<%= file_path %>, class: "<%= class_name %>"
5
+ # deliver_by :<%= file_name %>, class: "DeliveryMethods::<%= class_name %>"
6
6
  # end
7
7
 
8
8
  # Specify required options for the deliver_by config block
@@ -33,10 +33,10 @@ module Noticed
33
33
  request.body = body
34
34
  end
35
35
 
36
+ # Bodies aren't logged since they can contain credentials and access tokens
36
37
  logger.debug("POST #{url}")
37
- logger.debug(request.body)
38
38
  response = http.request(request)
39
- logger.debug("Response: #{response.code}: #{response.body.inspect}")
39
+ logger.debug("Response: #{response.code}")
40
40
 
41
41
  raise ResponseUnsuccessful.new(response, url, args) unless response.code.start_with?("20")
42
42
 
@@ -13,13 +13,10 @@ module Noticed
13
13
 
14
14
  def perform(delivery_method_name, event, recipient: nil, params: {}, overrides: {})
15
15
  # Ephemeral notifications
16
- if event.is_a? String
17
- @event = event.constantize.new_with_params(recipient, params)
18
- @config = overrides
19
- else
20
- @event = event
21
- @config = event.bulk_delivery_methods.fetch(delivery_method_name).config.merge(overrides)
22
- end
16
+ @event = event.is_a?(String) ? event.constantize.new_with_params(recipient, params).event : event
17
+
18
+ # Look up config from Notifier and merge overrides
19
+ @config = @event.bulk_delivery_methods.fetch(delivery_method_name).config.merge(overrides)
23
20
 
24
21
  return false if config.has_key?(:if) && !evaluate_option(:if)
25
22
  return false if config.has_key?(:unless) && evaluate_option(:unless)
@@ -34,15 +31,15 @@ module Noticed
34
31
  end
35
32
 
36
33
  def fetch_constant(name)
37
- option = config[name]
38
- option.is_a?(String) ? option.constantize : evaluate_option(option)
34
+ option = evaluate_option(name)
35
+ option.is_a?(String) ? option.constantize : option
39
36
  end
40
37
 
41
38
  def evaluate_option(name)
42
39
  option = config[name]
43
40
 
44
41
  # Evaluate Proc within the context of the notifier
45
- if option&.respond_to?(:call)
42
+ if option.respond_to?(:call)
46
43
  event.instance_exec(&option)
47
44
 
48
45
  # Call method if symbol and matching method
@@ -10,7 +10,6 @@ module Noticed
10
10
  # end
11
11
 
12
12
  def deliver
13
- Rails.logger.debug(evaluate_option(:json))
14
13
  post_request(
15
14
  "https://#{host}/xrpc/com.atproto.repo.createRecord",
16
15
  headers: {"Authorization" => "Bearer #{token}"},
@@ -1,39 +1,7 @@
1
1
  module Noticed
2
2
  module BulkDeliveryMethods
3
3
  class Slack < BulkDeliveryMethod
4
- DEFAULT_URL = "https://slack.com/api/chat.postMessage"
5
-
6
- required_options :json
7
-
8
- def deliver
9
- headers = evaluate_option(:headers)
10
- json = evaluate_option(:json)
11
- response = post_request url, headers: headers, json: json
12
-
13
- if raise_if_not_ok? && !success?(response)
14
- raise ResponseUnsuccessful.new(response, url, {headers: headers, json: json})
15
- end
16
-
17
- response
18
- end
19
-
20
- def url
21
- evaluate_option(:url) || DEFAULT_URL
22
- end
23
-
24
- def raise_if_not_ok?
25
- value = evaluate_option(:raise_if_not_ok)
26
- value.nil? || value
27
- end
28
-
29
- def success?(response)
30
- if response.content_type == "application/json"
31
- JSON.parse(response.body).dig("ok")
32
- else
33
- # https://api.slack.com/changelog/2016-05-17-changes-to-errors-for-incoming-webhooks
34
- response.is_a?(Net::HTTPSuccess)
35
- end
36
- end
4
+ include SlackDelivery
37
5
  end
38
6
  end
39
7
  end
@@ -1,19 +1,7 @@
1
1
  module Noticed
2
2
  module BulkDeliveryMethods
3
3
  class Webhook < BulkDeliveryMethod
4
- required_options :url
5
-
6
- def deliver
7
- Rails.logger.debug(evaluate_option(:json))
8
- post_request(
9
- evaluate_option(:url),
10
- basic_auth: evaluate_option(:basic_auth),
11
- headers: evaluate_option(:headers),
12
- json: evaluate_option(:json),
13
- form: evaluate_option(:form),
14
- body: evaluate_option(:body)
15
- )
16
- end
4
+ include WebhookDelivery
17
5
  end
18
6
  end
19
7
  end
data/lib/noticed/coder.rb CHANGED
@@ -2,10 +2,16 @@ require "active_job/arguments"
2
2
 
3
3
  module Noticed
4
4
  class Coder
5
+ # Rails 8.2+ (globalid 1.4+) raises GlobalID::Locator::RecordNotFound for missing records
6
+ # instead of ActiveRecord::RecordNotFound
7
+ RECORD_NOT_FOUND_ERRORS = [ActiveRecord::RecordNotFound].tap do |errors|
8
+ errors << GlobalID::Locator::RecordNotFound if defined?(GlobalID::Locator::RecordNotFound)
9
+ end.freeze
10
+
5
11
  def self.load(data)
6
12
  return if data.nil?
7
13
  ActiveJob::Arguments.send(:deserialize_argument, data)
8
- rescue ActiveRecord::RecordNotFound => error
14
+ rescue *RECORD_NOT_FOUND_ERRORS => error
9
15
  {noticed_error: error.message, original_params: data}
10
16
  end
11
17
 
@@ -4,7 +4,7 @@ module Noticed
4
4
  required_options :devices, :format
5
5
 
6
6
  def deliver
7
- notification = (!!evaluate_option(:silent)) ? notification_class.silent : notification_class
7
+ notification = evaluate_option(:silent) ? notification_class.silent : notification_class
8
8
 
9
9
  notification
10
10
  .with_apple(evaluate_option(:with_apple))
@@ -14,8 +14,9 @@ module Noticed
14
14
  .deliver_later_to(evaluate_option(:devices))
15
15
  end
16
16
 
17
+ # `notification_class` rather than `class` since `config.class` is reserved for overriding the delivery method
17
18
  def notification_class
18
- fetch_constant(:class) || ApplicationPushNotification
19
+ fetch_constant(:notification_class) || ApplicationPushNotification
19
20
  end
20
21
  end
21
22
  end
@@ -1,6 +1,6 @@
1
1
  module Noticed
2
2
  module DeliveryMethods
3
- class Discord < BulkDeliveryMethod
3
+ class Discord < DeliveryMethod
4
4
  required_options :json, :url
5
5
 
6
6
  def deliver
@@ -34,8 +34,17 @@ module Noticed
34
34
  end
35
35
  end
36
36
 
37
+ # FCM returns 404 UNREGISTERED for tokens that are no longer valid. 400 INVALID_ARGUMENT is also
38
+ # returned for malformed payloads, so only treat it as a bad token when the error is about the token.
39
+ # https://firebase.google.com/docs/reference/fcm/rest/v1/ErrorCode
37
40
  def bad_token?(response)
38
- response.code == "404" || response.code == "400"
41
+ response.code == "404" || (response.code == "400" && token_error?(response))
42
+ end
43
+
44
+ def token_error?(response)
45
+ JSON.parse(response.body).dig("error", "message").to_s.match?(/registration token/i)
46
+ rescue JSON::ParserError
47
+ false
39
48
  end
40
49
 
41
50
  def credentials
@@ -59,11 +68,14 @@ module Noticed
59
68
  end
60
69
 
61
70
  def access_token
71
+ @access_token ||= authorizer.fetch_access_token!["access_token"]
72
+ end
73
+
74
+ def authorizer
62
75
  @authorizer ||= (evaluate_option(:authorizer) || Google::Auth::ServiceAccountCredentials).make_creds(
63
76
  json_key_io: StringIO.new(credentials.to_json),
64
77
  scope: "https://www.googleapis.com/auth/firebase.messaging"
65
78
  )
66
- @authorizer.fetch_access_token!["access_token"]
67
79
  end
68
80
  end
69
81
  end
@@ -3,29 +3,29 @@ require "apnotic"
3
3
  module Noticed
4
4
  module DeliveryMethods
5
5
  class Ios < DeliveryMethod
6
- cattr_accessor :development_connection_pool, :production_connection_pool
7
-
8
6
  required_options :bundle_identifier, :key_id, :team_id, :apns_key, :device_tokens
9
7
 
8
+ # A connection is opened per delivery and closed afterwards. Long-lived connections
9
+ # get reset by Apple when idle, which stalls the next push for over a minute.
10
10
  def deliver
11
+ connection = new_connection
12
+
11
13
  evaluate_option(:device_tokens).each do |device_token|
12
14
  apn = Apnotic::Notification.new(device_token)
13
15
  format_notification(apn)
14
16
 
15
- connection_pool = (!!evaluate_option(:development)) ? development_pool : production_pool
16
- connection_pool.with do |connection|
17
- response = connection.push(apn)
18
- raise "Timeout sending iOS push notification" unless response
19
- connection.close
17
+ response = connection.push(apn)
18
+ raise "Timeout sending iOS push notification" unless response
20
19
 
21
- if bad_token?(response) && config[:invalid_token]
22
- # Allow notification to cleanup invalid iOS device tokens
23
- notification.instance_exec(device_token, &config[:invalid_token])
24
- elsif !response.ok?
25
- raise "Request failed #{response.body}"
26
- end
20
+ if bad_token?(response) && config[:invalid_token]
21
+ # Allow notification to cleanup invalid iOS device tokens
22
+ notification.instance_exec(device_token, &config[:invalid_token])
23
+ elsif !response.ok?
24
+ raise "Request failed #{response.body}"
27
25
  end
28
26
  end
27
+ ensure
28
+ connection&.close
29
29
  end
30
30
 
31
31
  private
@@ -51,32 +51,16 @@ module Noticed
51
51
  response.status == "410" || (response.status == "400" && response.body["reason"] == "BadDeviceToken")
52
52
  end
53
53
 
54
- def development_pool
55
- self.class.development_connection_pool ||= new_connection_pool(development: true)
56
- end
57
-
58
- def production_pool
59
- self.class.production_connection_pool ||= new_connection_pool(development: false)
60
- end
61
-
62
- def new_connection_pool(development:)
63
- handler = proc do |connection|
64
- connection.on(:error) do |exception|
65
- Rails.logger.info "Apnotic exception raised: #{exception}"
66
- if config[:error_handler].respond_to?(:call)
67
- notification.instance_exec(exception, &config[:error_handler])
68
- end
69
- end
70
- end
71
-
72
- if development
73
- Apnotic::ConnectionPool.development(connection_pool_options, pool_options, &handler)
74
- else
75
- Apnotic::ConnectionPool.new(connection_pool_options, pool_options, &handler)
54
+ def new_connection
55
+ connection = evaluate_option(:development) ? Apnotic::Connection.development(connection_options) : Apnotic::Connection.new(connection_options)
56
+ connection.on(:error) do |exception|
57
+ Rails.logger.info "Apnotic exception raised: #{exception}"
58
+ notification.instance_exec(exception, &config[:error_handler]) if config[:error_handler]
76
59
  end
60
+ connection
77
61
  end
78
62
 
79
- def connection_pool_options
63
+ def connection_options
80
64
  {
81
65
  auth_method: :token,
82
66
  cert_path: StringIO.new(evaluate_option(:apns_key)),
@@ -84,10 +68,6 @@ module Noticed
84
68
  team_id: evaluate_option(:team_id)
85
69
  }
86
70
  end
87
-
88
- def pool_options
89
- {size: evaluate_option(:pool_size) || 5}
90
- end
91
71
  end
92
72
  end
93
73
  end
@@ -1,39 +1,7 @@
1
1
  module Noticed
2
2
  module DeliveryMethods
3
3
  class Slack < DeliveryMethod
4
- DEFAULT_URL = "https://slack.com/api/chat.postMessage"
5
-
6
- required_options :json
7
-
8
- def deliver
9
- headers = evaluate_option(:headers)
10
- json = evaluate_option(:json)
11
- response = post_request url, headers: headers, json: json
12
-
13
- if raise_if_not_ok? && !success?(response)
14
- raise ResponseUnsuccessful.new(response, url, {headers: headers, json: json})
15
- end
16
-
17
- response
18
- end
19
-
20
- def url
21
- evaluate_option(:url) || DEFAULT_URL
22
- end
23
-
24
- def raise_if_not_ok?
25
- value = evaluate_option(:raise_if_not_ok)
26
- value.nil? || value
27
- end
28
-
29
- def success?(response)
30
- if response.content_type == "application/json"
31
- JSON.parse(response.body).dig("ok")
32
- else
33
- # https://api.slack.com/changelog/2016-05-17-changes-to-errors-for-incoming-webhooks
34
- response.is_a?(Net::HTTPSuccess)
35
- end
36
- end
4
+ include SlackDelivery
37
5
  end
38
6
  end
39
7
  end
@@ -1,18 +1,7 @@
1
1
  module Noticed
2
2
  module DeliveryMethods
3
3
  class Webhook < DeliveryMethod
4
- required_options :url
5
-
6
- def deliver
7
- post_request(
8
- evaluate_option(:url),
9
- basic_auth: evaluate_option(:basic_auth),
10
- headers: evaluate_option(:headers),
11
- json: evaluate_option(:json),
12
- form: evaluate_option(:form),
13
- body: evaluate_option(:body)
14
- )
15
- end
4
+ include WebhookDelivery
16
5
  end
17
6
  end
18
7
  end
@@ -5,7 +5,7 @@ module Noticed
5
5
  #
6
6
  # class User < ApplicationRecord
7
7
  # has_noticed_notifications
8
- # has_noticed_notifications param_name: :owner, destroy: false, model: "Notification"
8
+ # has_noticed_notifications param_name: :owner, destroy: false, model_name: "Notification"
9
9
  # end
10
10
  #
11
11
  # @user.notifications_as_user
@@ -17,7 +17,7 @@ module Noticed
17
17
  def has_noticed_notifications(param_name: model_name.singular, **options)
18
18
  define_method :"notifications_as_#{param_name}" do
19
19
  model = options.fetch(:model_name, "Noticed::Event").constantize
20
- case current_adapter
20
+ case model.connection_db_config.adapter
21
21
  when "postgresql", "postgis"
22
22
  model.where("params @> ?", Noticed::Coder.dump(param_name.to_sym => self).to_json)
23
23
  when "mysql2", "trilogy"
@@ -37,13 +37,5 @@ module Noticed
37
37
  end
38
38
  end
39
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
40
  end
49
41
  end
@@ -0,0 +1,42 @@
1
+ module Noticed
2
+ # Shared between the individual and bulk Slack delivery methods
3
+ module SlackDelivery
4
+ extend ActiveSupport::Concern
5
+
6
+ DEFAULT_URL = "https://slack.com/api/chat.postMessage"
7
+
8
+ included do
9
+ required_options :json
10
+ end
11
+
12
+ def deliver
13
+ headers = evaluate_option(:headers)
14
+ json = evaluate_option(:json)
15
+ response = post_request url, headers: headers, json: json
16
+
17
+ if raise_if_not_ok? && !success?(response)
18
+ raise ResponseUnsuccessful.new(response, url, {headers: headers, json: json})
19
+ end
20
+
21
+ response
22
+ end
23
+
24
+ def url
25
+ evaluate_option(:url) || DEFAULT_URL
26
+ end
27
+
28
+ def raise_if_not_ok?
29
+ value = evaluate_option(:raise_if_not_ok)
30
+ value.nil? || value
31
+ end
32
+
33
+ def success?(response)
34
+ if response.content_type == "application/json"
35
+ JSON.parse(response.body).dig("ok")
36
+ else
37
+ # https://api.slack.com/changelog/2016-05-17-changes-to-errors-for-incoming-webhooks
38
+ response.is_a?(Net::HTTPSuccess)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -12,11 +12,7 @@ module Noticed
12
12
  end
13
13
 
14
14
  def translate(key, **options)
15
- if defined?(::ActiveSupport::HtmlSafeTranslation)
16
- ActiveSupport::HtmlSafeTranslation.translate scope_translation_key(key), **options
17
- else
18
- I18n.translate scope_translation_key(key), **options
19
- end
15
+ ActiveSupport::HtmlSafeTranslation.translate scope_translation_key(key), **options
20
16
  end
21
17
  alias_method :t, :translate
22
18
 
@@ -1,3 +1,3 @@
1
1
  module Noticed
2
- VERSION = "3.0.0"
2
+ VERSION = "3.1.0"
3
3
  end
@@ -0,0 +1,21 @@
1
+ module Noticed
2
+ # Shared between the individual and bulk Webhook delivery methods
3
+ module WebhookDelivery
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ required_options :url
8
+ end
9
+
10
+ def deliver
11
+ post_request(
12
+ evaluate_option(:url),
13
+ basic_auth: evaluate_option(:basic_auth),
14
+ headers: evaluate_option(:headers),
15
+ json: evaluate_option(:json),
16
+ form: evaluate_option(:form),
17
+ body: evaluate_option(:body)
18
+ )
19
+ end
20
+ end
21
+ end
data/lib/noticed.rb CHANGED
@@ -14,7 +14,7 @@ module Noticed
14
14
  include ActiveSupport::Deprecation::DeprecatedConstantAccessor
15
15
 
16
16
  def self.deprecator # :nodoc:
17
- @deprecator ||= ActiveSupport::Deprecation.new("3.0", "Noticed")
17
+ @deprecator ||= ActiveSupport::Deprecation.new("4.0", "Noticed")
18
18
  end
19
19
 
20
20
  deprecate_constant :Base, "Noticed::Event", deprecator: deprecator
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: noticed
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 3.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Oliver
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: 6.1.0
18
+ version: 7.1.0
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
- version: 6.1.0
25
+ version: 7.1.0
26
26
  description: Database, browser, realtime ActionCable, Email, SMS, Slack notifications,
27
27
  and more for Rails apps
28
28
  email:
@@ -82,12 +82,15 @@ files:
82
82
  - lib/noticed/has_notifications.rb
83
83
  - lib/noticed/notification_channel.rb
84
84
  - lib/noticed/required_options.rb
85
+ - lib/noticed/slack_delivery.rb
85
86
  - lib/noticed/translation.rb
86
87
  - lib/noticed/version.rb
88
+ - lib/noticed/webhook_delivery.rb
87
89
  homepage: https://github.com/excid3/noticed
88
90
  licenses:
89
91
  - MIT
90
- metadata: {}
92
+ metadata:
93
+ rubygems_mfa_required: 'true'
91
94
  rdoc_options: []
92
95
  require_paths:
93
96
  - lib
@@ -102,7 +105,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
102
105
  - !ruby/object:Gem::Version
103
106
  version: '0'
104
107
  requirements: []
105
- rubygems_version: 4.0.3
108
+ rubygems_version: 4.0.20
106
109
  specification_version: 4
107
110
  summary: Notifications for Ruby on Rails applications
108
111
  test_files: []