activity_notification 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +56 -0
  4. data/.rspec +3 -0
  5. data/.travis.yml +28 -0
  6. data/Gemfile +8 -0
  7. data/Gemfile.lock +174 -0
  8. data/MIT-LICENSE +20 -0
  9. data/README.md +437 -0
  10. data/Rakefile +19 -0
  11. data/activity_notification.gemspec +33 -0
  12. data/app/controllers/activity_notification/notifications_controller.rb +119 -0
  13. data/app/controllers/activity_notification/notifications_with_devise_controller.rb +29 -0
  14. data/app/mailers/activity_notification/mailer.rb +13 -0
  15. data/app/views/activity_notification/mailer/default/default.html.erb +7 -0
  16. data/app/views/activity_notification/notifications/default/_default.html.erb +36 -0
  17. data/app/views/activity_notification/notifications/default/_index.html.erb +9 -0
  18. data/app/views/activity_notification/notifications/default/destroy.js.erb +2 -0
  19. data/app/views/activity_notification/notifications/default/index.html.erb +17 -0
  20. data/app/views/activity_notification/notifications/default/open.js.erb +2 -0
  21. data/app/views/activity_notification/notifications/default/open_all.js.erb +2 -0
  22. data/app/views/activity_notification/notifications/default/show.html.erb +2 -0
  23. data/config/locales/en.yml +8 -0
  24. data/lib/activity_notification.rb +52 -0
  25. data/lib/activity_notification/apis/notification_api.rb +147 -0
  26. data/lib/activity_notification/common.rb +86 -0
  27. data/lib/activity_notification/config.rb +23 -0
  28. data/lib/activity_notification/controllers/store_controller.rb +30 -0
  29. data/lib/activity_notification/helpers/polymorphic_helpers.rb +32 -0
  30. data/lib/activity_notification/helpers/view_helpers.rb +108 -0
  31. data/lib/activity_notification/mailers/helpers.rb +97 -0
  32. data/lib/activity_notification/models/notifiable.rb +136 -0
  33. data/lib/activity_notification/models/notification.rb +50 -0
  34. data/lib/activity_notification/models/notifier.rb +11 -0
  35. data/lib/activity_notification/models/target.rb +104 -0
  36. data/lib/activity_notification/rails.rb +6 -0
  37. data/lib/activity_notification/rails/routes.rb +105 -0
  38. data/lib/activity_notification/renderable.rb +142 -0
  39. data/lib/activity_notification/roles/acts_as_notifiable.rb +37 -0
  40. data/lib/activity_notification/roles/acts_as_target.rb +30 -0
  41. data/lib/activity_notification/version.rb +3 -0
  42. data/lib/generators/activity_notification/controllers_generator.rb +44 -0
  43. data/lib/generators/activity_notification/install_generator.rb +45 -0
  44. data/lib/generators/activity_notification/migration/migration_generator.rb +17 -0
  45. data/lib/generators/activity_notification/notification/notification_generator.rb +17 -0
  46. data/lib/generators/activity_notification/views_generator.rb +44 -0
  47. data/lib/generators/templates/README +53 -0
  48. data/lib/generators/templates/active_record/migration.rb +18 -0
  49. data/lib/generators/templates/activity_notification.rb +18 -0
  50. data/lib/generators/templates/controllers/README +13 -0
  51. data/lib/generators/templates/controllers/notifications_controller.rb +66 -0
  52. data/lib/generators/templates/controllers/notifications_with_devise_controller.rb +74 -0
  53. data/lib/generators/templates/notification/notification.rb +3 -0
  54. data/lib/tasks/activity_notification_tasks.rake +4 -0
  55. data/spec/concerns/notification_api_spec.rb +531 -0
  56. data/spec/factories/articles.rb +5 -0
  57. data/spec/factories/comments.rb +6 -0
  58. data/spec/factories/notifications.rb +7 -0
  59. data/spec/factories/users.rb +5 -0
  60. data/spec/models/notification_spec.rb +259 -0
  61. data/spec/rails_app/Rakefile +6 -0
  62. data/spec/rails_app/app/controllers/application_controller.rb +5 -0
  63. data/spec/rails_app/app/controllers/concerns/.keep +0 -0
  64. data/spec/rails_app/app/helpers/application_helper.rb +2 -0
  65. data/spec/rails_app/app/mailers/.keep +0 -0
  66. data/spec/rails_app/app/models/.keep +0 -0
  67. data/spec/rails_app/app/models/article.rb +12 -0
  68. data/spec/rails_app/app/models/comment.rb +18 -0
  69. data/spec/rails_app/app/models/concerns/.keep +0 -0
  70. data/spec/rails_app/app/models/user.rb +8 -0
  71. data/spec/rails_app/app/views/layouts/application.html.erb +14 -0
  72. data/spec/rails_app/bin/bundle +3 -0
  73. data/spec/rails_app/bin/rails +4 -0
  74. data/spec/rails_app/bin/rake +4 -0
  75. data/spec/rails_app/bin/setup +29 -0
  76. data/spec/rails_app/config.ru +4 -0
  77. data/spec/rails_app/config/application.rb +20 -0
  78. data/spec/rails_app/config/boot.rb +5 -0
  79. data/spec/rails_app/config/database.yml +25 -0
  80. data/spec/rails_app/config/environment.rb +12 -0
  81. data/spec/rails_app/config/environments/development.rb +44 -0
  82. data/spec/rails_app/config/environments/production.rb +79 -0
  83. data/spec/rails_app/config/environments/test.rb +45 -0
  84. data/spec/rails_app/config/initializers/activity_notification.rb +18 -0
  85. data/spec/rails_app/config/initializers/assets.rb +11 -0
  86. data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  87. data/spec/rails_app/config/initializers/cookies_serializer.rb +3 -0
  88. data/spec/rails_app/config/initializers/devise.rb +274 -0
  89. data/spec/rails_app/config/initializers/filter_parameter_logging.rb +4 -0
  90. data/spec/rails_app/config/initializers/inflections.rb +16 -0
  91. data/spec/rails_app/config/initializers/mime_types.rb +4 -0
  92. data/spec/rails_app/config/initializers/session_store.rb +3 -0
  93. data/spec/rails_app/config/initializers/wrap_parameters.rb +14 -0
  94. data/spec/rails_app/config/routes.rb +5 -0
  95. data/spec/rails_app/config/secrets.yml +22 -0
  96. data/spec/rails_app/db/migrate/20160715050420_create_notifications.rb +18 -0
  97. data/spec/rails_app/db/migrate/20160715050433_create_test_tables.rb +36 -0
  98. data/spec/rails_app/db/schema.rb +73 -0
  99. data/spec/rails_app/public/404.html +67 -0
  100. data/spec/rails_app/public/422.html +67 -0
  101. data/spec/rails_app/public/500.html +66 -0
  102. data/spec/rails_app/public/favicon.ico +0 -0
  103. data/spec/spec_helper.rb +34 -0
  104. metadata +309 -0
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'ActivityNotification'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+
18
+ Bundler::GemHelper.install_tasks
19
+
@@ -0,0 +1,33 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+
3
+ # Maintain your gem's version:
4
+ require "activity_notification/version"
5
+
6
+ # Describe your gem and declare its dependencies:
7
+ Gem::Specification.new do |s|
8
+ s.name = "activity_notification"
9
+ s.version = ActivityNotification::VERSION
10
+ s.platform = Gem::Platform::RUBY
11
+ s.authors = ["Shota Yamazaki"]
12
+ s.email = ["shota.yamazaki.8@gmail.com"]
13
+ s.homepage = "https://github.com/simukappu/activity_notification"
14
+ s.summary = "Integrated user activity notifications for Rails"
15
+ s.description = "Integrated user activity notifications for Rails. Provides functions to configure multiple notification targets and make activity notifications with notifiable models, like adding comments, responding etc."
16
+ s.license = "MIT"
17
+
18
+ s.files = `git ls-files`.split("\n")
19
+ s.test_files = `git ls-files -- spec/*`.split("\n")
20
+ s.require_paths = ["lib"]
21
+ s.required_ruby_version = '>= 2.1.0'
22
+
23
+ s.add_dependency 'rails', '~> 4.2'
24
+ #s.add_dependency 'railties', '>= 3.0.0'
25
+ s.add_dependency 'i18n', '>= 0.5.0'
26
+ s.add_dependency 'activerecord', '>= 3.0'
27
+
28
+ s.add_development_dependency 'sqlite3', '~> 1.3.11'
29
+ s.add_development_dependency "rspec-rails", '~> 3.5.1'
30
+ s.add_development_dependency "factory_girl_rails", '~> 4.7.0'
31
+ s.add_development_dependency 'simplecov', '~> 0.12.0'
32
+ s.add_development_dependency "devise", '~> 4.2.0'
33
+ end
@@ -0,0 +1,119 @@
1
+ module ActivityNotification
2
+ class NotificationsController < ActivityNotification.config.parent_controller.constantize
3
+ include ActivityNotification::StoreController
4
+ include ActivityNotification::PolymorphicHelpers
5
+ prepend_before_action :set_target
6
+ before_action :set_notification, except: [:index, :open_all]
7
+ before_action :set_view_prefixes, except: [:move]
8
+
9
+ DEFAULT_VIEW_DIRECTORY = "default"
10
+
11
+ # GET /:target_type/:target_id/notifcations
12
+ def index
13
+ @notifications = load_notification_index(params[:filter], params[:limit]) if params[:reload].to_s.to_boolean(true)
14
+ respond_to do |format|
15
+ format.html # index.html.erb
16
+ format.json { render json: @notifications.to_json(include: [:target, :notifiable, :group]) }
17
+ end
18
+ end
19
+
20
+ # POST /:target_type/:target_id/notifcations/open_all
21
+ def open_all
22
+ @target.open_all_notifications
23
+ @notifications = load_notification_index(params[:filter], params[:limit]) if params[:reload].to_s.to_boolean(true)
24
+ return_back_or_ajax(params[:filter], params[:limit])
25
+ end
26
+
27
+ # GET /:target_type/:target_id/notifcations/:id
28
+ def show
29
+ end
30
+
31
+ # DELETE /:target_type/:target_id/notifcations/:id
32
+ def destroy
33
+ @notification.destroy
34
+ @notifications = load_notification_index(params[:filter], params[:limit]) if params[:reload].to_s.to_boolean(true)
35
+ return_back_or_ajax(params[:filter], params[:limit])
36
+ end
37
+
38
+ # POST /:target_type/:target_id/notifcations/:id/open
39
+ def open
40
+ @notification.open!
41
+ @notifications = load_notification_index(params[:filter], params[:limit]) if params[:reload].to_s.to_boolean(true)
42
+ params[:move].to_s.to_boolean(false) ?
43
+ move :
44
+ return_back_or_ajax(params[:filter], params[:limit])
45
+ end
46
+
47
+ # GET /:target_type/:target_id/notifcations/:id/move
48
+ def move
49
+ @notification.open! if params[:open].to_s.to_boolean(false)
50
+ redirect_to @notification.notifiale_path
51
+ end
52
+
53
+ # No action routing
54
+ # This method is called from target_view_path method
55
+ # This method can be overriden
56
+ def controller_path
57
+ "activity_notification/notifications"
58
+ end
59
+
60
+ # No action routing
61
+ # This method needs to be public since it is called from view helper
62
+ def target_view_path
63
+ target_type = @target.to_resources_name
64
+ view_path = [controller_path, target_type].join('/')
65
+ lookup_context.exists?(action_name, view_path) ?
66
+ view_path :
67
+ [controller_path, DEFAULT_VIEW_DIRECTORY].join('/')
68
+ end
69
+
70
+ protected
71
+
72
+ def set_target
73
+ if (target_type = params[:target_type]).present?
74
+ target_class = target_type.to_model_class
75
+ @target = params[:target_id].present? ?
76
+ target_class.find_by_id!(params[:target_id]) :
77
+ target_class.find_by_id!(params["#{target_type.to_resource_name}_id"])
78
+ else
79
+ render text: "400 Bad Request: Missing parameter", status: 400
80
+ end
81
+ end
82
+
83
+ def set_notification
84
+ @notification = Notification.find_by_id!(params[:id])
85
+ if @target.present? and @notification.target != @target
86
+ render text: "403 Forbidden: Wrong target", status: 403
87
+ end
88
+ end
89
+
90
+ def load_notification_index(filter, limit)
91
+ limit = nil unless limit.to_i > 0
92
+ case filter
93
+ when 'opened'
94
+ @target.opened_notification_index_with_attributes(limit)
95
+ when 'unopened'
96
+ @target.unopened_notification_index_with_attributes(limit)
97
+ else
98
+ @target.notification_index_with_attributes(limit)
99
+ end
100
+ end
101
+
102
+ def set_view_prefixes
103
+ lookup_context.prefixes.prepend(target_view_path)
104
+ end
105
+
106
+ def return_back_or_ajax(filter, limit)
107
+ respond_to do |format|
108
+ if request.xhr?
109
+ format.js
110
+ elsif request.referer
111
+ redirect_to :back, filter: filter, limit: limit and return
112
+ else
113
+ redirect_to action: :index, filter: filter, limit: limit and return
114
+ end
115
+ end
116
+ end
117
+
118
+ end
119
+ end
@@ -0,0 +1,29 @@
1
+ module ActivityNotification
2
+ class NotificationsWithDeviseController < NotificationsController
3
+ prepend_before_action :authenticate_devise_resource!
4
+ before_action :authenticate_target!
5
+
6
+ protected
7
+
8
+ def authenticate_devise_resource!
9
+ authenticate_method_name = "authenticate_#{params[:devise_type].to_resource_name}!"
10
+ if respond_to?(authenticate_method_name)
11
+ send(authenticate_method_name)
12
+ else
13
+ render text: "403 Forbidden: Unauthenticated", status: 403
14
+ end
15
+ end
16
+
17
+ def authenticate_target!
18
+ current_resource_method_name = "current_#{params[:devise_type].to_resource_name}"
19
+ if respond_to?(current_resource_method_name)
20
+ unless @target.authenticate_with_devise?(send(current_resource_method_name))
21
+ render text: "403 Forbidden: Unauthorized target", status: 403
22
+ end
23
+ else
24
+ render text: "403 Forbidden: Unauthenticated", status: 403
25
+ end
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,13 @@
1
+ if defined?(ActionMailer)
2
+ class ActivityNotification::Mailer < ActivityNotification.config.parent_mailer.constantize
3
+ include ActivityNotification::Mailers::Helpers
4
+
5
+ def send_notification_email(notification)
6
+ if notification.target.notification_email_allowed?(notification.notifiable, notification.key) and
7
+ notification.notifiable.notification_email_allowed?(notification.target, notification.key)
8
+ notification_mail(notification)
9
+ end
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,7 @@
1
+ <p>Dear <%= @target.mailer_to %></p>
2
+
3
+ <p>This is a notification of <%= @notifiable.printable_name %> for you.</p>
4
+
5
+ <p><%= link_to "Move to #{@notifiable.printable_name}", move_notification_url_for(@notification, open: true) %></p>
6
+
7
+ <p>Thank you!</p>
@@ -0,0 +1,36 @@
1
+ <li>
2
+ <div>
3
+ <%= notification.notifier.present? ? notification.notifier.printable_name : 'Someone' %>
4
+ <% if notification.group_member_exists? %>
5
+ <%= " and other #{notification.group_member_count} people" %>
6
+ <% end %>
7
+ notified you of <%= notification.notifiable.printable_name %>
8
+ <%= "in #{notification.group.printable_name}" if notification.group.present? %>
9
+ </div>
10
+ <div>
11
+ <%= notification.opened? ? "Opened" : "Unopened" %>
12
+ </div>
13
+ <div>
14
+ <%= link_to "Move", move_notification_path_for(notification) %>
15
+ </div>
16
+ <% if notification.unopened? %>
17
+ <div>
18
+ <%= link_to "Open and move (GET)", move_notification_path_for(notification, open: true) %>
19
+ </div>
20
+ <div>
21
+ <%= link_to "Open and move (POST)", open_notification_path_for(notification, move: true), method: :post %>
22
+ </div>
23
+ <div>
24
+ <%= link_to "Open", open_notification_path_for(notification, filter: params[:filter], limit: params[:limit]), method: :post %>
25
+ </div>
26
+ <div>
27
+ <%= link_to "Open (Ajax)", open_notification_path_for(notification, filter: params[:filter], limit: params[:limit]), method: :post, remote: true %>
28
+ </div>
29
+ <% end %>
30
+ <div>
31
+ <%= link_to "Destroy", notification_path_for(notification, filter: params[:filter], limit: params[:limit]), method: :delete %>
32
+ </div>
33
+ <div>
34
+ <%= link_to "Destroy (Ajax)", notification_path_for(notification, filter: params[:filter], limit: params[:limit]), method: :delete, remote: true %>
35
+ </div>
36
+ </li>
@@ -0,0 +1,9 @@
1
+ <div>
2
+ <%= link_to open_all_notifications_path_for(@target), method: :post, remote: true do %>
3
+ <div id="notification_count"><%= @target.unopened_notification_count %></div>
4
+ <% end %>
5
+
6
+ <div id="notification_index">
7
+ <%= yield :notification_index %>
8
+ </div>
9
+ </div>
@@ -0,0 +1,2 @@
1
+ $("#notification_count").html("<%= @target.unopened_notification_count %>");
2
+ $("#notifications").html("<%= escape_javascript( render_notification(@notifications, fallback: "default") ) %>");
@@ -0,0 +1,17 @@
1
+ <h1>Notifications for <%= @target.printable_name %></h1>
2
+ <div id="notification_count"><%= @target.unopened_notification_count %></div>
3
+
4
+ <div>
5
+ <%= link_to "Open all", open_all_notifications_path_for(@target), method: :post %>
6
+ </div>
7
+ <div>
8
+ <%= link_to "Open all (ajax)", open_all_notifications_path_for(@target), method: :post, remote: true %>
9
+ </div>
10
+ <ul>
11
+ <div id="notifications">
12
+ <%= render_notification(@notifications, fallback: :default) %>
13
+ <%#= render_notification(@notifications, fallback: :text) %>
14
+ </div>
15
+ </ul>
16
+
17
+ <%#= render_notifications_of @target, fallback: :default, index_content: :with_attributes %>
@@ -0,0 +1,2 @@
1
+ $("#notification_count").html("<%= @target.unopened_notification_count %>");
2
+ $("#notifications").html("<%= escape_javascript( render_notification(@notifications, fallback: "default") ) %>");
@@ -0,0 +1,2 @@
1
+ $("#notification_count").html("<%= @target.unopened_notification_count %>");
2
+ $("#notifications").html("<%= escape_javascript( render_notification(@notifications, fallback: "default") ) %>");
@@ -0,0 +1,2 @@
1
+ <h1>Notification for <%= "#{@target.class.to_s.underscore} (#{@target.id})" %></h1>
2
+ <%= render_notification(@notification, fallback: "default") %>
@@ -0,0 +1,8 @@
1
+ # Additional translations of ActivityNotification
2
+
3
+ en:
4
+ notification:
5
+ default:
6
+ your_notifiable:
7
+ default:
8
+ mail_subject:
@@ -0,0 +1,52 @@
1
+ require 'rails'
2
+ require 'active_support'
3
+ require 'action_view'
4
+
5
+ module ActivityNotification
6
+ extend ActiveSupport::Concern
7
+ extend ActiveSupport::Autoload
8
+
9
+ autoload :Notification, 'activity_notification/models/notification'
10
+ autoload :Target, 'activity_notification/models/target'
11
+ autoload :Notifiable, 'activity_notification/models/notifiable'
12
+ autoload :ActsAsTarget, 'activity_notification/roles/acts_as_target'
13
+ autoload :ActsAsNotifiable, 'activity_notification/roles/acts_as_notifiable'
14
+ autoload :StoreController, 'activity_notification/controllers/store_controller'
15
+ autoload :NotificationApi, 'activity_notification/apis/notification_api'
16
+ autoload :Common
17
+ autoload :Config
18
+ autoload :Renderable
19
+ autoload :VERSION
20
+
21
+
22
+ module Mailers
23
+ autoload :Helpers, 'activity_notification/mailers/helpers'
24
+ end
25
+
26
+ # Returns ActivityNotification's configuration object.
27
+ def self.config
28
+ @config ||= ActivityNotification::Config.new
29
+ end
30
+
31
+ # Lets you set global configuration options.
32
+ #
33
+ # All available options and their defaults are in the example below:
34
+ # @example Initializer for Rails
35
+ # ActivityNotification.configure do |config|
36
+ # config.enabled = false
37
+ # config.table_name = "notifications"
38
+ # ...
39
+ # end
40
+ def self.configure(&block)
41
+ yield(config) if block_given?
42
+ end
43
+
44
+ end
45
+
46
+ # Load ActivityNotification helpers
47
+ require 'activity_notification/helpers/polymorphic_helpers'
48
+ require 'activity_notification/helpers/view_helpers'
49
+
50
+ # Define Rails::Engine
51
+ require 'activity_notification/rails'
52
+
@@ -0,0 +1,147 @@
1
+ module ActivityNotification
2
+ module NotificationApi
3
+ extend ActiveSupport::Concern
4
+
5
+ # Define store_notification as private clas method
6
+ included do
7
+ private_class_method :store_notification
8
+ end
9
+
10
+ # For notification API
11
+ class_methods do
12
+ def notify(target_type, notifiable, options = {})
13
+ targets = notifiable.notification_targets(target_type, options[:key])
14
+ unless targets.blank?
15
+ notify_all(targets, notifiable, options)
16
+ end
17
+ end
18
+
19
+ def notify_all(targets, notifiable, options = {})
20
+ targets.each do |target|
21
+ notify_to(target, notifiable, options)
22
+ end
23
+ end
24
+
25
+ def notify_to(target, notifiable, options = {})
26
+ send_email = options.has_key?(:send_email) ? options[:send_email] : true
27
+ send_later = options.has_key?(:send_later) ? options[:send_later] : true
28
+ # Store notification
29
+ notification = store_notification(target, notifiable, options)
30
+ # Send notification email
31
+ notification.send_notification_email(send_later) if send_email
32
+ notification
33
+ end
34
+
35
+ # Open all notifications of specified target
36
+ def open_all_of(target, opened_at = nil)
37
+ opened_at = DateTime.now if opened_at.blank?
38
+ Notification.where(target: target, opened_at: nil).update_all(opened_at: opened_at)
39
+ end
40
+
41
+ #TODO description
42
+ # Call from controllers or views to avoid N+1
43
+ def group_member_exists?(notifications)
44
+ Notification.where(group_owner_id: notifications.pluck(:id)).exists?
45
+ end
46
+
47
+ def available_options
48
+ [:key, :group, :parameters, :notifier, :send_email, :send_later].freeze
49
+ end
50
+
51
+ # Private class methods
52
+
53
+ def store_notification(target, notifiable, options = {})
54
+ target_type = target.to_class_name
55
+ key = options[:key] || notifiable.default_notification_key
56
+ group = options[:group] || notifiable.notification_group(target_type, key)
57
+ notifier = options[:notifier] || notifiable.notifier(target_type, key)
58
+ parameters = options[:parameters] || {}
59
+ parameters.merge!(options.except(*available_options))
60
+ parameters.merge!(notifiable.notification_parameters(target_type, key))
61
+
62
+ # Bundle notification group by target, notifiable_type, group and key
63
+ # Defferent notifiable.id can be made in a same group
64
+ group_owner = Notification.where(target: target, notifiable_type: notifiable.to_class_name, key: key, group: group)
65
+ .where(group_owner_id: nil, opened_at: nil).earliest
66
+ if group.present? and group_owner.present?
67
+ create(target: target, notifiable: notifiable, key: key, group: group, group_owner: group_owner, parameters: parameters, notifier: notifier)
68
+ else
69
+ create(target: target, notifiable: notifiable, key: key, group: group, parameters: parameters, notifier: notifier)
70
+ end
71
+ end
72
+ end
73
+
74
+
75
+ # Public instance methods
76
+
77
+ def send_notification_email(send_later = true)
78
+ if send_later
79
+ Mailer.send_notification_email(self).deliver_later
80
+ else
81
+ Mailer.send_notification_email(self).deliver_now
82
+ end
83
+ end
84
+
85
+ def open!(opened_at = nil)
86
+ opened_at = DateTime.now if opened_at.blank?
87
+ update(opened_at: opened_at)
88
+ group_members.update_all(opened_at: opened_at)
89
+ end
90
+
91
+ def unopened?
92
+ !opened?
93
+ end
94
+
95
+ def opened?
96
+ opened_at.present?
97
+ end
98
+
99
+ def group_owner?
100
+ group_owner_id.blank?
101
+ end
102
+
103
+ def group_member?
104
+ group_owner_id.present?
105
+ end
106
+
107
+ # Cache group-by query result to avoid N+1 call
108
+ def group_member_exists?(limit = ActivityNotification.config.opened_limit)
109
+ group_member_count(limit) > 0
110
+ end
111
+
112
+ # Cache group-by query result to avoid N+1 call
113
+ def group_member_count(limit = ActivityNotification.config.opened_limit)
114
+ notification = group_member? ? group_owner : self
115
+ notification.opened? ?
116
+ notification.opened_group_member_count(limit) :
117
+ notification.unopened_group_member_count
118
+ end
119
+
120
+ def notifiale_path
121
+ notifiable.notifiable_path(target_type)
122
+ end
123
+
124
+
125
+ # Protected instance methods
126
+ protected
127
+
128
+ def unopened_group_member_count
129
+ # Cache group-by query result to avoid N+1 call
130
+ unopened_group_member_counts = target.notifications
131
+ .unopened_index_group_members_only
132
+ .group(:group_owner_id)
133
+ .count
134
+ unopened_group_member_counts[id] || 0
135
+ end
136
+
137
+ def opened_group_member_count(limit = ActivityNotification.config.opened_limit)
138
+ # Cache group-by query result to avoid N+1 call
139
+ opened_group_member_counts = target.notifications
140
+ .opened_index_group_members_only(limit)
141
+ .group(:group_owner_id)
142
+ .count
143
+ opened_group_member_counts[id] || 0
144
+ end
145
+
146
+ end
147
+ end