activity_notification 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +33 -0
  3. data/.rubocop.yml +1157 -0
  4. data/.yardopts +3 -0
  5. data/CHANGELOG.md +25 -0
  6. data/Gemfile.lock +15 -17
  7. data/README.md +154 -27
  8. data/activity_notification.gemspec +1 -1
  9. data/app/controllers/activity_notification/notifications_controller.rb +30 -104
  10. data/app/controllers/activity_notification/notifications_with_devise_controller.rb +1 -33
  11. data/app/controllers/activity_notification/subscriptions_controller.rb +184 -0
  12. data/app/controllers/activity_notification/subscriptions_with_devise_controller.rb +6 -0
  13. data/app/mailers/activity_notification/mailer.rb +3 -3
  14. data/app/views/activity_notification/notifications/default/_index.html.erb +3 -0
  15. data/app/views/activity_notification/notifications/default/index.html.erb +8 -10
  16. data/app/views/activity_notification/notifications/default/show.html.erb +24 -2
  17. data/app/views/activity_notification/subscriptions/default/_form.html.erb +52 -0
  18. data/app/views/activity_notification/subscriptions/default/_notification_keys.html.erb +89 -0
  19. data/app/views/activity_notification/subscriptions/default/_subscription.html.erb +73 -0
  20. data/app/views/activity_notification/subscriptions/default/_subscriptions.html.erb +13 -0
  21. data/app/views/activity_notification/subscriptions/default/create.js.erb +5 -0
  22. data/app/views/activity_notification/subscriptions/default/destroy.js.erb +5 -0
  23. data/app/views/activity_notification/subscriptions/default/index.html.erb +197 -0
  24. data/app/views/activity_notification/subscriptions/default/show.html.erb +177 -0
  25. data/app/views/activity_notification/subscriptions/default/subscribe.js.erb +8 -0
  26. data/app/views/activity_notification/subscriptions/default/subscribe_to_email.js.erb +6 -0
  27. data/app/views/activity_notification/subscriptions/default/unsubscribe.js.erb +8 -0
  28. data/app/views/activity_notification/subscriptions/default/unsubscribe_to_email.js.erb +6 -0
  29. data/gemfiles/Gemfile.rails-4.2.lock +18 -20
  30. data/gemfiles/Gemfile.rails-5.0.lock +18 -20
  31. data/lib/activity_notification.rb +7 -3
  32. data/lib/activity_notification/apis/notification_api.rb +95 -61
  33. data/lib/activity_notification/apis/subscription_api.rb +51 -0
  34. data/lib/activity_notification/common.rb +1 -1
  35. data/lib/activity_notification/config.rb +65 -17
  36. data/lib/activity_notification/controllers/common_controller.rb +118 -0
  37. data/lib/activity_notification/controllers/devise_authentication_controller.rb +41 -0
  38. data/lib/activity_notification/helpers/view_helpers.rb +131 -3
  39. data/lib/activity_notification/mailers/helpers.rb +6 -8
  40. data/lib/activity_notification/models/concerns/notifiable.rb +45 -27
  41. data/lib/activity_notification/models/concerns/subscriber.rb +149 -0
  42. data/lib/activity_notification/models/concerns/target.rb +100 -66
  43. data/lib/activity_notification/models/notification.rb +7 -5
  44. data/lib/activity_notification/models/subscription.rb +93 -0
  45. data/lib/activity_notification/rails/routes.rb +148 -33
  46. data/lib/activity_notification/renderable.rb +3 -4
  47. data/lib/activity_notification/roles/acts_as_notifiable.rb +14 -1
  48. data/lib/activity_notification/roles/acts_as_target.rb +11 -8
  49. data/lib/activity_notification/version.rb +1 -1
  50. data/lib/generators/activity_notification/controllers_generator.rb +2 -2
  51. data/lib/generators/activity_notification/install_generator.rb +0 -1
  52. data/lib/generators/activity_notification/migration/migration_generator.rb +8 -2
  53. data/lib/generators/activity_notification/models_generator.rb +53 -0
  54. data/lib/generators/activity_notification/views_generator.rb +7 -7
  55. data/lib/generators/templates/activity_notification.rb +17 -3
  56. data/lib/generators/templates/controllers/notifications_controller.rb +18 -17
  57. data/lib/generators/templates/controllers/notifications_with_devise_controller.rb +18 -17
  58. data/lib/generators/templates/controllers/subscriptions_controller.rb +79 -0
  59. data/lib/generators/templates/controllers/subscriptions_with_devise_controller.rb +87 -0
  60. data/lib/generators/templates/migrations/migration.rb +57 -0
  61. data/lib/generators/templates/models/README +10 -0
  62. data/lib/generators/templates/{notification → models}/notification.rb +1 -3
  63. data/lib/generators/templates/models/subscription.rb +4 -0
  64. data/spec/concerns/apis/notification_api_spec.rb +48 -11
  65. data/spec/concerns/apis/subscription_api_spec.rb +167 -0
  66. data/spec/concerns/models/notifiable_spec.rb +60 -0
  67. data/spec/concerns/models/subscriber_spec.rb +525 -0
  68. data/spec/concerns/models/target_spec.rb +271 -42
  69. data/spec/controllers/common_controller_spec.rb +25 -0
  70. data/spec/controllers/dummy_common_controller.rb +5 -0
  71. data/spec/controllers/notifications_controller_shared_examples.rb +2 -6
  72. data/spec/controllers/subscriptions_controller_shared_examples.rb +735 -0
  73. data/spec/controllers/subscriptions_controller_spec.rb +12 -0
  74. data/spec/controllers/subscriptions_with_devise_controller_spec.rb +91 -0
  75. data/spec/factories/dummy/dummy_subscriber.rb +4 -0
  76. data/spec/factories/subscriptions.rb +8 -0
  77. data/spec/generators/controllers_generator_spec.rb +25 -2
  78. data/spec/generators/migration/migration_generator_spec.rb +3 -3
  79. data/spec/generators/models_generator_spec.rb +96 -0
  80. data/spec/generators/views_generator_spec.rb +84 -0
  81. data/spec/helpers/view_helpers_spec.rb +143 -0
  82. data/spec/mailers/mailer_spec.rb +5 -4
  83. data/spec/models/dummy/dummy_subscriber_spec.rb +5 -0
  84. data/spec/models/notification_spec.rb +7 -7
  85. data/spec/models/subscription_spec.rb +158 -0
  86. data/spec/rails_app/app/controllers/users/notifications_controller.rb +67 -0
  87. data/spec/rails_app/app/controllers/users/notifications_with_devise_controller.rb +75 -0
  88. data/spec/rails_app/app/controllers/users/subscriptions_controller.rb +79 -0
  89. data/spec/rails_app/app/controllers/users/subscriptions_with_devise_controller.rb +87 -0
  90. data/spec/rails_app/app/models/admin.rb +1 -0
  91. data/spec/rails_app/app/models/dummy/dummy_subscriber.rb +4 -0
  92. data/spec/rails_app/app/models/user.rb +2 -1
  93. data/spec/rails_app/app/views/activity_notification/mailer/dummy_subscribers/test_key.text.erb +1 -0
  94. data/spec/rails_app/app/views/articles/index.html.erb +6 -0
  95. data/spec/rails_app/config/initializers/activity_notification.rb +17 -3
  96. data/spec/rails_app/config/routes.rb +2 -2
  97. data/spec/rails_app/db/migrate/20160715050420_create_activity_notification_tables.rb +33 -0
  98. data/spec/rails_app/db/schema.rb +18 -0
  99. data/spec/roles/acts_as_notifiable_spec.rb +1 -1
  100. data/spec/roles/acts_as_target_spec.rb +1 -1
  101. metadata +70 -11
  102. data/lib/generators/activity_notification/notification/notification_generator.rb +0 -20
  103. data/lib/generators/templates/active_record/migration.rb +0 -18
  104. data/spec/generators/notification/notification_generator_spec.rb +0 -41
  105. data/spec/rails_app/db/migrate/20160715050420_create_notifications.rb +0 -18
@@ -0,0 +1,93 @@
1
+ module ActivityNotification
2
+ # Subscription model implementation generated by ActivityNotification.
3
+ class Subscription < ActiveRecord::Base
4
+ include SubscriptionApi
5
+ self.table_name = ActivityNotification.config.subscription_table_name
6
+
7
+ # Belongs to target instance of this subscription as polymorphic association.
8
+ # @scope instance
9
+ # @return [Object] Target instance of this subscription
10
+ belongs_to :target, polymorphic: true
11
+
12
+ # Serialize parameters Hash
13
+ serialize :parameters, Hash
14
+
15
+ validates :target, presence: true
16
+ validates :key, presence: true
17
+ validates_inclusion_of :subscribing, in: [true, false]
18
+ validates_inclusion_of :subscribing_to_email, in: [true, false]
19
+ validate :subscribing_to_email_cannot_be_true_when_subscribing_is_false
20
+ validates :subscribed_at, presence: true, if: :subscribing
21
+ validates :unsubscribed_at, presence: true, unless: :subscribing
22
+ validates :subscribed_to_email_at, presence: true, if: :subscribing_to_email
23
+ validates :unsubscribed_to_email_at, presence: true, unless: :subscribing_to_email
24
+
25
+ # Selects filtered subscriptions by target instance.
26
+ # ActivityNotification::Subscription.filtered_by_target(@user)
27
+ # is the same as
28
+ # @user.subscriptions
29
+ # @scope class
30
+ # @param [Object] target Target instance for filter
31
+ # @return [ActiveRecord_AssociationRelation<Subscription>] Array or database query of filtered subscriptions
32
+ scope :filtered_by_target, ->(target) { where(target: target) }
33
+
34
+ # Selects filtered subscriptions by key.
35
+ # @example Get filtered subscriptions of the @user with key 'comment.reply'
36
+ # @subscriptions = @user.subscriptions.filtered_by_key('comment.reply')
37
+ # @scope class
38
+ # @param [String] key Key of the subscription for filter
39
+ # @return [ActiveRecord_AssociationRelation<Subscription>] Array or database query of filtered subscriptions
40
+ scope :filtered_by_key, ->(key) { where(key: key) }
41
+
42
+ # Selects filtered subscriptions by key with filter options.
43
+ # @example Get filtered subscriptions of the @user with key 'comment.reply'
44
+ # @subscriptions = @user.subscriptions.filtered_by_key('comment.reply')
45
+ # @example Get custom filtered subscriptions of the @user
46
+ # @subscriptions = @user.subscriptions.filtered_by_options({ custom_filter: ["created_at >= ?", time.hour.ago] })
47
+ # @scope class
48
+ # @param [Hash] options Options for filter
49
+ # @option options [String] :filtered_by_key (nil) Key of the subscription for filter
50
+ # @option options [Array|Hash] :custom_filter (nil) Custom subscription filter (e.g. ["created_at >= ?", time.hour.ago])
51
+ # @return [ActiveRecord_AssociationRelation<Notificaion>] Array or database query of filtered subscriptions
52
+ scope :filtered_by_options, ->(options = {}) {
53
+ options = ActivityNotification.cast_to_indifferent_hash(options)
54
+ filtered_subscriptions = all
55
+ if options.has_key?(:filtered_by_key)
56
+ filtered_subscriptions = filtered_subscriptions.filtered_by_key(options[:filtered_by_key])
57
+ end
58
+ if options.has_key?(:custom_filter)
59
+ filtered_subscriptions = filtered_subscriptions.where(options[:custom_filter])
60
+ end
61
+ filtered_subscriptions
62
+ }
63
+
64
+ # Orders by latest (newest) first as created_at: :desc.
65
+ # @return [ActiveRecord_AssociationRelation<Subscription>] Database query of subscriptions ordered by latest first
66
+ scope :latest_order, -> { order(created_at: :desc) }
67
+
68
+ # Orders by earliest (older) first as created_at: :asc.
69
+ # @return [ActiveRecord_AssociationRelation<Subscription>] Database query of subscriptions ordered by earliest first
70
+ scope :earliest_order, -> { order(created_at: :asc) }
71
+
72
+ # Orders by latest (newest) first as subscribed_at: :desc.
73
+ # @return [ActiveRecord_AssociationRelation<Subscription>] Database query of subscriptions ordered by latest subscribed_at first
74
+ scope :latest_subscribed_order, -> { order(subscribed_at: :desc) }
75
+
76
+ # Orders by earliest (older) first as subscribed_at: :asc.
77
+ # @return [ActiveRecord_AssociationRelation<Subscription>] Database query of subscriptions ordered by earliest subscribed_at first
78
+ scope :earliest_subscribed_order, -> { order(subscribed_at: :asc) }
79
+
80
+ # Orders by key name as key: :asc.
81
+ # @return [ActiveRecord_AssociationRelation<Subscription>] Database query of subscriptions ordered by key name
82
+ scope :key_order, -> { order(key: :asc) }
83
+
84
+ private
85
+ # Validates subscribing_to_email cannot be true when subscribing isfalse.
86
+ def subscribing_to_email_cannot_be_true_when_subscribing_is_false
87
+ if !subscribing && subscribing_to_email
88
+ errors.add(:subscribing_to_email, "cannot be true when subscribing is false")
89
+ end
90
+ end
91
+
92
+ end
93
+ end
@@ -4,7 +4,9 @@ require "active_support/core_ext/hash/slice"
4
4
  module ActionDispatch::Routing
5
5
  # Extended ActionDispatch::Routing::Mapper implementation to add routing method of ActivityNotification.
6
6
  class Mapper
7
- # Includes notify_to method for routes, which is responsible to generate all necessary routes for activity_notification.
7
+ include ActivityNotification::PolymorphicHelpers
8
+
9
+ # Includes notify_to method for routes, which is responsible to generate all necessary routes for notifications of activity_notification.
8
10
  #
9
11
  # When you have an User model configured as a target (e.g. defined acts_as_target),
10
12
  # you can create as follows in your routes:
@@ -19,7 +21,7 @@ module ActionDispatch::Routing
19
21
  # { controller:"activity_notification/notifications", action:"destroy", target_type:"users" }
20
22
  # open_all_user_notifications POST /users/:user_id/notifications/open_all(.:format)
21
23
  # { controller:"activity_notification/notifications", action:"open_all", target_type:"users" }
22
- # move_user_notification POST /users/:user_id/notifications/:id/move(.:format)
24
+ # move_user_notification GET /users/:user_id/notifications/:id/move(.:format)
23
25
  # { controller:"activity_notification/notifications", action:"move", target_type:"users" }
24
26
  # open_user_notification POST /users/:user_id/notifications/:id/open(.:format)
25
27
  # { controller:"activity_notification/notifications", action:"open", target_type:"users" }
@@ -37,50 +39,46 @@ module ActionDispatch::Routing
37
39
  # { controller:"activity_notification/notifications_with_devise", action:"destroy", target_type:"users", devise_type:"users" }
38
40
  # open_all_user_notifications POST /users/:user_id/notifications/open_all(.:format)
39
41
  # { controller:"activity_notification/notifications_with_devise", action:"open_all", target_type:"users", devise_type:"users" }
40
- # move_user_notification POST /users/:user_id/notifications/:id/move(.:format)
42
+ # move_user_notification GET /users/:user_id/notifications/:id/move(.:format)
41
43
  # { controller:"activity_notification/notifications_with_devise", action:"move", target_type:"users", devise_type:"users" }
42
44
  # open_user_notification POST /users/:user_id/notifications/:id/open(.:format)
43
45
  # { controller:"activity_notification/notifications_with_devise", action:"open", target_type:"users", devise_type:"users" }
44
46
  #
47
+ # When you would like to define subscription management paths with notification paths,
48
+ # you can create as follows in your routes:
49
+ # notify_to :users, with_subscription: true
50
+ # or you can also set options for subscription path:
51
+ # notify_to :users, with_subscription: { except: [:index] }
52
+ # If you configure this :with_subscription option with :with_devise option, with_subscription paths are also automatically configured with devise authentication as the same as notifications
53
+ # notify_to :users, with_devise: :users, with_subscription: true
54
+ #
45
55
  # @example Define notify_to in config/routes.rb
46
56
  # notify_to :users
47
57
  # @example Define notify_to with options
48
58
  # notify_to :users, only: [:open, :open_all, :move]
49
59
  # @example Integrated with Devise authentication
50
60
  # notify_to :users, with_devise: :users
61
+ # @example Define notification paths including subscription paths
62
+ # notify_to :users, with_subscription: true
51
63
  #
52
64
  # @overload notify_to(*resources, *options)
53
- # @param [Symbol] resources Resources to notify
54
- # @option options [Symbol] :with_devise Devise resources name for devise integration. Devise integration will be enabled by this option.
55
- # @option options [String] :controller controller option as resources routing
56
- # @option options [Symbol] :as as option as resources routing
57
- # @option options [Array] :only only option as resources routing
58
- # @option options [Array] :except except option as resources routing
65
+ # @param [Symbol] resources Resources to notify
66
+ # @option options [Symbol] :with_devise (false) Devise resources name for devise integration. Devise integration will be enabled by this option.
67
+ # @option options [Hash|Boolean] :with_subscription (false) Subscription path options to define subscription management paths with notification paths. Calls subscribed_by routing when truthy value is passed as this option.
68
+ # @option options [String] :model (:notifications) Model name of notifications
69
+ # @option options [String] :controller ("activity_notification/notifications" | activity_notification/notifications_with_devise") :controller option as resources routing
70
+ # @option options [Symbol] :as (nil) :as option as resources routing
71
+ # @option options [Array] :only (nil) :only option as resources routing
72
+ # @option options [Array] :except (nil) :except option as resources routing
59
73
  # @return [ActionDispatch::Routing::Mapper] Routing mapper instance
60
74
  def notify_to(*resources)
61
- options = resources.extract_options!
62
-
63
- #TODO check resources if it includes target module
64
-
65
- if (with_devise = options.delete(:with_devise)).present?
66
- options[:controller] ||= "activity_notification/notifications_with_devise"
67
- options[:as] ||= "notifications"
68
- #TODO check devise configuration in model
69
- devise_defaults = { devise_type: with_devise.to_s }
70
- else
71
- options[:controller] ||= "activity_notification/notifications"
72
- end
73
- options[:except] ||= []
74
- options[:except].concat( [:new, :create, :edit, :update] )
75
- notification_resources = options[:model] || :notifications
75
+ options = create_options(:notifications, resources.extract_options!, [:new, :create, :edit, :update])
76
76
 
77
- #TODO other options
78
- # :as, :path_prefix, :path_names ...
79
-
80
- resources.each do |resource|
81
- self.resources resource, only: :none do
82
- options[:defaults] = (devise_defaults || {}).merge({ target_type: resource.to_s })
83
- self.resources notification_resources, options do
77
+ resources.each do |target|
78
+ self.resources target, only: :none do
79
+ options[:defaults] = { target_type: target.to_s }.merge(options[:devise_defaults])
80
+ resources_options = options.select { |key, _| [:with_devise, :with_subscription, :subscription_option, :model, :devise_defaults].exclude? key }
81
+ self.resources options[:model], resources_options do
84
82
  collection do
85
83
  post :open_all unless ignore_path?(:open_all, options)
86
84
  end
@@ -90,18 +88,135 @@ module ActionDispatch::Routing
90
88
  end
91
89
  end
92
90
  end
91
+
92
+ if options[:with_subscription].present? && target.to_s.to_model_class.subscription_enabled?
93
+ subscribed_by target, options[:subscription_option]
94
+ end
93
95
  end
94
96
 
95
97
  self
96
98
  end
97
99
 
100
+ # Includes subscribed_by method for routes, which is responsible to generate all necessary routes for subscriptions of activity_notification.
101
+ #
102
+ # When you have an User model configured as a target (e.g. defined acts_as_target),
103
+ # you can create as follows in your routes:
104
+ # subscribed_by :users
105
+ # This method creates the needed routes:
106
+ # # Subscription routes
107
+ # user_subscriptions GET /users/:user_id/subscriptions(.:format)
108
+ # { controller:"activity_notification/subscriptions", action:"index", target_type:"users" }
109
+ # user_subscription GET /users/:user_id/subscriptions/:id(.:format)
110
+ # { controller:"activity_notification/subscriptions", action:"show", target_type:"users" }
111
+ # open_all_user_subscriptions POST /users/:user_id/subscriptions(.:format)
112
+ # { controller:"activity_notification/subscriptions", action:"create", target_type:"users" }
113
+ # user_subscription DELETE /users/:user_id/subscriptions/:id(.:format)
114
+ # { controller:"activity_notification/subscriptions", action:"destroy", target_type:"users" }
115
+ # open_user_subscription POST /users/:user_id/subscriptions/:id/subscribe(.:format)
116
+ # { controller:"activity_notification/subscriptions", action:"subscribe", target_type:"users" }
117
+ # open_user_subscription POST /users/:user_id/subscriptions/:id/unsubscribe(.:format)
118
+ # { controller:"activity_notification/subscriptions", action:"unsubscribe", target_type:"users" }
119
+ # open_user_subscription POST /users/:user_id/subscriptions/:id/subscribe_to_email(.:format)
120
+ # { controller:"activity_notification/subscriptions", action:"subscribe_to_email", target_type:"users" }
121
+ # open_user_subscription POST /users/:user_id/subscriptions/:id/unsubscribe_to_email(.:format)
122
+ # { controller:"activity_notification/subscriptions", action:"unsubscribe_to_email", target_type:"users" }
123
+ #
124
+ # When you use devise authentication and you want make subscription targets assciated with devise,
125
+ # you can create as follows in your routes:
126
+ # notify_to :users, with_devise: :users
127
+ # This with_devise option creates the needed routes assciated with devise authentication:
128
+ # # Subscription with devise routes
129
+ # user_subscriptions GET /users/:user_id/subscriptions(.:format)
130
+ # { controller:"activity_notification/subscriptions_with_devise", action:"index", target_type:"users", devise_type:"users" }
131
+ # user_subscription GET /users/:user_id/subscriptions/:id(.:format)
132
+ # { controller:"activity_notification/subscriptions_with_devise", action:"show", target_type:"users", devise_type:"users" }
133
+ # open_all_user_subscriptions POST /users/:user_id/subscriptions(.:format)
134
+ # { controller:"activity_notification/subscriptions_with_devise", action:"create", target_type:"users", devise_type:"users" }
135
+ # user_subscription DELETE /users/:user_id/subscriptions/:id(.:format)
136
+ # { controller:"activity_notification/subscriptions_with_devise", action:"destroy", target_type:"users", devise_type:"users" }
137
+ # open_user_subscription POST /users/:user_id/subscriptions/:id/subscribe(.:format)
138
+ # { controller:"activity_notification/subscriptions_with_devise", action:"subscribe", target_type:"users", devise_type:"users" }
139
+ # open_user_subscription POST /users/:user_id/subscriptions/:id/unsubscribe(.:format)
140
+ # { controller:"activity_notification/subscriptions_with_devise", action:"unsubscribe", target_type:"users", devise_type:"users" }
141
+ # open_user_subscription POST /users/:user_id/subscriptions/:id/subscribe_to_email(.:format)
142
+ # { controller:"activity_notification/subscriptions_with_devise", action:"subscribe_to_email", target_type:"users", devise_type:"users" }
143
+ # open_user_subscription POST /users/:user_id/subscriptions/:id/unsubscribe_to_email(.:format)
144
+ # { controller:"activity_notification/subscriptions_with_devise", action:"unsubscribe_to_email", target_type:"users", devise_type:"users" }
145
+ #
146
+ # @example Define subscribed_by in config/routes.rb
147
+ # subscribed_by :users
148
+ # @example Define subscribed_by with options
149
+ # subscribed_by :users, except: [:index, :show]
150
+ # @example Integrated with Devise authentication
151
+ # subscribed_by :users, with_devise: :users
152
+ #
153
+ # @overload subscribed_by(*resources, *options)
154
+ # @param [Symbol] resources Resources to notify
155
+ # @option options [Symbol] :with_devise (false) Devise resources name for devise integration. Devise integration will be enabled by this option.
156
+ # @option options [String] :model (:subscriptions) Model name of subscriptions
157
+ # @option options [String] :controller ("activity_notification/subscriptions" | activity_notification/subscriptions_with_devise") :controller option as resources routing
158
+ # @option options [Symbol] :as (nil) :as option as resources routing
159
+ # @option options [Array] :only (nil) :only option as resources routing
160
+ # @option options [Array] :except (nil) :except option as resources routing
161
+ # @return [ActionDispatch::Routing::Mapper] Routing mapper instance
162
+ def subscribed_by(*resources)
163
+ options = create_options(:subscriptions, resources.extract_options!, [:new, :edit, :update])
164
+
165
+ resources.each do |target|
166
+ self.resources target, only: :none do
167
+ options[:defaults] = { target_type: target.to_s }.merge(options[:devise_defaults])
168
+ resources_options = options.select { |key, _| [:with_devise, :model, :devise_defaults].exclude? key }
169
+ self.resources options[:model], resources_options do
170
+ member do
171
+ post :subscribe unless ignore_path?(:subscribe, options)
172
+ post :unsubscribe unless ignore_path?(:unsubscribe, options)
173
+ post :subscribe_to_email unless ignore_path?(:subscribe_to_email, options)
174
+ post :unsubscribe_to_email unless ignore_path?(:unsubscribe_to_email, options)
175
+ end
176
+ end
177
+ end
178
+ end
179
+
180
+ self
181
+ end
182
+
183
+
98
184
  private
99
185
 
186
+ # Check whether action path is ignored by :except or :only options
187
+ # @api private
188
+ # @return [Boolean] Whether action path is ignored
100
189
  def ignore_path?(action, options)
101
- return true if options[:except].present? and options[:except].include?(action)
102
- return true if options[:only].present? and not options[:only].include?(action)
190
+ options[:except].present? && options[:except].include?(action) and return true
191
+ options[:only].present? && !options[:only].include?(action) and return true
103
192
  false
104
193
  end
105
194
 
195
+ # Create options fo routing
196
+ # @api private
197
+ # @param [Symbol] resource Name of the resource model
198
+ # @return [Boolean] Whether action path is ignored
199
+ def create_options(resource, options = {}, except_actions = [])
200
+ #TODO check resources if it includes target module
201
+ resources_name = resource.to_s.pluralize.underscore
202
+ options[:model] ||= resources_name.to_sym
203
+ if options[:with_devise].present?
204
+ options[:controller] ||= "activity_notification/#{resources_name}_with_devise"
205
+ options[:as] ||= resources_name
206
+ #TODO check devise configuration in model
207
+ options[:devise_defaults] = { devise_type: options[:with_devise].to_s }
208
+ else
209
+ options[:controller] ||= "activity_notification/#{resources_name}"
210
+ options[:devise_defaults] = {}
211
+ end
212
+ (options[:except] ||= []).concat(except_actions)
213
+ if options[:with_subscription].present?
214
+ options[:subscription_option] = (options[:with_subscription].is_a?(Hash) ? options[:with_subscription] : {})
215
+ .merge(with_devise: options[:with_devise])
216
+ end
217
+ #TODO other options like :as, :path_prefix, :path_names ...
218
+ options
219
+ end
220
+
106
221
  end
107
222
  end
@@ -120,7 +120,6 @@ module ActivityNotification
120
120
  # == Variables in templates
121
121
  #
122
122
  # From within a template there are three variables at your disposal:
123
- #
124
123
  # * notification
125
124
  # * controller
126
125
  # * parameters [converted into a HashWithIndifferentAccess]
@@ -136,7 +135,7 @@ module ActivityNotification
136
135
  # @param [Hash] params Parameters for rendering notifications
137
136
  # @option params [String, Symbol] :target (nil) Target type name to find template or i18n text
138
137
  # @option params [String] :partial_root ("activity_notification/notifications/#{target}", controller.target_view_path, 'activity_notification/notifications/default') Partial template name
139
- # @option params [String] :partial (self.key.gsub('.', '/')) Root path of partial template
138
+ # @option params [String] :partial (self.key.tr('.', '/')) Root path of partial template
140
139
  # @option params [String] :layout (nil) Layout template name
141
140
  # @option params [String] :layout_root ('layouts') Root path of layout template
142
141
  # @option params [String, Symbol] :fallback (nil) Fallback template to use when MissingTemplate is raised. Set :text to use i18n text as fallback.
@@ -172,9 +171,9 @@ module ActivityNotification
172
171
  def partial_path(path = nil, root = nil, target = nil)
173
172
  controller = ActivityNotification.get_controller if ActivityNotification.respond_to?(:get_controller)
174
173
  root ||= "activity_notification/notifications/#{target}" if target.present?
175
- root ||= controller.target_view_path if controller.present? and controller.respond_to?(:target_view_path)
174
+ root ||= controller.target_view_path if controller.present? && controller.respond_to?(:target_view_path)
176
175
  root ||= 'activity_notification/notifications/default'
177
- path ||= self.key.gsub('.', '/')
176
+ path ||= self.key.tr('.', '/')
178
177
  select_path(path, root)
179
178
  end
180
179
 
@@ -39,6 +39,17 @@ module ActivityNotification
39
39
  # acts_as_notifiable :users, targets: User.all, group: :article
40
40
  # end
41
41
  #
42
+ # * :group_expiry_delay
43
+ # * Expiry period of a notification group.
44
+ # Notifications will be bundled within the group expiry period.
45
+ # This parameter is a optional.
46
+ # @example All *unopened* notifications to the same target within 1 day will be grouped by `article`
47
+ # # app/models/comment.rb
48
+ # class Comment < ActiveRecord::Base
49
+ # belongs_to :article
50
+ # acts_as_notifiable :users, targets: User.all, group: :article, :group_expiry_delay: 1.day
51
+ # end
52
+ #
42
53
  # * :notifier
43
54
  # * Notifier of the notification.
44
55
  # This will be stored as notifier with notification record.
@@ -119,6 +130,7 @@ module ActivityNotification
119
130
  # @param [Hash] options Options for notifiable model configuration
120
131
  # @option options [Symbol, Proc, Array] :targets (nil) Targets to send notifications
121
132
  # @option options [Symbol, Proc, Object] :group (nil) Group unit of the notifications
133
+ # @option options [Symbol, Proc, Object] :group_expiry_delay (nil) Expiry period of a notification group
122
134
  # @option options [Symbol, Proc, Object] :notifier (nil) Notifier of the notifications
123
135
  # @option options [Symbol, Proc, Hash] :parameters ({}) Additional parameters of the notifications
124
136
  # @option options [Symbol, Proc, Boolean] :email_allowed (ActivityNotification.config.email_enabled) Whether activity_notification sends notification email
@@ -137,7 +149,7 @@ module ActivityNotification
137
149
  end
138
150
 
139
151
  options[:printable_notifiable_name] ||= options.delete(:printable_name)
140
- set_acts_as_parameters_for_target(target_type, [:targets, :group, :parameters, :email_allowed], options, "notification_")
152
+ set_acts_as_parameters_for_target(target_type, [:targets, :group, :group_expiry_delay, :parameters, :email_allowed], options, "notification_")
141
153
  .merge set_acts_as_parameters_for_target(target_type, [:notifier, :notifiable_path, :printable_notifiable_name], options)
142
154
  end
143
155
 
@@ -146,6 +158,7 @@ module ActivityNotification
146
158
  def available_notifiable_options
147
159
  [ :targets,
148
160
  :group,
161
+ :group_expiry_delay,
149
162
  :notifier,
150
163
  :parameters,
151
164
  :email_allowed,
@@ -85,26 +85,29 @@ module ActivityNotification
85
85
  # end
86
86
  #
87
87
  # @param [Hash] options Options for notifiable model configuration
88
- # @option options [Symbol, Proc, String] :email (nil) Email address to send notification email
89
- # @option options [Symbol, Proc, Boolean] :email_allowed (ActivityNotification.config.email_enabled) Whether activity_notification sends notification email to this target
90
- # @option options [Symbol, Proc, Boolean] :email_allowed (ActivityNotification.config.email_enabled) Whether activity_notification sends batch notification email to this target
91
- # @option options [Symbol, Proc, Object] :devise_resource (nil) Integrated resource with devise authentication
92
- # @option options [Symbol, Proc, String] :printable_name (ActivityNotification::Common.printable_name) Printable notification target name
88
+ # @option options [Symbol, Proc, String] :email (nil) Email address to send notification email
89
+ # @option options [Symbol, Proc, Boolean] :email_allowed (ActivityNotification.config.email_enabled) Whether activity_notification sends notification email to this target
90
+ # @option options [Symbol, Proc, Boolean] :batch_email_allowed (ActivityNotification.config.email_enabled) Whether activity_notification sends batch notification email to this target
91
+ # @option options [Symbol, Proc, Boolean] :subscription_allowed (ActivityNotification.config.subscription_enabled) Whether activity_notification manages subscriptions of this target
92
+ # @option options [Symbol, Proc, Object] :devise_resource (nil) Integrated resource with devise authentication
93
+ # @option options [Symbol, Proc, String] :printable_name (ActivityNotification::Common.printable_name) Printable notification target name
93
94
  # @return [Hash] Configured parameters as target model
94
95
  def acts_as_target(options = {})
95
96
  include Target
96
97
 
97
98
  options[:printable_notification_target_name] ||= options.delete(:printable_name)
98
99
  options[:batch_notification_email_allowed] ||= options.delete(:batch_email_allowed)
99
- set_acts_as_parameters([:email, :email_allowed, :devise_resource], options, "notification_")
100
- .merge set_acts_as_parameters([:batch_notification_email_allowed, :printable_notification_target_name], options)
100
+ acts_as_params = set_acts_as_parameters([:email, :email_allowed, :subscription_allowed, :devise_resource], options, "notification_")
101
+ .merge set_acts_as_parameters([:batch_notification_email_allowed, :printable_notification_target_name], options)
102
+ include Subscriber if subscription_enabled?
103
+ acts_as_params
101
104
  end
102
105
  alias_method :acts_as_notification_target, :acts_as_target
103
106
 
104
107
  # Returns array of available target options in acts_as_target.
105
108
  # @return [Array<Symbol>] Array of available target options
106
109
  def available_target_options
107
- [:email, :email_allowed, :batch_email_allowed, :devise_resource, :printable_notification_target_name, :printable_name].freeze
110
+ [:email, :email_allowed, :batch_email_allowed, :subscription_allowed, :devise_resource, :printable_notification_target_name, :printable_name].freeze
108
111
  end
109
112
  end
110
113
  end
@@ -1,3 +1,3 @@
1
1
  module ActivityNotification
2
- VERSION = "1.0.2"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -6,7 +6,7 @@ module ActivityNotification
6
6
  # @example Run controller generator for users as target
7
7
  # rails generate activity_notification:controllers users
8
8
  class ControllersGenerator < Rails::Generators::Base
9
- CONTROLLERS = ['notifications', 'notifications_with_devise'].freeze
9
+ CONTROLLERS = ['notifications', 'notifications_with_devise', 'subscriptions', 'subscriptions_with_devise'].freeze
10
10
 
11
11
  desc <<-DESC.strip_heredoc
12
12
  Create inherited ActivityNotification controllers in your app/controllers folder.
@@ -15,7 +15,7 @@ module ActivityNotification
15
15
  If you do no specify a controller, all controllers will be created.
16
16
  For example:
17
17
 
18
- rails generate activity_notification:controllers users -c=notifications
18
+ rails generate activity_notification:controllers users -c notifications
19
19
 
20
20
  This will create a controller class at app/controllers/users/notifications_controller.rb like this:
21
21