activity_notification 1.2.1 → 1.3.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.
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.