activity_notification 1.4.4 → 2.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (271) hide show
  1. checksums.yaml +5 -5
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +22 -0
  3. data/.github/ISSUE_TEMPLATE/feature_request.md +17 -0
  4. data/.github/pull_request_template.md +13 -0
  5. data/.github/workflows/build.yml +116 -0
  6. data/.gitignore +15 -3
  7. data/CHANGELOG.md +200 -1
  8. data/Gemfile +17 -2
  9. data/Procfile +2 -0
  10. data/README.md +168 -1033
  11. data/Rakefile +19 -10
  12. data/activity_notification.gemspec +14 -9
  13. data/app/channels/activity_notification/notification_api_channel.rb +12 -0
  14. data/app/channels/activity_notification/notification_api_with_devise_channel.rb +46 -0
  15. data/app/channels/activity_notification/notification_channel.rb +37 -0
  16. data/app/channels/activity_notification/notification_with_devise_channel.rb +51 -0
  17. data/app/controllers/activity_notification/apidocs_controller.rb +75 -0
  18. data/app/controllers/activity_notification/notifications_api_controller.rb +143 -0
  19. data/app/controllers/activity_notification/notifications_api_with_devise_controller.rb +7 -0
  20. data/app/controllers/activity_notification/notifications_controller.rb +60 -54
  21. data/app/controllers/activity_notification/subscriptions_api_controller.rb +197 -0
  22. data/app/controllers/activity_notification/subscriptions_api_with_devise_controller.rb +7 -0
  23. data/app/controllers/activity_notification/subscriptions_controller.rb +83 -73
  24. data/app/jobs/activity_notification/notify_all_job.rb +25 -0
  25. data/app/jobs/activity_notification/notify_job.rb +26 -0
  26. data/app/jobs/activity_notification/notify_to_job.rb +25 -0
  27. data/app/views/activity_notification/notifications/default/_default.html.erb +23 -23
  28. data/app/views/activity_notification/notifications/default/_default_without_grouping.html.erb +19 -19
  29. data/app/views/activity_notification/notifications/default/_index.html.erb +3 -3
  30. data/app/views/activity_notification/notifications/default/index.html.erb +60 -7
  31. data/app/views/activity_notification/notifications/default/open.js.erb +2 -2
  32. data/app/views/activity_notification/notifications/default/open_all.js.erb +2 -2
  33. data/app/views/activity_notification/notifications/default/show.html.erb +2 -2
  34. data/app/views/activity_notification/optional_targets/default/action_cable_channel/_default.html.erb +176 -0
  35. data/app/views/activity_notification/optional_targets/default/base/_default.text.erb +1 -1
  36. data/app/views/activity_notification/optional_targets/default/slack/_default.text.erb +1 -1
  37. data/app/views/activity_notification/subscriptions/default/_form.html.erb +2 -2
  38. data/app/views/activity_notification/subscriptions/default/_notification_keys.html.erb +5 -33
  39. data/app/views/activity_notification/subscriptions/default/_subscription.html.erb +8 -8
  40. data/app/views/activity_notification/subscriptions/default/index.html.erb +13 -9
  41. data/app/views/activity_notification/subscriptions/default/show.html.erb +3 -3
  42. data/app/views/activity_notification/subscriptions/default/subscribe.js.erb +1 -1
  43. data/app/views/activity_notification/subscriptions/default/subscribe_to_email.js.erb +1 -1
  44. data/app/views/activity_notification/subscriptions/default/subscribe_to_optional_target.js.erb +1 -1
  45. data/app/views/activity_notification/subscriptions/default/unsubscribe.js.erb +1 -1
  46. data/app/views/activity_notification/subscriptions/default/unsubscribe_to_email.js.erb +1 -1
  47. data/app/views/activity_notification/subscriptions/default/unsubscribe_to_optional_target.js.erb +1 -1
  48. data/bin/_dynamodblocal +4 -0
  49. data/bin/bundle_update.sh +7 -0
  50. data/bin/deploy_on_heroku.sh +16 -0
  51. data/bin/install_dynamodblocal.sh +5 -0
  52. data/bin/start_dynamodblocal.sh +47 -0
  53. data/bin/stop_dynamodblocal.sh +34 -0
  54. data/docs/CODE_OF_CONDUCT.md +76 -0
  55. data/docs/CONTRIBUTING.md +36 -0
  56. data/docs/Functions.md +1146 -0
  57. data/docs/Setup.md +817 -0
  58. data/docs/Testing.md +148 -0
  59. data/gemfiles/Gemfile.rails-5.0 +8 -1
  60. data/gemfiles/Gemfile.rails-5.1 +7 -1
  61. data/gemfiles/Gemfile.rails-5.2 +24 -0
  62. data/gemfiles/Gemfile.rails-6.0 +23 -0
  63. data/gemfiles/Gemfile.rails-6.1 +22 -0
  64. data/gemfiles/Gemfile.rails-7.0 +25 -0
  65. data/lib/activity_notification/apis/notification_api.rb +356 -159
  66. data/lib/activity_notification/apis/subscription_api.rb +98 -59
  67. data/lib/activity_notification/apis/swagger.rb +6 -0
  68. data/lib/activity_notification/common.rb +18 -7
  69. data/lib/activity_notification/config.rb +176 -30
  70. data/lib/activity_notification/controllers/common_api_controller.rb +30 -0
  71. data/lib/activity_notification/controllers/common_controller.rb +47 -27
  72. data/lib/activity_notification/controllers/concerns/swagger/error_responses.rb +55 -0
  73. data/lib/activity_notification/controllers/concerns/swagger/notifications_api.rb +273 -0
  74. data/lib/activity_notification/controllers/concerns/swagger/notifications_parameters.rb +92 -0
  75. data/lib/activity_notification/controllers/concerns/swagger/subscriptions_api.rb +405 -0
  76. data/lib/activity_notification/controllers/concerns/swagger/subscriptions_parameters.rb +50 -0
  77. data/lib/activity_notification/controllers/devise_authentication_controller.rb +22 -5
  78. data/lib/activity_notification/gem_version.rb +14 -0
  79. data/lib/activity_notification/helpers/errors.rb +6 -0
  80. data/lib/activity_notification/helpers/view_helpers.rb +118 -28
  81. data/lib/activity_notification/mailers/helpers.rb +19 -12
  82. data/lib/activity_notification/models/concerns/notifiable.rb +142 -55
  83. data/lib/activity_notification/models/concerns/subscriber.rb +28 -13
  84. data/lib/activity_notification/models/concerns/swagger/error_schema.rb +36 -0
  85. data/lib/activity_notification/models/concerns/swagger/notification_schema.rb +209 -0
  86. data/lib/activity_notification/models/concerns/swagger/subscription_schema.rb +162 -0
  87. data/lib/activity_notification/models/concerns/target.rb +131 -32
  88. data/lib/activity_notification/models/notification.rb +1 -0
  89. data/lib/activity_notification/models/subscription.rb +1 -0
  90. data/lib/activity_notification/models.rb +23 -1
  91. data/lib/activity_notification/optional_targets/action_cable_api_channel.rb +69 -0
  92. data/lib/activity_notification/optional_targets/action_cable_channel.rb +68 -0
  93. data/lib/activity_notification/optional_targets/base.rb +9 -15
  94. data/lib/activity_notification/orm/active_record/notification.rb +23 -34
  95. data/lib/activity_notification/orm/active_record/subscription.rb +1 -1
  96. data/lib/activity_notification/orm/active_record.rb +1 -1
  97. data/lib/activity_notification/orm/dynamoid/extension.rb +262 -0
  98. data/lib/activity_notification/orm/dynamoid/notification.rb +224 -0
  99. data/lib/activity_notification/orm/dynamoid/subscription.rb +82 -0
  100. data/lib/activity_notification/orm/dynamoid.rb +530 -0
  101. data/lib/activity_notification/orm/mongoid/notification.rb +29 -28
  102. data/lib/activity_notification/orm/mongoid/subscription.rb +3 -3
  103. data/lib/activity_notification/orm/mongoid.rb +33 -1
  104. data/lib/activity_notification/rails/routes.rb +273 -60
  105. data/lib/activity_notification/renderable.rb +22 -7
  106. data/lib/activity_notification/roles/acts_as_notifiable.rb +64 -1
  107. data/lib/activity_notification/roles/acts_as_target.rb +99 -9
  108. data/lib/activity_notification/version.rb +1 -1
  109. data/lib/activity_notification.rb +14 -0
  110. data/lib/generators/activity_notification/controllers_generator.rb +2 -1
  111. data/lib/generators/templates/activity_notification.rb +61 -7
  112. data/lib/generators/templates/controllers/README +2 -2
  113. data/lib/generators/templates/controllers/notifications_api_controller.rb +31 -0
  114. data/lib/generators/templates/controllers/notifications_api_with_devise_controller.rb +31 -0
  115. data/lib/generators/templates/controllers/notifications_controller.rb +1 -37
  116. data/lib/generators/templates/controllers/notifications_with_devise_controller.rb +1 -45
  117. data/lib/generators/templates/controllers/subscriptions_api_controller.rb +61 -0
  118. data/lib/generators/templates/controllers/subscriptions_api_with_devise_controller.rb +61 -0
  119. data/lib/generators/templates/controllers/subscriptions_controller.rb +14 -37
  120. data/lib/generators/templates/controllers/subscriptions_with_devise_controller.rb +14 -45
  121. data/lib/generators/templates/migrations/migration.rb +5 -5
  122. data/lib/generators/templates/models/README +8 -4
  123. data/lib/generators/templates/models/notification.rb +1 -1
  124. data/lib/generators/templates/models/subscription.rb +1 -1
  125. data/lib/tasks/activity_notification_tasks.rake +14 -4
  126. data/package.json +8 -0
  127. data/spec/channels/notification_api_channel_shared_examples.rb +59 -0
  128. data/spec/channels/notification_api_channel_spec.rb +49 -0
  129. data/spec/channels/notification_api_with_devise_channel_spec.rb +76 -0
  130. data/spec/channels/notification_channel_shared_examples.rb +59 -0
  131. data/spec/channels/notification_channel_spec.rb +48 -0
  132. data/spec/channels/notification_with_devise_channel_spec.rb +97 -0
  133. data/spec/concerns/apis/notification_api_spec.rb +177 -12
  134. data/spec/concerns/apis/subscription_api_spec.rb +146 -4
  135. data/spec/concerns/common_spec.rb +25 -3
  136. data/spec/concerns/models/notifiable_spec.rb +161 -11
  137. data/spec/concerns/models/subscriber_spec.rb +253 -79
  138. data/spec/concerns/models/target_spec.rb +180 -47
  139. data/spec/concerns/renderable_spec.rb +35 -16
  140. data/spec/config_spec.rb +52 -1
  141. data/spec/controllers/controller_spec_utility.rb +100 -0
  142. data/spec/controllers/notifications_api_controller_shared_examples.rb +506 -0
  143. data/spec/controllers/notifications_api_controller_spec.rb +19 -0
  144. data/spec/controllers/notifications_api_with_devise_controller_spec.rb +60 -0
  145. data/spec/controllers/notifications_controller_shared_examples.rb +55 -76
  146. data/spec/controllers/notifications_controller_spec.rb +1 -2
  147. data/spec/controllers/notifications_with_devise_controller_spec.rb +14 -8
  148. data/spec/controllers/subscriptions_api_controller_shared_examples.rb +750 -0
  149. data/spec/controllers/subscriptions_api_controller_spec.rb +19 -0
  150. data/spec/controllers/subscriptions_api_with_devise_controller_spec.rb +60 -0
  151. data/spec/controllers/subscriptions_controller_shared_examples.rb +99 -121
  152. data/spec/controllers/subscriptions_controller_spec.rb +1 -2
  153. data/spec/controllers/subscriptions_with_devise_controller_spec.rb +14 -8
  154. data/spec/factories/notifications.rb +1 -1
  155. data/spec/factories/subscriptions.rb +3 -3
  156. data/spec/factories/users.rb +3 -3
  157. data/spec/generators/migration/migration_generator_spec.rb +29 -4
  158. data/spec/helpers/view_helpers_spec.rb +31 -21
  159. data/spec/jobs/notify_all_job_spec.rb +23 -0
  160. data/spec/jobs/notify_job_spec.rb +23 -0
  161. data/spec/jobs/notify_to_job_spec.rb +23 -0
  162. data/spec/mailers/mailer_spec.rb +42 -1
  163. data/spec/models/dummy/dummy_group_spec.rb +4 -0
  164. data/spec/models/dummy/dummy_notifiable_spec.rb +4 -0
  165. data/spec/models/dummy/dummy_notifier_spec.rb +4 -0
  166. data/spec/models/dummy/dummy_subscriber_spec.rb +3 -0
  167. data/spec/models/dummy/dummy_target_spec.rb +4 -0
  168. data/spec/models/notification_spec.rb +181 -45
  169. data/spec/models/subscription_spec.rb +77 -27
  170. data/spec/optional_targets/action_cable_api_channel_spec.rb +34 -0
  171. data/spec/optional_targets/action_cable_channel_spec.rb +41 -0
  172. data/spec/optional_targets/amazon_sns_spec.rb +0 -2
  173. data/spec/optional_targets/slack_spec.rb +0 -2
  174. data/spec/orm/dynamoid_spec.rb +115 -0
  175. data/spec/rails_app/Rakefile +9 -0
  176. data/spec/rails_app/app/assets/config/manifest.js +3 -0
  177. data/spec/rails_app/app/assets/javascripts/application.js +2 -1
  178. data/spec/rails_app/app/assets/javascripts/cable.js +12 -0
  179. data/spec/rails_app/app/controllers/admins_controller.rb +21 -0
  180. data/spec/rails_app/app/controllers/application_controller.rb +1 -1
  181. data/spec/rails_app/app/controllers/articles_controller.rb +6 -1
  182. data/spec/rails_app/app/controllers/comments_controller.rb +3 -1
  183. data/spec/rails_app/app/controllers/spa_controller.rb +7 -0
  184. data/spec/rails_app/app/controllers/users/notifications_controller.rb +0 -65
  185. data/spec/rails_app/app/controllers/users/notifications_with_devise_controller.rb +0 -73
  186. data/spec/rails_app/app/controllers/users/subscriptions_controller.rb +0 -77
  187. data/spec/rails_app/app/controllers/users/subscriptions_with_devise_controller.rb +0 -85
  188. data/spec/rails_app/app/controllers/users_controller.rb +26 -0
  189. data/spec/rails_app/app/javascript/App.vue +40 -0
  190. data/spec/rails_app/app/javascript/components/DeviseTokenAuth.vue +82 -0
  191. data/spec/rails_app/app/javascript/components/Top.vue +98 -0
  192. data/spec/rails_app/app/javascript/components/notifications/Index.vue +200 -0
  193. data/spec/rails_app/app/javascript/components/notifications/Notification.vue +133 -0
  194. data/spec/rails_app/app/javascript/components/notifications/NotificationContent.vue +122 -0
  195. data/spec/rails_app/app/javascript/components/subscriptions/Index.vue +279 -0
  196. data/spec/rails_app/app/javascript/components/subscriptions/NewSubscription.vue +112 -0
  197. data/spec/rails_app/app/javascript/components/subscriptions/NotificationKey.vue +141 -0
  198. data/spec/rails_app/app/javascript/components/subscriptions/Subscription.vue +226 -0
  199. data/spec/rails_app/app/javascript/config/development.js +5 -0
  200. data/spec/rails_app/app/javascript/config/environment.js +7 -0
  201. data/spec/rails_app/app/javascript/config/production.js +5 -0
  202. data/spec/rails_app/app/javascript/config/test.js +5 -0
  203. data/spec/rails_app/app/javascript/packs/application.js +18 -0
  204. data/spec/rails_app/app/javascript/packs/spa.js +14 -0
  205. data/spec/rails_app/app/javascript/router/index.js +73 -0
  206. data/spec/rails_app/app/javascript/store/index.js +37 -0
  207. data/spec/rails_app/app/models/admin.rb +15 -10
  208. data/spec/rails_app/app/models/article.rb +25 -20
  209. data/spec/rails_app/app/models/comment.rb +27 -62
  210. data/spec/rails_app/app/models/dummy/dummy_base.rb +1 -0
  211. data/spec/rails_app/app/models/dummy/dummy_group.rb +9 -0
  212. data/spec/rails_app/app/models/dummy/dummy_notifiable.rb +1 -0
  213. data/spec/rails_app/app/models/dummy/dummy_notifiable_target.rb +27 -0
  214. data/spec/rails_app/app/models/dummy/dummy_notifier.rb +1 -0
  215. data/spec/rails_app/app/models/dummy/dummy_subscriber.rb +1 -0
  216. data/spec/rails_app/app/models/dummy/dummy_target.rb +1 -0
  217. data/spec/rails_app/app/models/user.rb +44 -18
  218. data/spec/rails_app/app/views/activity_notification/notifications/default/article/_update.html.erb +146 -0
  219. data/spec/rails_app/app/views/activity_notification/notifications/users/overridden/custom/_test.html.erb +1 -0
  220. data/spec/rails_app/app/views/activity_notification/optional_targets/admins/amazon_sns/comment/_default.text.erb +1 -1
  221. data/spec/rails_app/app/views/articles/index.html.erb +68 -20
  222. data/spec/rails_app/app/views/articles/show.html.erb +1 -1
  223. data/spec/rails_app/app/views/layouts/_header.html.erb +9 -3
  224. data/spec/rails_app/app/views/spa/index.html.erb +2 -0
  225. data/spec/rails_app/babel.config.js +72 -0
  226. data/spec/rails_app/bin/webpack +18 -0
  227. data/spec/rails_app/bin/webpack-dev-server +18 -0
  228. data/spec/rails_app/config/application.rb +26 -6
  229. data/spec/rails_app/config/cable.yml +8 -0
  230. data/spec/rails_app/config/database.yml +1 -1
  231. data/spec/rails_app/config/dynamoid.rb +13 -0
  232. data/spec/rails_app/config/environment.rb +5 -1
  233. data/spec/rails_app/config/environments/development.rb +5 -0
  234. data/spec/rails_app/config/environments/production.rb +7 -1
  235. data/spec/rails_app/config/environments/test.rb +7 -11
  236. data/spec/rails_app/config/initializers/activity_notification.rb +63 -9
  237. data/spec/rails_app/config/initializers/copy_it.aws.rb.template +6 -0
  238. data/spec/rails_app/config/initializers/devise_token_auth.rb +55 -0
  239. data/spec/rails_app/config/initializers/mysql.rb +9 -0
  240. data/spec/rails_app/config/locales/activity_notification.en.yml +10 -4
  241. data/spec/rails_app/config/routes.rb +42 -1
  242. data/spec/rails_app/config/webpack/development.js +5 -0
  243. data/spec/rails_app/config/webpack/environment.js +7 -0
  244. data/spec/rails_app/config/webpack/loaders/vue.js +6 -0
  245. data/spec/rails_app/config/webpack/production.js +5 -0
  246. data/spec/rails_app/config/webpack/test.js +5 -0
  247. data/spec/rails_app/config/webpacker.yml +97 -0
  248. data/spec/rails_app/db/migrate/{20160715050433_create_test_tables.rb → 20160716000000_create_test_tables.rb} +1 -1
  249. data/spec/rails_app/db/migrate/{20160715050420_create_activity_notification_tables.rb → 20181209000000_create_activity_notification_tables.rb} +3 -3
  250. data/spec/rails_app/db/migrate/20191201000000_add_tokens_to_users.rb +10 -0
  251. data/spec/rails_app/db/schema.rb +46 -43
  252. data/spec/rails_app/db/seeds.rb +28 -4
  253. data/spec/rails_app/lib/custom_optional_targets/raise_error.rb +14 -0
  254. data/spec/rails_app/lib/mailer_previews/mailer_preview.rb +14 -4
  255. data/spec/rails_app/package.json +23 -0
  256. data/spec/rails_app/postcss.config.js +12 -0
  257. data/spec/roles/acts_as_group_spec.rb +0 -2
  258. data/spec/roles/acts_as_notifiable_spec.rb +80 -20
  259. data/spec/roles/acts_as_notifier_spec.rb +0 -2
  260. data/spec/roles/acts_as_target_spec.rb +1 -5
  261. data/spec/spec_helper.rb +13 -11
  262. data/spec/version_spec.rb +31 -0
  263. metadata +306 -53
  264. data/.travis.yml +0 -85
  265. data/Gemfile.lock +0 -234
  266. data/gemfiles/Gemfile.rails-4.2 +0 -17
  267. data/gemfiles/Gemfile.rails-4.2.lock +0 -225
  268. data/gemfiles/Gemfile.rails-5.0.lock +0 -234
  269. data/gemfiles/Gemfile.rails-5.1.lock +0 -234
  270. data/spec/rails_app/app/views/activity_notification/notifications/users/overriden/custom/_test.html.erb +0 -1
  271. /data/spec/rails_app/app/{models → assets/images}/.keep +0 -0
data/docs/Setup.md ADDED
@@ -0,0 +1,817 @@
1
+ ## Setup
2
+
3
+ ### Gem installation
4
+
5
+ You can install *activity_notification* as you would any other gem:
6
+
7
+ ```console
8
+ $ gem install activity_notification
9
+ ```
10
+ or in your Gemfile:
11
+
12
+ ```ruby
13
+ gem 'activity_notification'
14
+ ```
15
+
16
+ After you install *activity_notification* and add it to your Gemfile, you need to run the generator:
17
+
18
+ ```console
19
+ $ bin/rails generate activity_notification:install
20
+ ```
21
+
22
+ The generator will install an initializer which describes all configuration options of *activity_notification*.
23
+ It also generates a i18n based translation file which we can configure the presentation of notifications.
24
+
25
+ ### Database setup
26
+
27
+ #### Using ActiveRecord ORM
28
+
29
+ When you use *activity_notification* with ActiveRecord ORM as default configuration,
30
+ create migration for notifications and migrate the database in your Rails project:
31
+
32
+ ```console
33
+ $ bin/rails generate activity_notification:migration
34
+ $ bin/rake db:migrate
35
+ ```
36
+
37
+ If you are using a different table name from *"notifications"*, change the settings in your *config/initializers/activity_notification.rb* file, e.g., if you're using the table name *"activity_notifications"* instead of the default *"notifications"*:
38
+
39
+ ```ruby
40
+ config.notification_table_name = "activity_notifications"
41
+ ```
42
+
43
+ The same can be done for the subscription table name, e.g., if you're using the table name *"notifications_subscriptions"* instead of the default *"subscriptions"*:
44
+
45
+ ```ruby
46
+ config.subscription_table_name = "notifications_subscriptions"
47
+ ```
48
+
49
+ If you're redefining `yaml_column_permitted_classes` in *config/application.rb*, then you need to add a few classes to the whitelist to make sure *activity_notification* still works as expected.
50
+
51
+ ```ruby
52
+ config.active_record.yaml_column_permitted_classes ||= []
53
+
54
+ # your override(s), e.g: MyWhitelistedClass
55
+ config.active_record.yaml_column_permitted_classes << MyWhitelistedClass
56
+
57
+ # overrides required for activity_notification to work
58
+ config.yaml_column_permitted_classes << ActiveSupport::HashWithIndifferentAccess
59
+ config.yaml_column_permitted_classes << ActiveSupport::TimeWithZone
60
+ config.yaml_column_permitted_classes << ActiveSupport::TimeZone
61
+ config.yaml_column_permitted_classes << Symbol
62
+ config.yaml_column_permitted_classes << Time
63
+ ```
64
+
65
+ #### Using Mongoid ORM
66
+
67
+ When you use *activity_notification* with [Mongoid](http://mongoid.org) ORM, set **AN_ORM** environment variable to **mongoid**:
68
+
69
+ ```console
70
+ $ export AN_ORM=mongoid
71
+ ```
72
+
73
+ You can also configure ORM in initializer **activity_notification.rb**:
74
+
75
+ ```ruby
76
+ config.orm = :mongoid
77
+ ```
78
+
79
+ You need to configure Mongoid in your Rails application for your MongoDB environment. Then, your notifications and subscriptions will be stored in your MongoDB.
80
+
81
+ #### Using Dynamoid ORM
82
+
83
+ Currently, *activity_notification* only works with Dynamoid 3.1.0.
84
+
85
+ ```ruby
86
+ gem 'dynamoid', '3.1.0'
87
+ ```
88
+
89
+ When you use *activity_notification* with [Dynamoid](https://github.com/Dynamoid/dynamoid) ORM, set **AN_ORM** environment variable to **dynamoid**:
90
+
91
+ ```console
92
+ $ export AN_ORM=dynamoid
93
+ ```
94
+
95
+ You can also configure ORM in initializer **activity_notification.rb**:
96
+
97
+ ```ruby
98
+ config.orm = :dynamoid
99
+ ```
100
+
101
+ You need to configure Dynamoid in your Rails application for your Amazon DynamoDB environment.
102
+ Then, you can use this rake task to create DynamoDB tables used by *activity_notification* with Dynamoid:
103
+
104
+ ```console
105
+ $ bin/rake activity_notification:create_dynamodb_tables
106
+ ```
107
+
108
+ After these configurations, your notifications and subscriptions will be stored in your Amazon DynamoDB.
109
+
110
+ Note: Amazon DynamoDB integration using Dynamoid ORM is only supported with Rails 5.0+.
111
+
112
+ ##### Integration with DynamoDB Streams
113
+
114
+ You can capture *activity_notification*'s table activity with [DynamoDB Streams](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Streams.html).
115
+ Using DynamoDB Streams, activity notifications in your Rails application will be integrated into cloud computing and available as event stream processed by [DynamoDB Streams Kinesis Adapter](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Streams.KCLAdapter.html) or [AWS Lambda](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Streams.Lambda.html).
116
+
117
+ When you consume your activity notifications from DynamoDB Streams, sometimes you need to process notification records with associated target, notifiable or notifier record which is stored in database of your Rails application.
118
+ In such cases, you can use **store_with_associated_records** option in initializer **activity_notification.rb**:
119
+
120
+ ```ruby
121
+ config.store_with_associated_records = true
122
+ ```
123
+
124
+ When **store_with_associated_records** is set to *false* as default, *activity_notification* stores notificaion records with association like this:
125
+
126
+ ```json
127
+ {
128
+ "id": {
129
+ "S": "f05756ef-661e-4ef5-9e99-5af51243125c"
130
+ },
131
+ "target_key": {
132
+ "S": "User#1"
133
+ },
134
+ "notifiable_key": {
135
+ "S": "Comment#2"
136
+ },
137
+ "key": {
138
+ "S": "comment.default"
139
+ },
140
+ "group_key": {
141
+ "S": "Article#1"
142
+ },
143
+ "notifier_key": {
144
+ "S": "User#2"
145
+ },
146
+ "created_at": {
147
+ "S": "2020-03-08T08:22:53+00:00"
148
+ },
149
+ "updated_at": {
150
+ "S": "2020-03-08T08:22:53+00:00"
151
+ },
152
+ "parameters": {
153
+ "M": {}
154
+ }
155
+ }
156
+ ```
157
+
158
+ When you set **store_with_associated_records** to *true*, *activity_notification* stores notificaion records including associated target, notifiable, notifier and several instance methods like this:
159
+
160
+ ```json
161
+ {
162
+ "id": {
163
+ "S": "f05756ef-661e-4ef5-9e99-5af51243125c"
164
+ },
165
+ "target_key": {
166
+ "S": "User#1"
167
+ },
168
+ "notifiable_key": {
169
+ "S": "Comment#2"
170
+ },
171
+ "key": {
172
+ "S": "comment.default"
173
+ },
174
+ "group_key": {
175
+ "S": "Article#1"
176
+ },
177
+ "notifier_key": {
178
+ "S": "User#2"
179
+ },
180
+ "created_at": {
181
+ "S": "2020-03-08T08:22:53+00:00"
182
+ },
183
+ "updated_at": {
184
+ "S": "2020-03-08T08:22:53+00:00"
185
+ },
186
+ "parameters": {
187
+ "M": {}
188
+ },
189
+ "stored_target": {
190
+ "M": {
191
+ "id": {
192
+ "N": "1"
193
+ },
194
+ "email": {
195
+ "S": "ichiro@example.com"
196
+ },
197
+ "name": {
198
+ "S": "Ichiro"
199
+ },
200
+ "created_at": {
201
+ "S": "2020-03-08T08:22:23.451Z"
202
+ },
203
+ "updated_at": {
204
+ "S": "2020-03-08T08:22:23.451Z"
205
+ },
206
+ // { ... },
207
+ "printable_type": {
208
+ "S": "User"
209
+ },
210
+ "printable_target_name": {
211
+ "S": "Ichiro"
212
+ },
213
+ }
214
+ },
215
+ "stored_notifiable": {
216
+ "M": {
217
+ "id": {
218
+ "N": "2"
219
+ },
220
+ "user_id": {
221
+ "N": "2"
222
+ },
223
+ "article_id": {
224
+ "N": "1"
225
+ },
226
+ "body": {
227
+ "S": "This is the first Stephen's comment to Ichiro's article."
228
+ },
229
+ "created_at": {
230
+ "S": "2020-03-08T08:22:47.683Z"
231
+ },
232
+ "updated_at": {
233
+ "S": "2020-03-08T08:22:47.683Z"
234
+ },
235
+ "printable_type": {
236
+ "S": "Comment"
237
+ }
238
+ }
239
+ },
240
+ "stored_notifier": {
241
+ "M": {
242
+ "id": {
243
+ "N": "2"
244
+ },
245
+ "email": {
246
+ "S": "stephen@example.com"
247
+ },
248
+ "name": {
249
+ "S": "Stephen"
250
+ },
251
+ "created_at": {
252
+ "S": "2020-03-08T08:22:23.573Z"
253
+ },
254
+ "updated_at": {
255
+ "S": "2020-03-08T08:22:23.573Z"
256
+ },
257
+ // { ... },
258
+ "printable_type": {
259
+ "S": "User"
260
+ },
261
+ "printable_notifier_name": {
262
+ "S": "Stephen"
263
+ }
264
+ }
265
+ },
266
+ "stored_group": {
267
+ "M": {
268
+ "id": {
269
+ "N": "1"
270
+ },
271
+ "user_id": {
272
+ "N": "1"
273
+ },
274
+ "title": {
275
+ "S": "Ichiro's first article"
276
+ },
277
+ "body": {
278
+ "S": "This is the first Ichiro's article. Please read it!"
279
+ },
280
+ "created_at": {
281
+ "S": "2020-03-08T08:22:23.952Z"
282
+ },
283
+ "updated_at": {
284
+ "S": "2020-03-08T08:22:23.952Z"
285
+ },
286
+ "printable_type": {
287
+ "S": "Article"
288
+ },
289
+ "printable_group_name": {
290
+ "S": "article \"Ichiro's first article\""
291
+ }
292
+ }
293
+ },
294
+ "stored_notifiable_path": {
295
+ "S": "/articles/1"
296
+ },
297
+ "stored_printable_notifiable_name": {
298
+ "S": "comment \"This is the first Stephen's comment to Ichiro's article.\""
299
+ },
300
+ "stored_group_member_notifier_count": {
301
+ "N": "2"
302
+ },
303
+ "stored_group_notification_count": {
304
+ "N": "3"
305
+ },
306
+ "stored_group_members": {
307
+ "L": [
308
+ // { ... }, { ... }, ...
309
+ ]
310
+ }
311
+ }
312
+ ```
313
+
314
+ Then, you can process notification records with associated records in your DynamoDB Streams.
315
+
316
+ Note: This **store_with_associated_records** option can be set true only when you use mongoid or dynamoid ORM.
317
+
318
+ ### Configuring models
319
+
320
+ #### Configuring target models
321
+
322
+ Configure your target model (e.g. *app/models/user.rb*).
323
+ Add **acts_as_target** configuration to your target model to get notifications.
324
+
325
+ ##### Target as an ActiveRecord model
326
+
327
+ ```ruby
328
+ class User < ActiveRecord::Base
329
+ # acts_as_target configures your model as ActivityNotification::Target
330
+ # with parameters as value or custom methods defined in your model as lambda or symbol.
331
+ # This is an example without any options (default configuration) as the target.
332
+ acts_as_target
333
+ end
334
+ ```
335
+
336
+ ##### Target as a Mongoid model
337
+
338
+ ```ruby
339
+ require 'mongoid'
340
+ class User
341
+ include Mongoid::Document
342
+ include Mongoid::Timestamps
343
+ include GlobalID::Identification
344
+
345
+ # You need include ActivityNotification::Models except models which extend ActiveRecord::Base
346
+ include ActivityNotification::Models
347
+ acts_as_target
348
+ end
349
+ ```
350
+
351
+ *Note*: *acts_as_notification_target* is an alias for *acts_as_target* and does the same.
352
+
353
+ #### Configuring notifiable models
354
+
355
+ Configure your notifiable model (e.g. *app/models/comment.rb*).
356
+ Add **acts_as_notifiable** configuration to your notifiable model representing activity to notify for each of your target model.
357
+ You have to define notification targets for all notifications from this notifiable model by *:targets* option. Other configurations are optional. *:notifiable_path* option is a path to move when the notification is opened by the target user.
358
+
359
+ ##### Notifiable as an ActiveRecord model
360
+
361
+ ```ruby
362
+ class Article < ActiveRecord::Base
363
+ belongs_to :user
364
+ has_many :comments, dependent: :destroy
365
+ has_many :commented_users, through: :comments, source: :user
366
+ end
367
+
368
+ class Comment < ActiveRecord::Base
369
+ belongs_to :article
370
+ belongs_to :user
371
+
372
+ # acts_as_notifiable configures your model as ActivityNotification::Notifiable
373
+ # with parameters as value or custom methods defined in your model as lambda or symbol.
374
+ # The first argument is the plural symbol name of your target model.
375
+ acts_as_notifiable :users,
376
+ # Notification targets as :targets is a necessary option
377
+ # Set to notify to author and users commented to the article, except comment owner self
378
+ targets: ->(comment, key) {
379
+ ([comment.article.user] + comment.article.commented_users.to_a - [comment.user]).uniq
380
+ },
381
+ # Path to move when the notification is opened by the target user
382
+ # This is an optional configuration since activity_notification uses polymorphic_path as default
383
+ notifiable_path: :article_notifiable_path
384
+
385
+ def article_notifiable_path
386
+ article_path(article)
387
+ end
388
+ end
389
+ ```
390
+
391
+ ##### Notifiable as a Mongoid model
392
+
393
+ ```ruby
394
+ require 'mongoid'
395
+ class Article
396
+ include Mongoid::Document
397
+ include Mongoid::Timestamps
398
+
399
+ belongs_to :user
400
+ has_many :comments, dependent: :destroy
401
+
402
+ def commented_users
403
+ User.where(:id.in => comments.pluck(:user_id))
404
+ end
405
+ end
406
+
407
+ require 'mongoid'
408
+ class Comment
409
+ include Mongoid::Document
410
+ include Mongoid::Timestamps
411
+ include GlobalID::Identification
412
+
413
+ # You need include ActivityNotification::Models except models which extend ActiveRecord::Base
414
+ include ActivityNotification::Models
415
+ acts_as_notifiable :users,
416
+ targets: ->(comment, key) {
417
+ ([comment.article.user] + comment.article.commented_users.to_a - [comment.user]).uniq
418
+ },
419
+ notifiable_path: :article_notifiable_path
420
+
421
+ def article_notifiable_path
422
+ article_path(article)
423
+ end
424
+ end
425
+ ```
426
+
427
+ ##### Advanced notifiable path
428
+
429
+ Sometimes it might be necessary to provide extra information in the *notifiable_path*. In those cases, passing a lambda function to the *notifiable_path* will give you the notifiable object and the notifiable key to play around with:
430
+
431
+ ```ruby
432
+ acts_as_notifiable :users,
433
+ targets: ->(comment, key) {
434
+ ([comment.article.user] + comment.article.commented_users.to_a - [comment.user]).uniq
435
+ },
436
+  notifiable_path: ->(comment, key) { "#{comment.article_notifiable_path}##{key}" }
437
+ ```
438
+
439
+ This will attach the key of the notification to the notifiable path.
440
+
441
+ ### Configuring views
442
+
443
+ *activity_notification* provides view templates to customize your notification views. The view generator can generate default views for all targets.
444
+
445
+ ```console
446
+ $ bin/rails generate activity_notification:views
447
+ ```
448
+
449
+ If you have multiple target models in your application, such as *User* and *Admin*, you will be able to have views based on the target like *notifications/users/index* and *notifications/admins/index*. If no view is found for the target, *activity_notification* will use the default view at *notifications/default/index*. You can also use the generator to generate views for the specified target:
450
+
451
+ ```console
452
+ $ bin/rails generate activity_notification:views users
453
+ ```
454
+
455
+ If you would like to generate only a few sets of views, like the ones for the *notifications* (for notification views) and *mailer* (for notification email views),
456
+ you can pass a list of modules to the generator with the *-v* flag.
457
+
458
+ ```console
459
+ $ bin/rails generate activity_notification:views -v notifications
460
+ ```
461
+
462
+ ### Configuring routes
463
+
464
+ *activity_notification* also provides routing helper for notifications. Add **notify_to** method to *config/routes.rb* for the target (e.g. *:users*):
465
+
466
+ ```ruby
467
+ Rails.application.routes.draw do
468
+ notify_to :users
469
+ end
470
+ ```
471
+
472
+ Then, you can access several pages like */users/1/notifications* and manage open/unopen of notifications using *[ActivityNotification::NotificationsController](/app/controllers/activity_notification/notifications_controller.rb)*.
473
+ If you use Devise integration and you want to configure simple default routes for authenticated users, see [Configuring simple default routes](#configuring-simple-default-routes).
474
+
475
+ #### Routes with namespaced model
476
+
477
+ It is possible to configure a target model as a submodule, e.g. if your target is `Entity::User`,
478
+ however by default the **ActivityNotification** controllers will be placed under the same namespace,
479
+ so it is mandatory to explicitly call the controllers this way
480
+
481
+ ```ruby
482
+ Rails.application.routes.draw do
483
+ notify_to :users, controller: '/activity_notification/notifications', target_type: 'entity/users'
484
+ end
485
+ ```
486
+
487
+ This will generate the necessary routes for the `Entity::User` target with parameters `:user_id`
488
+
489
+ #### Routes with scope
490
+
491
+ You can also configure *activity_notification* routes with scope like this:
492
+
493
+ ```ruby
494
+ Rails.application.routes.draw do
495
+ scope :myscope, as: :myscope do
496
+ notify_to :users, routing_scope: :myscope
497
+ end
498
+ end
499
+ ```
500
+
501
+ Then, pages are shown as */myscope/users/1/notifications*.
502
+
503
+ #### Routes as REST API backend
504
+
505
+ You can configure *activity_notification* routes as REST API backend with *api_mode* option like this:
506
+
507
+ ```ruby
508
+ Rails.application.routes.draw do
509
+ scope :api do
510
+ scope :"v2" do
511
+ notify_to :users, api_mode: true
512
+ end
513
+ end
514
+ end
515
+ ```
516
+
517
+ Then, you can call *activity_notification* REST API as */api/v2/notifications* from your frontend application. See [REST API backend](#rest-api-backend) for more details.
518
+
519
+ ### Creating notifications
520
+
521
+ #### Notification API
522
+
523
+ You can trigger notifications by setting all your required parameters and triggering **notify** on the notifiable model, like this:
524
+
525
+ ```ruby
526
+ @comment.notify :users, key: "comment.reply"
527
+ ```
528
+
529
+ Or, you can call public API as **ActivityNotification::Notification.notify**
530
+
531
+ ```ruby
532
+ ActivityNotification::Notification.notify :users, @comment, key: "comment.reply"
533
+ ```
534
+
535
+ The first argument is the plural symbol name of your target model, which is configured in notifiable model by *acts_as_notifiable*.
536
+ The new instances of **ActivityNotification::Notification** model will be generated for the specified targets.
537
+
538
+ *Hint*: *:key* is a option. Default key `#{notifiable_type}.default` which means *comment.default* will be used without specified key.
539
+ You can override it by *Notifiable#default_notification_key*.
540
+
541
+ #### Asynchronous notification API with ActiveJob
542
+
543
+ Using Notification API with default configurations, the notifications will be generated synchronously. *activity_notification* also supports **asynchronous notification API** with ActiveJob to improve application performance. You can use **notify_later** method on the notifiable model, like this:
544
+
545
+ ```ruby
546
+ @comment.notify_later :users, key: "comment.reply"
547
+ ```
548
+
549
+ You can also use *:notify_later* option in *notify* method. This is the same operation as calling *notify_later* method.
550
+
551
+ ```ruby
552
+ @comment.notify :users, key: "comment.reply", notify_later: true
553
+ ```
554
+
555
+ *Note*: *notify_now* is an alias for *notify* and does the same.
556
+
557
+ When you use asynchronous notification API, you should setup ActiveJob with background queuing service such as Sidekiq.
558
+ You can set *config.active_job_queue* in your initializer to specify a queue name *activity_notification* will use.
559
+ The default queue name is *:activity_notification*.
560
+
561
+ ```ruby
562
+ # Configure ActiveJob queue name for delayed notifications.
563
+ config.active_job_queue = :my_notification_queue
564
+ ```
565
+
566
+ #### Automatic tracked notifications
567
+
568
+ You can also generate automatic tracked notifications by **:tracked** option in *acts_as_notifiable*.
569
+ *:tracked* option adds required callbacks to generate notifications for creation and update of the notifiable model.
570
+ Set true to *:tracked* option to generate all tracked notifications, like this:
571
+
572
+ ```ruby
573
+ class Comment < ActiveRecord::Base
574
+ acts_as_notifiable :users,
575
+ targets: ->(comment, key) {
576
+ ([comment.article.user] + comment.article.commented_users.to_a - [comment.user]).uniq
577
+ },
578
+ # Set true to :tracked option to generate automatic tracked notifications.
579
+ # It adds required callbacks to generate notifications for creation and update of the notifiable model.
580
+ tracked: true
581
+ end
582
+ ```
583
+
584
+ Or, set *:only* or *:except* option to generate specified tracked notifications, like this:
585
+
586
+ ```ruby
587
+ class Comment < ActiveRecord::Base
588
+ acts_as_notifiable :users,
589
+ targets: ->(comment, key) {
590
+ ([comment.article.user] + comment.article.commented_users.to_a - [comment.user]).uniq
591
+ },
592
+ # Set { only: [:create] } to :tracked option to generate tracked notifications for creation only.
593
+ # It adds required callbacks to generate notifications for creation of the notifiable model.
594
+ tracked: { only: [:create] }
595
+ end
596
+ ```
597
+
598
+ ```ruby
599
+ class Comment < ActiveRecord::Base
600
+ acts_as_notifiable :users,
601
+ targets: ->(comment, key) {
602
+ ([comment.article.user] + comment.article.commented_users.to_a - [comment.user]).uniq
603
+ },
604
+ # Set { except: [:update] } to :tracked option to generate tracked notifications except update (creation only).
605
+ # It adds required callbacks to generate notifications for creation of the notifiable model.
606
+ tracked: { except: [:update], key: 'comment.create.now', send_later: false }
607
+ end
608
+ ```
609
+
610
+ *Hint*: `#{notifiable_type}.create` and `#{notifiable_type}.update` will be used as the key of tracked notifications.
611
+ You can override them by *Notifiable#notification_key_for_tracked_creation* and *Notifiable#notification_key_for_tracked_update*.
612
+ You can also specify key option in the *:tracked* statement.
613
+
614
+ As a default, the notifications will be generated synchronously along with model creation or update. If you want to generate notifications asynchronously, use *:notify_later* option with the *:tracked* option, like this:
615
+
616
+ ```ruby
617
+ class Comment < ActiveRecord::Base
618
+ acts_as_notifiable :users,
619
+ targets: ->(comment, key) {
620
+ ([comment.article.user] + comment.article.commented_users.to_a - [comment.user]).uniq
621
+ },
622
+ # It adds required callbacks to generate notifications asynchronously for creation of the notifiable model.
623
+ tracked: { only: [:create], key: 'comment.create.later', notify_later: true }
624
+ end
625
+ ```
626
+
627
+ ### Displaying notifications
628
+
629
+ #### Preparing target notifications
630
+
631
+ To display notifications, you can use **notifications** association of the target model:
632
+
633
+ ```ruby
634
+ # custom_notifications_controller.rb
635
+ def index
636
+ @notifications = @target.notifications
637
+ end
638
+ ```
639
+
640
+ You can also use several scope to filter notifications. For example, **unopened_only** to filter them unopened notifications only.
641
+
642
+ ```ruby
643
+ # custom_notifications_controller.rb
644
+ def index
645
+ @notifications = @target.notifications.unopened_only
646
+ end
647
+ ```
648
+
649
+ Moreover, you can use **notification_index** or **notification_index_with_attributes** methods to automatically prepare notification index for the target.
650
+
651
+ ```ruby
652
+ # custom_notifications_controller.rb
653
+ def index
654
+ @notifications = @target.notification_index_with_attributes
655
+ end
656
+ ```
657
+
658
+ #### Rendering notifications
659
+
660
+ You can use **render_notifications** helper in your views to show the notification index:
661
+
662
+ ```erb
663
+ <%= render_notifications(@notifications) %>
664
+ ```
665
+
666
+ We can set *:target* option to specify the target type of notifications:
667
+
668
+ ```erb
669
+ <%= render_notifications(@notifications, target: :users) %>
670
+ ```
671
+
672
+ *Note*: *render_notifications* is an alias for *render_notification* and does the same.
673
+
674
+ If you want to set notification index in the common layout, such as common header, you can use **render_notifications_of** helper like this:
675
+
676
+ ```shared/_header.html.erb
677
+ <%= render_notifications_of current_user, index_content: :with_attributes %>
678
+ ```
679
+
680
+ Then, content named **:notification_index** will be prepared and you can use it in your partial template.
681
+
682
+ ```activity_notifications/notifications/users/_index.html.erb
683
+ ...
684
+ <%= yield :notification_index %>
685
+ ...
686
+ ```
687
+
688
+ Sometimes, it's desirable to pass additional local variables to partials. It can be done this way:
689
+
690
+ ```erb
691
+ <%= render_notification(@notification, locals: { friends: current_user.friends }) %>
692
+ ```
693
+
694
+ #### Notification views
695
+
696
+ *activity_notification* looks for views in *app/views/activity_notification/notifications/:target* with **:key** of the notifications.
697
+
698
+ For example, if you have a notification with *:key* set to *"notification.comment.reply"* and rendered it with *:target* set to *:users*, the gem will look for a partial in *app/views/activity_notification/notifications/users/comment/_reply.html.(|erb|haml|slim|something_else)*.
699
+
700
+ *Hint*: the *"notification."* prefix in *:key* is completely optional, you can skip it in your projects or use this prefix only to make namespace.
701
+
702
+ If you would like to fallback to a partial, you can utilize the **:fallback** parameter to specify the path of a partial to use when one is missing:
703
+
704
+ ```erb
705
+ <%= render_notification(@notification, target: :users, fallback: :default) %>
706
+ ```
707
+
708
+ When used in this manner, if a partial with the specified *:key* cannot be located, it will use the partial defined in the *:fallback* instead. In the example above this would resolve to *activity_notification/notifications/users/_default.html.(|erb|haml|slim|something_else)*.
709
+
710
+ If you do not specify *:target* option like this,
711
+
712
+ ```erb
713
+ <%= render_notification(@notification, fallback: :default) %>
714
+ ```
715
+
716
+ the gem will look for a partial in *default* as the target type which means *activity_notification/notifications/default/_default.html.(|erb|haml|slim|something_else)*.
717
+
718
+ If a view file does not exist then *ActionView::MisingTemplate* will be raised. If you wish to fallback to the old behaviour and use an i18n based translation in this situation you can specify a *:fallback* parameter of *:text* to fallback to this mechanism like such:
719
+
720
+ ```erb
721
+ <%= render_notification(@notification, fallback: :text) %>
722
+ ```
723
+
724
+ Finally, default views of *activity_notification* depends on jQuery and you have to add requirements to *application.js* in your apps:
725
+
726
+ ```app/assets/javascripts/application.js
727
+ //= require jquery
728
+ //= require jquery_ujs
729
+ ```
730
+
731
+ #### i18n for notifications
732
+
733
+ Translations are used by the *#text* method, to which you can pass additional options in form of a hash. *#render* method uses translations when view templates have not been provided. You can render pure i18n strings by passing `{ i18n: true }` to *#render_notification* or *#render*.
734
+
735
+ Translations should be put in your locale *.yml* files as **text** field. To render pure strings from I18n example structure:
736
+
737
+ ```yaml
738
+ notification:
739
+ user:
740
+ article:
741
+ create:
742
+ text: 'Article has been created'
743
+ update:
744
+ text: 'Article %{article_title} has been updated'
745
+ destroy:
746
+ text: 'Some user removed an article!'
747
+ comment:
748
+ create:
749
+ text: '%{notifier_name} posted a comment on the article "%{article_title}"'
750
+ post:
751
+ text:
752
+ one: "<p>%{notifier_name} posted a comment on your article %{article_title}</p>"
753
+ other: "<p>%{notifier_name} posted %{count} comments on your article %{article_title}</p>"
754
+ reply:
755
+ text: "<p>%{notifier_name} and %{group_member_count} other people replied %{group_notification_count} times to your comment</p>"
756
+ mail_subject: 'New comment on your article'
757
+ admin:
758
+ article:
759
+ post:
760
+ text: '[Admin] Article has been created'
761
+ ```
762
+
763
+ This structure is valid for notifications with keys *"notification.comment.reply"* or *"comment.reply"*. As mentioned before, *"notification."* part of the key is optional. In addition for above example, `%{notifier_name}` and `%{article_title}` are used from parameter field in the notification record. Pluralization is supported (but optional) for grouped notifications using the `%{group_notification_count}` value.
764
+
765
+ ### Customizing controllers (optional)
766
+
767
+ If the customization at the views level is not enough, you can customize each controller by following these steps:
768
+
769
+ 1. Create your custom controllers using the generator with a target:
770
+
771
+ ```console
772
+ $ bin/rails generate activity_notification:controllers users
773
+ ```
774
+
775
+ If you specify *users* as the target, controllers will be created in *app/controllers/users*.
776
+ And the notifications controller will look like this:
777
+
778
+ ```ruby
779
+ class Users::NotificationsController < ActivityNotification::NotificationsController
780
+ # GET /:target_type/:target_id/notifications
781
+ # def index
782
+ # super
783
+ # end
784
+
785
+ # ...
786
+
787
+ # PUT /:target_type/:target_id/notifications/:id/open
788
+ # def open
789
+ # super
790
+ # end
791
+
792
+ # ...
793
+ end
794
+ ```
795
+
796
+ 2. Tell the router to use this controller:
797
+
798
+ ```ruby
799
+ notify_to :users, controller: 'users/notifications'
800
+ ```
801
+
802
+ 3. Finally, change or extend the desired controller actions.
803
+
804
+ You can completely override a controller action
805
+ ```ruby
806
+ class Users::NotificationsController < ActivityNotification::NotificationsController
807
+ # ...
808
+
809
+ # PUT /:target_type/:target_id/notifications/:id/open
810
+ def open
811
+ # Custom code to open notification here
812
+
813
+ # super
814
+ end
815
+
816
+ # ...
817
+ end