activity_notification 2.5.1 → 2.6.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 +4 -4
- data/README.md +2 -0
- data/app/channels/activity_notification/notification_api_channel.rb +5 -5
- data/app/channels/activity_notification/notification_api_with_devise_channel.rb +4 -4
- data/app/channels/activity_notification/notification_channel.rb +4 -0
- data/app/channels/activity_notification/notification_with_devise_channel.rb +4 -4
- data/app/controllers/activity_notification/notifications_controller.rb +1 -2
- data/app/controllers/activity_notification/subscriptions_controller.rb +2 -3
- data/app/jobs/activity_notification/notify_all_job.rb +2 -2
- data/app/jobs/activity_notification/notify_job.rb +2 -2
- data/app/jobs/activity_notification/notify_to_job.rb +1 -1
- data/app/mailers/activity_notification/mailer.rb +1 -1
- data/app/views/activity_notification/mailer/default/batch_default.text.erb +1 -1
- data/app/views/activity_notification/notifications/default/index.html.erb +1 -1
- data/app/views/activity_notification/subscriptions/default/_notification_keys.html.erb +1 -1
- data/app/views/activity_notification/subscriptions/default/_subscription.html.erb +1 -1
- data/docs/Functions.md +93 -9
- data/docs/Setup.md +7 -7
- data/docs/Testing.md +1 -1
- data/docs/Upgrade-to-2.6.md +108 -0
- data/lib/activity_notification/apis/notification_api.rb +55 -40
- data/lib/activity_notification/apis/subscription_api.rb +10 -10
- data/lib/activity_notification/common.rb +5 -5
- data/lib/activity_notification/config.rb +15 -5
- data/lib/activity_notification/controllers/common_controller.rb +2 -4
- data/lib/activity_notification/controllers/devise_authentication_controller.rb +2 -2
- data/lib/activity_notification/helpers/polymorphic_helpers.rb +6 -6
- data/lib/activity_notification/helpers/view_helpers.rb +3 -3
- data/lib/activity_notification/mailers/helpers.rb +88 -2
- data/lib/activity_notification/models/concerns/notifiable.rb +60 -30
- data/lib/activity_notification/models/concerns/notifier.rb +1 -1
- data/lib/activity_notification/models/concerns/subscriber.rb +72 -15
- data/lib/activity_notification/models/concerns/target.rb +38 -35
- data/lib/activity_notification/optional_targets/action_cable_api_channel.rb +1 -1
- data/lib/activity_notification/optional_targets/slack.rb +2 -2
- data/lib/activity_notification/orm/active_record/notification.rb +25 -25
- data/lib/activity_notification/orm/active_record/subscription.rb +21 -1
- data/lib/activity_notification/orm/dynamoid/extension.rb +3 -3
- data/lib/activity_notification/orm/dynamoid/subscription.rb +8 -1
- data/lib/activity_notification/orm/dynamoid.rb +18 -18
- data/lib/activity_notification/orm/mongoid/notification.rb +26 -28
- data/lib/activity_notification/orm/mongoid/subscription.rb +21 -1
- data/lib/activity_notification/orm/mongoid.rb +1 -1
- data/lib/activity_notification/rails/routes.rb +11 -11
- data/lib/activity_notification/roles/acts_as_group.rb +1 -1
- data/lib/activity_notification/roles/acts_as_notifiable.rb +5 -5
- data/lib/activity_notification/roles/acts_as_notifier.rb +1 -1
- data/lib/activity_notification/roles/acts_as_target.rb +1 -1
- data/lib/activity_notification/version.rb +1 -1
- data/lib/generators/activity_notification/add_notifiable_to_subscriptions/add_notifiable_to_subscriptions_generator.rb +23 -0
- data/lib/generators/activity_notification/add_notifiable_to_subscriptions/templates/add_notifiable_to_subscriptions.rb +13 -0
- data/lib/generators/templates/activity_notification.rb +14 -2
- data/lib/generators/templates/migrations/migration.rb +4 -2
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ba3fa5f43981ca60a99f7b6ace089f4ceca465187b886b022bfca601df919bac
|
|
4
|
+
data.tar.gz: b09792d6faae2462287d227d2b12ac279bada455c40ab550f4894b373db57481
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bbd0135c886fec12c0a93955fb65ef8d6539b2353c88210e5986d50be428a4a7b03a0c015b23777d47011005889869f5bcb126f1d8a833127e9ef9debe98a47f
|
|
7
|
+
data.tar.gz: ee2f15a533731db9a4d9bc6a65b7d5935f38daa138885806774134da4cdc831b5acb51116c9af1bd83c154b070d83c178d1768a51eb3c27b182171f830e80047
|
data/README.md
CHANGED
|
@@ -23,10 +23,12 @@
|
|
|
23
23
|
* Automatic tracked notifications (generating notifications along with the lifecycle of notifiable models)
|
|
24
24
|
* Grouping notifications (grouping like *"Kevin and 7 other users posted comments to this article"*)
|
|
25
25
|
* Email notification
|
|
26
|
+
* Email attachments (configurable at global, target, and notifiable levels)
|
|
26
27
|
* Batch email notification (event driven or periodical email notification, daily or weekly etc)
|
|
27
28
|
* Cascading notifications (progressive notification escalation through multiple channels with time delays)
|
|
28
29
|
* Push notification with [Action Cable](https://guides.rubyonrails.org/action_cable_overview.html)
|
|
29
30
|
* Subscription management (subscribing and unsubscribing for each target and notification type)
|
|
31
|
+
* Instance-level subscriptions (subscribing to notifications from a specific notifiable instance)
|
|
30
32
|
* REST API backend and [OpenAPI Specification](https://github.com/OAI/OpenAPI-Specification)
|
|
31
33
|
* Integration with [Devise](https://github.com/plataformatec/devise) authentication
|
|
32
34
|
* Activity notifications stream integrated into cloud computing using [Amazon DynamoDB Streams](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Streams.html)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
# Action Cable API channel to subscribe broadcasted notifications.
|
|
2
|
+
class ActivityNotification::NotificationApiChannel < ActivityNotification::NotificationChannel
|
|
3
|
+
if defined?(ActionCable)
|
|
4
|
+
# ActionCable::Channel::Base#subscribed
|
|
5
|
+
# @see https://api.rubyonrails.org/classes/ActionCable/Channel/Base.html#method-i-subscribed
|
|
6
6
|
def subscribed
|
|
7
7
|
stream_from "#{ActivityNotification.config.notification_api_channel_prefix}_#{@target.to_class_name}#{ActivityNotification.config.composite_key_delimiter}#{@target.id}"
|
|
8
8
|
rescue
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
# Action Cable API channel to subscribe broadcasted notifications with Devise authentication.
|
|
2
|
+
class ActivityNotification::NotificationApiWithDeviseChannel < ActivityNotification::NotificationApiChannel
|
|
3
|
+
if defined?(ActionCable)
|
|
4
4
|
# Include PolymorphicHelpers to resolve string extentions
|
|
5
5
|
include ActivityNotification::PolymorphicHelpers
|
|
6
6
|
|
|
@@ -18,7 +18,7 @@ if defined?(ActionCable)
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
# Set @target instance variable from request parameters.
|
|
21
|
-
# This method overrides super (
|
|
21
|
+
# This method overrides super (ActivityNotification::NotificationChannel#set_target)
|
|
22
22
|
# to set devise authenticated target when the target_id params is not specified.
|
|
23
23
|
# @api protected
|
|
24
24
|
# @return [Object] Target instance (Reject subscription when request parameters are not enough)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
# Action Cable channel to subscribe broadcasted notifications with Devise authentication.
|
|
2
|
+
class ActivityNotification::NotificationWithDeviseChannel < ActivityNotification::NotificationChannel
|
|
3
|
+
if defined?(ActionCable)
|
|
4
4
|
# Include PolymorphicHelpers to resolve string extentions
|
|
5
5
|
include ActivityNotification::PolymorphicHelpers
|
|
6
6
|
|
|
@@ -23,7 +23,7 @@ if defined?(ActionCable)
|
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
# Sets @target instance variable from request parameters.
|
|
26
|
-
# This method override super (
|
|
26
|
+
# This method override super (ActivityNotification::NotificationChannel#set_target)
|
|
27
27
|
# to set devise authenticated target when the target_id params is not specified.
|
|
28
28
|
# @api protected
|
|
29
29
|
# @return [Object] Target instance (Reject subscription when request parameters are not enough)
|
|
@@ -159,8 +159,7 @@ module ActivityNotification
|
|
|
159
159
|
limit = params[:limit].to_i > 0 ? params[:limit].to_i : nil
|
|
160
160
|
reverse = params[:reverse].present? ?
|
|
161
161
|
params[:reverse].to_s.to_boolean(false) : nil
|
|
162
|
-
with_group_members = params[:with_group_members].present? || params[:without_grouping].present? ?
|
|
163
|
-
params[:with_group_members].to_s.to_boolean(false) || params[:without_grouping].to_s.to_boolean(false) : nil
|
|
162
|
+
with_group_members = params[:with_group_members].present? || params[:without_grouping].present? ? params[:with_group_members].to_s.to_boolean(false) || params[:without_grouping].to_s.to_boolean(false) : nil
|
|
164
163
|
@index_options = params.permit(:filter, :filtered_by_type, :filtered_by_group_type, :filtered_by_group_id, :filtered_by_key, :later_than, :earlier_than, :routing_scope, :devise_default_routes)
|
|
165
164
|
.to_h.symbolize_keys
|
|
166
165
|
.merge(limit: limit, reverse: reverse, with_group_members: with_group_members)
|
|
@@ -191,7 +191,7 @@ module ActivityNotification
|
|
|
191
191
|
params[:subscription][:optional_targets][optional_target_key] = boolean_value
|
|
192
192
|
end
|
|
193
193
|
end
|
|
194
|
-
params.require(:subscription).permit(:key, :subscribing, :subscribing_to_email, optional_targets: optional_target_keys)
|
|
194
|
+
params.require(:subscription).permit(:key, :subscribing, :subscribing_to_email, :notifiable_type, :notifiable_id, optional_targets: optional_target_keys)
|
|
195
195
|
end
|
|
196
196
|
|
|
197
197
|
# Sets options to load subscription index from request parameters.
|
|
@@ -199,8 +199,7 @@ module ActivityNotification
|
|
|
199
199
|
# @return [Hash] options to load subscription index
|
|
200
200
|
def set_index_options
|
|
201
201
|
limit = params[:limit].to_i > 0 ? params[:limit].to_i : nil
|
|
202
|
-
reverse = params[:reverse].present? ?
|
|
203
|
-
params[:reverse].to_s.to_boolean(false) : nil
|
|
202
|
+
reverse = params[:reverse].present? ? params[:reverse].to_s.to_boolean(false) : nil
|
|
204
203
|
@index_options = params.permit(:filter, :filtered_by_key, :routing_scope, :devise_default_routes)
|
|
205
204
|
.to_h.symbolize_keys.merge(limit: limit, reverse: reverse)
|
|
206
205
|
end
|
|
@@ -16,8 +16,8 @@ if defined?(ActiveJob)
|
|
|
16
16
|
# @option options [Boolean] :send_email (true) Whether it sends notification email
|
|
17
17
|
# @option options [Boolean] :send_later (true) Whether it sends notification email asynchronously
|
|
18
18
|
# @option options [Boolean] :publish_optional_targets (true) Whether it publishes notification to optional targets
|
|
19
|
-
# @option options [Hash<String, Hash>] :optional_targets ({}) Options for optional targets, keys are optional target name (:amazon_sns or :slack etc) and values are options
|
|
20
|
-
# @return [Array<
|
|
19
|
+
# @option options [Hash<String, Hash>] :optional_targets ({}) Options for optional targets, keys are optional target name (:amazon_sns or :slack etc.) and values are options
|
|
20
|
+
# @return [Array<Notification>] Array of generated notifications
|
|
21
21
|
def perform(targets, notifiable, options = {})
|
|
22
22
|
ActivityNotification::Notification.notify_all(targets, notifiable, options)
|
|
23
23
|
end
|
|
@@ -17,8 +17,8 @@ if defined?(ActiveJob)
|
|
|
17
17
|
# @option options [Boolean] :send_later (true) Whether it sends notification email asynchronously
|
|
18
18
|
# @option options [Boolean] :publish_optional_targets (true) Whether it publishes notification to optional targets
|
|
19
19
|
# @option options [Boolean] :pass_full_options (false) Whether it passes full options to notifiable.notification_targets, not a key only
|
|
20
|
-
# @option options [Hash<String, Hash>] :optional_targets ({}) Options for optional targets, keys are optional target name (:amazon_sns or :slack etc) and values are options
|
|
21
|
-
# @return [Array<
|
|
20
|
+
# @option options [Hash<String, Hash>] :optional_targets ({}) Options for optional targets, keys are optional target name (:amazon_sns or :slack etc.) and values are options
|
|
21
|
+
# @return [Array<Notification>] Array of generated notifications
|
|
22
22
|
def perform(target_type, notifiable, options = {})
|
|
23
23
|
ActivityNotification::Notification.notify(target_type, notifiable, options)
|
|
24
24
|
end
|
|
@@ -16,7 +16,7 @@ if defined?(ActiveJob)
|
|
|
16
16
|
# @option options [Boolean] :send_email (true) Whether it sends notification email
|
|
17
17
|
# @option options [Boolean] :send_later (true) Whether it sends notification email asynchronously
|
|
18
18
|
# @option options [Boolean] :publish_optional_targets (true) Whether it publishes notification to optional targets
|
|
19
|
-
# @option options [Hash<String, Hash>] :optional_targets ({}) Options for optional targets, keys are optional target name (:amazon_sns or :slack etc) and values are options
|
|
19
|
+
# @option options [Hash<String, Hash>] :optional_targets ({}) Options for optional targets, keys are optional target name (:amazon_sns or :slack etc.) and values are options
|
|
20
20
|
# @return [Notification] Generated notification instance
|
|
21
21
|
def perform(target, notifiable, options = {})
|
|
22
22
|
ActivityNotification::Notification.notify_to(target, notifiable, options)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
if defined?(ActionMailer)
|
|
2
|
-
# Mailer for email notification of
|
|
2
|
+
# Mailer for email notification of ActivityNotification.
|
|
3
3
|
class ActivityNotification::Mailer < ActivityNotification.config.parent_mailer.constantize
|
|
4
4
|
include ActivityNotification::Mailers::Helpers
|
|
5
5
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Dear <%= @target.printable_target_name %>
|
|
2
2
|
|
|
3
|
-
You have
|
|
3
|
+
You have received the following notifications.
|
|
4
4
|
|
|
5
5
|
<% @notifications.each do |notification| %>
|
|
6
6
|
<%= notification.notifier.present? ? notification.notifier.printable_notifier_name : 'Someone' %> notified you of <%= notification.notifiable.printable_notifiable_name(notification.target) %><%= " in #{notification.group.printable_group_name}" if notification.group.present? %>.
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
$(".notifications").prepend(notification.group_owner_view);
|
|
68
68
|
$(".notification_count").html("<span class=unopened>" + notification.unopened_notification_count + "</span>");
|
|
69
69
|
}
|
|
70
|
-
// Push
|
|
70
|
+
// Push notification using Web Notification API by Push.js
|
|
71
71
|
Push.create('ActivityNotification', {
|
|
72
72
|
body: notification.text,
|
|
73
73
|
timeout: 5000,
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
<% target.notifications.filtered_by_key(key).latest.optional_target_names.each do |optional_target_name| %>
|
|
50
50
|
<div class="field_label">
|
|
51
51
|
<label>
|
|
52
|
-
Optional
|
|
52
|
+
Optional target (<%= optional_target_name %>)
|
|
53
53
|
</label>
|
|
54
54
|
</div>
|
|
55
55
|
<div class="field">
|
data/docs/Functions.md
CHANGED
|
@@ -47,6 +47,19 @@ class Comment < ActiveRecord::Base
|
|
|
47
47
|
end
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
+
You can also control email delivery per-notification by overriding `notification_email_allowed?` on the notifiable model:
|
|
51
|
+
|
|
52
|
+
```ruby
|
|
53
|
+
class Comment < ActiveRecord::Base
|
|
54
|
+
# ...acts_as_notifiable configuration...
|
|
55
|
+
|
|
56
|
+
def notification_email_allowed?(target, key)
|
|
57
|
+
# Example: skip email for comments on draft articles
|
|
58
|
+
!article.draft?
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
```
|
|
62
|
+
|
|
50
63
|
#### Sender configuration
|
|
51
64
|
|
|
52
65
|
You can configure the notification *"from"* address inside of *activity_notification.rb* in two ways.
|
|
@@ -202,6 +215,76 @@ class Article < ActiveRecord::Base
|
|
|
202
215
|
end
|
|
203
216
|
```
|
|
204
217
|
|
|
218
|
+
#### Email attachments
|
|
219
|
+
|
|
220
|
+
*activity_notification* supports email attachments at three levels with the same priority order as CC:
|
|
221
|
+
|
|
222
|
+
1. **Notifiable model override** (highest priority) - using `overriding_notification_email_attachments` method
|
|
223
|
+
2. **Target model method** - using `mailer_attachments` method
|
|
224
|
+
3. **Global configuration** - using `config.mailer_attachments` setting
|
|
225
|
+
|
|
226
|
+
Attachments are specified as a Hash (or Array of Hashes) with `:filename` and either `:content` (binary data) or `:path` (local file path). An optional `:mime_type` can be provided; otherwise it is inferred from the filename.
|
|
227
|
+
|
|
228
|
+
##### Global attachment configuration
|
|
229
|
+
|
|
230
|
+
Configure default attachments in *activity_notification.rb* initializer:
|
|
231
|
+
|
|
232
|
+
```ruby
|
|
233
|
+
# Single attachment from a local file
|
|
234
|
+
config.mailer_attachments = {
|
|
235
|
+
filename: 'terms.pdf',
|
|
236
|
+
path: Rails.root.join('public', 'terms.pdf')
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
# Multiple attachments
|
|
240
|
+
config.mailer_attachments = [
|
|
241
|
+
{ filename: 'logo.png', path: Rails.root.join('app/assets/images/logo.png') },
|
|
242
|
+
{ filename: 'terms.pdf', path: Rails.root.join('public', 'terms.pdf') }
|
|
243
|
+
]
|
|
244
|
+
|
|
245
|
+
# Dynamic attachments based on notification key
|
|
246
|
+
config.mailer_attachments = ->(key) {
|
|
247
|
+
if key.include?('invoice')
|
|
248
|
+
{ filename: 'invoice.pdf', content: generate_invoice_pdf }
|
|
249
|
+
else
|
|
250
|
+
nil # No attachments
|
|
251
|
+
end
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
##### Target-level attachment configuration
|
|
256
|
+
|
|
257
|
+
Define `mailer_attachments` method in your target model:
|
|
258
|
+
|
|
259
|
+
```ruby
|
|
260
|
+
class User < ActiveRecord::Base
|
|
261
|
+
acts_as_target
|
|
262
|
+
|
|
263
|
+
def mailer_attachments
|
|
264
|
+
if admin?
|
|
265
|
+
{ filename: 'admin_guide.pdf', path: Rails.root.join('docs', 'admin_guide.pdf') }
|
|
266
|
+
else
|
|
267
|
+
nil # Falls back to global config
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
##### Notifiable-level attachment override
|
|
274
|
+
|
|
275
|
+
For per-notification attachments, implement `overriding_notification_email_attachments` in your notifiable model:
|
|
276
|
+
|
|
277
|
+
```ruby
|
|
278
|
+
class Invoice < ActiveRecord::Base
|
|
279
|
+
acts_as_notifiable :users,
|
|
280
|
+
targets: ->(invoice, key) { [invoice.user] }
|
|
281
|
+
|
|
282
|
+
def overriding_notification_email_attachments(target, key)
|
|
283
|
+
{ filename: "invoice_#{number}.pdf", content: generate_pdf }
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
```
|
|
287
|
+
|
|
205
288
|
#### i18n for email
|
|
206
289
|
|
|
207
290
|
The subject of notification email can be put in your locale *.yml* files as **mail_subject** field:
|
|
@@ -525,7 +608,7 @@ config.subscribe_to_email_as_default = false
|
|
|
525
608
|
config.subscribe_to_optional_targets_as_default = true
|
|
526
609
|
```
|
|
527
610
|
|
|
528
|
-
However if **subscribe_as_default** is not enabled, **subscribe_to_email_as_default** and **subscribe_to_optional_targets_as_default** won't change anything.
|
|
611
|
+
However, if **subscribe_as_default** is not enabled, **subscribe_to_email_as_default** and **subscribe_to_optional_targets_as_default** won't change anything.
|
|
529
612
|
|
|
530
613
|
##### Creating and updating subscriptions
|
|
531
614
|
|
|
@@ -860,7 +943,7 @@ See [Devise Token Auth documents](https://devise-token-auth.gitbook.io/devise-to
|
|
|
860
943
|
|
|
861
944
|
*activity_notification* supports push notification with Action Cable by WebSocket.
|
|
862
945
|
*activity_notification* only provides Action Cable channels implementation, does not connections.
|
|
863
|
-
You can use default
|
|
946
|
+
You can use default implementation in Rails or your custom `ApplicationCable::Connection` for Action Cable connections.
|
|
864
947
|
|
|
865
948
|
#### Enabling broadcasting notifications to channels
|
|
866
949
|
|
|
@@ -928,7 +1011,7 @@ You can simply create subscriptions for the specified target in your view like t
|
|
|
928
1011
|
received: function(notification) {
|
|
929
1012
|
// Display notification
|
|
930
1013
|
|
|
931
|
-
// Push
|
|
1014
|
+
// Push notification using Web Notification API by Push.js
|
|
932
1015
|
Push.create('ActivityNotification', {
|
|
933
1016
|
body: notification.text,
|
|
934
1017
|
timeout: 5000,
|
|
@@ -978,7 +1061,7 @@ export default {
|
|
|
978
1061
|
notify (data) {
|
|
979
1062
|
// Display notification
|
|
980
1063
|
|
|
981
|
-
// Push
|
|
1064
|
+
// Push notification using Web Notification API by Push.js
|
|
982
1065
|
Push.create('ActivityNotification', {
|
|
983
1066
|
body: data.notification.text,
|
|
984
1067
|
timeout: 5000,
|
|
@@ -1045,7 +1128,7 @@ App.activity_notification = App.cable.subscriptions.create(
|
|
|
1045
1128
|
|
|
1046
1129
|
*ActivityNotification::NotificationWithDeviseChannel* will confirm subscription requests from authenticated cookies by Devise. If the user has not signed in, the subscription request will be rejected. If the user has signed in as unauthorized user, the subscription request will be also rejected.
|
|
1047
1130
|
|
|
1048
|
-
In
|
|
1131
|
+
In addition, you can use `Target#notification_action_cable_channel_class_name` method to select channel class depending on your *action_cable_with_devise* configuration for the target.
|
|
1049
1132
|
|
|
1050
1133
|
```js
|
|
1051
1134
|
App.activity_notification = App.cable.subscriptions.create(
|
|
@@ -1253,7 +1336,7 @@ First, add **slack-notifier** gem to your Gemfile and create Incoming WebHooks i
|
|
|
1253
1336
|
gem 'slack-notifier'
|
|
1254
1337
|
```
|
|
1255
1338
|
|
|
1256
|
-
Then, write `require 'activity_notification/optional_targets/slack'` statement in your notifiable model and set *ActivityNotification::OptionalTarget::Slack* to *acts_as_notifiable* with *:webhook_url* and *:target_username* initializing parameters. *:webhook_url* is created WebHook URL and required, *:target_username* is target's slack
|
|
1339
|
+
Then, write `require 'activity_notification/optional_targets/slack'` statement in your notifiable model and set *ActivityNotification::OptionalTarget::Slack* to *acts_as_notifiable* with *:webhook_url* and *:target_username* initializing parameters. *:webhook_url* is created WebHook URL and required, *:target_username* is target's slack username as String value, symbol method name or lambda function and is optional.
|
|
1257
1340
|
Any other options for `Slack::Notifier.new` are available as initializing parameters. See [Github slack-notifier](https://github.com/stevenosloan/slack-notifier) and [API Reference of Class: Slack::Notifier](http://www.rubydoc.info/gems/slack-notifier/1.5.1/Slack/Notifier) for more details.
|
|
1258
1341
|
|
|
1259
1342
|
```ruby
|
|
@@ -1323,14 +1406,15 @@ end
|
|
|
1323
1406
|
*ActivityNotification::Subscription* model provides API to subscribe and unsubscribe optional notification targets. Call these methods with optional target name like this:
|
|
1324
1407
|
|
|
1325
1408
|
```ruby
|
|
1326
|
-
# Subscribe
|
|
1409
|
+
# Subscribe Action Cable channel for 'comment.reply' notifications
|
|
1327
1410
|
user.find_or_create_subscription('comment.reply').subscribe_to_optional_target(:action_cable_channel)
|
|
1328
1411
|
|
|
1329
|
-
# Subscribe
|
|
1412
|
+
# Subscribe Action Cable API channel for 'comment.reply' notifications
|
|
1330
1413
|
user.find_or_create_subscription('comment.reply').subscribe_to_optional_target(:action_cable_api_channel)
|
|
1331
1414
|
|
|
1332
1415
|
# Unsubscribe Slack notification for 'comment.reply' notifications
|
|
1333
1416
|
user.find_or_create_subscription('comment.reply').unsubscribe_to_optional_target(:slack)
|
|
1334
1417
|
```
|
|
1335
1418
|
|
|
1336
|
-
You can also manage subscriptions of optional targets by subscriptions REST API. See [REST API backend](#rest-api-backend) for more details.
|
|
1419
|
+
You can also manage subscriptions of optional targets by subscriptions REST API. See [REST API backend](#rest-api-backend) for more details.
|
|
1420
|
+
|
data/docs/Setup.md
CHANGED
|
@@ -20,7 +20,7 @@ $ bin/rails generate activity_notification:install
|
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
The generator will install an initializer which describes all configuration options of *activity_notification*.
|
|
23
|
-
It also generates
|
|
23
|
+
It also generates an i18n based translation file which we can configure the presentation of notifications.
|
|
24
24
|
|
|
25
25
|
#### ORM Dependencies
|
|
26
26
|
|
|
@@ -147,7 +147,7 @@ In such cases, you can use **store_with_associated_records** option in initializ
|
|
|
147
147
|
config.store_with_associated_records = true
|
|
148
148
|
```
|
|
149
149
|
|
|
150
|
-
When **store_with_associated_records** is set to *false* as default, *activity_notification* stores
|
|
150
|
+
When **store_with_associated_records** is set to *false* as default, *activity_notification* stores notification records with association like this:
|
|
151
151
|
|
|
152
152
|
```json
|
|
153
153
|
{
|
|
@@ -181,7 +181,7 @@ When **store_with_associated_records** is set to *false* as default, *activity_n
|
|
|
181
181
|
}
|
|
182
182
|
```
|
|
183
183
|
|
|
184
|
-
When you set **store_with_associated_records** to *true*, *activity_notification* stores
|
|
184
|
+
When you set **store_with_associated_records** to *true*, *activity_notification* stores notification records including associated target, notifiable, notifier and several instance methods like this:
|
|
185
185
|
|
|
186
186
|
```json
|
|
187
187
|
{
|
|
@@ -561,7 +561,7 @@ ActivityNotification::Notification.notify :users, @comment, key: "comment.reply"
|
|
|
561
561
|
The first argument is the plural symbol name of your target model, which is configured in notifiable model by *acts_as_notifiable*.
|
|
562
562
|
The new instances of **ActivityNotification::Notification** model will be generated for the specified targets.
|
|
563
563
|
|
|
564
|
-
*Hint*: *:key* is
|
|
564
|
+
*Hint*: *:key* is an option. Default key `#{notifiable_type}.default` which means *comment.default* will be used without specified key.
|
|
565
565
|
You can override it by *Notifiable#default_notification_key*.
|
|
566
566
|
|
|
567
567
|
#### Asynchronous notification API with ActiveJob
|
|
@@ -580,7 +580,7 @@ You can also use *:notify_later* option in *notify* method. This is the same ope
|
|
|
580
580
|
|
|
581
581
|
*Note*: *notify_now* is an alias for *notify* and does the same.
|
|
582
582
|
|
|
583
|
-
When you use asynchronous notification API, you should
|
|
583
|
+
When you use asynchronous notification API, you should set up ActiveJob with background queuing service such as Sidekiq.
|
|
584
584
|
You can set *config.active_job_queue* in your initializer to specify a queue name *activity_notification* will use.
|
|
585
585
|
The default queue name is *:activity_notification*.
|
|
586
586
|
|
|
@@ -725,7 +725,7 @@ For example, if you have a notification with *:key* set to *"notification.commen
|
|
|
725
725
|
|
|
726
726
|
*Hint*: the *"notification."* prefix in *:key* is completely optional, you can skip it in your projects or use this prefix only to make namespace.
|
|
727
727
|
|
|
728
|
-
If you would like to
|
|
728
|
+
If you would like to fall back to a partial, you can utilize the **:fallback** parameter to specify the path of a partial to use when one is missing:
|
|
729
729
|
|
|
730
730
|
```erb
|
|
731
731
|
<%= render_notification(@notification, target: :users, fallback: :default) %>
|
|
@@ -741,7 +741,7 @@ If you do not specify *:target* option like this,
|
|
|
741
741
|
|
|
742
742
|
the gem will look for a partial in *default* as the target type which means *activity_notification/notifications/default/_default.html.(|erb|haml|slim|something_else)*.
|
|
743
743
|
|
|
744
|
-
If a view file does not exist then *ActionView::MisingTemplate* will be raised. If you wish to
|
|
744
|
+
If a view file does not exist then *ActionView::MisingTemplate* will be raised. If you wish to fall back to the old behaviour and use an i18n based translation in this situation you can specify a *:fallback* parameter of *:text* to fall back to this mechanism like such:
|
|
745
745
|
|
|
746
746
|
```erb
|
|
747
747
|
<%= render_notification(@notification, fallback: :text) %>
|
data/docs/Testing.md
CHANGED
|
@@ -149,7 +149,7 @@ $ export AN_ORM=dynamoid AN_TEST_DB=postgresql
|
|
|
149
149
|
```
|
|
150
150
|
|
|
151
151
|
Then, configure *spec/rails_app/config/database.yml* or *spec/rails_app/config/mongoid.yml*, *spec/rails_app/config/dynamoid.rb* as your local database.
|
|
152
|
-
Finally, run database migration, seed data script and the example
|
|
152
|
+
Finally, run database migration, seed data script and the example application.
|
|
153
153
|
```console
|
|
154
154
|
$ cd spec/rails_app
|
|
155
155
|
$ # You don't need migration when you use MongoDB only (AN_ORM=mongoid and AN_TEST_DB=mongodb)
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Upgrade Guide: v2.5.x → v2.6.0
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
v2.6.0 adds instance-level subscription support ([#202](https://github.com/simukappu/activity_notification/issues/202)). This requires a database migration for existing installations.
|
|
6
|
+
|
|
7
|
+
**You must run the migration before deploying the updated gem.** The gem will raise errors if the new columns are missing.
|
|
8
|
+
|
|
9
|
+
## Step 1: Update the gem
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
# Gemfile
|
|
13
|
+
gem 'activity_notification', '~> 2.6.0'
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
```console
|
|
17
|
+
$ bundle update activity_notification
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Step 2: Run the migration
|
|
21
|
+
|
|
22
|
+
### ActiveRecord
|
|
23
|
+
|
|
24
|
+
Generate and run the migration:
|
|
25
|
+
|
|
26
|
+
```console
|
|
27
|
+
$ bin/rails generate activity_notification:add_notifiable_to_subscriptions
|
|
28
|
+
$ bin/rails db:migrate
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
This will:
|
|
32
|
+
- Add `notifiable_type` (string, nullable) and `notifiable_id` (integer, nullable) columns to the `subscriptions` table
|
|
33
|
+
- Remove the old unique index on `[:target_type, :target_id, :key]`
|
|
34
|
+
- Add a new unique index on `[:target_type, :target_id, :key, :notifiable_type, :notifiable_id]` with prefix lengths for MySQL compatibility
|
|
35
|
+
|
|
36
|
+
### Mongoid
|
|
37
|
+
|
|
38
|
+
No migration is needed. Mongoid is schemaless and the new fields will be added automatically. However, if you have custom indexes on the subscriptions collection, you may want to update them:
|
|
39
|
+
|
|
40
|
+
```console
|
|
41
|
+
$ bin/rails db:mongoid:create_indexes
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Dynamoid
|
|
45
|
+
|
|
46
|
+
No migration is needed. The new `notifiable_key` field will be added automatically to new records.
|
|
47
|
+
|
|
48
|
+
## Step 3: Verify
|
|
49
|
+
|
|
50
|
+
After migrating, verify that existing subscriptions still work:
|
|
51
|
+
|
|
52
|
+
```ruby
|
|
53
|
+
# Existing key-level subscriptions should still work
|
|
54
|
+
user.subscribes_to_notification?('comment.default') # => true/false as before
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## What changed
|
|
58
|
+
|
|
59
|
+
### Subscription queries
|
|
60
|
+
|
|
61
|
+
Key-level subscription lookups now explicitly filter by `notifiable_type IS NULL`. This ensures that instance-level subscriptions (where `notifiable_type` is set) are not confused with key-level subscriptions.
|
|
62
|
+
|
|
63
|
+
Before:
|
|
64
|
+
```ruby
|
|
65
|
+
subscriptions.where(key: key).first
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
After:
|
|
69
|
+
```ruby
|
|
70
|
+
subscriptions.where(key: key, notifiable_type: nil).first
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
For existing databases where all subscriptions have `NULL` notifiable fields, the results are identical.
|
|
74
|
+
|
|
75
|
+
### Method signature changes
|
|
76
|
+
|
|
77
|
+
The following methods have new optional keyword arguments. Existing calls without these arguments are fully compatible:
|
|
78
|
+
|
|
79
|
+
- `find_subscription(key, notifiable: nil)` — pass `notifiable:` to look up instance-level subscriptions
|
|
80
|
+
- `find_or_create_subscription(key, subscription_params)` — pass `notifiable:` in `subscription_params` to create instance-level subscriptions
|
|
81
|
+
- `subscribes_to_notification?(key, subscribe_as_default, notifiable: nil)` — pass `notifiable:` to check instance-level subscriptions
|
|
82
|
+
|
|
83
|
+
### Uniqueness constraint
|
|
84
|
+
|
|
85
|
+
The subscription uniqueness constraint now includes `notifiable_type` and `notifiable_id`. This allows a target to have:
|
|
86
|
+
- One key-level subscription per key (where notifiable is NULL)
|
|
87
|
+
- One instance-level subscription per key per notifiable instance
|
|
88
|
+
|
|
89
|
+
## Using instance-level subscriptions
|
|
90
|
+
|
|
91
|
+
```ruby
|
|
92
|
+
# Subscribe a user to notifications from a specific post
|
|
93
|
+
user.create_subscription(
|
|
94
|
+
key: 'comment.default',
|
|
95
|
+
notifiable_type: 'Post',
|
|
96
|
+
notifiable_id: post.id
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# Check if user subscribes to notifications from this specific post
|
|
100
|
+
user.subscribes_to_notification?('comment.default', notifiable: post)
|
|
101
|
+
|
|
102
|
+
# Find an instance-level subscription
|
|
103
|
+
user.find_subscription('comment.default', notifiable: post)
|
|
104
|
+
|
|
105
|
+
# When notify is called, targets from instance-level subscriptions
|
|
106
|
+
# are automatically merged with notification_targets
|
|
107
|
+
Notification.notify(:users, comment)
|
|
108
|
+
```
|