activity_notification 1.2.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +15 -5
  3. data/CHANGELOG.md +13 -2
  4. data/Gemfile +1 -1
  5. data/Gemfile.lock +78 -71
  6. data/README.md +64 -43
  7. data/activity_notification.gemspec +5 -4
  8. data/app/controllers/activity_notification/notifications_controller.rb +1 -1
  9. data/app/controllers/activity_notification/subscriptions_controller.rb +1 -1
  10. data/gemfiles/Gemfile.rails-4.2 +1 -1
  11. data/gemfiles/Gemfile.rails-4.2.lock +71 -62
  12. data/gemfiles/Gemfile.rails-5.0 +1 -1
  13. data/gemfiles/Gemfile.rails-5.0.lock +74 -67
  14. data/lib/activity_notification.rb +9 -2
  15. data/lib/activity_notification/apis/notification_api.rb +142 -76
  16. data/lib/activity_notification/apis/subscription_api.rb +72 -0
  17. data/lib/activity_notification/config.rb +12 -0
  18. data/lib/activity_notification/models/concerns/notifiable.rb +56 -6
  19. data/lib/activity_notification/models/concerns/notifier.rb +8 -1
  20. data/lib/activity_notification/models/concerns/subscriber.rb +13 -10
  21. data/lib/activity_notification/models/concerns/target.rb +7 -5
  22. data/lib/activity_notification/models/notification.rb +2 -270
  23. data/lib/activity_notification/models/subscription.rb +3 -101
  24. data/lib/activity_notification/optional_targets/base.rb +5 -1
  25. data/lib/activity_notification/orm/active_record.rb +16 -0
  26. data/lib/activity_notification/orm/active_record/notification.rb +252 -0
  27. data/lib/activity_notification/orm/active_record/subscription.rb +52 -0
  28. data/lib/activity_notification/orm/mongoid.rb +63 -0
  29. data/lib/activity_notification/orm/mongoid/notification.rb +255 -0
  30. data/lib/activity_notification/orm/mongoid/subscription.rb +73 -0
  31. data/lib/activity_notification/rails/routes.rb +7 -3
  32. data/lib/activity_notification/renderable.rb +5 -1
  33. data/lib/activity_notification/roles/acts_as_notifiable.rb +4 -18
  34. data/lib/activity_notification/version.rb +1 -1
  35. data/lib/generators/activity_notification/install_generator.rb +3 -5
  36. data/lib/generators/templates/activity_notification.rb +9 -4
  37. data/spec/concerns/apis/notification_api_spec.rb +27 -14
  38. data/spec/concerns/models/notifiable_spec.rb +10 -8
  39. data/spec/concerns/models/subscriber_spec.rb +12 -12
  40. data/spec/concerns/models/target_spec.rb +61 -51
  41. data/spec/controllers/subscriptions_controller_shared_examples.rb +0 -1
  42. data/spec/helpers/view_helpers_spec.rb +24 -7
  43. data/spec/models/notification_spec.rb +63 -52
  44. data/spec/models/subscription_spec.rb +6 -3
  45. data/spec/optional_targets/amazon_sns_spec.rb +8 -5
  46. data/spec/optional_targets/base_spec.rb +3 -1
  47. data/spec/optional_targets/slack_spec.rb +5 -5
  48. data/spec/rails_app/app/models/comment.rb +1 -1
  49. data/spec/rails_app/app/views/activity_notification/notifications/users/overriden/custom/_test.html.erb +1 -0
  50. data/spec/rails_app/config/application.rb +7 -0
  51. data/spec/rails_app/config/initializers/activity_notification.rb +5 -0
  52. data/spec/rails_app/config/mongoid.yml +13 -0
  53. data/spec/rails_app/db/seeds.rb +1 -1
  54. data/spec/rails_app/lib/custom_optional_targets/console_output.rb +7 -4
  55. data/spec/rails_app/lib/custom_optional_targets/wrong_target.rb +3 -0
  56. data/spec/roles/acts_as_notifiable_spec.rb +77 -16
  57. data/spec/spec_helper.rb +7 -0
  58. metadata +38 -14
@@ -7,9 +7,7 @@ module ActivityNotification
7
7
  extend ActiveSupport::Autoload
8
8
 
9
9
  autoload :Notification, 'activity_notification/models/notification'
10
- autoload :NotificationApi, 'activity_notification/apis/notification_api'
11
10
  autoload :Subscription, 'activity_notification/models/subscription'
12
- autoload :SubscriptionApi, 'activity_notification/apis/subscription_api'
13
11
  autoload :Target, 'activity_notification/models/concerns/target'
14
12
  autoload :Subscriber, 'activity_notification/models/concerns/subscriber'
15
13
  autoload :Notifiable, 'activity_notification/models/concerns/notifiable'
@@ -44,8 +42,17 @@ module ActivityNotification
44
42
  # end
45
43
  def self.configure
46
44
  yield(config) if block_given?
45
+ autoload :Association, "activity_notification/orm/#{ActivityNotification.config.orm}"
47
46
  end
48
47
 
48
+ # Method used to choose which ORM to load
49
+ # when ActivityNotification::Notification class or ActivityNotification::Subscription class
50
+ # are being autoloaded
51
+ def self.inherit_orm(model)
52
+ orm = ActivityNotification.config.orm
53
+ require "activity_notification/orm/#{orm}"
54
+ "ActivityNotification::ORM::#{orm.to_s.classify}::#{model}".constantize
55
+ end
49
56
  end
50
57
 
51
58
  # Load ActivityNotification helpers
@@ -6,6 +6,131 @@ module ActivityNotification
6
6
  included do
7
7
  # Defines store_notification as private clas method
8
8
  private_class_method :store_notification
9
+
10
+ # Selects all notification index.
11
+ # ActivityNotification::Notification.all_index!
12
+ # is defined same as
13
+ # ActivityNotification::Notification.group_owners_only.latest_order
14
+ # @scope class
15
+ # @example Get all notification index of the @user
16
+ # @notifications = @user.notifications.all_index!
17
+ # @notifications = @user.notifications.group_owners_only.latest_order
18
+ # @param [Boolean] reverse If notification index will be ordered as earliest first
19
+ # @param [Boolean] with_group_members If notification index will include group members
20
+ # @return [ActiveRecord_AssociationRelation<Notificaion>, Mongoid::Criteria<Notificaion>] Database query of filtered notifications
21
+ scope :all_index!, ->(reverse = false, with_group_members = false) {
22
+ target_index = with_group_members ? self : group_owners_only
23
+ reverse ? target_index.earliest_order : target_index.latest_order
24
+ }
25
+
26
+ # Selects unopened notification index.
27
+ # ActivityNotification::Notification.unopened_index
28
+ # is defined same as
29
+ # ActivityNotification::Notification.unopened_only.group_owners_only.latest_order
30
+ # @scope class
31
+ # @example Get unopened notificaton index of the @user
32
+ # @notifications = @user.notifications.unopened_index
33
+ # @notifications = @user.notifications.unopened_only.group_owners_only.latest_order
34
+ # @param [Boolean] reverse If notification index will be ordered as earliest first
35
+ # @param [Boolean] with_group_members If notification index will include group members
36
+ # @return [ActiveRecord_AssociationRelation<Notificaion>, Mongoid::Criteria<Notificaion>] Database query of filtered notifications
37
+ scope :unopened_index, ->(reverse = false, with_group_members = false) {
38
+ target_index = with_group_members ? unopened_only : unopened_only.group_owners_only
39
+ reverse ? target_index.earliest_order : target_index.latest_order
40
+ }
41
+
42
+ # Selects unopened notification index.
43
+ # ActivityNotification::Notification.opened_index(limit)
44
+ # is defined same as
45
+ # ActivityNotification::Notification.opened_only(limit).group_owners_only.latest_order
46
+ # @scope class
47
+ # @example Get unopened notificaton index of the @user with limit 10
48
+ # @notifications = @user.notifications.opened_index(10)
49
+ # @notifications = @user.notifications.opened_only(10).group_owners_only.latest_order
50
+ # @param [Integer] limit Limit to query for opened notifications
51
+ # @param [Boolean] reverse If notification index will be ordered as earliest first
52
+ # @param [Boolean] with_group_members If notification index will include group members
53
+ # @return [ActiveRecord_AssociationRelation<Notificaion>, Mongoid::Criteria<Notificaion>] Database query of filtered notifications
54
+ scope :opened_index, ->(limit, reverse = false, with_group_members = false) {
55
+ target_index = with_group_members ? opened_only(limit) : opened_only(limit).group_owners_only
56
+ reverse ? target_index.earliest_order : target_index.latest_order
57
+ }
58
+
59
+ # Selects filtered notifications by target_type.
60
+ # @example Get filtered unopened notificatons of User as target type
61
+ # @notifications = ActivityNotification.Notification.unopened_only.filtered_by_target_type('User')
62
+ # @scope class
63
+ # @param [String] target_type Target type for filter
64
+ # @return [ActiveRecord_AssociationRelation<Notificaion>, Mongoid::Criteria<Notificaion>] Database query of filtered notifications
65
+ scope :filtered_by_target_type, ->(target_type) { where(target_type: target_type) }
66
+
67
+ # Selects filtered notifications by notifiable_type.
68
+ # @example Get filtered unopened notificatons of the @user for Comment notifiable class
69
+ # @notifications = @user.notifications.unopened_only.filtered_by_type('Comment')
70
+ # @scope class
71
+ # @param [String] notifiable_type Notifiable type for filter
72
+ # @return [ActiveRecord_AssociationRelation<Notificaion>, Mongoid::Criteria<Notificaion>] Database query of filtered notifications
73
+ scope :filtered_by_type, ->(notifiable_type) { where(notifiable_type: notifiable_type) }
74
+
75
+ # Selects filtered notifications by key.
76
+ # @example Get filtered unopened notificatons of the @user with key 'comment.reply'
77
+ # @notifications = @user.notifications.unopened_only.filtered_by_key('comment.reply')
78
+ # @scope class
79
+ # @param [String] key Key of the notification for filter
80
+ # @return [ActiveRecord_AssociationRelation<Notificaion>, Mongoid::Criteria<Notificaion>] Database query of filtered notifications
81
+ scope :filtered_by_key, ->(key) { where(key: key) }
82
+
83
+ # Selects filtered notifications by notifiable_type, group or key with filter options.
84
+ # @example Get filtered unopened notificatons of the @user for Comment notifiable class
85
+ # @notifications = @user.notifications.unopened_only.filtered_by_options({ filtered_by_type: 'Comment' })
86
+ # @example Get filtered unopened notificatons of the @user for @article as group
87
+ # @notifications = @user.notifications.unopened_only.filtered_by_options({ filtered_by_group: @article })
88
+ # @example Get filtered unopened notificatons of the @user for Article instance id=1 as group
89
+ # @notifications = @user.notifications.unopened_only.filtered_by_options({ filtered_by_group_type: 'Article', filtered_by_group_id: '1' })
90
+ # @example Get filtered unopened notificatons of the @user with key 'comment.reply'
91
+ # @notifications = @user.notifications.unopened_only.filtered_by_options({ filtered_by_key: 'comment.reply' })
92
+ # @example Get filtered unopened notificatons of the @user for Comment notifiable class with key 'comment.reply'
93
+ # @notifications = @user.notifications.unopened_only.filtered_by_options({ filtered_by_type: 'Comment', filtered_by_key: 'comment.reply' })
94
+ # @example Get custom filtered notificatons of the @user
95
+ # @notifications = @user.notifications.unopened_only.filtered_by_options({ custom_filter: ["created_at >= ?", time.hour.ago] })
96
+ # @scope class
97
+ # @param [Hash] options Options for filter
98
+ # @option options [String] :filtered_by_type (nil) Notifiable type for filter
99
+ # @option options [Object] :filtered_by_group (nil) Group instance for filter
100
+ # @option options [String] :filtered_by_group_type (nil) Group type for filter, valid with :filtered_by_group_id
101
+ # @option options [String] :filtered_by_group_id (nil) Group instance id for filter, valid with :filtered_by_group_type
102
+ # @option options [String] :filtered_by_key (nil) Key of the notification for filter
103
+ # @option options [Array|Hash] :custom_filter (nil) Custom notification filter (e.g. ["created_at >= ?", time.hour.ago])
104
+ # @return [ActiveRecord_AssociationRelation<Notificaion>, Mongoid::Criteria<Notificaion>] Database query of filtered notifications
105
+ scope :filtered_by_options, ->(options = {}) {
106
+ options = ActivityNotification.cast_to_indifferent_hash(options)
107
+ filtered_notifications = all
108
+ if options.has_key?(:filtered_by_type)
109
+ filtered_notifications = filtered_notifications.filtered_by_type(options[:filtered_by_type])
110
+ end
111
+ if options.has_key?(:filtered_by_group)
112
+ filtered_notifications = filtered_notifications.filtered_by_group(options[:filtered_by_group])
113
+ end
114
+ if options.has_key?(:filtered_by_group_type) && options.has_key?(:filtered_by_group_id)
115
+ filtered_notifications = filtered_notifications
116
+ .where(group_type: options[:filtered_by_group_type], group_id: options[:filtered_by_group_id])
117
+ end
118
+ if options.has_key?(:filtered_by_key)
119
+ filtered_notifications = filtered_notifications.filtered_by_key(options[:filtered_by_key])
120
+ end
121
+ if options.has_key?(:custom_filter)
122
+ filtered_notifications = filtered_notifications.where(options[:custom_filter])
123
+ end
124
+ filtered_notifications
125
+ }
126
+
127
+ # Orders by latest (newest) first as created_at: :desc.
128
+ # @return [Mongoid::Criteria<Notificaion>] Database query of notifications ordered by latest first
129
+ scope :latest_order, -> { order(created_at: :desc) }
130
+
131
+ # Orders by earliest (older) first as created_at: :asc.
132
+ # @return [Mongoid::Criteria<Notificaion>] Database query of notifications ordered by earliest first
133
+ scope :earliest_order, -> { order(created_at: :asc) }
9
134
  end
10
135
 
11
136
  class_methods do
@@ -129,16 +254,19 @@ module ActivityNotification
129
254
  # @todo Add filter option
130
255
  def open_all_of(target, options = {})
131
256
  opened_at = options[:opened_at] || Time.current
132
- target.notifications.unopened_only.filtered_by_options(options).update_all(opened_at: opened_at)
257
+ target_unopened_notifications = target.notifications.unopened_only.filtered_by_options(options)
258
+ unopened_notification_count = target_unopened_notifications.count
259
+ target_unopened_notifications.update_all(opened_at: opened_at)
260
+ unopened_notification_count
133
261
  end
134
262
 
135
263
  # Returns if group member of the notifications exists.
136
264
  # This method is designed to be called from controllers or views to avoid N+1.
137
265
  #
138
- # @param [Array<Notificaion>, ActiveRecord_AssociationRelation<Notificaion>] notifications Array or database query of the notifications to test member exists
266
+ # @param [Array<Notificaion>, ActiveRecord_AssociationRelation<Notificaion>, Mongoid::Criteria<Notificaion>] notifications Array or database query of the notifications to test member exists
139
267
  # @return [Boolean] If group member of the notifications exists
140
268
  def group_member_exists?(notifications)
141
- notifications.present? && where(group_owner_id: notifications.map(&:id)).exists?
269
+ notifications.present? && group_members_of_owner_ids_only(notifications.map(&:id)).exists?
142
270
  end
143
271
 
144
272
  # Sends batch notification email to the target.
@@ -181,16 +309,15 @@ module ActivityNotification
181
309
  parameters.merge!(notifiable.notification_parameters(target_type, key))
182
310
 
183
311
  # Bundle notification group by target, notifiable_type, group and key
184
- # Defferent notifiable.id can be made in a same group
312
+ # Different notifiable.id can be made in a same group
185
313
  group_owner_notifications = filtered_by_target(target).filtered_by_type(notifiable.to_class_name).filtered_by_key(key)
186
314
  .filtered_by_group(group).group_owners_only.unopened_only
187
315
  group_owner = group_expiry_delay.present? ?
188
- group_owner_notifications.where("created_at > ?", group_expiry_delay.ago).earliest :
316
+ group_owner_notifications.within_expiration_only(group_expiry_delay).earliest :
189
317
  group_owner_notifications.earliest
190
318
  notification_fields = { target: target, notifiable: notifiable, key: key, group: group, parameters: parameters, notifier: notifier }
191
- group.present? && group_owner.present? ?
192
- create(notification_fields.merge(group_owner: group_owner)) :
193
- create(notification_fields)
319
+ notification_fields = notification_fields.merge(group_owner: group_owner) if group.present? && group_owner.present?
320
+ create(notification_fields)
194
321
  end
195
322
  end
196
323
 
@@ -235,10 +362,13 @@ module ActivityNotification
235
362
  # @option options [Boolean] :with_members (true) If it opens notifications including group members
236
363
  # @return [Integer] Number of opened notification records
237
364
  def open!(options = {})
238
- opened_at = options[:opened_at] || Time.current
365
+ opened? and return 0
366
+ opened_at = options[:opened_at] || Time.current
239
367
  with_members = options.has_key?(:with_members) ? options[:with_members] : true
368
+ unopened_member_count = with_members ? group_members.unopened_only.count : 0
369
+ group_members.update_all(opened_at: opened_at) if with_members
240
370
  update(opened_at: opened_at)
241
- with_members ? group_members.update_all(opened_at: opened_at) + 1 : 1
371
+ unopened_member_count + 1
242
372
  end
243
373
 
244
374
  # Returns if the notification is unopened.
@@ -259,7 +389,7 @@ module ActivityNotification
259
389
  #
260
390
  # @return [Boolean] If the notification is group owner
261
391
  def group_owner?
262
- group_owner_id.blank?
392
+ !group_member?
263
393
  end
264
394
 
265
395
  # Returns if the notification is group member belonging to owner.
@@ -345,7 +475,7 @@ module ActivityNotification
345
475
  new_group_owner = group_members.earliest
346
476
  if new_group_owner.present?
347
477
  new_group_owner.update(group_owner_id: nil)
348
- group_members.update_all(group_owner_id: new_group_owner)
478
+ group_members.update_all(group_owner_id: new_group_owner.id)
349
479
  end
350
480
  new_group_owner
351
481
  end
@@ -391,70 +521,6 @@ module ActivityNotification
391
521
 
392
522
  protected
393
523
 
394
- # Returns count of group members of the unopened notification.
395
- # This method is designed to cache group by query result to avoid N+1 call.
396
- # @api protected
397
- #
398
- # @return [Integer] Count of group members of the unopened notification
399
- def unopened_group_member_count
400
- # Cache group by query result to avoid N+1 call
401
- unopened_group_member_counts = target.notifications
402
- .unopened_index_group_members_only
403
- .group(:group_owner_id)
404
- .count
405
- unopened_group_member_counts[id] || 0
406
- end
407
-
408
- # Returns count of group members of the opened notification.
409
- # This method is designed to cache group by query result to avoid N+1 call.
410
- # @api protected
411
- #
412
- # @return [Integer] Count of group members of the opened notification
413
- def opened_group_member_count(limit = ActivityNotification.config.opened_index_limit)
414
- # Cache group by query result to avoid N+1 call
415
- opened_group_member_counts = target.notifications
416
- .opened_index_group_members_only(limit)
417
- .group(:group_owner_id)
418
- .count
419
- opened_group_member_counts[id] || 0
420
- end
421
-
422
- # Returns count of group member notifiers of the unopened notification not including group owner notifier.
423
- # This method is designed to cache group by query result to avoid N+1 call.
424
- # @api protected
425
- #
426
- # @return [Integer] Count of group member notifiers of the unopened notification
427
- def unopened_group_member_notifier_count
428
- # Cache group by query result to avoid N+1 call
429
- unopened_group_member_notifier_counts = target.notifications
430
- .unopened_index_group_members_only
431
- .includes(:group_owner)
432
- .where('group_owners_notifications.notifier_type = notifications.notifier_type')
433
- .where.not('group_owners_notifications.notifier_id = notifications.notifier_id')
434
- .references(:group_owner)
435
- .group(:group_owner_id, :notifier_type)
436
- .count('distinct notifications.notifier_id')
437
- unopened_group_member_notifier_counts[[id, notifier_type]] || 0
438
- end
439
-
440
- # Returns count of group member notifiers of the opened notification not including group owner notifier.
441
- # This method is designed to cache group by query result to avoid N+1 call.
442
- # @api protected
443
- #
444
- # @return [Integer] Count of group member notifiers of the opened notification
445
- def opened_group_member_notifier_count(limit = ActivityNotification.config.opened_index_limit)
446
- # Cache group by query result to avoid N+1 call
447
- opened_group_member_notifier_counts = target.notifications
448
- .opened_index_group_members_only(limit)
449
- .includes(:group_owner)
450
- .where('group_owners_notifications.notifier_type = notifications.notifier_type')
451
- .where.not('group_owners_notifications.notifier_id = notifications.notifier_id')
452
- .references(:group_owner)
453
- .group(:group_owner_id, :notifier_type)
454
- .count('distinct notifications.notifier_id')
455
- opened_group_member_notifier_counts[[id, notifier_type]] || 0
456
- end
457
-
458
524
  # Returns count of various members of the notification.
459
525
  # This method is designed to cache group by query result to avoid N+1 call.
460
526
  # @api protected
@@ -3,6 +3,58 @@ module ActivityNotification
3
3
  module SubscriptionApi
4
4
  extend ActiveSupport::Concern
5
5
 
6
+ included do
7
+ # Selects filtered subscriptions by key.
8
+ # @example Get filtered subscriptions of the @user with key 'comment.reply'
9
+ # @subscriptions = @user.subscriptions.filtered_by_key('comment.reply')
10
+ # @scope class
11
+ # @param [String] key Key of the subscription for filter
12
+ # @return [ActiveRecord_AssociationRelation<Subscription>] Database query of filtered subscriptions
13
+ scope :filtered_by_key, ->(key) { where(key: key) }
14
+
15
+ # Selects filtered subscriptions by key with filter options.
16
+ # @example Get filtered subscriptions of the @user with key 'comment.reply'
17
+ # @subscriptions = @user.subscriptions.filtered_by_key('comment.reply')
18
+ # @example Get custom filtered subscriptions of the @user
19
+ # @subscriptions = @user.subscriptions.filtered_by_options({ custom_filter: ["created_at >= ?", time.hour.ago] })
20
+ # @scope class
21
+ # @param [Hash] options Options for filter
22
+ # @option options [String] :filtered_by_key (nil) Key of the subscription for filter
23
+ # @option options [Array|Hash] :custom_filter (nil) Custom subscription filter (e.g. ["created_at >= ?", time.hour.ago])
24
+ # @return [Mongoid::Criteria<Notificaion>] Database query of filtered subscriptions
25
+ scope :filtered_by_options, ->(options = {}) {
26
+ options = ActivityNotification.cast_to_indifferent_hash(options)
27
+ filtered_subscriptions = all
28
+ if options.has_key?(:filtered_by_key)
29
+ filtered_subscriptions = filtered_subscriptions.filtered_by_key(options[:filtered_by_key])
30
+ end
31
+ if options.has_key?(:custom_filter)
32
+ filtered_subscriptions = filtered_subscriptions.where(options[:custom_filter])
33
+ end
34
+ filtered_subscriptions
35
+ }
36
+
37
+ # Orders by latest (newest) first as created_at: :desc.
38
+ # @return [ActiveRecord_AssociationRelation<Subscription>] Database query of subscriptions ordered by latest first
39
+ scope :latest_order, -> { order(created_at: :desc) }
40
+
41
+ # Orders by earliest (older) first as created_at: :asc.
42
+ # @return [ActiveRecord_AssociationRelation<Subscription>] Database query of subscriptions ordered by earliest first
43
+ scope :earliest_order, -> { order(created_at: :asc) }
44
+
45
+ # Orders by latest (newest) first as subscribed_at: :desc.
46
+ # @return [ActiveRecord_AssociationRelation<Subscription>] Database query of subscriptions ordered by latest subscribed_at first
47
+ scope :latest_subscribed_order, -> { order(subscribed_at: :desc) }
48
+
49
+ # Orders by earliest (older) first as subscribed_at: :asc.
50
+ # @return [ActiveRecord_AssociationRelation<Subscription>] Database query of subscriptions ordered by earliest subscribed_at first
51
+ scope :earliest_subscribed_order, -> { order(subscribed_at: :asc) }
52
+
53
+ # Orders by key name as key: :asc.
54
+ # @return [ActiveRecord_AssociationRelation<Subscription>] Database query of subscriptions ordered by key name
55
+ scope :key_order, -> { order(key: :asc) }
56
+ end
57
+
6
58
  class_methods do
7
59
  # Returns key of optional_targets hash from symbol class name of the optional target implementation.
8
60
  # @param [String, Symbol] optional_target_name Class name of the optional target implementation (e.g. :amazon_sns, :slack)
@@ -132,5 +184,25 @@ module ActivityNotification
132
184
  def optional_target_names
133
185
  optional_targets.keys.select { |key| key.to_s.start_with?("subscribing_to_") }.map { |key| key.slice(15..-1) }
134
186
  end
187
+
188
+
189
+ protected
190
+
191
+ # Validates subscribing_to_email cannot be true when subscribing isfalse.
192
+ def subscribing_to_email_cannot_be_true_when_subscribing_is_false
193
+ if !subscribing && subscribing_to_email?
194
+ errors.add(:subscribing_to_email, "cannot be true when subscribing is false")
195
+ end
196
+ end
197
+
198
+ # Validates subscribing_to_optional_target cannot be true when subscribing isfalse.
199
+ def subscribing_to_optional_target_cannot_be_true_when_subscribing_is_false
200
+ optional_target_names.each do |optional_target_name|
201
+ if !subscribing && subscribing_to_optional_target?(optional_target_name)
202
+ errors.add(:optional_targets, "#Subscription.to_optional_target_key(optional_target_name) cannot be true when subscribing is false")
203
+ end
204
+ end
205
+ end
206
+
135
207
  end
136
208
  end
@@ -110,6 +110,11 @@ module ActivityNotification
110
110
  # @return [Integer] Default limit to query for opened notifications.
111
111
  attr_accessor :opened_index_limit
112
112
 
113
+ # @overload :orm
114
+ # Returns ORM name for ActivityNotification (:active_record or :mongoid)
115
+ # @return [Boolean] ORM name for ActivityNotification (:active_record or :mongoid).
116
+ attr_reader :orm
117
+
113
118
  # Initialize configuration for ActivityNotification.
114
119
  # These configuration can be overriden in initializer.
115
120
  # @return [Config] A new instance of Config
@@ -125,7 +130,14 @@ module ActivityNotification
125
130
  @parent_mailer = 'ActionMailer::Base'
126
131
  @parent_controller = 'ApplicationController'
127
132
  @opened_index_limit = 10
133
+ @orm = :active_record
128
134
  end
129
135
 
136
+ # Sets ORM name for ActivityNotification (:active_record or :mongoid)
137
+ # @param [Symbol, String] orm The new ORM name for ActivityNotification (:active_record or :mongoid)
138
+ # @return [Symbol] ORM name for ActivityNotification (:active_record or :mongoid).
139
+ def orm=(orm)
140
+ @orm = orm.to_sym
141
+ end
130
142
  end
131
143
  end
@@ -7,14 +7,15 @@ module ActivityNotification
7
7
 
8
8
  included do
9
9
  include Common
10
+ include Association
10
11
  include ActionDispatch::Routing::PolymorphicRoutes
11
12
  include Rails.application.routes.url_helpers
12
13
 
13
14
  # Has many notification instances for this notifiable.
14
15
  # Dependency for these notifications can be overriden from acts_as_notifiable.
15
16
  # @scope instance
16
- # @return [Array<Notificaion>] Array or database query of notifications for this notifiable
17
- has_many :generated_notifications_as_notifiable,
17
+ # @return [Array<Notificaion>, Mongoid::Criteria<Notificaion>] Array or database query of notifications for this notifiable
18
+ has_many_records :generated_notifications_as_notifiable,
18
19
  class_name: "::ActivityNotification::Notification",
19
20
  as: :notifiable
20
21
 
@@ -207,8 +208,14 @@ module ActivityNotification
207
208
  optional_targets(target_type, key).map { |optional_target| optional_target.to_optional_target_name }
208
209
  end
209
210
 
211
+ # overriding_notification_template_key is the method to override key definition for Renderable
212
+ # When respond_to?(:overriding_notification_template_key) returns true,
213
+ # Renderable uses overriding_notification_template_key instead of original key.
214
+ #
215
+ # overriding_notification_template_key(target, key)
216
+
210
217
  # overriding_notification_email_key is the method to override key definition for Mailer
211
- # When respond_to?(overriding_notification_email_key) returns true,
218
+ # When respond_to?(:overriding_notification_email_key) returns true,
212
219
  # Mailer uses overriding_notification_email_key instead of original key.
213
220
  #
214
221
  # overriding_notification_email_key(target, key)
@@ -303,11 +310,54 @@ module ActivityNotification
303
310
  end
304
311
  end
305
312
 
306
- # Remove generated notifications from notification group to new group owner.
313
+ # Gets generated notifications for specified target type.
314
+ # @api private
315
+ # @param [String] target_type Target type of generated notifications
316
+ def generated_notifications_as_notifiable_for(target_type = nil)
317
+ target_type.nil? ?
318
+ generated_notifications_as_notifiable.all :
319
+ generated_notifications_as_notifiable.filtered_by_target_type(target_type.to_s.to_model_name)
320
+ end
321
+
322
+ # Destroies generated notifications for specified target type with dependency.
323
+ # This method is intended to be called before destroy this notifiable as dependent configuration.
324
+ # @api private
325
+ # @param [Symbol] dependent Has_many dependency, [:delete_all, :destroy, :restrict_with_error, :restrict_with_exception] are available
326
+ # @param [String] target_type Target type of generated notifications
327
+ # @param [Boolean] remove_from_group Whether it removes generated notifications from notification group before destroy
328
+ def destroy_generated_notifications_with_dependency(dependent = :delete_all, target_type = nil, remove_from_group = false)
329
+ remove_generated_notifications_from_group(target_type) if remove_from_group
330
+ generated_notifications = generated_notifications_as_notifiable_for(target_type)
331
+ case dependent
332
+ when :restrict_with_exception
333
+ raise ActiveRecord::DeleteRestrictionError.new("generated_notifications_as_notifiable_for_#{target_type.to_s.pluralize.underscore}") unless generated_notifications.empty?
334
+ when :restrict_with_error
335
+ unless generated_notifications.empty?
336
+ record = self.class.human_attribute_name("generated_notifications_as_notifiable_for_#{target_type.to_s.pluralize.underscore}").downcase
337
+ self.errors.add(:base, :'restrict_dependent_destroy.has_many', record: record)
338
+ # :skip-rails4:
339
+ if Rails::VERSION::MAJOR >= 5
340
+ throw(:abort)
341
+ # :skip-rails4:
342
+ # :skip-rails5:
343
+ else
344
+ false
345
+ end
346
+ # :skip-rails5:
347
+ end
348
+ when :destroy
349
+ generated_notifications.each { |n| n.destroy }
350
+ when :delete_all
351
+ generated_notifications.delete_all
352
+ end
353
+ end
354
+
355
+ # Removes generated notifications from notification group to new group owner.
307
356
  # This method is intended to be called before destroy this notifiable as dependent configuration.
308
357
  # @api private
309
- def remove_generated_notifications_from_group
310
- generated_notifications_as_notifiable.group_owners_only.each { |n| n.remove_from_group }
358
+ # @param [String] target_type Target type of generated notifications
359
+ def remove_generated_notifications_from_group(target_type = nil)
360
+ generated_notifications_as_notifiable_for(target_type).group_owners_only.each { |n| n.remove_from_group }
311
361
  end
312
362
 
313
363
  # Casts to resources name.