activity_notification 1.1.0 → 1.2.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/.gitignore +3 -0
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +23 -11
- data/Gemfile.lock +52 -40
- data/README.md +177 -14
- data/activity_notification.gemspec +3 -1
- data/app/controllers/activity_notification/subscriptions_controller.rb +48 -2
- data/app/views/activity_notification/optional_targets/default/base/_default.text.erb +10 -0
- data/app/views/activity_notification/optional_targets/default/slack/_default.text.erb +6 -0
- data/app/views/activity_notification/subscriptions/default/_notification_keys.html.erb +19 -0
- data/app/views/activity_notification/subscriptions/default/_subscription.html.erb +32 -3
- data/app/views/activity_notification/subscriptions/default/index.html.erb +4 -0
- data/app/views/activity_notification/subscriptions/default/show.html.erb +5 -0
- data/app/views/activity_notification/subscriptions/default/subscribe_to_optional_target.js.erb +6 -0
- data/app/views/activity_notification/subscriptions/default/unsubscribe_to_optional_target.js.erb +6 -0
- data/gemfiles/Gemfile.rails-4.2.lock +15 -3
- data/gemfiles/Gemfile.rails-5.0.lock +47 -35
- data/lib/activity_notification.rb +1 -0
- data/lib/activity_notification/apis/notification_api.rb +83 -26
- data/lib/activity_notification/apis/subscription_api.rb +93 -8
- data/lib/activity_notification/common.rb +6 -2
- data/lib/activity_notification/helpers/view_helpers.rb +43 -0
- data/lib/activity_notification/models/concerns/notifiable.rb +73 -28
- data/lib/activity_notification/models/concerns/subscriber.rb +34 -7
- data/lib/activity_notification/models/concerns/target.rb +25 -13
- data/lib/activity_notification/models/subscription.rb +13 -2
- data/lib/activity_notification/optional_targets/amazon_sns.rb +42 -0
- data/lib/activity_notification/optional_targets/base.rb +79 -0
- data/lib/activity_notification/optional_targets/slack.rb +33 -0
- data/lib/activity_notification/rails/routes.rb +30 -20
- data/lib/activity_notification/roles/acts_as_notifiable.rb +70 -11
- data/lib/activity_notification/version.rb +1 -1
- data/lib/generators/activity_notification/views_generator.rb +2 -2
- data/lib/generators/templates/migrations/migration.rb +2 -2
- data/spec/concerns/apis/notification_api_spec.rb +97 -0
- data/spec/concerns/apis/subscription_api_spec.rb +206 -41
- data/spec/concerns/common_spec.rb +7 -2
- data/spec/concerns/models/notifiable_spec.rb +88 -2
- data/spec/concerns/models/subscriber_spec.rb +114 -13
- data/spec/concerns/models/target_spec.rb +17 -0
- data/spec/controllers/subscriptions_controller_shared_examples.rb +251 -28
- data/spec/helpers/view_helpers_spec.rb +56 -0
- data/spec/optional_targets/amazon_sns_spec.rb +46 -0
- data/spec/optional_targets/base_spec.rb +43 -0
- data/spec/optional_targets/slack_spec.rb +46 -0
- data/spec/rails_app/app/controllers/comments_controller.rb +1 -0
- data/spec/rails_app/app/models/admin.rb +1 -2
- data/spec/rails_app/app/models/article.rb +2 -3
- data/spec/rails_app/app/models/comment.rb +19 -7
- data/spec/rails_app/app/views/activity_notification/optional_targets/admins/amazon_sns/comment/_default.text.erb +8 -0
- data/spec/rails_app/db/migrate/20160715050420_create_activity_notification_tables.rb +1 -1
- data/spec/rails_app/db/migrate/20160715050433_create_test_tables.rb +2 -0
- data/spec/rails_app/db/schema.rb +3 -1
- data/spec/rails_app/db/seeds.rb +1 -1
- data/spec/rails_app/lib/custom_optional_targets/console_output.rb +13 -0
- data/spec/rails_app/lib/custom_optional_targets/wrong_target.rb +10 -0
- data/spec/roles/acts_as_notifiable_spec.rb +124 -2
- metadata +49 -4
- data/spec/rails_app/app/models/notification.rb +0 -6
@@ -50,12 +50,25 @@ module ActivityNotification
|
|
50
50
|
subscription_params[:subscribing_to_email] = subscription_params[:subscribing]
|
51
51
|
end
|
52
52
|
subscription = subscriptions.new(subscription_params)
|
53
|
-
subscription.subscribing ?
|
53
|
+
subscription.subscribing? ?
|
54
54
|
subscription.assign_attributes(subscribing: true, subscribed_at: created_at) :
|
55
55
|
subscription.assign_attributes(subscribing: false, unsubscribed_at: created_at)
|
56
|
-
subscription.subscribing_to_email ?
|
56
|
+
subscription.subscribing_to_email? ?
|
57
57
|
subscription.assign_attributes(subscribing_to_email: true, subscribed_to_email_at: created_at) :
|
58
58
|
subscription.assign_attributes(subscribing_to_email: false, unsubscribed_to_email_at: created_at)
|
59
|
+
optional_targets = subscription.optional_targets
|
60
|
+
subscription.optional_target_names.each do |optional_target_name|
|
61
|
+
optional_targets = subscription.subscribing_to_optional_target?(optional_target_name) ?
|
62
|
+
optional_targets.merge(
|
63
|
+
Subscription.to_optional_target_key(optional_target_name) => true,
|
64
|
+
Subscription.to_optional_target_subscribed_at_key(optional_target_name) => created_at
|
65
|
+
) :
|
66
|
+
optional_targets.merge(
|
67
|
+
Subscription.to_optional_target_key(optional_target_name) => false,
|
68
|
+
Subscription.to_optional_target_unsubscribed_at_key(optional_target_name) => created_at
|
69
|
+
)
|
70
|
+
end
|
71
|
+
subscription.assign_attributes(optional_targets: optional_targets)
|
59
72
|
subscription.save ? subscription : nil
|
60
73
|
end
|
61
74
|
|
@@ -118,7 +131,7 @@ module ActivityNotification
|
|
118
131
|
# @param [Boolean] subscribe_as_default Default subscription value to use when the subscription record does not configured
|
119
132
|
# @return [Boolean] If the target subscribes to the notification
|
120
133
|
def _subscribes_to_notification?(key, subscribe_as_default = ActivityNotification.config.subscribe_as_default)
|
121
|
-
evaluate_subscription(subscriptions.find_by_key(key), :subscribing
|
134
|
+
evaluate_subscription(subscriptions.find_by_key(key), :subscribing?, subscribe_as_default)
|
122
135
|
end
|
123
136
|
|
124
137
|
# Returns if the target subscribes to the notification email.
|
@@ -129,20 +142,34 @@ module ActivityNotification
|
|
129
142
|
# @param [Boolean] subscribe_as_default Default subscription value to use when the subscription record does not configured
|
130
143
|
# @return [Boolean] If the target subscribes to the notification
|
131
144
|
def _subscribes_to_notification_email?(key, subscribe_as_default = ActivityNotification.config.subscribe_as_default)
|
132
|
-
evaluate_subscription(subscriptions.find_by_key(key), :subscribing_to_email
|
145
|
+
evaluate_subscription(subscriptions.find_by_key(key), :subscribing_to_email?, subscribe_as_default)
|
133
146
|
end
|
134
147
|
alias_method :_subscribes_to_email?, :_subscribes_to_notification_email?
|
135
148
|
|
149
|
+
# Returns if the target subscribes to the specified optional target.
|
150
|
+
# This method can be overriden.
|
151
|
+
# @api protected
|
152
|
+
#
|
153
|
+
# @param [String] key Key of the notification
|
154
|
+
# @param [String, Symbol] optional_target_name Class name of the optional target implementation (e.g. :amazon_sns, :slack)
|
155
|
+
# @param [Boolean] subscribe_as_default Default subscription value to use when the subscription record does not configured
|
156
|
+
# @return [Boolean] If the target subscribes to the specified optional target
|
157
|
+
def _subscribes_to_optional_target?(key, optional_target_name, subscribe_as_default = ActivityNotification.config.subscribe_as_default)
|
158
|
+
_subscribes_to_notification?(key, subscribe_as_default) &&
|
159
|
+
evaluate_subscription(subscriptions.find_by_key(key), :subscribing_to_optional_target?, subscribe_as_default, optional_target_name, subscribe_as_default)
|
160
|
+
end
|
161
|
+
|
136
162
|
private
|
137
163
|
|
138
164
|
# Returns if the target subscribes.
|
139
165
|
# @api private
|
140
166
|
# @param [Boolean] record Subscription record
|
141
|
-
# @param [Symbol] field Evaluating subscription field of the record
|
167
|
+
# @param [Symbol] field Evaluating subscription field or method of the record
|
142
168
|
# @param [Boolean] default Default subscription value to use when the subscription record does not configured
|
169
|
+
# @param [Array] args Arguments of evaluating subscription method
|
143
170
|
# @return [Boolean] If the target subscribes
|
144
|
-
def evaluate_subscription(record, field, default)
|
145
|
-
default ? record.blank? || record.send(field) : record.present? && record.send(field)
|
171
|
+
def evaluate_subscription(record, field, default, *args)
|
172
|
+
default ? record.blank? || record.send(field, *args) : record.present? && record.send(field)
|
146
173
|
end
|
147
174
|
|
148
175
|
end
|
@@ -302,13 +302,15 @@ module ActivityNotification
|
|
302
302
|
#
|
303
303
|
# @param [Object] notifiable Notifiable instance to notify
|
304
304
|
# @param [Hash] options Options for notifications
|
305
|
-
# @option options [String] :key
|
306
|
-
# @option options [Object] :group
|
307
|
-
# @option options [ActiveSupport::Duration] :group_expiry_delay
|
308
|
-
# @option options [Object] :notifier
|
309
|
-
# @option options [Hash] :parameters
|
310
|
-
# @option options [Boolean] :send_email
|
311
|
-
# @option options [Boolean] :send_later
|
305
|
+
# @option options [String] :key (notifiable.default_notification_key) Key of the notification
|
306
|
+
# @option options [Object] :group (nil) Group unit of the notifications
|
307
|
+
# @option options [ActiveSupport::Duration] :group_expiry_delay (nil) Expiry period of a notification group
|
308
|
+
# @option options [Object] :notifier (nil) Notifier of the notifications
|
309
|
+
# @option options [Hash] :parameters ({}) Additional parameters of the notifications
|
310
|
+
# @option options [Boolean] :send_email (true) Whether it sends notification email
|
311
|
+
# @option options [Boolean] :send_later (true) Whether it sends notification email asynchronously
|
312
|
+
# @option options [Boolean] :publish_optional_targets (true) Whether it publishes notification to optional targets
|
313
|
+
# @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
|
312
314
|
# @return [Notification] Generated notification instance
|
313
315
|
def notify_to(notifiable, options = {})
|
314
316
|
Notification.notify_to(self, notifiable, options)
|
@@ -433,24 +435,34 @@ module ActivityNotification
|
|
433
435
|
# Returns if the target subscribes to the notification.
|
434
436
|
# It also returns true when the subscription management is not allowed for the target.
|
435
437
|
#
|
436
|
-
# @param [String]
|
437
|
-
# @param [
|
438
|
+
# @param [String] key Key of the notification
|
439
|
+
# @param [Boolean] subscribe_as_default Default subscription value to use when the subscription record does not configured
|
438
440
|
# @return [Boolean] If the target subscribes the notification or the subscription management is not allowed for the target
|
439
441
|
def subscribes_to_notification?(key, subscribe_as_default = ActivityNotification.config.subscribe_as_default)
|
440
|
-
!subscription_allowed?(key)
|
442
|
+
!subscription_allowed?(key) || _subscribes_to_notification?(key, subscribe_as_default)
|
441
443
|
end
|
442
444
|
|
443
445
|
# Returns if the target subscribes to the notification email.
|
444
446
|
# It also returns true when the subscription management is not allowed for the target.
|
445
447
|
#
|
446
|
-
# @param [String]
|
447
|
-
# @param [
|
448
|
+
# @param [String] key Key of the notification
|
449
|
+
# @param [Boolean] subscribe_as_default Default subscription value to use when the subscription record does not configured
|
448
450
|
# @return [Boolean] If the target subscribes the notification email or the subscription management is not allowed for the target
|
449
451
|
def subscribes_to_notification_email?(key, subscribe_as_default = ActivityNotification.config.subscribe_as_default)
|
450
|
-
!subscription_allowed?(key)
|
452
|
+
!subscription_allowed?(key) || _subscribes_to_notification_email?(key, subscribe_as_default)
|
451
453
|
end
|
452
454
|
alias_method :subscribes_to_email?, :subscribes_to_notification_email?
|
453
455
|
|
456
|
+
# Returns if the target subscribes to the specified optional target.
|
457
|
+
# It also returns true when the subscription management is not allowed for the target.
|
458
|
+
#
|
459
|
+
# @param [String] key Key of the notification
|
460
|
+
# @param [String, Symbol] optional_target_name Class name of the optional target implementation (e.g. :amazon_sns, :slack)
|
461
|
+
# @param [Boolean] subscribe_as_default Default subscription value to use when the subscription record does not configured
|
462
|
+
# @return [Boolean] If the target subscribes the notification email or the subscription management is not allowed for the target
|
463
|
+
def subscribes_to_optional_target?(key, optional_target_name, subscribe_as_default = ActivityNotification.config.subscribe_as_default)
|
464
|
+
!subscription_allowed?(key) || _subscribes_to_optional_target?(key, optional_target_name, subscribe_as_default)
|
465
|
+
end
|
454
466
|
|
455
467
|
private
|
456
468
|
|
@@ -10,7 +10,7 @@ module ActivityNotification
|
|
10
10
|
belongs_to :target, polymorphic: true
|
11
11
|
|
12
12
|
# Serialize parameters Hash
|
13
|
-
serialize :
|
13
|
+
serialize :optional_targets, Hash
|
14
14
|
|
15
15
|
validates :target, presence: true
|
16
16
|
validates :key, presence: true
|
@@ -21,6 +21,7 @@ module ActivityNotification
|
|
21
21
|
validates :unsubscribed_at, presence: true, unless: :subscribing
|
22
22
|
validates :subscribed_to_email_at, presence: true, if: :subscribing_to_email
|
23
23
|
validates :unsubscribed_to_email_at, presence: true, unless: :subscribing_to_email
|
24
|
+
validate :subscribing_to_optional_target_cannot_be_true_when_subscribing_is_false
|
24
25
|
|
25
26
|
# Selects filtered subscriptions by target instance.
|
26
27
|
# ActivityNotification::Subscription.filtered_by_target(@user)
|
@@ -82,12 +83,22 @@ module ActivityNotification
|
|
82
83
|
scope :key_order, -> { order(key: :asc) }
|
83
84
|
|
84
85
|
private
|
86
|
+
|
85
87
|
# Validates subscribing_to_email cannot be true when subscribing isfalse.
|
86
88
|
def subscribing_to_email_cannot_be_true_when_subscribing_is_false
|
87
|
-
if !subscribing && subscribing_to_email
|
89
|
+
if !subscribing && subscribing_to_email?
|
88
90
|
errors.add(:subscribing_to_email, "cannot be true when subscribing is false")
|
89
91
|
end
|
90
92
|
end
|
91
93
|
|
94
|
+
# Validates subscribing_to_optional_target cannot be true when subscribing isfalse.
|
95
|
+
def subscribing_to_optional_target_cannot_be_true_when_subscribing_is_false
|
96
|
+
optional_target_names.each do |optional_target_name|
|
97
|
+
if !subscribing && subscribing_to_optional_target?(optional_target_name)
|
98
|
+
errors.add(:optional_targets, "#Subscription.to_optional_target_key(optional_target_name) cannot be true when subscribing is false")
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
92
103
|
end
|
93
104
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module ActivityNotification
|
2
|
+
module OptionalTarget
|
3
|
+
# Optional target implementation for mobile push notification or SMS using Amazon SNS.
|
4
|
+
class AmazonSNS < ActivityNotification::OptionalTarget::Base
|
5
|
+
require 'aws-sdk'
|
6
|
+
|
7
|
+
# Initialize method to prepare Aws::SNS::Client
|
8
|
+
# @param [Hash] options Options for initializing
|
9
|
+
# @option options [String, Proc, Symbol] :topic_arn (nil) :topic_arn option for Aws::SNS::Client#publish, it resolved by target instance like email_allowed?
|
10
|
+
# @option options [String, Proc, Symbol] :target_arn (nil) :target_arn option for Aws::SNS::Client#publish, it resolved by target instance like email_allowed?
|
11
|
+
# @option options [String, Proc, Symbol] :phone_number (nil) :phone_number option for Aws::SNS::Client#publish, it resolved by target instance like email_allowed?
|
12
|
+
# @option options [Hash] others Other options to be set Aws::SNS::Client.new
|
13
|
+
def initialize_target(options = {})
|
14
|
+
@topic_arn = options.delete(:topic_arn)
|
15
|
+
@target_arn = options.delete(:target_arn)
|
16
|
+
@phone_number = options.delete(:phone_number)
|
17
|
+
@sns_client = Aws::SNS::Client.new(options)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Publishes notification message to Amazon SNS
|
21
|
+
# @param [Notification] notification Notification instance
|
22
|
+
# @param [Hash] options Options for publishing
|
23
|
+
# @option options [String, Proc, Symbol] :topic_arn (nil) :topic_arn option for Aws::SNS::Client#publish, it resolved by target instance like email_allowed?
|
24
|
+
# @option options [String, Proc, Symbol] :target_arn (nil) :target_arn option for Aws::SNS::Client#publish, it resolved by target instance like email_allowed?
|
25
|
+
# @option options [String, Proc, Symbol] :phone_number (nil) :phone_number option for Aws::SNS::Client#publish, it resolved by target instance like email_allowed?
|
26
|
+
# @option options [String] :partial_root ("activity_notification/optional_targets/#{target}/#{optional_target_name}", "activity_notification/optional_targets/#{target}/base", "activity_notification/optional_targets/default/#{optional_target_name}", "activity_notification/optional_targets/default/base") Partial template name
|
27
|
+
# @option options [String] :partial (self.key.tr('.', '/')) Root path of partial template
|
28
|
+
# @option options [String] :layout (nil) Layout template name
|
29
|
+
# @option options [String] :layout_root ('layouts') Root path of layout template
|
30
|
+
# @option options [String, Symbol] :fallback (:default) Fallback template to use when MissingTemplate is raised. Set :text to use i18n text as fallback.
|
31
|
+
# @option options [Hash] others Parameters to be set as locals
|
32
|
+
def notify(notification, options = {})
|
33
|
+
@sns_client.publish(
|
34
|
+
topic_arn: notification.target.resolve_value(options.delete(:topic_arn) || @topic_arn),
|
35
|
+
target_arn: notification.target.resolve_value(options.delete(:target_arn) || @target_arn),
|
36
|
+
phone_number: notification.target.resolve_value(options.delete(:phone_number) || @phone_number),
|
37
|
+
message: render_notification_message(notification, options)
|
38
|
+
)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module ActivityNotification
|
2
|
+
# Optional target module to develop optional notification target classes.
|
3
|
+
module OptionalTarget
|
4
|
+
# Abstract optional target class to develop optional notification target class.
|
5
|
+
class Base
|
6
|
+
# View context to render notification message
|
7
|
+
# @return View context to render notification message
|
8
|
+
attr_accessor :view_context
|
9
|
+
|
10
|
+
# Initialize method to create view context in this OptionalTarget instance
|
11
|
+
def initialize
|
12
|
+
@view_context = ActionView::Base.new(ActionController::Base.view_paths, {})
|
13
|
+
@view_context.class_eval do
|
14
|
+
include Rails.application.routes.url_helpers
|
15
|
+
def default_url_options
|
16
|
+
ActionMailer::Base.default_url_options
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns demodulized symbol class name as optional target name
|
22
|
+
# @return Demodulized symbol class name as optional target name
|
23
|
+
def to_optional_target_name
|
24
|
+
self.class.name.demodulize.underscore.to_sym
|
25
|
+
end
|
26
|
+
|
27
|
+
# Initialize method to be overriden in user implementation class
|
28
|
+
# @param [Hash] _options Options for initializing
|
29
|
+
def initialize_target(_options = {})
|
30
|
+
raise NotImplementedError, "You have to implement #{self.class}##{__method__}"
|
31
|
+
end
|
32
|
+
|
33
|
+
# Publishing notification method to be overriden in user implementation class
|
34
|
+
# @param [Notification] _notification Notification instance
|
35
|
+
# @param [Hash] _options Options for publishing
|
36
|
+
def notify(_notification, _options = {})
|
37
|
+
raise NotImplementedError, "You have to implement #{self.class}##{__method__}"
|
38
|
+
end
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
# Renders notification message with view context
|
43
|
+
# @param [Notification] notification Notification instance
|
44
|
+
# @param [Hash] options Options for rendering
|
45
|
+
# @option options [Hash] :assignment (nil) Optional instance variables to assign for views
|
46
|
+
# @option options [String] :partial_root ("activity_notification/optional_targets/#{target}/#{optional_target_name}", "activity_notification/optional_targets/#{target}/base", "activity_notification/optional_targets/default/#{optional_target_name}", "activity_notification/optional_targets/default/base") Partial template name
|
47
|
+
# @option options [String] :partial (self.key.tr('.', '/')) Root path of partial template
|
48
|
+
# @option options [String] :layout (nil) Layout template name
|
49
|
+
# @option options [String] :layout_root ('layouts') Root path of layout template
|
50
|
+
# @option options [String, Symbol] :fallback (:default) Fallback template to use when MissingTemplate is raised. Set :text to use i18n text as fallback.
|
51
|
+
# @option options [Hash] others Parameters to be set as locals
|
52
|
+
# @return [String] Rendered view or text as string
|
53
|
+
def render_notification_message(notification, options = {})
|
54
|
+
partial_root_list =
|
55
|
+
options[:partial_root].present? ?
|
56
|
+
[ options[:partial_root] ] :
|
57
|
+
[ "activity_notification/optional_targets/#{notification.target.to_resources_name}/#{to_optional_target_name}",
|
58
|
+
"activity_notification/optional_targets/#{notification.target.to_resources_name}/base",
|
59
|
+
"activity_notification/optional_targets/default/#{to_optional_target_name}",
|
60
|
+
"activity_notification/optional_targets/default/base"
|
61
|
+
]
|
62
|
+
options[:fallback] ||= :default
|
63
|
+
@view_context.assign((options[:assignment] || {}).merge(notification: notification, target: notification.target))
|
64
|
+
|
65
|
+
message, missing_template = nil, nil
|
66
|
+
partial_root_list.each do |partial_root|
|
67
|
+
begin
|
68
|
+
message = notification.render(@view_context, options.merge(partial_root: partial_root)).to_str
|
69
|
+
break
|
70
|
+
rescue ActionView::MissingTemplate => e
|
71
|
+
missing_template = e
|
72
|
+
# Continue to next partial root
|
73
|
+
end
|
74
|
+
end
|
75
|
+
message.blank? ? (raise missing_template) : message
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module ActivityNotification
|
2
|
+
module OptionalTarget
|
3
|
+
# Optional target implementation for Slack.
|
4
|
+
class Slack < ActivityNotification::OptionalTarget::Base
|
5
|
+
require 'slack-notifier'
|
6
|
+
|
7
|
+
# Initialize method to prepare Slack::Notifier
|
8
|
+
# @param [Hash] options Options for initializing
|
9
|
+
# @option options [String, Proc, Symbol] :slack_name (nil) Target user name of Slack, it resolved by target instance like email_allowed?
|
10
|
+
# @option options [required, String] :webhook_url (nil) Webhook URL of Slack Incoming WebHooks integration
|
11
|
+
# @option options [Hash] others Other options to be set Slack::Notifier.new, like :channel, :username, :icon_emoji etc
|
12
|
+
def initialize_target(options = {})
|
13
|
+
@slack_name = options.delete(:slack_name)
|
14
|
+
@notifier = ::Slack::Notifier.new(options.delete(:webhook_url), options)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Publishes notification message to Slack
|
18
|
+
# @param [Notification] notification Notification instance
|
19
|
+
# @param [Hash] options Options for publishing
|
20
|
+
# @option options [String, Proc, Symbol] :slack_name (nil) Target user name of Slack, it resolved by target instance like email_allowed?
|
21
|
+
# @option options [String] :partial_root ("activity_notification/optional_targets/#{target}/#{optional_target_name}", "activity_notification/optional_targets/#{target}/base", "activity_notification/optional_targets/default/#{optional_target_name}", "activity_notification/optional_targets/default/base") Partial template name
|
22
|
+
# @option options [String] :partial (self.key.tr('.', '/')) Root path of partial template
|
23
|
+
# @option options [String] :layout (nil) Layout template name
|
24
|
+
# @option options [String] :layout_root ('layouts') Root path of layout template
|
25
|
+
# @option options [String, Symbol] :fallback (:default) Fallback template to use when MissingTemplate is raised. Set :text to use i18n text as fallback.
|
26
|
+
# @option options [Hash] others Parameters to be set as locals
|
27
|
+
def notify(notification, options = {})
|
28
|
+
slack_name = notification.target.resolve_value(options.delete(:slack_name) || @slack_name)
|
29
|
+
@notifier.ping(render_notification_message(notification, options.merge(assignment: { slack_name: slack_name })))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -104,44 +104,52 @@ module ActionDispatch::Routing
|
|
104
104
|
# subscribed_by :users
|
105
105
|
# This method creates the needed routes:
|
106
106
|
# # Subscription routes
|
107
|
-
# user_subscriptions
|
107
|
+
# user_subscriptions GET /users/:user_id/subscriptions(.:format)
|
108
108
|
# { controller:"activity_notification/subscriptions", action:"index", target_type:"users" }
|
109
|
-
# user_subscription
|
109
|
+
# user_subscription GET /users/:user_id/subscriptions/:id(.:format)
|
110
110
|
# { controller:"activity_notification/subscriptions", action:"show", target_type:"users" }
|
111
|
-
# open_all_user_subscriptions
|
111
|
+
# open_all_user_subscriptions POST /users/:user_id/subscriptions(.:format)
|
112
112
|
# { controller:"activity_notification/subscriptions", action:"create", target_type:"users" }
|
113
|
-
# user_subscription
|
113
|
+
# user_subscription DELETE /users/:user_id/subscriptions/:id(.:format)
|
114
114
|
# { controller:"activity_notification/subscriptions", action:"destroy", target_type:"users" }
|
115
|
-
#
|
115
|
+
# subscribe_user_subscription POST /users/:user_id/subscriptions/:id/subscribe(.:format)
|
116
116
|
# { controller:"activity_notification/subscriptions", action:"subscribe", target_type:"users" }
|
117
|
-
#
|
117
|
+
# unsubscribe_user_subscription POST /users/:user_id/subscriptions/:id/unsubscribe(.:format)
|
118
118
|
# { controller:"activity_notification/subscriptions", action:"unsubscribe", target_type:"users" }
|
119
|
-
#
|
119
|
+
# subscribe_to_email_user_subscription POST /users/:user_id/subscriptions/:id/subscribe_to_email(.:format)
|
120
120
|
# { controller:"activity_notification/subscriptions", action:"subscribe_to_email", target_type:"users" }
|
121
|
-
#
|
121
|
+
# unsubscribe_to_email_user_subscription POST /users/:user_id/subscriptions/:id/unsubscribe_to_email(.:format)
|
122
122
|
# { controller:"activity_notification/subscriptions", action:"unsubscribe_to_email", target_type:"users" }
|
123
|
+
# subscribe_to_optional_target_user_subscription POST /users/:user_id/subscriptions/:id/subscribe_to_optional_target(.:format)
|
124
|
+
# { controller:"activity_notification/subscriptions", action:"subscribe_to_optional_target", target_type:"users" }
|
125
|
+
# unsubscribe_to_optional_target_user_subscription POST /users/:user_id/subscriptions/:id/unsubscribe_to_optional_target(.:format)
|
126
|
+
# { controller:"activity_notification/subscriptions", action:"unsubscribe_to_optional_target", target_type:"users" }
|
123
127
|
#
|
124
128
|
# When you use devise authentication and you want make subscription targets assciated with devise,
|
125
129
|
# you can create as follows in your routes:
|
126
130
|
# notify_to :users, with_devise: :users
|
127
131
|
# This with_devise option creates the needed routes assciated with devise authentication:
|
128
132
|
# # Subscription with devise routes
|
129
|
-
# user_subscriptions
|
133
|
+
# user_subscriptions GET /users/:user_id/subscriptions(.:format)
|
130
134
|
# { controller:"activity_notification/subscriptions_with_devise", action:"index", target_type:"users", devise_type:"users" }
|
131
|
-
# user_subscription
|
135
|
+
# user_subscription GET /users/:user_id/subscriptions/:id(.:format)
|
132
136
|
# { controller:"activity_notification/subscriptions_with_devise", action:"show", target_type:"users", devise_type:"users" }
|
133
|
-
# open_all_user_subscriptions
|
137
|
+
# open_all_user_subscriptions POST /users/:user_id/subscriptions(.:format)
|
134
138
|
# { controller:"activity_notification/subscriptions_with_devise", action:"create", target_type:"users", devise_type:"users" }
|
135
|
-
# user_subscription
|
139
|
+
# user_subscription DELETE /users/:user_id/subscriptions/:id(.:format)
|
136
140
|
# { controller:"activity_notification/subscriptions_with_devise", action:"destroy", target_type:"users", devise_type:"users" }
|
137
|
-
#
|
141
|
+
# subscribe_user_subscription POST /users/:user_id/subscriptions/:id/subscribe(.:format)
|
138
142
|
# { controller:"activity_notification/subscriptions_with_devise", action:"subscribe", target_type:"users", devise_type:"users" }
|
139
|
-
#
|
143
|
+
# unsubscribe_user_subscription POST /users/:user_id/subscriptions/:id/unsubscribe(.:format)
|
140
144
|
# { controller:"activity_notification/subscriptions_with_devise", action:"unsubscribe", target_type:"users", devise_type:"users" }
|
141
|
-
#
|
145
|
+
# subscribe_to_email_user_subscription POST /users/:user_id/subscriptions/:id/subscribe_to_email(.:format)
|
142
146
|
# { controller:"activity_notification/subscriptions_with_devise", action:"subscribe_to_email", target_type:"users", devise_type:"users" }
|
143
|
-
#
|
147
|
+
# unsubscribe_to_email_user_subscription POST /users/:user_id/subscriptions/:id/unsubscribe_to_email(.:format)
|
144
148
|
# { controller:"activity_notification/subscriptions_with_devise", action:"unsubscribe_to_email", target_type:"users", devise_type:"users" }
|
149
|
+
# subscribe_to_optional_target_user_subscription POST /users/:user_id/subscriptions/:id/subscribe_to_optional_target(.:format)
|
150
|
+
# { controller:"activity_notification/subscriptions_with_devise", action:"subscribe_to_optional_target", target_type:"users", devise_type:"users" }
|
151
|
+
# unsubscribe_to_optional_target_user_subscription POST /users/:user_id/subscriptions/:id/unsubscribe_to_optional_target(.:format)
|
152
|
+
# { controller:"activity_notification/subscriptions_with_devise", action:"unsubscribe_to_optional_target", target_type:"users", devise_type:"users" }
|
145
153
|
#
|
146
154
|
# @example Define subscribed_by in config/routes.rb
|
147
155
|
# subscribed_by :users
|
@@ -168,10 +176,12 @@ module ActionDispatch::Routing
|
|
168
176
|
resources_options = options.select { |key, _| [:with_devise, :model, :devise_defaults].exclude? key }
|
169
177
|
self.resources options[:model], resources_options do
|
170
178
|
member do
|
171
|
-
post :subscribe
|
172
|
-
post :unsubscribe
|
173
|
-
post :subscribe_to_email
|
174
|
-
post :unsubscribe_to_email
|
179
|
+
post :subscribe unless ignore_path?(:subscribe, options)
|
180
|
+
post :unsubscribe unless ignore_path?(:unsubscribe, options)
|
181
|
+
post :subscribe_to_email unless ignore_path?(:subscribe_to_email, options)
|
182
|
+
post :unsubscribe_to_email unless ignore_path?(:unsubscribe_to_email, options)
|
183
|
+
post :subscribe_to_optional_target unless ignore_path?(:subscribe_to_optional_target, options)
|
184
|
+
post :unsubscribe_to_optional_target unless ignore_path?(:unsubscribe_to_optional_target, options)
|
175
185
|
end
|
176
186
|
end
|
177
187
|
end
|
@@ -118,14 +118,39 @@ module ActivityNotification
|
|
118
118
|
# * :dependent_notifications
|
119
119
|
# * Dependency for notifications to delete generated notifications with this notifiable.
|
120
120
|
# This option is used to configure generated_notifications_as_notifiable association.
|
121
|
-
# You can use :delete_all, :destroy, or :
|
122
|
-
#
|
121
|
+
# You can use :delete_all, :destroy, :restrict_with_error, :restrict_with_exception, :update_group_and_delete_all or :update_group_and_destroy for this option.
|
122
|
+
# When you use :update_group_and_delete_all or :update_group_and_destroy to this parameter, the oldest group member notification becomes a new group owner as `before_destroy` of this Notifiable.
|
123
|
+
# This parameter is effective for all target and is a optional since no dependent option is used as default.
|
123
124
|
# @example Define :delete_all dependency to generated notifications
|
124
125
|
# # app/models/comment.rb
|
125
126
|
# class Comment < ActiveRecord::Base
|
126
127
|
# acts_as_notifiable :users, targets: User.all, dependent_notifications: :delete_all
|
127
128
|
# end
|
128
129
|
#
|
130
|
+
# * :optional_targets
|
131
|
+
# * Optional targets to integrate external notification serveces like Amazon SNS or Slack.
|
132
|
+
# You can use hash of optional target implementation class as key and initializing parameters as value for this parameter.
|
133
|
+
# When the hash parameter is passed, acts_as_notifiable will create new instance of optional target class and call initialize_target method with initializing parameters, then configure them as optional_targets for this notifiable and target.
|
134
|
+
# You can also use symbol of method name or lambda function which returns array of initialized optional target intstances.
|
135
|
+
# All optional target class must extends ActivityNotification::OptionalTarget::Base.
|
136
|
+
# This parameter is completely optional.
|
137
|
+
# @example Define to integrate with Amazon SNS, Slack and your custom ConsoleOutput targets
|
138
|
+
# # app/models/comment.rb
|
139
|
+
# class Comment < ActiveRecord::Base
|
140
|
+
# require 'activity_notification/optional_targets/amazon_sns'
|
141
|
+
# require 'activity_notification/optional_targets/slack'
|
142
|
+
# require 'custom_optional_targets/console_output'
|
143
|
+
# acts_as_notifiable :admins, targets: Admin.all,
|
144
|
+
# optional_targets: {
|
145
|
+
# ActivityNotification::OptionalTarget::AmazonSNS => { topic_arn: '<Topin ARN of yours>' },
|
146
|
+
# ActivityNotification::OptionalTarget::Slack => {
|
147
|
+
# webhook_url: '<Slack Webhook URL>',
|
148
|
+
# slack_name: :slack_name, channel: 'activity_notification', username: 'ActivityNotification', icon_emoji: ":ghost:"
|
149
|
+
# },
|
150
|
+
# CustomOptionalTarget::ConsoleOutput => {}
|
151
|
+
# }
|
152
|
+
# end
|
153
|
+
#
|
129
154
|
# @param [Symbol] target_type Type of notification target as symbol
|
130
155
|
# @param [Hash] options Options for notifiable model configuration
|
131
156
|
# @option options [Symbol, Proc, Array] :targets (nil) Targets to send notifications
|
@@ -136,21 +161,42 @@ module ActivityNotification
|
|
136
161
|
# @option options [Symbol, Proc, Boolean] :email_allowed (ActivityNotification.config.email_enabled) Whether activity_notification sends notification email
|
137
162
|
# @option options [Symbol, Proc, String] :notifiable_path (polymorphic_path(self)) Path to redirect from open or move action of notification controller
|
138
163
|
# @option options [Symbol, Proc, String] :printable_name (ActivityNotification::Common.printable_name) Printable notifiable name
|
139
|
-
# @option options [Symbol, Proc] :dependent_notifications (nil) Dependency for notifications to delete generated notifications with this notifiable
|
164
|
+
# @option options [Symbol, Proc] :dependent_notifications (nil) Dependency for notifications to delete generated notifications with this notifiable, [:delete_all, :destroy, :restrict_with_error, :restrict_with_exception, :update_group_and_delete_all, :update_group_and_destroy] are available
|
165
|
+
# @option options [Hash<Class, Hash>] :optional_targets (nil) Optional target configurations with hash of `OptionalTarget` implementation class as key and initializing option parameter as value
|
140
166
|
# @return [Hash] Configured parameters as notifiable model
|
141
167
|
def acts_as_notifiable(target_type, options = {})
|
142
168
|
include Notifiable
|
169
|
+
configured_params = {}
|
143
170
|
|
144
|
-
if [:delete_all, :destroy, :
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
171
|
+
if [:delete_all, :destroy, :restrict_with_error, :restrict_with_exception, :update_group_and_delete_all, :update_group_and_destroy].include? options[:dependent_notifications]
|
172
|
+
case options[:dependent_notifications]
|
173
|
+
when :delete_all, :destroy, :restrict_with_error, :restrict_with_exception
|
174
|
+
has_many_generated_notifications options[:dependent_notifications]
|
175
|
+
when :update_group_and_delete_all
|
176
|
+
before_destroy :remove_generated_notifications_from_group
|
177
|
+
has_many_generated_notifications :delete_all
|
178
|
+
when :update_group_and_destroy
|
179
|
+
before_destroy :remove_generated_notifications_from_group
|
180
|
+
has_many_generated_notifications :destroy
|
181
|
+
end
|
182
|
+
configured_params = { dependent_notifications: options[:dependent_notifications] }
|
183
|
+
end
|
184
|
+
|
185
|
+
if options[:optional_targets].is_a?(Hash)
|
186
|
+
options[:optional_targets] = options[:optional_targets].map { |target_class, target_options|
|
187
|
+
optional_target = target_class.new
|
188
|
+
unless optional_target.kind_of?(ActivityNotification::OptionalTarget::Base)
|
189
|
+
raise TypeError, "#{optional_target.class.name} for an optional target is not a kind of ActivityNotification::OptionalTarget::Base"
|
190
|
+
end
|
191
|
+
optional_target.initialize_target(target_options)
|
192
|
+
optional_target
|
193
|
+
}
|
149
194
|
end
|
150
195
|
|
151
196
|
options[:printable_notifiable_name] ||= options.delete(:printable_name)
|
152
|
-
|
153
|
-
.merge set_acts_as_parameters_for_target(target_type, [:
|
197
|
+
configured_params
|
198
|
+
.merge set_acts_as_parameters_for_target(target_type, [:targets, :group, :group_expiry_delay, :parameters, :email_allowed], options, "notification_")
|
199
|
+
.merge set_acts_as_parameters_for_target(target_type, [:notifier, :notifiable_path, :printable_notifiable_name, :optional_targets], options)
|
154
200
|
end
|
155
201
|
|
156
202
|
# Returns array of available notifiable options in acts_as_notifiable.
|
@@ -164,9 +210,22 @@ module ActivityNotification
|
|
164
210
|
:email_allowed,
|
165
211
|
:notifiable_path,
|
166
212
|
:printable_notifiable_name, :printable_name,
|
167
|
-
:dependent_notifications
|
213
|
+
:dependent_notifications,
|
214
|
+
:optional_targets
|
168
215
|
].freeze
|
169
216
|
end
|
217
|
+
|
218
|
+
private
|
219
|
+
|
220
|
+
# Define to have many notification instances for this notifiable with dependent option.
|
221
|
+
# @api private
|
222
|
+
def has_many_generated_notifications(dependent_option)
|
223
|
+
has_many :generated_notifications_as_notifiable,
|
224
|
+
class_name: "::ActivityNotification::Notification",
|
225
|
+
as: :notifiable,
|
226
|
+
dependent: dependent_option
|
227
|
+
end
|
228
|
+
|
170
229
|
end
|
171
230
|
end
|
172
231
|
end
|