noticed 2.0.2 → 2.0.4

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: 2526071997479bc9e570c51d5b148ce569f9afb45912b6c2b1bea71be65fc30e
4
- data.tar.gz: ffa64bdc283245497a456f125680a41da2cf0ba7ad52c2c6320af24b5ec3d956
3
+ metadata.gz: af64af61a772e3e67456ecab3807b7ad1c9c689a8b411d45583f1c68f61ebc4e
4
+ data.tar.gz: eed9bec207e731b07727e7a85d500e90d7f9db6437cdfadc6ac0b39153a2bb11
5
5
  SHA512:
6
- metadata.gz: 1cf980bb8d8ae9057ff80bd6313158997b1cee1fd664e3e4fef819d1cf3ba5cac469e55e02e8c4d1a2e70bf53658a58cd6c524e93da00fb2dc7a228bdda1a195
7
- data.tar.gz: 109fe7195032238f53ed001910db4bdce5e58a89543b45c2aefc88046f7e649b68cd77421dc005fa932fe1984e610349597b1f5ffb60fde9550356b91585f5b9
6
+ metadata.gz: c442042bac58a37b9f3adca3b534c20839f662ce9c7d3336691425e0363990686bbfefbc42cfbf581593d11b2fc24ebea99c6b925bafde71934ba9d9ef580a8a
7
+ data.tar.gz: 26215e49a2786596852dcf539dab854a5e8550eb83c1240acfe872aa64a163227237ca212d6e9f936d537768df6117793fa2b9f1234cd525083393604860bf2b
data/README.md CHANGED
@@ -62,13 +62,13 @@ rails db:migrate
62
62
 
63
63
  Noticed operates with a few constructs: Notifiers, delivery methods, and Notification records.
64
64
 
65
- To start, generate a Notifier:
65
+ To start, generate a Notifier:
66
66
 
67
67
  ```sh
68
68
  rails generate noticed:notifier NewCommentNotifier
69
69
  ```
70
70
 
71
- #### Notifier Objects
71
+ ### Notifier Objects
72
72
 
73
73
  Notifiers are essentially the controllers of the Noticed ecosystem and represent an Event. As such, we recommend naming them with the event they model in mind — be it a `NewSaleNotifier,` `ChargeFailureNotifier`, etc.
74
74
 
@@ -117,7 +117,75 @@ end
117
117
 
118
118
  For deeper specifics on setting up the `:action_cable`, `:email`, and `:discord` (bulk) delivery methods, refer to their docs: [`action_cable`](docs/delivery_methods/action_cable.md), [`email`](docs/delivery_methods/email.md), and [`discord` (bulk)](docs/bulk_delivery_methods/discord.md).
119
119
 
120
- ##### Required Params
120
+ #### Delivery Method Configuration
121
+
122
+ Each delivery method can be configured with a block that yields a `config` object.
123
+
124
+ Procs/Lambdas will be evaluated when needed and symbols can be used to call a method.
125
+
126
+ 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
+ Your method must accept a single argument. If you don't need to use the object you can just use `(*)`.
128
+
129
+ ```ruby
130
+ class CommentNotifier < Noticed::Event
131
+ deliver_by :ios do |config|
132
+ config.format = :ios_format
133
+ config.apns_key = :ios_cert
134
+ config.key_id = :ios_key_id
135
+ config.team_id = :ios_team_id
136
+ config.bundle_identifier = Rails.application.credentials.dig(:ios, :bundle_identifier)
137
+ config.device_tokens = :ios_device_tokens
138
+ config.if = ->(notification) { recipient.send_ios_notification? }
139
+ end
140
+
141
+ def ios_format(apn)
142
+ apn.alert = { title:, body: }
143
+ apn.mutable_content = true
144
+ apn.content_available = true
145
+ apn.sound = "notification.m4r"
146
+ apn.custom_payload = {
147
+ url:,
148
+ type: self.class.name,
149
+ id: record.id,
150
+ image_url: "" || image_url,
151
+ params: params.to_json
152
+ }
153
+ end
154
+
155
+ def ios_cert(*)
156
+ Rails.application.credentials.dig(:ios, Rails.env.to_sym, :apns_token_cert)
157
+ end
158
+
159
+ def ios_key_id(*)
160
+ Rails.application.credentials.dig(:ios, Rails.env.to_sym, :key_id)
161
+ end
162
+
163
+ def ios_team_id(*)
164
+ Rails.application.credentials.dig(:ios, Rails.env.to_sym, :team_id)
165
+ end
166
+
167
+ def ios_bundle_id(*)
168
+ Rails.application.credentials.dig(:ios, Rails.env.to_sym, :bundle_identifier)
169
+ end
170
+
171
+ def ios_device_tokens(notification)
172
+ notification.recipient.ios_device_tokens
173
+ end
174
+
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
+ def url
183
+ comment_thread_path(record.thread)
184
+ end
185
+ end
186
+ ```
187
+
188
+ #### Required Params
121
189
 
122
190
  While explicit / required parameters are completely optional, Notifiers are able to opt in to required parameters via the `required_params` method:
123
191
 
@@ -126,7 +194,10 @@ class CarSaleNotifier < Noticed::Event
126
194
  deliver_by :email { |c| c.mailer = "BranchMailer" }
127
195
 
128
196
  # `record` is the Car record, `Branch` is the dealership
129
- required_params :record, :branch
197
+ required_params :branch
198
+
199
+ # To validate the `:record` param, add a validation since it is an association on the Noticed::Event
200
+ validates :record, presence: true
130
201
  end
131
202
  ```
132
203
 
@@ -141,8 +212,7 @@ CarSaleNotifier.with(record: Car.last, branch: Branch.last).deliver(Branch.hq)
141
212
  ```
142
213
 
143
214
 
144
-
145
- ##### Helper Methods
215
+ #### Helper Methods
146
216
 
147
217
  Notifiers can implement various helper methods, within a `notification_methods` block, that make it easier to render the resulting notification directly. These helpers can be helpful depending on where and how you choose to render notifications. A common use is rendering a user’s notifications in your web UI as standard ERB. These notification helper methods make that rendering much simpler:
148
218
 
@@ -156,7 +226,7 @@ Notifiers can implement various helper methods, within a `notification_methods`
156
226
 
157
227
  On the other hand, if you’re using email delivery, ActionMailer has its own full stack for setting up objects and rendering. Your notification helper methods will always be available from the notification object, but using ActionMailer’s own paradigms may fit better for that particular delivery method. YMMV.
158
228
 
159
- ###### URL Helpers
229
+ #### URL Helpers
160
230
 
161
231
  Rails url helpers are included in Notifiers by default so you have full access to them in your notification helper methods, just like you would in your controllers and views.
162
232
 
@@ -166,7 +236,7 @@ _But don't forget_, you'll need to configure `default_url_options` in order for
166
236
  Rails.application.routes.default_url_options[:host] = 'localhost:3000'
167
237
  ```
168
238
 
169
- ###### Translations
239
+ #### Translations
170
240
 
171
241
  We've also included Rails’ `translate` and `t` helpers for you to use in your notification helper methods. This also provides an easy way of scoping translations. If the key starts with a period, it will automatically scope the key under `notifiers`, the underscored name of the notifier class, and `notification`. For example:
172
242
 
@@ -175,13 +245,13 @@ From the above Notifier...
175
245
  ```ruby
176
246
  class NewCommentNotifier < Noticed::Event
177
247
  # ...
178
-
248
+
179
249
  notification_methods do
180
250
  def message
181
251
  t(".message")
182
252
  end
183
253
  end
184
-
254
+
185
255
  # ...
186
256
  end
187
257
  ```
@@ -206,7 +276,7 @@ en:
206
276
 
207
277
  Or, if you have your Notifier within another module, such as `Admin::NewCommentNotifier`, the resulting lookup path will be `en.notifiers.admin.new_comment_notifier.notification.message` (modules become nesting steps).
208
278
 
209
- ##### Tip: Capture User Preferences
279
+ #### Tip: Capture User Preferences
210
280
 
211
281
  You can use the `if:` and `unless: ` options on your delivery methods to check the user's preferences and skip processing if they have disabled that type of notification.
212
282
 
@@ -221,8 +291,42 @@ class CommentNotifier < Noticed::Event
221
291
  end
222
292
  end
223
293
  ```
294
+ #### Tip: Extracting Delivery Method Configurations
295
+
296
+ If you want to reuse delivery method configurations across multiple Notifiers, you can extract them into a module and include them in your Notifiers.
297
+
298
+ ```ruby
299
+ # /app/notifiers/notifiers/comment_notifier.rb
300
+ class CommentNotifier < Noticed::Event
301
+ include IosNotifier
302
+ include AndriodNotifer
303
+ include EmailNotifier
304
+
305
+ validates :record, presence: true
306
+ end
307
+
308
+ # /app/notifiers/concerns/ios_notifier.rb
309
+ module IosNotifier
310
+ extend ActiveSupport::Concern
311
+
312
+ included do
313
+ deliver_by :ios do |config|
314
+ config.device_tokens = ->(recipient) { recipient.notification_tokens.where(platform: :iOS).pluck(:token) }
315
+ config.format = ->(apn) {
316
+ apn.alert = "Hello world"
317
+ apn.custom_payload = {url: root_url(host: "example.org")}
318
+ }
319
+ config.bundle_identifier = Rails.application.credentials.dig(:ios, :bundle_id)
320
+ config.key_id = Rails.application.credentials.dig(:ios, :key_id)
321
+ config.team_id = Rails.application.credentials.dig(:ios, :team_id)
322
+ config.apns_key = Rails.application.credentials.dig(:ios, :apns_key)
323
+ config.if = ->(recipient) { recipient.ios_notifications? }
324
+ end
325
+ end
326
+ end
327
+ ```
224
328
 
225
- **Shared Delivery Method Options**
329
+ #### Shared Delivery Method Options
226
330
 
227
331
  Each of these options are available for every delivery method (individual or bulk). The value passed may be a lambda, a symbol that represents a callable method, a symbol value, or a string value.
228
332
 
@@ -232,7 +336,7 @@ Each of these options are available for every delivery method (individual or bul
232
336
  * `config.wait_until` — (Should yield a specific time object) Delays the job that runs this delivery method until the specific time specified
233
337
  * `config.queue` — Sets the ActiveJob queue name to be used for the job that runs this delivery method
234
338
 
235
- #### Sending Notifications
339
+ ### Sending Notifications
236
340
 
237
341
  Following the `NewCommentNotifier` example above, here’s how we might invoke the Notifier to send notifications to every author in the thread about a new comment being added:
238
342
 
@@ -400,6 +504,18 @@ class DeliveryMethods::WhatsApp < Noticed::DeliveryMethod
400
504
  end
401
505
  ```
402
506
 
507
+ #### Callbacks
508
+
509
+ 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.
510
+
511
+ ```ruby
512
+ class DeliveryMethods::Discord < Noticed::DeliveryMethod
513
+ after_deliver do
514
+ # Do whatever you want
515
+ end
516
+ end
517
+ ```
518
+
403
519
  ### 📦 Database Model
404
520
 
405
521
  The Noticed database models include several helpful features to make working with notifications easier.
@@ -484,6 +600,14 @@ end
484
600
  # @post.noticed_events.each { |ne| ne.notifications... }
485
601
  ```
486
602
 
603
+ #### ActiveJob Parent Class
604
+
605
+ Noticed uses its own `Noticed::ApplicationJob` as the base job for all notifications. In the event that you would like to customize the parent job class, there is a `parent_class` attribute that can be overridden with your own class. This should be done in a `noticed.rb` initializer.
606
+
607
+ ```ruby
608
+ Noticed.parent_class = "ApplicationJob"
609
+ ```
610
+
487
611
  #### Handling Deleted Records
488
612
 
489
613
  Generally we recommend using a `dependent: ___` relationship on your models to avoid cases where Noticed Events or Notifications are left lingering when your models are destroyed. In the case that they are or data becomes mis-matched, you’ll likely run into deserialization issues. That may be globally alleviated with the following snippet, but use with caution.
@@ -508,4 +632,3 @@ DATABASE_URL=postgres://127.0.0.1/noticed_test rails test
508
632
 
509
633
  ## 📝 License
510
634
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
511
-
@@ -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
@@ -91,7 +91,7 @@ module Noticed
91
91
  def recipient_attributes_for(recipient)
92
92
  {
93
93
  type: "#{self.class.name}::Notification",
94
- recipient_type: recipient.class.name,
94
+ recipient_type: recipient.class.base_class.name,
95
95
  recipient_id: recipient.id
96
96
  }
97
97
  end
@@ -103,7 +103,7 @@ module Noticed
103
103
 
104
104
  def validate_params!
105
105
  required_param_names.each do |param_name|
106
- raise ValidationError, "Param `#{param_name}` is required for #{self.class.name}." unless params[param_name].present?
106
+ raise ValidationError, "Param `#{param_name}` is required for #{self.class.name}." unless params.has_key?(param_name)
107
107
  end
108
108
  end
109
109
 
@@ -12,6 +12,7 @@ module Noticed
12
12
  desc "Generates a class for a custom delivery method with the given NAME."
13
13
 
14
14
  def generate_notification
15
+ template "application_delivery_method.rb", "app/notifiers/application_delivery_method.rb"
15
16
  template "delivery_method.rb", "app/notifiers/delivery_methods/#{singular_name}.rb"
16
17
  end
17
18
  end
@@ -7,12 +7,21 @@ module Noticed
7
7
  class NotifierGenerator < Rails::Generators::NamedBase
8
8
  include Rails::Generators::ResourceHelpers
9
9
 
10
+ check_class_collision suffix: "Notifier"
11
+
10
12
  source_root File.expand_path("../templates", __FILE__)
11
13
 
12
14
  desc "Generates a notification with the given NAME."
13
15
 
14
16
  def generate_notification
15
- template "notifier.rb", "app/notifiers/#{file_path}.rb"
17
+ template "application_notifier.rb", "app/notifiers/application_notifier.rb"
18
+ template "notifier.rb", "app/notifiers/#{file_path}_notifier.rb"
19
+ end
20
+
21
+ private
22
+
23
+ def file_name # :doc:
24
+ @_file_name ||= super.sub(/_notifier\z/i, "")
16
25
  end
17
26
  end
18
27
  end
@@ -0,0 +1,2 @@
1
+ class ApplicationDeliveryMethod < Noticed::DeliveryMethod
2
+ end
@@ -0,0 +1,2 @@
1
+ class ApplicationNotifier < Noticed::Event
2
+ end
@@ -1,4 +1,4 @@
1
- class DeliveryMethods::<%= class_name %> < Noticed::DeliveryMethod
1
+ class DeliveryMethods::<%= class_name %> < ApplicationDeliveryMethod
2
2
  # Specify the config options your delivery method requires in its config block
3
3
  required_options # :foo, :bar
4
4
 
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # <%= class_name %>.with(record: @post, message: "New post").deliver(User.all)
4
4
 
5
- class <%= class_name %> < Noticed::Event
5
+ class <%= class_name %>Notifier < ApplicationNotifier
6
6
  # Add your delivery methods
7
7
  #
8
8
  # deliver_by :email do |config|
@@ -1,8 +1,11 @@
1
1
  module Noticed
2
- class DeliveryMethod < ApplicationJob
2
+ class DeliveryMethod < Noticed.parent_class.constantize
3
3
  include ApiClient
4
4
  include RequiredOptions
5
5
 
6
+ extend ActiveModel::Callbacks
7
+ define_model_callbacks :deliver
8
+
6
9
  class_attribute :logger, default: Rails.logger
7
10
 
8
11
  attr_reader :config, :event, :notification
@@ -19,7 +22,9 @@ module Noticed
19
22
  return false if config.has_key?(:if) && !evaluate_option(:if)
20
23
  return false if config.has_key?(:unless) && evaluate_option(:unless)
21
24
 
22
- deliver
25
+ run_callbacks :deliver do
26
+ deliver
27
+ end
23
28
  end
24
29
 
25
30
  def deliver
@@ -40,7 +45,7 @@ module Noticed
40
45
 
41
46
  # Call method if symbol and matching method on Notifier
42
47
  elsif option.is_a?(Symbol) && event.respond_to?(option)
43
- event.send(option, self)
48
+ event.send(option, notification)
44
49
 
45
50
  # Return the value
46
51
  else
@@ -14,7 +14,7 @@ module Noticed
14
14
  def send_notification(device_token)
15
15
  post_request("https://fcm.googleapis.com/v1/projects/#{credentials[:project_id]}/messages:send",
16
16
  headers: {authorization: "Bearer #{access_token}"},
17
- json: notification.instance_exec(device_token, &config[:json]))
17
+ json: format_notification(device_token))
18
18
  rescue Noticed::ResponseUnsuccessful => exception
19
19
  if exception.response.code == "404" && config[:invalid_token]
20
20
  notification.instance_exec(device_token, &config[:invalid_token])
@@ -23,6 +23,15 @@ module Noticed
23
23
  end
24
24
  end
25
25
 
26
+ def format_notification(device_token)
27
+ method = config[:json]
28
+ if method.is_a?(Symbol) && event.respond_to?(method)
29
+ event.send(method, device_token)
30
+ else
31
+ notification.instance_exec(device_token, &method)
32
+ end
33
+ end
34
+
26
35
  def credentials
27
36
  @credentials ||= begin
28
37
  value = evaluate_option(:credentials)
@@ -33,6 +33,7 @@ module Noticed
33
33
  apn.topic = evaluate_option(:bundle_identifier)
34
34
 
35
35
  if (method = config[:format])
36
+ method = event.send(method, apn) if method.is_a?(Symbol) && event.respond_to?(method)
36
37
  notification.instance_exec(apn, &method)
37
38
  elsif notification.params.try(:has_key?, :message)
38
39
  apn.alert = notification.params[:message]
@@ -70,9 +71,9 @@ module Noticed
70
71
  def connection_pool_options
71
72
  {
72
73
  auth_method: :token,
73
- cert_path: StringIO.new(config.fetch(:apns_key)),
74
- key_id: config.fetch(:key_id),
75
- team_id: config.fetch(:team_id)
74
+ cert_path: StringIO.new(evaluate_option(:apns_key)),
75
+ key_id: evaluate_option(:key_id),
76
+ team_id: evaluate_option(:team_id)
76
77
  }
77
78
  end
78
79
 
@@ -1,3 +1,3 @@
1
1
  module Noticed
2
- VERSION = "2.0.2"
2
+ VERSION = "2.0.4"
3
3
  end
data/lib/noticed.rb CHANGED
@@ -39,6 +39,9 @@ module Noticed
39
39
  autoload :Webhook, "noticed/delivery_methods/webhook"
40
40
  end
41
41
 
42
+ mattr_accessor :parent_class
43
+ @@parent_class = "Noticed::ApplicationJob"
44
+
42
45
  class ValidationError < StandardError
43
46
  end
44
47
 
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.2
4
+ version: 2.0.4
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-19 00:00:00.000000000 Z
11
+ date: 2024-01-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -50,6 +50,8 @@ files:
50
50
  - lib/generators/noticed/install_generator.rb
51
51
  - lib/generators/noticed/notifier_generator.rb
52
52
  - lib/generators/noticed/templates/README
53
+ - lib/generators/noticed/templates/application_delivery_method.rb.tt
54
+ - lib/generators/noticed/templates/application_notifier.rb.tt
53
55
  - lib/generators/noticed/templates/delivery_method.rb.tt
54
56
  - lib/generators/noticed/templates/notifier.rb.tt
55
57
  - lib/noticed.rb
@@ -94,7 +96,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
94
96
  - !ruby/object:Gem::Version
95
97
  version: '0'
96
98
  requirements: []
97
- rubygems_version: 3.5.4
99
+ rubygems_version: 3.5.3
98
100
  signing_key:
99
101
  specification_version: 4
100
102
  summary: Notifications for Ruby on Rails applications