activity_notification 2.0.0 → 2.1.4
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/.github/ISSUE_TEMPLATE/bug_report.md +22 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +17 -0
- data/.github/pull_request_template.md +13 -0
- data/.gitignore +10 -3
- data/.travis.yml +6 -5
- data/CHANGELOG.md +60 -0
- data/Gemfile +8 -3
- data/Procfile +1 -1
- data/README.md +153 -1510
- data/activity_notification.gemspec +4 -1
- data/app/channels/activity_notification/notification_api_channel.rb +12 -0
- data/app/channels/activity_notification/notification_api_with_devise_channel.rb +46 -0
- data/app/channels/activity_notification/notification_channel.rb +2 -2
- data/app/channels/activity_notification/notification_with_devise_channel.rb +2 -2
- data/app/controllers/activity_notification/apidocs_controller.rb +75 -0
- data/app/controllers/activity_notification/notifications_api_controller.rb +143 -0
- data/app/controllers/activity_notification/notifications_api_with_devise_controller.rb +7 -0
- data/app/controllers/activity_notification/notifications_controller.rb +79 -53
- data/app/controllers/activity_notification/subscriptions_api_controller.rb +197 -0
- data/app/controllers/activity_notification/subscriptions_api_with_devise_controller.rb +7 -0
- data/app/controllers/activity_notification/subscriptions_controller.rb +78 -69
- data/app/views/activity_notification/notifications/default/_default.html.erb +18 -18
- data/app/views/activity_notification/notifications/default/_default_without_grouping.html.erb +14 -14
- data/app/views/activity_notification/notifications/default/index.html.erb +6 -6
- data/app/views/activity_notification/optional_targets/default/action_cable_channel/_default.html.erb +176 -0
- data/app/views/activity_notification/subscriptions/default/_form.html.erb +1 -1
- data/app/views/activity_notification/subscriptions/default/_notification_keys.html.erb +3 -31
- data/app/views/activity_notification/subscriptions/default/_subscription.html.erb +7 -7
- data/app/views/activity_notification/subscriptions/default/index.html.erb +11 -7
- data/bin/deploy_on_heroku.sh +3 -1
- data/docs/CODE_OF_CONDUCT.md +76 -0
- data/docs/CONTRIBUTING.md +36 -0
- data/docs/Functions.md +1130 -0
- data/docs/Setup.md +801 -0
- data/docs/Testing.md +148 -0
- data/gemfiles/Gemfile.rails-4.2 +5 -1
- data/gemfiles/Gemfile.rails-5.0 +6 -1
- data/gemfiles/Gemfile.rails-5.1 +6 -1
- data/gemfiles/Gemfile.rails-5.2 +6 -1
- data/gemfiles/{Gemfile.rails-6.0.rc → Gemfile.rails-6.0} +6 -5
- data/lib/activity_notification.rb +13 -0
- data/lib/activity_notification/apis/notification_api.rb +37 -93
- data/lib/activity_notification/apis/subscription_api.rb +20 -8
- data/lib/activity_notification/apis/swagger.rb +6 -0
- data/lib/activity_notification/common.rb +4 -1
- data/lib/activity_notification/config.rb +41 -21
- data/lib/activity_notification/controllers/common_api_controller.rb +30 -0
- data/lib/activity_notification/controllers/common_controller.rb +45 -21
- data/lib/activity_notification/controllers/concerns/swagger/error_responses.rb +55 -0
- data/lib/activity_notification/controllers/concerns/swagger/notifications_api.rb +273 -0
- data/lib/activity_notification/controllers/concerns/swagger/notifications_parameters.rb +92 -0
- data/lib/activity_notification/controllers/concerns/swagger/subscriptions_api.rb +405 -0
- data/lib/activity_notification/controllers/concerns/swagger/subscriptions_parameters.rb +50 -0
- data/lib/activity_notification/controllers/devise_authentication_controller.rb +7 -6
- data/lib/activity_notification/gem_version.rb +14 -0
- data/lib/activity_notification/helpers/errors.rb +2 -0
- data/lib/activity_notification/helpers/view_helpers.rb +4 -0
- data/lib/activity_notification/mailers/helpers.rb +17 -10
- data/lib/activity_notification/models/concerns/notifiable.rb +31 -15
- data/lib/activity_notification/models/concerns/subscriber.rb +12 -1
- data/lib/activity_notification/models/concerns/swagger/error_schema.rb +36 -0
- data/lib/activity_notification/models/concerns/swagger/notification_schema.rb +209 -0
- data/lib/activity_notification/models/concerns/swagger/subscription_schema.rb +162 -0
- data/lib/activity_notification/models/concerns/target.rb +36 -10
- data/lib/activity_notification/models/notification.rb +1 -0
- data/lib/activity_notification/models/subscription.rb +1 -0
- data/lib/activity_notification/optional_targets/action_cable_api_channel.rb +69 -0
- data/lib/activity_notification/optional_targets/action_cable_channel.rb +68 -0
- data/lib/activity_notification/optional_targets/base.rb +7 -13
- data/lib/activity_notification/orm/active_record/notification.rb +17 -1
- data/lib/activity_notification/orm/active_record/subscription.rb +1 -1
- data/lib/activity_notification/orm/dynamoid.rb +38 -3
- data/lib/activity_notification/orm/dynamoid/extension.rb +79 -1
- data/lib/activity_notification/orm/dynamoid/notification.rb +49 -14
- data/lib/activity_notification/orm/dynamoid/subscription.rb +2 -2
- data/lib/activity_notification/orm/mongoid.rb +32 -3
- data/lib/activity_notification/orm/mongoid/notification.rb +24 -6
- data/lib/activity_notification/orm/mongoid/subscription.rb +1 -1
- data/lib/activity_notification/rails/routes.rb +132 -48
- data/lib/activity_notification/renderable.rb +13 -2
- data/lib/activity_notification/roles/acts_as_notifiable.rb +39 -20
- data/lib/activity_notification/version.rb +1 -1
- data/lib/generators/activity_notification/controllers_generator.rb +2 -1
- data/lib/generators/templates/activity_notification.rb +8 -0
- data/lib/generators/templates/controllers/notifications_api_controller.rb +31 -0
- data/lib/generators/templates/controllers/notifications_api_with_devise_controller.rb +31 -0
- data/lib/generators/templates/controllers/notifications_controller.rb +1 -37
- data/lib/generators/templates/controllers/notifications_with_devise_controller.rb +1 -45
- data/lib/generators/templates/controllers/subscriptions_api_controller.rb +61 -0
- data/lib/generators/templates/controllers/subscriptions_api_with_devise_controller.rb +61 -0
- data/lib/generators/templates/controllers/subscriptions_controller.rb +14 -37
- data/lib/generators/templates/controllers/subscriptions_with_devise_controller.rb +14 -45
- data/lib/generators/templates/models/README +8 -4
- data/lib/generators/templates/models/notification.rb +1 -1
- data/lib/generators/templates/models/subscription.rb +1 -1
- data/package.json +8 -0
- data/spec/channels/notification_api_channel_shared_examples.rb +59 -0
- data/spec/channels/notification_api_channel_spec.rb +51 -0
- data/spec/channels/notification_api_with_devise_channel_spec.rb +78 -0
- data/spec/concerns/apis/notification_api_spec.rb +38 -3
- data/spec/concerns/models/notifiable_spec.rb +82 -18
- data/spec/concerns/models/subscriber_spec.rb +13 -16
- data/spec/concerns/models/target_spec.rb +32 -0
- data/spec/concerns/renderable_spec.rb +2 -2
- data/spec/config_spec.rb +26 -15
- data/spec/controllers/controller_spec_utility.rb +136 -0
- data/spec/controllers/notifications_api_controller_shared_examples.rb +506 -0
- data/spec/controllers/notifications_api_controller_spec.rb +19 -0
- data/spec/controllers/notifications_api_with_devise_controller_spec.rb +60 -0
- data/spec/controllers/notifications_controller_shared_examples.rb +54 -79
- data/spec/controllers/notifications_controller_spec.rb +1 -2
- data/spec/controllers/notifications_with_devise_controller_spec.rb +3 -12
- data/spec/controllers/subscriptions_api_controller_shared_examples.rb +750 -0
- data/spec/controllers/subscriptions_api_controller_spec.rb +19 -0
- data/spec/controllers/subscriptions_api_with_devise_controller_spec.rb +60 -0
- data/spec/controllers/subscriptions_controller_shared_examples.rb +94 -121
- data/spec/controllers/subscriptions_controller_spec.rb +1 -2
- data/spec/controllers/subscriptions_with_devise_controller_spec.rb +3 -12
- data/spec/helpers/view_helpers_spec.rb +4 -11
- data/spec/mailers/mailer_spec.rb +41 -0
- data/spec/models/notification_spec.rb +17 -0
- data/spec/models/subscription_spec.rb +8 -13
- data/spec/optional_targets/action_cable_api_channel_spec.rb +37 -0
- data/spec/optional_targets/action_cable_channel_spec.rb +44 -0
- data/spec/optional_targets/amazon_sns_spec.rb +0 -2
- data/spec/optional_targets/slack_spec.rb +0 -2
- data/spec/rails_app/Rakefile +9 -0
- data/spec/rails_app/app/assets/config/manifest.js +3 -0
- data/spec/rails_app/app/assets/images/.keep +0 -0
- data/spec/rails_app/app/controllers/admins_controller.rb +21 -0
- data/spec/rails_app/app/controllers/application_controller.rb +1 -1
- data/spec/rails_app/app/controllers/articles_controller.rb +6 -3
- data/spec/rails_app/app/controllers/spa_controller.rb +7 -0
- data/spec/rails_app/app/controllers/users/notifications_controller.rb +0 -65
- data/spec/rails_app/app/controllers/users/notifications_with_devise_controller.rb +0 -73
- data/spec/rails_app/app/controllers/users/subscriptions_controller.rb +0 -77
- data/spec/rails_app/app/controllers/users/subscriptions_with_devise_controller.rb +0 -85
- data/spec/rails_app/app/controllers/users_controller.rb +26 -0
- data/spec/rails_app/app/javascript/App.vue +40 -0
- data/spec/rails_app/app/javascript/components/DeviseTokenAuth.vue +82 -0
- data/spec/rails_app/app/javascript/components/Top.vue +98 -0
- data/spec/rails_app/app/javascript/components/notifications/Index.vue +200 -0
- data/spec/rails_app/app/javascript/components/notifications/Notification.vue +133 -0
- data/spec/rails_app/app/javascript/components/notifications/NotificationContent.vue +122 -0
- data/spec/rails_app/app/javascript/components/subscriptions/Index.vue +279 -0
- data/spec/rails_app/app/javascript/components/subscriptions/NewSubscription.vue +112 -0
- data/spec/rails_app/app/javascript/components/subscriptions/NotificationKey.vue +141 -0
- data/spec/rails_app/app/javascript/components/subscriptions/Subscription.vue +226 -0
- data/spec/rails_app/app/javascript/config/development.js +5 -0
- data/spec/rails_app/app/javascript/config/environment.js +7 -0
- data/spec/rails_app/app/javascript/config/production.js +5 -0
- data/spec/rails_app/app/javascript/config/test.js +5 -0
- data/spec/rails_app/app/javascript/packs/application.js +18 -0
- data/spec/rails_app/app/javascript/packs/spa.js +14 -0
- data/spec/rails_app/app/javascript/router/index.js +73 -0
- data/spec/rails_app/app/javascript/store/index.js +37 -0
- data/spec/rails_app/app/models/admin.rb +16 -15
- data/spec/rails_app/app/models/article.rb +26 -21
- data/spec/rails_app/app/models/comment.rb +24 -71
- data/spec/rails_app/app/models/dummy/dummy_group.rb +8 -0
- data/spec/rails_app/app/models/dummy/dummy_notifiable_target.rb +8 -0
- data/spec/rails_app/app/models/user.rb +44 -20
- data/spec/rails_app/app/views/activity_notification/notifications/default/article/_update.html.erb +146 -0
- data/spec/rails_app/app/views/articles/index.html.erb +51 -7
- data/spec/rails_app/app/views/articles/show.html.erb +1 -1
- data/spec/rails_app/app/views/layouts/_header.html.erb +8 -10
- data/spec/rails_app/app/views/spa/index.html.erb +2 -0
- data/spec/rails_app/babel.config.js +72 -0
- data/spec/rails_app/bin/webpack +18 -0
- data/spec/rails_app/bin/webpack-dev-server +18 -0
- data/spec/rails_app/config/application.rb +18 -2
- data/spec/rails_app/config/dynamoid.rb +11 -3
- data/spec/rails_app/config/environment.rb +2 -1
- data/spec/rails_app/config/environments/development.rb +5 -0
- data/spec/rails_app/config/environments/production.rb +6 -0
- data/spec/rails_app/config/environments/test.rb +5 -0
- data/spec/rails_app/config/initializers/activity_notification.rb +11 -3
- data/spec/rails_app/config/initializers/copy_it.aws.rb.template +6 -0
- data/spec/rails_app/config/initializers/devise_token_auth.rb +55 -0
- data/spec/rails_app/config/initializers/mysql.rb +9 -0
- data/spec/rails_app/config/locales/activity_notification.en.yml +2 -2
- data/spec/rails_app/config/routes.rb +37 -1
- data/spec/rails_app/config/webpack/development.js +5 -0
- data/spec/rails_app/config/webpack/environment.js +7 -0
- data/spec/rails_app/config/webpack/loaders/vue.js +6 -0
- data/spec/rails_app/config/webpack/production.js +5 -0
- data/spec/rails_app/config/webpack/test.js +5 -0
- data/spec/rails_app/config/webpacker.yml +97 -0
- data/spec/rails_app/db/migrate/20191201000000_add_tokens_to_users.rb +10 -0
- data/spec/rails_app/db/schema.rb +4 -1
- data/spec/rails_app/db/seeds.rb +10 -2
- data/spec/rails_app/lib/custom_optional_targets/raise_error.rb +14 -0
- data/spec/rails_app/package.json +23 -0
- data/spec/rails_app/postcss.config.js +12 -0
- data/spec/roles/acts_as_group_spec.rb +0 -2
- data/spec/roles/acts_as_notifiable_spec.rb +6 -8
- data/spec/roles/acts_as_notifier_spec.rb +0 -2
- data/spec/roles/acts_as_target_spec.rb +0 -4
- data/spec/spec_helper.rb +7 -15
- data/spec/version_spec.rb +31 -0
- metadata +191 -13
|
@@ -3,22 +3,11 @@ module ActivityNotification
|
|
|
3
3
|
module OptionalTarget
|
|
4
4
|
# Abstract optional target class to develop optional notification target class.
|
|
5
5
|
class Base
|
|
6
|
-
# View context to render notification message
|
|
7
|
-
# @return View context to render notification message
|
|
8
|
-
attr_accessor :view_context
|
|
9
|
-
|
|
10
6
|
# Initialize method to create view context in this OptionalTarget instance
|
|
11
7
|
# @param [Hash] options Options for initializing target
|
|
12
8
|
# @option options [Boolean] :skip_initializing_target (false) Whether skip calling initialize_target method
|
|
13
9
|
# @option options [Hash] others Options for initializing target
|
|
14
10
|
def initialize(options = {})
|
|
15
|
-
@view_context = ActionView::Base.new(ActionController::Base.view_paths, {})
|
|
16
|
-
@view_context.class_eval do
|
|
17
|
-
include Rails.application.routes.url_helpers
|
|
18
|
-
def default_url_options
|
|
19
|
-
ActionMailer::Base.default_url_options
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
11
|
initialize_target(options) unless options.delete(:skip_initializing_target)
|
|
23
12
|
end
|
|
24
13
|
|
|
@@ -64,12 +53,17 @@ module ActivityNotification
|
|
|
64
53
|
"activity_notification/optional_targets/default/base"
|
|
65
54
|
]
|
|
66
55
|
options[:fallback] ||= :default
|
|
67
|
-
@view_context.assign((options[:assignment] || {}).merge(notification: notification, target: notification.target))
|
|
68
56
|
|
|
69
57
|
message, missing_template = nil, nil
|
|
70
58
|
partial_root_list.each do |partial_root|
|
|
71
59
|
begin
|
|
72
|
-
message = notification.render(
|
|
60
|
+
message = notification.render(
|
|
61
|
+
ActivityNotification::NotificationsController.renderer,
|
|
62
|
+
options.merge(
|
|
63
|
+
partial_root: partial_root,
|
|
64
|
+
assigns: (options[:assignment] || {}).merge(notification: notification, target: notification.target)
|
|
65
|
+
)
|
|
66
|
+
).to_s
|
|
73
67
|
break
|
|
74
68
|
rescue ActionView::MissingTemplate => e
|
|
75
69
|
missing_template = e
|
|
@@ -126,6 +126,22 @@ module ActivityNotification
|
|
|
126
126
|
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of filtered notifications
|
|
127
127
|
scope :filtered_by_group, ->(group) { where(group: group) }
|
|
128
128
|
|
|
129
|
+
# Selects filtered notifications later than specified time.
|
|
130
|
+
# @example Get filtered unopened notificatons of the @user later than @notification
|
|
131
|
+
# @notifications = @user.notifications.unopened_only.later_than(@notification.created_at)
|
|
132
|
+
# @scope class
|
|
133
|
+
# @param [Time] Created time of the notifications for filter
|
|
134
|
+
# @return [ActiveRecord_AssociationRelation<Notificaion>, Mongoid::Criteria<Notificaion>] Database query of filtered notifications
|
|
135
|
+
scope :later_than, ->(created_time) { where('created_at > ?', created_time) }
|
|
136
|
+
|
|
137
|
+
# Selects filtered notifications earlier than specified time.
|
|
138
|
+
# @example Get filtered unopened notificatons of the @user earlier than @notification
|
|
139
|
+
# @notifications = @user.notifications.unopened_only.earlier_than(@notification.created_at)
|
|
140
|
+
# @scope class
|
|
141
|
+
# @param [Time] Created time of the notifications for filter
|
|
142
|
+
# @return [ActiveRecord_AssociationRelation<Notificaion>, Mongoid::Criteria<Notificaion>] Database query of filtered notifications
|
|
143
|
+
scope :earlier_than, ->(created_time) { where('created_at < ?', created_time) }
|
|
144
|
+
|
|
129
145
|
# Includes target instance with query for notifications.
|
|
130
146
|
# @return [ActiveRecord_AssociationRelation<Notificaion>] Database query of notifications with target
|
|
131
147
|
scope :with_target, -> { includes(:target) }
|
|
@@ -152,7 +168,7 @@ module ActivityNotification
|
|
|
152
168
|
|
|
153
169
|
# Raise DeleteRestrictionError for notifications.
|
|
154
170
|
# @param [String] error_text Error text for raised exception
|
|
155
|
-
# @raise DeleteRestrictionError
|
|
171
|
+
# @raise [ActiveRecord::DeleteRestrictionError] DeleteRestrictionError from used ORM
|
|
156
172
|
# @return [void]
|
|
157
173
|
def self.raise_delete_restriction_error(error_text)
|
|
158
174
|
raise ::ActiveRecord::DeleteRestrictionError.new(error_text)
|
|
@@ -17,7 +17,7 @@ module ActivityNotification
|
|
|
17
17
|
serialize :optional_targets, Hash
|
|
18
18
|
|
|
19
19
|
validates :target, presence: true
|
|
20
|
-
validates :key, presence: true
|
|
20
|
+
validates :key, presence: true, uniqueness: { scope: :target }
|
|
21
21
|
validates_inclusion_of :subscribing, in: [true, false]
|
|
22
22
|
validates_inclusion_of :subscribing_to_email, in: [true, false]
|
|
23
23
|
validate :subscribing_to_email_cannot_be_true_when_subscribing_is_false
|
|
@@ -22,8 +22,8 @@ module ActivityNotification
|
|
|
22
22
|
association_name = name.to_s.singularize.underscore
|
|
23
23
|
composite_field = "#{association_name}_key".to_sym
|
|
24
24
|
field composite_field, :string
|
|
25
|
-
associated_record_field = "#{association_name}
|
|
26
|
-
field associated_record_field, :
|
|
25
|
+
associated_record_field = "stored_#{association_name}".to_sym
|
|
26
|
+
field associated_record_field, :raw if ActivityNotification.config.store_with_associated_records && _options[:store_with_associated_records]
|
|
27
27
|
|
|
28
28
|
self.instance_eval do
|
|
29
29
|
define_method(name) do |reload = false|
|
|
@@ -43,7 +43,14 @@ module ActivityNotification
|
|
|
43
43
|
self.send("#{composite_field}=", nil)
|
|
44
44
|
else
|
|
45
45
|
self.send("#{composite_field}=", "#{new_instance.class.name}#{ActivityNotification.config.composite_key_delimiter}#{new_instance.id}")
|
|
46
|
-
|
|
46
|
+
associated_record_json = new_instance.as_json(_options[:as_json_options] || {})
|
|
47
|
+
# Cast Time and DateTime field to String to handle Dynamoid unsupported type error
|
|
48
|
+
if associated_record_json.present?
|
|
49
|
+
associated_record_json.each do |k, v|
|
|
50
|
+
associated_record_json[k] = v.to_s if v.is_a?(Time) || v.is_a?(DateTime)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
self.send("#{associated_record_field}=", associated_record_json) if ActivityNotification.config.store_with_associated_records && _options[:store_with_associated_records]
|
|
47
54
|
end
|
|
48
55
|
self.instance_variable_set("@#{name}", nil)
|
|
49
56
|
end
|
|
@@ -249,6 +256,26 @@ module Dynamoid # :nodoc: all
|
|
|
249
256
|
where(key: key)
|
|
250
257
|
end
|
|
251
258
|
|
|
259
|
+
# Selects filtered notifications later than specified time.
|
|
260
|
+
# @example Get filtered unopened notificatons of the @user later than @notification
|
|
261
|
+
# @notifications = @user.notifications.unopened_only.later_than(@notification.created_at)
|
|
262
|
+
# @scope class
|
|
263
|
+
# @param [Time] Created time of the notifications for filter
|
|
264
|
+
# @return [ActiveRecord_AssociationRelation<Notificaion>, Mongoid::Criteria<Notificaion>] Database query of filtered notifications
|
|
265
|
+
def later_than(created_time)
|
|
266
|
+
where('created_at.gt': created_time)
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# Selects filtered notifications earlier than specified time.
|
|
270
|
+
# @example Get filtered unopened notificatons of the @user earlier than @notification
|
|
271
|
+
# @notifications = @user.notifications.unopened_only.earlier_than(@notification.created_at)
|
|
272
|
+
# @scope class
|
|
273
|
+
# @param [Time] Created time of the notifications for filter
|
|
274
|
+
# @return [ActiveRecord_AssociationRelation<Notificaion>, Mongoid::Criteria<Notificaion>] Database query of filtered notifications
|
|
275
|
+
def earlier_than(created_time)
|
|
276
|
+
where('created_at.lt': created_time)
|
|
277
|
+
end
|
|
278
|
+
|
|
252
279
|
# Selects filtered notifications or subscriptions by notifiable_type, group or key with filter options.
|
|
253
280
|
# @example Get filtered unopened notificatons of the @user for Comment notifiable class
|
|
254
281
|
# @notifications = @user.notifications.unopened_only.filtered_by_options({ filtered_by_type: 'Comment' })
|
|
@@ -269,6 +296,8 @@ module Dynamoid # :nodoc: all
|
|
|
269
296
|
# @option options [String] :filtered_by_group_type (nil) Group type for filter, valid with :filtered_by_group_id
|
|
270
297
|
# @option options [String] :filtered_by_group_id (nil) Group instance id for filter, valid with :filtered_by_group_type
|
|
271
298
|
# @option options [String] :filtered_by_key (nil) Key of the notification for filter
|
|
299
|
+
# @option options [String] :later_than (nil) ISO 8601 format time to filter notification index later than specified time
|
|
300
|
+
# @option options [String] :earlier_than (nil) ISO 8601 format time to filter notification index earlier than specified time
|
|
272
301
|
# @option options [Array|Hash] :custom_filter (nil) Custom notification filter (e.g. ['created_at.gt': time.hour.ago])
|
|
273
302
|
# @return [Dynamoid::Criteria::Chain] Database query of filtered notifications or subscriptions
|
|
274
303
|
def filtered_by_options(options = {})
|
|
@@ -286,6 +315,12 @@ module Dynamoid # :nodoc: all
|
|
|
286
315
|
if options.has_key?(:filtered_by_key)
|
|
287
316
|
filtered_notifications = filtered_notifications.filtered_by_key(options[:filtered_by_key])
|
|
288
317
|
end
|
|
318
|
+
if options.has_key?(:later_than)
|
|
319
|
+
filtered_notifications = filtered_notifications.later_than(Time.iso8601(options[:later_than]))
|
|
320
|
+
end
|
|
321
|
+
if options.has_key?(:earlier_than)
|
|
322
|
+
filtered_notifications = filtered_notifications.earlier_than(Time.iso8601(options[:earlier_than]))
|
|
323
|
+
end
|
|
289
324
|
if options.has_key?(:custom_filter)
|
|
290
325
|
filtered_notifications = filtered_notifications.where(options[:custom_filter])
|
|
291
326
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
require 'dynamoid/adapter_plugin/aws_sdk_v3'
|
|
2
2
|
|
|
3
|
-
# Entend Dynamoid to support none, limit, exists?, update_all in Dynamoid::Criteria::Chain.
|
|
3
|
+
# Entend Dynamoid v3.1.0 to support none, limit, exists?, update_all, serializable_hash in Dynamoid::Criteria::Chain.
|
|
4
4
|
# ActivityNotification project will try to contribute these fundamental functions to Dynamoid upstream.
|
|
5
5
|
# @private
|
|
6
6
|
module Dynamoid # :nodoc: all
|
|
@@ -32,6 +32,7 @@ module Dynamoid # :nodoc: all
|
|
|
32
32
|
# https://github.com/Dynamoid/dynamoid/blob/master/lib/dynamoid/criteria/chain.rb
|
|
33
33
|
# @private
|
|
34
34
|
class Chain
|
|
35
|
+
# Return new none object
|
|
35
36
|
def none
|
|
36
37
|
None.new(self.source)
|
|
37
38
|
end
|
|
@@ -64,6 +65,11 @@ module Dynamoid # :nodoc: all
|
|
|
64
65
|
document.update_attributes(conditions)
|
|
65
66
|
end
|
|
66
67
|
end
|
|
68
|
+
|
|
69
|
+
# Return serializable_hash as array
|
|
70
|
+
def serializable_hash(options = {})
|
|
71
|
+
all.to_a.map { |r| r.serializable_hash(options) }
|
|
72
|
+
end
|
|
67
73
|
end
|
|
68
74
|
|
|
69
75
|
# https://github.com/Dynamoid/dynamoid/blob/master/lib/dynamoid/criteria.rb
|
|
@@ -163,6 +169,78 @@ module Dynamoid # :nodoc: all
|
|
|
163
169
|
end
|
|
164
170
|
end
|
|
165
171
|
|
|
172
|
+
# Entend Dynamoid to support uniqueness validator
|
|
173
|
+
# @private
|
|
174
|
+
module Dynamoid # :nodoc: all
|
|
175
|
+
# https://github.com/Dynamoid/dynamoid/blob/master/lib/dynamoid/validations.rb
|
|
176
|
+
# @private
|
|
177
|
+
module Validations
|
|
178
|
+
# Validates whether or not a field is unique against the records in the database.
|
|
179
|
+
class UniquenessValidator < ActiveModel::EachValidator
|
|
180
|
+
# Validate the document for uniqueness violations.
|
|
181
|
+
# @param [Document] document The document to validate.
|
|
182
|
+
# @param [Symbol] attribute The name of the attribute.
|
|
183
|
+
# @param [Object] value The value of the object.
|
|
184
|
+
def validate_each(document, attribute, value)
|
|
185
|
+
return unless validation_required?(document, attribute)
|
|
186
|
+
document.errors.add(attribute, :taken, options.except(:scope).merge(value: value)) if not_unique?(document, attribute, value)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
private
|
|
190
|
+
|
|
191
|
+
# Are we required to validate the document?
|
|
192
|
+
# @api private
|
|
193
|
+
def validation_required?(document, attribute)
|
|
194
|
+
document.new_record? ||
|
|
195
|
+
document.send("attribute_changed?", attribute.to_s) ||
|
|
196
|
+
scope_value_changed?(document)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Scope reference has changed?
|
|
200
|
+
# @api private
|
|
201
|
+
def scope_value_changed?(document)
|
|
202
|
+
Array.wrap(options[:scope]).any? do |item|
|
|
203
|
+
document.send("attribute_changed?", item.to_s)
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Check whether a record is uniqueness.
|
|
208
|
+
# @api private
|
|
209
|
+
def not_unique?(document, attribute, value)
|
|
210
|
+
klass = document.class
|
|
211
|
+
while klass.superclass.respond_to?(:validators) && klass.superclass.validators.include?(self)
|
|
212
|
+
klass = klass.superclass
|
|
213
|
+
end
|
|
214
|
+
criteria = create_criteria(klass, document, attribute, value)
|
|
215
|
+
criteria.exists?
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# Create the validation criteria.
|
|
219
|
+
# @api private
|
|
220
|
+
def create_criteria(base, document, attribute, value)
|
|
221
|
+
criteria = scope(base, document)
|
|
222
|
+
filter_criteria(criteria, document, attribute)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# Scope the criteria to the scope options provided.
|
|
226
|
+
# @api private
|
|
227
|
+
def scope(criteria, document)
|
|
228
|
+
Array.wrap(options[:scope]).each do |item|
|
|
229
|
+
criteria = filter_criteria(criteria, document, item)
|
|
230
|
+
end
|
|
231
|
+
criteria
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# Filter the criteria.
|
|
235
|
+
# @api private
|
|
236
|
+
def filter_criteria(criteria, document, attribute)
|
|
237
|
+
value = document.read_attribute(attribute)
|
|
238
|
+
value.nil? ? criteria.where("#{attribute}.null" => true) : criteria.where(attribute => value)
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
166
244
|
module ActivityNotification
|
|
167
245
|
# Dynamoid extension module for ActivityNotification.
|
|
168
246
|
module DynamoidExtension
|
|
@@ -20,17 +20,17 @@ module ActivityNotification
|
|
|
20
20
|
# Belongs to target instance of this notification as polymorphic association using composite key.
|
|
21
21
|
# @scope instance
|
|
22
22
|
# @return [Object] Target instance of this notification
|
|
23
|
-
belongs_to_composite_xdb_record :target, store_with_associated_records: true
|
|
23
|
+
belongs_to_composite_xdb_record :target, store_with_associated_records: true, as_json_options: { methods: [:printable_type, :printable_target_name] }
|
|
24
24
|
|
|
25
25
|
# Belongs to notifiable instance of this notification as polymorphic association using composite key.
|
|
26
26
|
# @scope instance
|
|
27
27
|
# @return [Object] Notifiable instance of this notification
|
|
28
|
-
belongs_to_composite_xdb_record :notifiable, store_with_associated_records: true
|
|
28
|
+
belongs_to_composite_xdb_record :notifiable, store_with_associated_records: true, as_json_options: { methods: [:printable_type] }
|
|
29
29
|
|
|
30
30
|
# Belongs to group instance of this notification as polymorphic association using composite key.
|
|
31
31
|
# @scope instance
|
|
32
32
|
# @return [Object] Group instance of this notification
|
|
33
|
-
belongs_to_composite_xdb_record :group
|
|
33
|
+
belongs_to_composite_xdb_record :group, store_with_associated_records: true, as_json_options: { methods: [:printable_type, :printable_group_name] }
|
|
34
34
|
|
|
35
35
|
field :key, :string
|
|
36
36
|
field :parameters, :raw, default: {}
|
|
@@ -64,14 +64,54 @@ module ActivityNotification
|
|
|
64
64
|
# Belongs to :otifier instance of this notification.
|
|
65
65
|
# @scope instance
|
|
66
66
|
# @return [Object] Notifier instance of this notification
|
|
67
|
-
belongs_to_composite_xdb_record :notifier, store_with_associated_records: true
|
|
67
|
+
belongs_to_composite_xdb_record :notifier, store_with_associated_records: true, as_json_options: { methods: [:printable_type, :printable_notifier_name] }
|
|
68
|
+
|
|
69
|
+
# Additional fields to store from instance method when config.store_with_associated_records is enabled
|
|
70
|
+
if ActivityNotification.config.store_with_associated_records
|
|
71
|
+
field :stored_notifiable_path, :string
|
|
72
|
+
field :stored_printable_notifiable_name, :string
|
|
73
|
+
field :stored_group_member_notifier_count, :integer
|
|
74
|
+
field :stored_group_notification_count, :integer
|
|
75
|
+
field :stored_group_members, :array
|
|
76
|
+
|
|
77
|
+
# Returns prepared notification object to store
|
|
78
|
+
# @return [Object] prepared notification object to store
|
|
79
|
+
def prepare_to_store
|
|
80
|
+
self.stored_notifiable_path = notifiable_path
|
|
81
|
+
self.stored_printable_notifiable_name = printable_notifiable_name
|
|
82
|
+
if group_owner?
|
|
83
|
+
self.stored_group_notification_count = 0
|
|
84
|
+
self.stored_group_member_notifier_count = 0
|
|
85
|
+
self.stored_group_members = []
|
|
86
|
+
end
|
|
87
|
+
self
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Call after store action with stored notification
|
|
91
|
+
def after_store
|
|
92
|
+
if group_owner?
|
|
93
|
+
self.stored_group_notification_count = group_notification_count
|
|
94
|
+
self.stored_group_member_notifier_count = group_member_notifier_count
|
|
95
|
+
self.stored_group_members = group_members.as_json
|
|
96
|
+
self.stored_group_members.each do |group_member|
|
|
97
|
+
# Cast Time and DateTime field to String to handle Dynamoid unsupported type error
|
|
98
|
+
group_member.each do |k, v|
|
|
99
|
+
group_member[k] = v.to_s if v.is_a?(Time) || v.is_a?(DateTime)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
save
|
|
103
|
+
else
|
|
104
|
+
group_owner.after_store
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
68
108
|
|
|
69
109
|
# Mandatory global secondary index to query effectively
|
|
70
|
-
global_secondary_index hash_key: :target_key, range_key: :created_at, projected_attributes: :all
|
|
71
|
-
global_secondary_index hash_key: :group_owner_id, range_key: :created_at, projected_attributes: :all
|
|
110
|
+
global_secondary_index name: :index_target_key_created_at, hash_key: :target_key, range_key: :created_at, projected_attributes: :all
|
|
111
|
+
global_secondary_index name: :index_group_owner_id_created_at, hash_key: :group_owner_id, range_key: :created_at, projected_attributes: :all
|
|
72
112
|
# Optional global secondary index to sort by created_at
|
|
73
|
-
global_secondary_index hash_key: :notifier_key, range_key: :created_at, projected_attributes: :all
|
|
74
|
-
global_secondary_index hash_key: :notifiable_key, range_key: :created_at, projected_attributes: :all
|
|
113
|
+
global_secondary_index name: :index_notifier_key_created_at, hash_key: :notifier_key, range_key: :created_at, projected_attributes: :all
|
|
114
|
+
global_secondary_index name: :index_notifiable_key_created_at, hash_key: :notifiable_key, range_key: :created_at, projected_attributes: :all
|
|
75
115
|
|
|
76
116
|
validates :target, presence: true
|
|
77
117
|
validates :notifiable, presence: true
|
|
@@ -116,17 +156,12 @@ module ActivityNotification
|
|
|
116
156
|
|
|
117
157
|
# Raise ActivityNotification::DeleteRestrictionError for notifications.
|
|
118
158
|
# @param [String] error_text Error text for raised exception
|
|
119
|
-
# @raise ActivityNotification::DeleteRestrictionError
|
|
159
|
+
# @raise [ActivityNotification::DeleteRestrictionError] DeleteRestrictionError from used ORM
|
|
120
160
|
# @return [void]
|
|
121
161
|
def self.raise_delete_restriction_error(error_text)
|
|
122
162
|
raise ActivityNotification::DeleteRestrictionError, error_text
|
|
123
163
|
end
|
|
124
164
|
|
|
125
|
-
# Returns prepared notification object to store
|
|
126
|
-
# @return [Object] prepared notification object to store
|
|
127
|
-
# def prepare_to_store
|
|
128
|
-
# end
|
|
129
|
-
|
|
130
165
|
protected
|
|
131
166
|
|
|
132
167
|
# Returns count of group members of the unopened notification.
|
|
@@ -28,10 +28,10 @@ module ActivityNotification
|
|
|
28
28
|
field :unsubscribed_to_email_at, :datetime
|
|
29
29
|
field :optional_targets, :raw, default: {}
|
|
30
30
|
|
|
31
|
-
global_secondary_index hash_key: :target_key, range_key: :created_at, projected_attributes: :all
|
|
31
|
+
global_secondary_index name: :index_target_key_created_at, hash_key: :target_key, range_key: :created_at, projected_attributes: :all
|
|
32
32
|
|
|
33
33
|
validates :target, presence: true
|
|
34
|
-
validates :key, presence: true
|
|
34
|
+
validates :key, presence: true, uniqueness: { scope: :target_key }
|
|
35
35
|
validates_inclusion_of :subscribing, in: [true, false]
|
|
36
36
|
validates_inclusion_of :subscribing_to_email, in: [true, false]
|
|
37
37
|
validate :subscribing_to_email_cannot_be_true_when_subscribing_is_false
|
|
@@ -24,8 +24,8 @@ module ActivityNotification
|
|
|
24
24
|
id_field, type_field = "#{association_name}_id", "#{association_name}_type"
|
|
25
25
|
field id_field, type: String
|
|
26
26
|
field type_field, type: String
|
|
27
|
-
associated_record_field = "#{association_name}
|
|
28
|
-
field associated_record_field, type:
|
|
27
|
+
associated_record_field = "stored_#{association_name}"
|
|
28
|
+
field associated_record_field, type: Hash if ActivityNotification.config.store_with_associated_records && _options[:store_with_associated_records]
|
|
29
29
|
|
|
30
30
|
self.instance_eval do
|
|
31
31
|
define_method(name) do |reload = false|
|
|
@@ -43,7 +43,14 @@ module ActivityNotification
|
|
|
43
43
|
if new_instance.nil? then instance_id, instance_type = nil, nil else instance_id, instance_type = new_instance.id, new_instance.class.name end
|
|
44
44
|
self.send("#{id_field}=", instance_id)
|
|
45
45
|
self.send("#{type_field}=", instance_type)
|
|
46
|
-
|
|
46
|
+
associated_record_json = new_instance.as_json(_options[:as_json_options] || {})
|
|
47
|
+
# Cast Hash $oid field to String id to handle BSON::String::IllegalKey
|
|
48
|
+
if associated_record_json.present?
|
|
49
|
+
associated_record_json.each do |k, v|
|
|
50
|
+
associated_record_json[k] = v['$oid'] if v.is_a?(Hash) && v.has_key?('$oid')
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
self.send("#{associated_record_field}=", associated_record_json) if ActivityNotification.config.store_with_associated_records && _options[:store_with_associated_records]
|
|
47
54
|
self.instance_variable_set("@#{name}", nil)
|
|
48
55
|
end
|
|
49
56
|
end
|
|
@@ -72,5 +79,27 @@ module ActivityNotification
|
|
|
72
79
|
end
|
|
73
80
|
end
|
|
74
81
|
|
|
82
|
+
# Monkey patching for Mongoid::Document as_json
|
|
83
|
+
module Mongoid
|
|
84
|
+
# Monkey patching for Mongoid::Document as_json
|
|
85
|
+
module Document
|
|
86
|
+
# Monkey patching for Mongoid::Document as_json
|
|
87
|
+
# @param [Hash] options Options parameter
|
|
88
|
+
# @return [Hash] Hash representing the model
|
|
89
|
+
def as_json(options = {})
|
|
90
|
+
json = super(options)
|
|
91
|
+
json["id"] = json["_id"].to_s.start_with?("{\"$oid\"=>") ? self.id.to_s : json["_id"].to_s
|
|
92
|
+
if options.has_key?(:include)
|
|
93
|
+
case options[:include]
|
|
94
|
+
when Symbol then json[options[:include].to_s] = self.send(options[:include]).as_json
|
|
95
|
+
when Array then options[:include].each {|model| json[model.to_s] = self.send(model).as_json }
|
|
96
|
+
when Hash then options[:include].each {|model, options| json[model.to_s] = self.send(model).as_json(options) }
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
json
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
75
104
|
require_relative 'mongoid/notification.rb'
|
|
76
105
|
require_relative 'mongoid/subscription.rb'
|
|
@@ -19,17 +19,17 @@ module ActivityNotification
|
|
|
19
19
|
# Belongs to target instance of this notification as polymorphic association.
|
|
20
20
|
# @scope instance
|
|
21
21
|
# @return [Object] Target instance of this notification
|
|
22
|
-
belongs_to_polymorphic_xdb_record :target, store_with_associated_records: true
|
|
22
|
+
belongs_to_polymorphic_xdb_record :target, store_with_associated_records: true, as_json_options: { methods: [:printable_type, :printable_target_name] }
|
|
23
23
|
|
|
24
24
|
# Belongs to notifiable instance of this notification as polymorphic association.
|
|
25
25
|
# @scope instance
|
|
26
26
|
# @return [Object] Notifiable instance of this notification
|
|
27
|
-
belongs_to_polymorphic_xdb_record :notifiable, store_with_associated_records: true
|
|
27
|
+
belongs_to_polymorphic_xdb_record :notifiable, store_with_associated_records: true, as_json_options: { methods: [:printable_type] }
|
|
28
28
|
|
|
29
29
|
# Belongs to group instance of this notification as polymorphic association.
|
|
30
30
|
# @scope instance
|
|
31
31
|
# @return [Object] Group instance of this notification
|
|
32
|
-
belongs_to_polymorphic_xdb_record :group
|
|
32
|
+
belongs_to_polymorphic_xdb_record :group, as_json_options: { methods: [:printable_type, :printable_group_name] }
|
|
33
33
|
|
|
34
34
|
field :key, type: String
|
|
35
35
|
field :parameters, type: Hash, default: {}
|
|
@@ -53,7 +53,7 @@ module ActivityNotification
|
|
|
53
53
|
# Belongs to :otifier instance of this notification.
|
|
54
54
|
# @scope instance
|
|
55
55
|
# @return [Object] Notifier instance of this notification
|
|
56
|
-
belongs_to_polymorphic_xdb_record :notifier, store_with_associated_records: true
|
|
56
|
+
belongs_to_polymorphic_xdb_record :notifier, store_with_associated_records: true, as_json_options: { methods: [:printable_type, :printable_notifier_name] }
|
|
57
57
|
|
|
58
58
|
validates :target, presence: true
|
|
59
59
|
validates :notifiable, presence: true
|
|
@@ -140,9 +140,27 @@ module ActivityNotification
|
|
|
140
140
|
scope :filtered_by_group, ->(group) {
|
|
141
141
|
group.present? ?
|
|
142
142
|
where(group_id: group.id, group_type: group.class.name) :
|
|
143
|
-
|
|
143
|
+
Gem::Version.new(::Mongoid::VERSION) >= Gem::Version.new('7.1.0') ?
|
|
144
|
+
where(:group_id.exists => false, :group_type.exists => false).or(group_id: nil, group_type: nil) :
|
|
145
|
+
any_of({ :group_id.exists => false, :group_type.exists => false }, { group_id: nil, group_type: nil })
|
|
144
146
|
}
|
|
145
147
|
|
|
148
|
+
# Selects filtered notifications later than specified time.
|
|
149
|
+
# @example Get filtered unopened notificatons of the @user later than @notification
|
|
150
|
+
# @notifications = @user.notifications.unopened_only.later_than(@notification.created_at)
|
|
151
|
+
# @scope class
|
|
152
|
+
# @param [Time] Created time of the notifications for filter
|
|
153
|
+
# @return [ActiveRecord_AssociationRelation<Notificaion>, Mongoid::Criteria<Notificaion>] Database query of filtered notifications
|
|
154
|
+
scope :later_than, ->(created_time) { where(:created_at.gt => created_time) }
|
|
155
|
+
|
|
156
|
+
# Selects filtered notifications earlier than specified time.
|
|
157
|
+
# @example Get filtered unopened notificatons of the @user earlier than @notification
|
|
158
|
+
# @notifications = @user.notifications.unopened_only.earlier_than(@notification.created_at)
|
|
159
|
+
# @scope class
|
|
160
|
+
# @param [Time] Created time of the notifications for filter
|
|
161
|
+
# @return [ActiveRecord_AssociationRelation<Notificaion>, Mongoid::Criteria<Notificaion>] Database query of filtered notifications
|
|
162
|
+
scope :earlier_than, ->(created_time) { where(:created_at.lt => created_time) }
|
|
163
|
+
|
|
146
164
|
# Includes target instance with query for notifications.
|
|
147
165
|
# @return [Mongoid::Criteria<Notificaion>] Database query of notifications with target
|
|
148
166
|
scope :with_target, -> { }
|
|
@@ -179,7 +197,7 @@ module ActivityNotification
|
|
|
179
197
|
|
|
180
198
|
# Raise ActivityNotification::DeleteRestrictionError for notifications.
|
|
181
199
|
# @param [String] error_text Error text for raised exception
|
|
182
|
-
# @raise ActivityNotification::DeleteRestrictionError
|
|
200
|
+
# @raise [ActivityNotification::DeleteRestrictionError] DeleteRestrictionError from used ORM
|
|
183
201
|
# @return [void]
|
|
184
202
|
def self.raise_delete_restriction_error(error_text)
|
|
185
203
|
raise ActivityNotification::DeleteRestrictionError, error_text
|