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.
- 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
|