activity_notification 1.0.2 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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