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.
- checksums.yaml +4 -4
- data/.travis.yml +15 -5
- data/CHANGELOG.md +13 -2
- data/Gemfile +1 -1
- data/Gemfile.lock +78 -71
- data/README.md +64 -43
- data/activity_notification.gemspec +5 -4
- data/app/controllers/activity_notification/notifications_controller.rb +1 -1
- data/app/controllers/activity_notification/subscriptions_controller.rb +1 -1
- data/gemfiles/Gemfile.rails-4.2 +1 -1
- data/gemfiles/Gemfile.rails-4.2.lock +71 -62
- data/gemfiles/Gemfile.rails-5.0 +1 -1
- data/gemfiles/Gemfile.rails-5.0.lock +74 -67
- data/lib/activity_notification.rb +9 -2
- data/lib/activity_notification/apis/notification_api.rb +142 -76
- data/lib/activity_notification/apis/subscription_api.rb +72 -0
- data/lib/activity_notification/config.rb +12 -0
- data/lib/activity_notification/models/concerns/notifiable.rb +56 -6
- data/lib/activity_notification/models/concerns/notifier.rb +8 -1
- data/lib/activity_notification/models/concerns/subscriber.rb +13 -10
- data/lib/activity_notification/models/concerns/target.rb +7 -5
- data/lib/activity_notification/models/notification.rb +2 -270
- data/lib/activity_notification/models/subscription.rb +3 -101
- data/lib/activity_notification/optional_targets/base.rb +5 -1
- data/lib/activity_notification/orm/active_record.rb +16 -0
- data/lib/activity_notification/orm/active_record/notification.rb +252 -0
- data/lib/activity_notification/orm/active_record/subscription.rb +52 -0
- data/lib/activity_notification/orm/mongoid.rb +63 -0
- data/lib/activity_notification/orm/mongoid/notification.rb +255 -0
- data/lib/activity_notification/orm/mongoid/subscription.rb +73 -0
- data/lib/activity_notification/rails/routes.rb +7 -3
- data/lib/activity_notification/renderable.rb +5 -1
- data/lib/activity_notification/roles/acts_as_notifiable.rb +4 -18
- data/lib/activity_notification/version.rb +1 -1
- data/lib/generators/activity_notification/install_generator.rb +3 -5
- data/lib/generators/templates/activity_notification.rb +9 -4
- data/spec/concerns/apis/notification_api_spec.rb +27 -14
- data/spec/concerns/models/notifiable_spec.rb +10 -8
- data/spec/concerns/models/subscriber_spec.rb +12 -12
- data/spec/concerns/models/target_spec.rb +61 -51
- data/spec/controllers/subscriptions_controller_shared_examples.rb +0 -1
- data/spec/helpers/view_helpers_spec.rb +24 -7
- data/spec/models/notification_spec.rb +63 -52
- data/spec/models/subscription_spec.rb +6 -3
- data/spec/optional_targets/amazon_sns_spec.rb +8 -5
- data/spec/optional_targets/base_spec.rb +3 -1
- data/spec/optional_targets/slack_spec.rb +5 -5
- data/spec/rails_app/app/models/comment.rb +1 -1
- data/spec/rails_app/app/views/activity_notification/notifications/users/overriden/custom/_test.html.erb +1 -0
- data/spec/rails_app/config/application.rb +7 -0
- data/spec/rails_app/config/initializers/activity_notification.rb +5 -0
- data/spec/rails_app/config/mongoid.yml +13 -0
- data/spec/rails_app/db/seeds.rb +1 -1
- data/spec/rails_app/lib/custom_optional_targets/console_output.rb +7 -4
- data/spec/rails_app/lib/custom_optional_targets/wrong_target.rb +3 -0
- data/spec/roles/acts_as_notifiable_spec.rb +77 -16
- data/spec/spec_helper.rb +7 -0
- metadata +38 -14
@@ -8,7 +8,10 @@ module ActivityNotification
|
|
8
8
|
attr_accessor :view_context
|
9
9
|
|
10
10
|
# Initialize method to create view context in this OptionalTarget instance
|
11
|
-
|
11
|
+
# @param [Hash] options Options for initializing target
|
12
|
+
# @option options [Boolean] :skip_initializing_target (false) Whether skip calling initialize_target method
|
13
|
+
# @option options [Hash] others Options for initializing target
|
14
|
+
def initialize(options = {})
|
12
15
|
@view_context = ActionView::Base.new(ActionController::Base.view_paths, {})
|
13
16
|
@view_context.class_eval do
|
14
17
|
include Rails.application.routes.url_helpers
|
@@ -16,6 +19,7 @@ module ActivityNotification
|
|
16
19
|
ActionMailer::Base.default_url_options
|
17
20
|
end
|
18
21
|
end
|
22
|
+
initialize_target(options) unless options.delete(:skip_initializing_target)
|
19
23
|
end
|
20
24
|
|
21
25
|
# Returns demodulized symbol class name as optional target name
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module ActivityNotification
|
2
|
+
module Association
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
class_methods do
|
6
|
+
# Defines has_many association with ActivityNotification models.
|
7
|
+
# @return [ActiveRecord_AssociationRelation<Object>] Database query of associated model instances
|
8
|
+
def has_many_records(name, options = {})
|
9
|
+
has_many name, options
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
require_relative 'active_record/notification.rb'
|
16
|
+
require_relative 'active_record/subscription.rb'
|
@@ -0,0 +1,252 @@
|
|
1
|
+
require 'activity_notification/apis/notification_api'
|
2
|
+
|
3
|
+
module ActivityNotification
|
4
|
+
module ORM
|
5
|
+
module ActiveRecord
|
6
|
+
# Notification model implementation generated by ActivityNotification.
|
7
|
+
class Notification < ::ActiveRecord::Base
|
8
|
+
include Common
|
9
|
+
include Renderable
|
10
|
+
include NotificationApi
|
11
|
+
# @deprecated ActivityNotification.config.table_name as of 1.1.0
|
12
|
+
self.table_name = ActivityNotification.config.table_name || ActivityNotification.config.notification_table_name
|
13
|
+
# self.table_name = ActivityNotification.config.notification_table_name
|
14
|
+
|
15
|
+
# Belongs to target instance of this notification as polymorphic association.
|
16
|
+
# @scope instance
|
17
|
+
# @return [Object] Target instance of this notification
|
18
|
+
belongs_to :target, polymorphic: true
|
19
|
+
|
20
|
+
# Belongs to notifiable instance of this notification as polymorphic association.
|
21
|
+
# @scope instance
|
22
|
+
# @return [Object] Notifiable instance of this notification
|
23
|
+
belongs_to :notifiable, polymorphic: true
|
24
|
+
|
25
|
+
# Belongs to group instance of this notification as polymorphic association.
|
26
|
+
# @scope instance
|
27
|
+
# @return [Object] Group instance of this notification
|
28
|
+
belongs_to :group, polymorphic: true
|
29
|
+
|
30
|
+
# Belongs to group owner notification instance of this notification.
|
31
|
+
# Only group member instance has :group_owner value.
|
32
|
+
# Group owner instance has nil as :group_owner association.
|
33
|
+
# @scope instance
|
34
|
+
# @return [Notification] Group owner notification instance of this notification
|
35
|
+
belongs_to :group_owner, { class_name: "ActivityNotification::Notification" }.merge(Rails::VERSION::MAJOR >= 5 ? { optional: true } : {})
|
36
|
+
|
37
|
+
# Has many group member notification instances of this notification.
|
38
|
+
# Only group owner instance has :group_members value.
|
39
|
+
# Group member instance has nil as :group_members association.
|
40
|
+
# @scope instance
|
41
|
+
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of the group member notification instances of this notification
|
42
|
+
has_many :group_members, class_name: "ActivityNotification::Notification", foreign_key: :group_owner_id
|
43
|
+
|
44
|
+
# Belongs to :otifier instance of this notification.
|
45
|
+
# @scope instance
|
46
|
+
# @return [Object] Notifier instance of this notification
|
47
|
+
belongs_to :notifier, polymorphic: true
|
48
|
+
|
49
|
+
# Serialize parameters Hash
|
50
|
+
serialize :parameters, Hash
|
51
|
+
|
52
|
+
validates :target, presence: true
|
53
|
+
validates :notifiable, presence: true
|
54
|
+
validates :key, presence: true
|
55
|
+
|
56
|
+
# Selects group owner notifications only.
|
57
|
+
# @scope class
|
58
|
+
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of filtered notifications
|
59
|
+
scope :group_owners_only, -> { where(group_owner_id: nil) }
|
60
|
+
|
61
|
+
# Selects group member notifications only.
|
62
|
+
# @scope class
|
63
|
+
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of filtered notifications
|
64
|
+
scope :group_members_only, -> { where.not(group_owner_id: nil) }
|
65
|
+
|
66
|
+
# Selects unopened notifications only.
|
67
|
+
# @scope class
|
68
|
+
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of filtered notifications
|
69
|
+
scope :unopened_only, -> { where(opened_at: nil) }
|
70
|
+
|
71
|
+
# Selects opened notifications only without limit.
|
72
|
+
# Be careful to get too many records with this method.
|
73
|
+
# @scope class
|
74
|
+
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of filtered notifications
|
75
|
+
scope :opened_only!, -> { where.not(opened_at: nil) }
|
76
|
+
|
77
|
+
# Selects opened notifications only with limit.
|
78
|
+
# @scope class
|
79
|
+
# @param [Integer] limit Limit to query for opened notifications
|
80
|
+
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of filtered notifications
|
81
|
+
scope :opened_only, ->(limit) { opened_only!.limit(limit) }
|
82
|
+
|
83
|
+
# Selects group member notifications in unopened_index.
|
84
|
+
# @scope class
|
85
|
+
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of filtered notifications
|
86
|
+
scope :unopened_index_group_members_only, -> { where(group_owner_id: unopened_index.map(&:id)) }
|
87
|
+
|
88
|
+
# Selects group member notifications in opened_index.
|
89
|
+
# @scope class
|
90
|
+
# @param [Integer] limit Limit to query for opened notifications
|
91
|
+
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of filtered notifications
|
92
|
+
scope :opened_index_group_members_only, ->(limit) { where(group_owner_id: opened_index(limit).map(&:id)) }
|
93
|
+
|
94
|
+
# Selects notifications within expiration.
|
95
|
+
# @scope class
|
96
|
+
# @param [ActiveSupport::Duration] expiry_delay Expiry period of notifications
|
97
|
+
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of filtered notifications
|
98
|
+
scope :within_expiration_only, ->(expiry_delay) { where("created_at > ?", expiry_delay.ago) }
|
99
|
+
|
100
|
+
# Selects group member notifications with specified group owner ids.
|
101
|
+
# @scope class
|
102
|
+
# @param [Array<String>] owner_ids Array of group owner ids
|
103
|
+
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of filtered notifications
|
104
|
+
scope :group_members_of_owner_ids_only, ->(owner_ids) { where(group_owner_id: owner_ids) }
|
105
|
+
|
106
|
+
# Selects filtered notifications by target instance.
|
107
|
+
# ActivityNotification::Notification.filtered_by_target(@user)
|
108
|
+
# is the same as
|
109
|
+
# @user.notifications
|
110
|
+
# @scope class
|
111
|
+
# @param [Object] target Target instance for filter
|
112
|
+
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of filtered notifications
|
113
|
+
scope :filtered_by_target, ->(target) { where(target: target) }
|
114
|
+
|
115
|
+
# Selects filtered notifications by notifiable instance.
|
116
|
+
# @example Get filtered unopened notificatons of the @user for @comment as notifiable
|
117
|
+
# @notifications = @user.notifications.unopened_only.filtered_by_instance(@comment)
|
118
|
+
# @scope class
|
119
|
+
# @param [Object] notifiable Notifiable instance for filter
|
120
|
+
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of filtered notifications
|
121
|
+
scope :filtered_by_instance, ->(notifiable) { where(notifiable: notifiable) }
|
122
|
+
|
123
|
+
# Selects filtered notifications by notifiable_type.
|
124
|
+
# @example Get filtered unopened notificatons of the @user for Comment notifiable class
|
125
|
+
# @notifications = @user.notifications.unopened_only.filtered_by_type('Comment')
|
126
|
+
# @scope class
|
127
|
+
# @param [String] notifiable_type Notifiable type for filter
|
128
|
+
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of filtered notifications
|
129
|
+
scope :filtered_by_type, ->(notifiable_type) { where(notifiable_type: notifiable_type) }
|
130
|
+
|
131
|
+
# Selects filtered notifications by group instance.
|
132
|
+
# @example Get filtered unopened notificatons of the @user for @article as group
|
133
|
+
# @notifications = @user.notifications.unopened_only.filtered_by_group(@article)
|
134
|
+
# @scope class
|
135
|
+
# @param [Object] group Group instance for filter
|
136
|
+
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of filtered notifications
|
137
|
+
scope :filtered_by_group, ->(group) { where(group: group) }
|
138
|
+
|
139
|
+
# Includes target instance with query for notifications.
|
140
|
+
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of notifications with target
|
141
|
+
scope :with_target, -> { includes(:target) }
|
142
|
+
|
143
|
+
# Includes notifiable instance with query for notifications.
|
144
|
+
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of notifications with notifiable
|
145
|
+
scope :with_notifiable, -> { includes(:notifiable) }
|
146
|
+
|
147
|
+
# Includes group instance with query for notifications.
|
148
|
+
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of notifications with group
|
149
|
+
scope :with_group, -> { includes(:group) }
|
150
|
+
|
151
|
+
# Includes group owner instances with query for notifications.
|
152
|
+
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of notifications with group owner
|
153
|
+
scope :with_group_owner, -> { includes(:group_owner) }
|
154
|
+
|
155
|
+
# Includes group member instances with query for notifications.
|
156
|
+
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of notifications with group members
|
157
|
+
scope :with_group_members, -> { includes(:group_members) }
|
158
|
+
|
159
|
+
# Includes notifier instance with query for notifications.
|
160
|
+
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of notifications with notifier
|
161
|
+
scope :with_notifier, -> { includes(:notifier) }
|
162
|
+
|
163
|
+
# Returns latest notification instance.
|
164
|
+
# @return [Notification] Latest notification instance
|
165
|
+
def self.latest
|
166
|
+
latest_order.first
|
167
|
+
end
|
168
|
+
|
169
|
+
# Returns earliest notification instance.
|
170
|
+
# @return [Notification] Earliest notification instance
|
171
|
+
def self.earliest
|
172
|
+
earliest_order.first
|
173
|
+
end
|
174
|
+
|
175
|
+
# Selects unique keys from query for notifications.
|
176
|
+
# @return [Array<String>] Array of notification unique keys
|
177
|
+
def self.uniq_keys
|
178
|
+
select(:key).distinct.pluck(:key)
|
179
|
+
end
|
180
|
+
|
181
|
+
protected
|
182
|
+
|
183
|
+
# Returns count of group members of the unopened notification.
|
184
|
+
# This method is designed to cache group by query result to avoid N+1 call.
|
185
|
+
# @api protected
|
186
|
+
#
|
187
|
+
# @return [Integer] Count of group members of the unopened notification
|
188
|
+
def unopened_group_member_count
|
189
|
+
# Cache group by query result to avoid N+1 call
|
190
|
+
unopened_group_member_counts = target.notifications
|
191
|
+
.unopened_index_group_members_only
|
192
|
+
.group(:group_owner_id)
|
193
|
+
.count
|
194
|
+
unopened_group_member_counts[id] || 0
|
195
|
+
end
|
196
|
+
|
197
|
+
# Returns count of group members of the opened notification.
|
198
|
+
# This method is designed to cache group by query result to avoid N+1 call.
|
199
|
+
# @api protected
|
200
|
+
#
|
201
|
+
# @return [Integer] Count of group members of the opened notification
|
202
|
+
def opened_group_member_count(limit = ActivityNotification.config.opened_index_limit)
|
203
|
+
# Cache group by query result to avoid N+1 call
|
204
|
+
opened_group_member_counts = target.notifications
|
205
|
+
.opened_index_group_members_only(limit)
|
206
|
+
.group(:group_owner_id)
|
207
|
+
.count
|
208
|
+
count = opened_group_member_counts[id] || 0
|
209
|
+
count > limit ? limit : count
|
210
|
+
end
|
211
|
+
|
212
|
+
# Returns count of group member notifiers of the unopened notification not including group owner notifier.
|
213
|
+
# This method is designed to cache group by query result to avoid N+1 call.
|
214
|
+
# @api protected
|
215
|
+
#
|
216
|
+
# @return [Integer] Count of group member notifiers of the unopened notification
|
217
|
+
def unopened_group_member_notifier_count
|
218
|
+
# Cache group by query result to avoid N+1 call
|
219
|
+
unopened_group_member_notifier_counts = target.notifications
|
220
|
+
.unopened_index_group_members_only
|
221
|
+
.includes(:group_owner)
|
222
|
+
.where('group_owners_notifications.notifier_type = notifications.notifier_type')
|
223
|
+
.where.not('group_owners_notifications.notifier_id = notifications.notifier_id')
|
224
|
+
.references(:group_owner)
|
225
|
+
.group(:group_owner_id, :notifier_type)
|
226
|
+
.count('distinct notifications.notifier_id')
|
227
|
+
unopened_group_member_notifier_counts[[id, notifier_type]] || 0
|
228
|
+
end
|
229
|
+
|
230
|
+
# Returns count of group member notifiers of the opened notification not including group owner notifier.
|
231
|
+
# This method is designed to cache group by query result to avoid N+1 call.
|
232
|
+
# @api protected
|
233
|
+
#
|
234
|
+
# @return [Integer] Count of group member notifiers of the opened notification
|
235
|
+
def opened_group_member_notifier_count(limit = ActivityNotification.config.opened_index_limit)
|
236
|
+
# Cache group by query result to avoid N+1 call
|
237
|
+
opened_group_member_notifier_counts = target.notifications
|
238
|
+
.opened_index_group_members_only(limit)
|
239
|
+
.includes(:group_owner)
|
240
|
+
.where('group_owners_notifications.notifier_type = notifications.notifier_type')
|
241
|
+
.where.not('group_owners_notifications.notifier_id = notifications.notifier_id')
|
242
|
+
.references(:group_owner)
|
243
|
+
.group(:group_owner_id, :notifier_type)
|
244
|
+
.count('distinct notifications.notifier_id')
|
245
|
+
count = opened_group_member_notifier_counts[[id, notifier_type]] || 0
|
246
|
+
count > limit ? limit : count
|
247
|
+
end
|
248
|
+
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'activity_notification/apis/subscription_api'
|
2
|
+
|
3
|
+
module ActivityNotification
|
4
|
+
module ORM
|
5
|
+
module ActiveRecord
|
6
|
+
# Subscription model implementation generated by ActivityNotification.
|
7
|
+
class Subscription < ::ActiveRecord::Base
|
8
|
+
include SubscriptionApi
|
9
|
+
self.table_name = ActivityNotification.config.subscription_table_name
|
10
|
+
|
11
|
+
# Belongs to target instance of this subscription as polymorphic association.
|
12
|
+
# @scope instance
|
13
|
+
# @return [Object] Target instance of this subscription
|
14
|
+
belongs_to :target, polymorphic: true
|
15
|
+
|
16
|
+
# Serialize parameters Hash
|
17
|
+
serialize :optional_targets, Hash
|
18
|
+
|
19
|
+
validates :target, presence: true
|
20
|
+
validates :key, presence: true
|
21
|
+
validates_inclusion_of :subscribing, in: [true, false]
|
22
|
+
validates_inclusion_of :subscribing_to_email, in: [true, false]
|
23
|
+
validate :subscribing_to_email_cannot_be_true_when_subscribing_is_false
|
24
|
+
validates :subscribed_at, presence: true, if: :subscribing
|
25
|
+
validates :unsubscribed_at, presence: true, unless: :subscribing
|
26
|
+
validates :subscribed_to_email_at, presence: true, if: :subscribing_to_email
|
27
|
+
validates :unsubscribed_to_email_at, presence: true, unless: :subscribing_to_email
|
28
|
+
validate :subscribing_to_optional_target_cannot_be_true_when_subscribing_is_false
|
29
|
+
|
30
|
+
# Selects filtered subscriptions by target instance.
|
31
|
+
# ActivityNotification::Subscription.filtered_by_target(@user)
|
32
|
+
# is the same as
|
33
|
+
# @user.subscriptions
|
34
|
+
# @scope class
|
35
|
+
# @param [Object] target Target instance for filter
|
36
|
+
# @return [ActiveRecord_AssociationRelation<Subscription>] Database query of filtered subscriptions
|
37
|
+
scope :filtered_by_target, ->(target) { where(target: target) }
|
38
|
+
|
39
|
+
# Includes target instance with query for subscriptions.
|
40
|
+
# @return [ActiveRecord_AssociationRelation<Subscription>] Database query of subscriptions with target
|
41
|
+
scope :with_target, -> { includes(:target) }
|
42
|
+
|
43
|
+
# Selects unique keys from query for subscriptions.
|
44
|
+
# @return [Array<String>] Array of subscription unique keys
|
45
|
+
def self.uniq_keys
|
46
|
+
select(:key).distinct.pluck(:key)
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module ActivityNotification
|
2
|
+
module Association
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
class_methods do
|
6
|
+
# Defines has_many association with ActivityNotification models.
|
7
|
+
# @return [Mongoid::Criteria<Object>] Database query of associated model instances
|
8
|
+
def has_many_records(name, options = {})
|
9
|
+
has_many_polymorphic_xdb_records name, options
|
10
|
+
end
|
11
|
+
|
12
|
+
# Defines polymorphic belongs_to association with models in other database.
|
13
|
+
def belongs_to_polymorphic_xdb_record(name, _options = {})
|
14
|
+
association_name = name.to_s.singularize.underscore
|
15
|
+
id_field, type_field = "#{association_name}_id", "#{association_name}_type"
|
16
|
+
field id_field, type: Integer
|
17
|
+
field type_field, type: String
|
18
|
+
|
19
|
+
self.instance_eval do
|
20
|
+
define_method(name) do |reload = false|
|
21
|
+
reload and self.instance_variable_set("@#{name}", nil)
|
22
|
+
if self.instance_variable_get("@#{name}").blank?
|
23
|
+
if (class_name = self.send(type_field)).present?
|
24
|
+
object_class = class_name.classify.constantize
|
25
|
+
self.instance_variable_set("@#{name}", object_class.where(object_class.primary_key => self.send(id_field)).first)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
self.instance_variable_get("@#{name}")
|
29
|
+
end
|
30
|
+
|
31
|
+
define_method("#{name}=") do |new_instance|
|
32
|
+
if new_instance.nil? then instance_id, instance_type = nil, nil else instance_id, instance_type = new_instance.id, new_instance.class.name end
|
33
|
+
self.send("#{id_field}=", instance_id)
|
34
|
+
self.send("#{type_field}=", instance_type)
|
35
|
+
self.instance_variable_set("@#{name}", nil)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Defines polymorphic has_many association with models in other database.
|
41
|
+
# @todo Add dependent option
|
42
|
+
def has_many_polymorphic_xdb_records(name, options = {})
|
43
|
+
association_name = options[:as] || name.to_s.underscore
|
44
|
+
id_field, type_field = "#{association_name}_id", "#{association_name}_type"
|
45
|
+
object_name = options[:class_name] || name.to_s.singularize.camelize
|
46
|
+
object_class = object_name.classify.constantize
|
47
|
+
|
48
|
+
self.instance_eval do
|
49
|
+
define_method(name) do |reload = false|
|
50
|
+
reload and self.instance_variable_set("@#{name}", nil)
|
51
|
+
if self.instance_variable_get("@#{name}").blank?
|
52
|
+
self.instance_variable_set("@#{name}", object_class.where(id_field => self.id, type_field => self.class.name))
|
53
|
+
end
|
54
|
+
self.instance_variable_get("@#{name}")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
require_relative 'mongoid/notification.rb'
|
63
|
+
require_relative 'mongoid/subscription.rb'
|
@@ -0,0 +1,255 @@
|
|
1
|
+
require 'mongoid'
|
2
|
+
require 'activity_notification/apis/notification_api'
|
3
|
+
|
4
|
+
module ActivityNotification
|
5
|
+
module ORM
|
6
|
+
module Mongoid
|
7
|
+
# Notification model implementation generated by ActivityNotification.
|
8
|
+
class Notification
|
9
|
+
include ::Mongoid::Document
|
10
|
+
include ::Mongoid::Timestamps
|
11
|
+
include ::Mongoid::Attributes::Dynamic
|
12
|
+
include GlobalID::Identification
|
13
|
+
include Common
|
14
|
+
include Renderable
|
15
|
+
include Association
|
16
|
+
include NotificationApi
|
17
|
+
store_in collection: ActivityNotification.config.notification_table_name
|
18
|
+
|
19
|
+
# Belongs to target instance of this notification as polymorphic association.
|
20
|
+
# @scope instance
|
21
|
+
# @return [Object] Target instance of this notification
|
22
|
+
belongs_to_polymorphic_xdb_record :target
|
23
|
+
|
24
|
+
# Belongs to notifiable instance of this notification as polymorphic association.
|
25
|
+
# @scope instance
|
26
|
+
# @return [Object] Notifiable instance of this notification
|
27
|
+
belongs_to_polymorphic_xdb_record :notifiable
|
28
|
+
|
29
|
+
# Belongs to group instance of this notification as polymorphic association.
|
30
|
+
# @scope instance
|
31
|
+
# @return [Object] Group instance of this notification
|
32
|
+
belongs_to_polymorphic_xdb_record :group
|
33
|
+
|
34
|
+
field :key, type: String
|
35
|
+
field :parameters, type: Hash, default: {}
|
36
|
+
field :opened_at, type: DateTime
|
37
|
+
field :group_owner_id, type: String
|
38
|
+
|
39
|
+
# Belongs to group owner notification instance of this notification.
|
40
|
+
# Only group member instance has :group_owner value.
|
41
|
+
# Group owner instance has nil as :group_owner association.
|
42
|
+
# @scope instance
|
43
|
+
# @return [Notification] Group owner notification instance of this notification
|
44
|
+
belongs_to :group_owner, { class_name: "ActivityNotification::Notification" }.merge(Rails::VERSION::MAJOR >= 5 ? { optional: true } : {})
|
45
|
+
|
46
|
+
# Has many group member notification instances of this notification.
|
47
|
+
# Only group owner instance has :group_members value.
|
48
|
+
# Group member instance has nil as :group_members association.
|
49
|
+
# @scope instance
|
50
|
+
# @return [Mongoid::Criteria<Notificaion>] Database query of the group member notification instances of this notification
|
51
|
+
has_many :group_members, class_name: "ActivityNotification::Notification", foreign_key: :group_owner_id
|
52
|
+
|
53
|
+
# Belongs to :otifier instance of this notification.
|
54
|
+
# @scope instance
|
55
|
+
# @return [Object] Notifier instance of this notification
|
56
|
+
belongs_to_polymorphic_xdb_record :notifier
|
57
|
+
|
58
|
+
validates :target, presence: true
|
59
|
+
validates :notifiable, presence: true
|
60
|
+
validates :key, presence: true
|
61
|
+
|
62
|
+
# Selects filtered notifications by type of the object.
|
63
|
+
# Filtering with ActivityNotification::Notification is defined as default scope.
|
64
|
+
# @return [Mongoid::Criteria<Notification>] Database query of filtered notifications
|
65
|
+
default_scope -> { where(_type: "ActivityNotification::Notification") }
|
66
|
+
|
67
|
+
# Selects group owner notifications only.
|
68
|
+
# @scope class
|
69
|
+
# @return [Mongoid::Criteria<Notificaion>] Database query of filtered notifications
|
70
|
+
scope :group_owners_only, -> { where(:group_owner_id.exists => false) }
|
71
|
+
|
72
|
+
# Selects group member notifications only.
|
73
|
+
# @scope class
|
74
|
+
# @return [Mongoid::Criteria<Notificaion>] Database query of filtered notifications
|
75
|
+
scope :group_members_only, -> { where(:group_owner_id.exists => true) }
|
76
|
+
|
77
|
+
# Selects unopened notifications only.
|
78
|
+
# @scope class
|
79
|
+
# @return [Mongoid::Criteria<Notificaion>] Database query of filtered notifications
|
80
|
+
scope :unopened_only, -> { where(:opened_at.exists => false) }
|
81
|
+
|
82
|
+
# Selects opened notifications only without limit.
|
83
|
+
# Be careful to get too many records with this method.
|
84
|
+
# @scope class
|
85
|
+
# @return [Mongoid::Criteria<Notificaion>] Database query of filtered notifications
|
86
|
+
scope :opened_only!, -> { where(:opened_at.exists => true) }
|
87
|
+
|
88
|
+
# Selects opened notifications only with limit.
|
89
|
+
# @scope class
|
90
|
+
# @param [Integer] limit Limit to query for opened notifications
|
91
|
+
# @return [Mongoid::Criteria<Notificaion>] Database query of filtered notifications
|
92
|
+
scope :opened_only, ->(limit) { limit == 0 ? none : opened_only!.limit(limit) }
|
93
|
+
|
94
|
+
# Selects group member notifications in unopened_index.
|
95
|
+
# @scope class
|
96
|
+
# @return [Mongoid::Criteria<Notificaion>] Database query of filtered notifications
|
97
|
+
scope :unopened_index_group_members_only, -> { where(:group_owner_id.in => unopened_index.map(&:id)) }
|
98
|
+
|
99
|
+
# Selects group member notifications in opened_index.
|
100
|
+
# @scope class
|
101
|
+
# @param [Integer] limit Limit to query for opened notifications
|
102
|
+
# @return [Mongoid::Criteria<Notificaion>] Database query of filtered notifications
|
103
|
+
scope :opened_index_group_members_only, ->(limit) { where(:group_owner_id.in => opened_index(limit).map(&:id)) }
|
104
|
+
|
105
|
+
# Selects notifications within expiration.
|
106
|
+
# @scope class
|
107
|
+
# @param [ActiveSupport::Duration] expiry_delay Expiry period of notifications
|
108
|
+
# @return [Mongoid::Criteria<Notificaion>] Database query of filtered notifications
|
109
|
+
scope :within_expiration_only, ->(expiry_delay) { where(:created_at.gt => expiry_delay.ago) }
|
110
|
+
|
111
|
+
# Selects group member notifications with specified group owner ids.
|
112
|
+
# @scope class
|
113
|
+
# @param [Array<String>] owner_ids Array of group owner ids
|
114
|
+
# @return [Mongoid::Criteria<Notificaion>] Database query of filtered notifications
|
115
|
+
scope :group_members_of_owner_ids_only, ->(owner_ids) { where(:group_owner_id.in => owner_ids) }
|
116
|
+
|
117
|
+
# Selects filtered notifications by target instance.
|
118
|
+
# ActivityNotification::Notification.filtered_by_target(@user)
|
119
|
+
# is the same as
|
120
|
+
# @user.notifications
|
121
|
+
# @scope class
|
122
|
+
# @param [Object] target Target instance for filter
|
123
|
+
# @return [Mongoid::Criteria<Notificaion>] Database query of filtered notifications
|
124
|
+
scope :filtered_by_target, ->(target) { target.present? ? where(target_id: target.id, target_type: target.class.name) : none }
|
125
|
+
|
126
|
+
# Selects filtered notifications by notifiable instance.
|
127
|
+
# @example Get filtered unopened notificatons of the @user for @comment as notifiable
|
128
|
+
# @notifications = @user.notifications.unopened_only.filtered_by_instance(@comment)
|
129
|
+
# @scope class
|
130
|
+
# @param [Object] notifiable Notifiable instance for filter
|
131
|
+
# @return [Mongoid::Criteria<Notificaion>] Database query of filtered notifications
|
132
|
+
scope :filtered_by_instance, ->(notifiable) { notifiable.present? ? where(notifiable_id: notifiable.id, notifiable_type: notifiable.class.name) : none }
|
133
|
+
|
134
|
+
# Selects filtered notifications by group instance.
|
135
|
+
# @example Get filtered unopened notificatons of the @user for @article as group
|
136
|
+
# @notifications = @user.notifications.unopened_only.filtered_by_group(@article)
|
137
|
+
# @scope class
|
138
|
+
# @param [Object] group Group instance for filter
|
139
|
+
# @return [Mongoid::Criteria<Notificaion>] Database query of filtered notifications
|
140
|
+
scope :filtered_by_group, ->(group) {
|
141
|
+
group.present? ?
|
142
|
+
where(group_id: group.id, group_type: group.class.name) :
|
143
|
+
any_of({ :group_id.exists => false, :group_type.exists => false }, { group_id: nil, group_type: nil })
|
144
|
+
}
|
145
|
+
|
146
|
+
# Includes target instance with query for notifications.
|
147
|
+
# @return [Mongoid::Criteria<Notificaion>] Database query of notifications with target
|
148
|
+
scope :with_target, -> { }
|
149
|
+
|
150
|
+
# Includes notifiable instance with query for notifications.
|
151
|
+
# @return [Mongoid::Criteria<Notificaion>] Database query of notifications with notifiable
|
152
|
+
scope :with_notifiable, -> { }
|
153
|
+
|
154
|
+
# Includes group instance with query for notifications.
|
155
|
+
# @return [Mongoid::Criteria<Notificaion>] Database query of notifications with group
|
156
|
+
scope :with_group, -> { }
|
157
|
+
|
158
|
+
# Includes group owner instances with query for notifications.
|
159
|
+
# @return [Mongoid::Criteria<Notificaion>] Database query of notifications with group owner
|
160
|
+
scope :with_group_owner, -> { }
|
161
|
+
|
162
|
+
# Includes group member instances with query for notifications.
|
163
|
+
# @return [Mongoid::Criteria<Notificaion>] Database query of notifications with group members
|
164
|
+
scope :with_group_members, -> { }
|
165
|
+
|
166
|
+
# Includes notifier instance with query for notifications.
|
167
|
+
# @return [Mongoid::Criteria<Notificaion>] Database query of notifications with notifier
|
168
|
+
scope :with_notifier, -> { }
|
169
|
+
|
170
|
+
# Dummy reload method for test of notifications.
|
171
|
+
scope :reload, -> { }
|
172
|
+
|
173
|
+
# Returns latest notification instance.
|
174
|
+
# @return [Notification] Latest notification instance
|
175
|
+
def self.latest
|
176
|
+
latest_order.first
|
177
|
+
end
|
178
|
+
|
179
|
+
# Returns earliest notification instance.
|
180
|
+
# @return [Notification] Earliest notification instance
|
181
|
+
def self.earliest
|
182
|
+
earliest_order.first
|
183
|
+
end
|
184
|
+
|
185
|
+
# Selects unique keys from query for notifications.
|
186
|
+
# @return [Array<String>] Array of notification unique keys
|
187
|
+
def self.uniq_keys
|
188
|
+
# distinct method cannot keep original sort
|
189
|
+
# distinct(:key)
|
190
|
+
pluck(:key).uniq
|
191
|
+
end
|
192
|
+
|
193
|
+
# Returns if the notification is group owner.
|
194
|
+
# Calls NotificationApi#group_owner? as super method.
|
195
|
+
# @return [Boolean] If the notification is group owner
|
196
|
+
def group_owner?
|
197
|
+
super
|
198
|
+
end
|
199
|
+
|
200
|
+
protected
|
201
|
+
|
202
|
+
# Returns count of group members of the unopened notification.
|
203
|
+
# This method is designed to cache group by query result to avoid N+1 call.
|
204
|
+
# @api protected
|
205
|
+
# @todo Avoid N+1 call
|
206
|
+
#
|
207
|
+
# @return [Integer] Count of group members of the unopened notification
|
208
|
+
def unopened_group_member_count
|
209
|
+
group_members.unopened_only.count
|
210
|
+
end
|
211
|
+
|
212
|
+
# Returns count of group members of the opened notification.
|
213
|
+
# This method is designed to cache group by query result to avoid N+1 call.
|
214
|
+
# @api protected
|
215
|
+
# @todo Avoid N+1 call
|
216
|
+
#
|
217
|
+
# @return [Integer] Count of group members of the opened notification
|
218
|
+
def opened_group_member_count(limit = ActivityNotification.config.opened_index_limit)
|
219
|
+
limit == 0 and return 0
|
220
|
+
group_members.opened_only(limit).to_a.length #.count(true)
|
221
|
+
end
|
222
|
+
|
223
|
+
# Returns count of group member notifiers of the unopened notification not including group owner notifier.
|
224
|
+
# This method is designed to cache group by query result to avoid N+1 call.
|
225
|
+
# @api protected
|
226
|
+
# @todo Avoid N+1 call
|
227
|
+
#
|
228
|
+
# @return [Integer] Count of group member notifiers of the unopened notification
|
229
|
+
def unopened_group_member_notifier_count
|
230
|
+
group_members.unopened_only
|
231
|
+
.where(notifier_type: notifier_type)
|
232
|
+
.where(:notifier_id.ne => notifier_id)
|
233
|
+
.distinct(:notifier_id)
|
234
|
+
.count
|
235
|
+
end
|
236
|
+
|
237
|
+
# Returns count of group member notifiers of the opened notification not including group owner notifier.
|
238
|
+
# This method is designed to cache group by query result to avoid N+1 call.
|
239
|
+
# @api protected
|
240
|
+
# @todo Avoid N+1 call
|
241
|
+
#
|
242
|
+
# @return [Integer] Count of group member notifiers of the opened notification
|
243
|
+
def opened_group_member_notifier_count(limit = ActivityNotification.config.opened_index_limit)
|
244
|
+
limit == 0 and return 0
|
245
|
+
group_members.opened_only(limit)
|
246
|
+
.where(notifier_type: notifier_type)
|
247
|
+
.where(:notifier_id.ne => notifier_id)
|
248
|
+
.distinct(:notifier_id)
|
249
|
+
.to_a.length #.count(true)
|
250
|
+
end
|
251
|
+
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|