/
notification.rb
250 lines (214 loc) · 14.4 KB
/
notification.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
require 'activity_notification/apis/notification_api'
module ActivityNotification
module ORM
module ActiveRecord
# Notification model implementation generated by ActivityNotification.
class Notification < ::ActiveRecord::Base
include Common
include Renderable
include NotificationApi
self.table_name = ActivityNotification.config.notification_table_name
# Belongs to target instance of this notification as polymorphic association.
# @scope instance
# @return [Object] Target instance of this notification
belongs_to :target, polymorphic: true
# Belongs to notifiable instance of this notification as polymorphic association.
# @scope instance
# @return [Object] Notifiable instance of this notification
belongs_to :notifiable, polymorphic: true
# Belongs to group instance of this notification as polymorphic association.
# @scope instance
# @return [Object] Group instance of this notification
belongs_to :group, **{ polymorphic: true, optional: true }
# Belongs to group owner notification instance of this notification.
# Only group member instance has :group_owner value.
# Group owner instance has nil as :group_owner association.
# @scope instance
# @return [Notification] Group owner notification instance of this notification
belongs_to :group_owner, **{ class_name: "ActivityNotification::Notification", optional: true }
# Has many group member notification instances of this notification.
# Only group owner instance has :group_members value.
# Group member instance has nil as :group_members association.
# @scope instance
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of the group member notification instances of this notification
has_many :group_members, class_name: "ActivityNotification::Notification", foreign_key: :group_owner_id
# Belongs to :notifier instance of this notification.
# @scope instance
# @return [Object] Notifier instance of this notification
belongs_to :notifier, **{ polymorphic: true, optional: true }
# Serialize parameters Hash
serialize :parameters, Hash
validates :target, presence: true
validates :notifiable, presence: true
validates :key, presence: true
# Selects group owner notifications only.
# @scope class
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of filtered notifications
scope :group_owners_only, -> { where(group_owner_id: nil) }
# Selects group member notifications only.
# @scope class
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of filtered notifications
scope :group_members_only, -> { where.not(group_owner_id: nil) }
# Selects unopened notifications only.
# @scope class
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of filtered notifications
scope :unopened_only, -> { where(opened_at: nil) }
# Selects opened notifications only without limit.
# Be careful to get too many records with this method.
# @scope class
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of filtered notifications
scope :opened_only!, -> { where.not(opened_at: nil) }
# Selects opened notifications only with limit.
# @scope class
# @param [Integer] limit Limit to query for opened notifications
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of filtered notifications
scope :opened_only, ->(limit) { opened_only!.limit(limit) }
# Selects group member notifications in unopened_index.
# @scope class
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of filtered notifications
scope :unopened_index_group_members_only, -> { where(group_owner_id: unopened_index.map(&:id)) }
# Selects group member notifications in opened_index.
# @scope class
# @param [Integer] limit Limit to query for opened notifications
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of filtered notifications
scope :opened_index_group_members_only, ->(limit) { where(group_owner_id: opened_index(limit).map(&:id)) }
# Selects notifications within expiration.
# @scope class
# @param [ActiveSupport::Duration] expiry_delay Expiry period of notifications
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of filtered notifications
scope :within_expiration_only, ->(expiry_delay) { where("created_at > ?", expiry_delay.ago) }
# Selects group member notifications with specified group owner ids.
# @scope class
# @param [Array<String>] owner_ids Array of group owner ids
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of filtered notifications
scope :group_members_of_owner_ids_only, ->(owner_ids) { where(group_owner_id: owner_ids) }
# Selects filtered notifications by target instance.
# ActivityNotification::Notification.filtered_by_target(@user)
# is the same as
# @user.notifications
# @scope class
# @param [Object] target Target instance for filter
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of filtered notifications
scope :filtered_by_target, ->(target) { where(target: target) }
# Selects filtered notifications by notifiable instance.
# @example Get filtered unopened notificatons of the @user for @comment as notifiable
# @notifications = @user.notifications.unopened_only.filtered_by_instance(@comment)
# @scope class
# @param [Object] notifiable Notifiable instance for filter
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of filtered notifications
scope :filtered_by_instance, ->(notifiable) { where(notifiable: notifiable) }
# Selects filtered notifications by group instance.
# @example Get filtered unopened notificatons of the @user for @article as group
# @notifications = @user.notifications.unopened_only.filtered_by_group(@article)
# @scope class
# @param [Object] group Group instance for filter
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of filtered notifications
scope :filtered_by_group, ->(group) { where(group: group) }
# Selects filtered notifications later than specified time.
# @example Get filtered unopened notificatons of the @user later than @notification
# @notifications = @user.notifications.unopened_only.later_than(@notification.created_at)
# @scope class
# @param [Time] Created time of the notifications for filter
# @return [ActiveRecord_AssociationRelation<Notificaion>, Mongoid::Criteria<Notificaion>] Database query of filtered notifications
scope :later_than, ->(created_time) { where('created_at > ?', created_time) }
# Selects filtered notifications earlier than specified time.
# @example Get filtered unopened notificatons of the @user earlier than @notification
# @notifications = @user.notifications.unopened_only.earlier_than(@notification.created_at)
# @scope class
# @param [Time] Created time of the notifications for filter
# @return [ActiveRecord_AssociationRelation<Notificaion>, Mongoid::Criteria<Notificaion>] Database query of filtered notifications
scope :earlier_than, ->(created_time) { where('created_at < ?', created_time) }
# Includes target instance with query for notifications.
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of notifications with target
scope :with_target, -> { includes(:target) }
# Includes notifiable instance with query for notifications.
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of notifications with notifiable
scope :with_notifiable, -> { includes(:notifiable) }
# Includes group instance with query for notifications.
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of notifications with group
scope :with_group, -> { includes(:group) }
# Includes group owner instances with query for notifications.
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of notifications with group owner
scope :with_group_owner, -> { includes(:group_owner) }
# Includes group member instances with query for notifications.
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of notifications with group members
scope :with_group_members, -> { includes(:group_members) }
# Includes notifier instance with query for notifications.
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of notifications with notifier
scope :with_notifier, -> { includes(:notifier) }
# Raise DeleteRestrictionError for notifications.
# @param [String] error_text Error text for raised exception
# @raise [ActiveRecord::DeleteRestrictionError] DeleteRestrictionError from used ORM
# @return [void]
def self.raise_delete_restriction_error(error_text)
raise ::ActiveRecord::DeleteRestrictionError.new(error_text)
end
protected
# Returns count of group members of the unopened notification.
# This method is designed to cache group by query result to avoid N+1 call.
# @api protected
#
# @return [Integer] Count of group members of the unopened notification
def unopened_group_member_count
# Cache group by query result to avoid N+1 call
unopened_group_member_counts = target.notifications
.unopened_index_group_members_only
.group(:group_owner_id)
.count
unopened_group_member_counts[id] || 0
end
# Returns count of group members of the opened notification.
# This method is designed to cache group by query result to avoid N+1 call.
# @api protected
#
# @param [Integer] limit Limit to query for opened notifications
# @return [Integer] Count of group members of the opened notification
def opened_group_member_count(limit = ActivityNotification.config.opened_index_limit)
# Cache group by query result to avoid N+1 call
opened_group_member_counts = target.notifications
.opened_index_group_members_only(limit)
.group(:group_owner_id)
.count
count = opened_group_member_counts[id] || 0
count > limit ? limit : count
end
# Returns count of group member notifiers of the unopened notification not including group owner notifier.
# This method is designed to cache group by query result to avoid N+1 call.
# @api protected
#
# @return [Integer] Count of group member notifiers of the unopened notification
def unopened_group_member_notifier_count
# Cache group by query result to avoid N+1 call
unopened_group_member_notifier_counts = target.notifications
.unopened_index_group_members_only
.includes(:group_owner)
.where("group_owners_#{self.class.table_name}.notifier_type = #{self.class.table_name}.notifier_type")
.where.not("group_owners_#{self.class.table_name}.notifier_id = #{self.class.table_name}.notifier_id")
.references(:group_owner)
.group(:group_owner_id, :notifier_type)
.count("distinct #{self.class.table_name}.notifier_id")
unopened_group_member_notifier_counts[[id, notifier_type]] || 0
end
# Returns count of group member notifiers of the opened notification not including group owner notifier.
# This method is designed to cache group by query result to avoid N+1 call.
# @api protected
#
# @param [Integer] limit Limit to query for opened notifications
# @return [Integer] Count of group member notifiers of the opened notification
def opened_group_member_notifier_count(limit = ActivityNotification.config.opened_index_limit)
# Cache group by query result to avoid N+1 call
opened_group_member_notifier_counts = target.notifications
.opened_index_group_members_only(limit)
.includes(:group_owner)
.where("group_owners_#{self.class.table_name}.notifier_type = #{self.class.table_name}.notifier_type")
.where.not("group_owners_#{self.class.table_name}.notifier_id = #{self.class.table_name}.notifier_id")
.references(:group_owner)
.group(:group_owner_id, :notifier_type)
.count("distinct #{self.class.table_name}.notifier_id")
count = opened_group_member_notifier_counts[[id, notifier_type]] || 0
count > limit ? limit : count
end
end
end
end
end