Rails-way to implement site-wide notifications - ruby-on-rails

I'm not new to MVC but I'm new to Rails, and I'm struggling trying to make everything "the Rails way". I want to implement site-wide notifications, so I've created a Notification model and created the many-to-many relationship between User and Notification.
The problem I face is when creating notifications, I have several entities which generate notifications in my code. You might get a notification when someone replies to a forum post you follow, you get a new private message, someone follows you, favs one of your entries, etc. I don't really know how to create new notifications.
Creating them in the controller feels bad, it can result in fat controllers very easily. I know about Service Objects and I love them, but I'm unsure if I should use them in this situation. I could have, for example, a notifications service which takes care of all notifications.
Another thing I thought is making all models which can generate notifications a "to_notification" method which returns the message the notification has, for example, the PrivateMessage model would have a method to_notification which returns You've got a new private message from #{username}.
The third solution I considered is adding a notify_user(user, message) method in the Notification model. But I guess the Controller still has to manage quite a lot of logic, like the notification message itself.
What is the most Rail-ish way to solve this?
Edit: I'm aware of Mailboxer but I'd rather do it myself.
Update
Okay so I ended up using a service object. Here's the Notification model
class Notification < ActiveRecord::Base
validates :message, presence: true
belongs_to :user
end
Pretty simple, then I created an action called NotifyUser in app/actions/notify_user.rb which takes cares of sending notifications
class NotifyUser
def initialize(user, message)
#user = user
#message = message
end
def notify
Notification.create(user: #user, message: #message)
end
end
Finally to send a notification I just call the action's notify method by doing NotifyUser.new(user, message).notify.
The logic for the notification message is handled directly in the controller or action. For example:
def follow(user)
success = current_user.follow user
message = "You've got a new follower, #{view_context.link_to(current_user.username, current_user)}!"
NotifyUser.new(user, message).notify if success
end
I still can't think of a more elegant way to create the messages. I guess this works good enough for me.

I prefer service objects for a non-trivial app. Here are my personal opinions.
Suppose you have a typical use case where a user sends a message to another user, e.g. Alice sends a notification "Hello" to Bob.
Here's an example of creating them in the controller that is "the Rails way".
Alice visits a page such as "www.example.com/notifications/", writes her message, taps the "Send" button.
Rails uses NotificationsController to create a new Notification model instance, i.e. record in the database.
When Bob signs in, the app queries the notifications to see if any are for Bob.
Here's an example of creating them using a service object:
You have a file such as "./app/services/notifier.rb" that exposes a plain old Ruby object such as "Notifier".
The Notifier object has a "send" method (or whatever methods you want).
The method takes care of creating a new Notification.
Here is an example of a hybrid:
E.g. a typical NotificationsController has some extra code: a non-user-visible class-level method, such as "def self.send".
The controller does double-duty as a typical REST resource CRUD manager, and also as a service object with an internal-facing API method.
My personal experience with this kind of hybrid is it works and it's easy for quick apps because everything's in one place, yet in a real app the hybrid tends to grows into a big ball of code that is hard to test with clean separation, mocks, and stubs.
To show the notifications to the user, and let a user interact with a notification (e.g. edit, delete), I do recommend a typical Rails controller, because notifications are essentially resources.
Another thing I thought is making all models which can generate notifications a "to_notification" method
Don't do this if you're building a real app. I've had to clean up many apps like this. Also it gets hairy when internationalization and localization are involved.
The only solution I've seen succeed this way is DCI, which can add a to_notifciation method at the point in time you need it. It's a great way to do it, except that DCI tends to run slowly in Ruby.
I considered adding a notify_user(user, message) method in the Notification model.
This is essentially making a class that is a hybrid: it does double-duty as your resource and as your service object.
It's fine, but it's harder to reason about because it's mixing model-related concepts (e.g. business logic, record persistence, etc.) with controller-related concepts (e.g. creating new resources).
As above, it's fine for a quick app, yet tends to grow into a big ball of code that's hard to test cleanly.

Related

Approach to a Notifications system with Rails 4

I have a USER model in rails that I would like to implement a notifications system for.The notifications system is intended to work like Facebook's notifications. Using guides around the internet as a basis, I have theorized that notifications would require its own Model.
Therefore it would be:
Users :has_many Notifications
Notifications :belongs_to Users
The Users model interacts with many models on the app such as articles, post, comments on the articles and posts. Users can "follow" those resources and receive notifications for them. My theory is that I create notifications for each "follower" whenever those resources are updated like so:
Example of Update for an article:
def update
#after the code to update article
#followers.each do |follower|
Notification.create(#code to associate notification with user)
end
end
Then, just display the notification using
current_user.notifications
My primary question is that Is there a better approach to notifications than what I've outlined? Also on a related note, creating notifications like so would fill up the rows in the database with objects that are not needed over time(i.e notifications from a year ago are irrelevant). Is there any negative consequences for leaving those unused objects in the database and letting them add up?
If you are open to using a gem, I would use the Public Activity gem for this. The architecture for Public Activity creates an activity record based on a recipient, an owner and a key, i.e owner: "User 1" key: commented on recipient: "User 2" post. The benefit to this is that there is only one record in the database for the activity and you can present it any way you like through different views for each type of activity.
Public Activity allows you to render different partials based on the activity key and access any of the data of the action referenced in the database. You can even create different views for a newsfeed(activities that involve the users followed by the current user) and a notification feed (activities that directly involve the current user).
This allows your notification feed to remain flexible.
Further, if you combine it with the Unread gem and some javascript you can even mark read and unread notifications (for each particular user) just like Facebook. The only drawback is that the activities would appear only on refresh or visiting the page rather than constant polling like facebook does. There is an excellent tutorial on Public Activity here.
I would use observers for notification as its an orthogonal concern to your model. See https://github.com/rails/rails-observers. for earlier versions of rails, it is part of the framework.
I would create a class Notifier which would observer lifecycle event (in the above case after_update on user) and would send out notifications. One observer can observe many models
While that would work you would still have a problem: how to push those notifications to the user. What this means is that if I get notifications from the system yet I don't reload the page I won't notice until I do so which brings you to two different solutions, which have to do with the client side management of it really:
Polling
Something around WebSockets
Polling
In case you decide to go with the option number 1 you would need to add client-side JavaScript code that keeps polling one endpoint of your application to check for the current user notifications so you can update the UI.
Pros: You have a great control over the client side code and this can be implemented in almost all browsers as long as they support JavaScript. You can support legacy users.
Cons: Tedious to develop and can code can become messy. Not real time at all. Sometimes, using timers in several places within the same client code can lead to unexpected behaviors.
WebSockets
This is a newer technology so while it should work right out of the box in most modern browsers there are still some caveats here and there, which is the reason why few developers still avoid it. Web Sockets basically allow to have an open connection to the application (because as you may know HTTP is stateless) so that you can actually push or send notifications from the application and not the other way around (like when polling, where the client requests for the new info)
Pros: Newer technology but also more flexible in the way that you can control many aspects of the notifications in the backend (like having queues, a different, speedier, data store like nosql for it, etc.)
Cons: Some browsers don't properly support it yet.
I would say that if you are still developing your app and plan to release a little bit later and you don't mind some users having that option disabled when they have ancient browsers then take a look at ActionCable from Rails 5 which serves exactly this purpose. Here's a video from dhh doing a whole demo.
I would also separate notifications from your SQL table and instead of using ActiveRecord just use redis or mongo for that purpose while you keep ActiveRecord for everything else. The advantage is that redis and mongo are super fast and are well-known to outperform SQL in these kind of requirements. You can use the ActiveRecord user ID to map the notifications within the nosql database and subscriptions to those as well.
Good luck!
If all articles, posts and comments, are sources of notifications, it seems to suggest that you could use Single/Multi Table Inheritance or Polymorphism, rather than generate a notification record for each subscribed user for each post, comment, and article.
So, for the sake of argument, let the collective name of articles, posts and comments be Notifications. One notification for each article, post, and comment.
Now it just becomes a matter of working out how to keep track of the Notifications a user has seen. It could either be a simple column on the Notification table that holds a list of user ids {user_1}{user_5} that can be used to filter the records, or a many-many table that stores the user id and the notification id a user has seen. I'm sure there are many more possibilities, but I'm not familiar with the different approaches.
Using this info, it shouldn't be too hard to determine which Notifications have not yet been seen, and then display a message of such.

General Guidance on Rails Mailers

I am making an event registration tool in Rails and I am having trouble working out the mailing section. I am using Mailgun API and I've got a generic "Thank you for Registering" email working when the user signs up as well as a contact form submission that comes to my email. Part of the requirements for the application is the ability to send promotional emails (separate from Thank you for Registering emails). These promotional emails are more like (One week reminder) type emails.
So these emails need to be able to be created by the admin setting up the event as this is a general purpose tool. So to save the emails the admin creates, I have a mailings object. So the relationship is a bit like this:
Event has many mailers, registrations, etc. (and those belong to the event). They are nested resources because they are specific to an event. Now I need to bridge the gap of how to go from the mailers created by the admin to sending them to Mailgun. The problem is we will have to have the ability to add recipients because they may want to send to people besides the registrants for the event. So I need to go from the mailing#show (which shows a preview of the mailing and will need to be able to add/remove recipients), loop through all of the recipients, and send the message that is in the mailing.message field.
I am so close to finishing this tool except for this mailing which I cannot wrap my head around. I see a lot of examples that create a mailer but I am not sure if that would work for me since the message are unique and it needs to get the message and subject from the mailer object. Any advice or guidance? I am really struggling to get this part done.
I assume you have a User model with an email column
I would setup an extra model i.e.
class PromoMail < ActiveRecord::Base
has_many :users # recpients
validates :body, presence: true
end
Then add a controller, where admins can create these and insert the Mail content in the body field and add recipients.
Then create a new method in your existing mailer to send the mail with the yielded body to one user.
Then add a action to the forementioned controller to send the PromoMail by looping over the associated users and call the ne mailer metod with each.
Couple of steps here, without going into much detail.
1) Make a rake task which looks for any emails which needs to be sent out, and sends them out. You might need to expand your schema to record whether a mail (or mailing or whatever) has been sent already. The rake task itself shouldn't have much code, it should just call a class method in eg User or Mailing or something.
You'll need to think about how the system can decide which emails need to get sent out. I find that flowcharts can be helpful here: it's going to involve iterating over all users, or all mailings, or something, and applying various logical tests to them. You may find that your current schema isn't up to the job, in which case expand it.
2) Schedule this rake task to be run at regular intervals, eg once a day or once a week. Various scheduled task runners are available, eg cron, or if you're on Heroku you'll need to use their Scheduler tool, for example.

Doing Notification in Rails

What i want to do is a little different from what people think when they hear rails notification cause i've been reading up a lot about it but it didn't seem to help.
What i want to do is on creation of a new entry in a particular table i want certain users (admins) to get a notification in their home page or whatever page they're in, as the notification apparatus would be in the navigation bar.
How would i go about doing this ? i'm an intermediate rails developer, and i'm using rails 3.2.13.
Thanks in advance!
There are a couple of things you'll want to do (I agree with the approach proposed by japed):
Create the notification model with the required fields, I would suggest make it polymorphic so you can have access to the object that triggered the notification later (for example: having notifiable_type and notifiable_id fields, if an article creation triggered the notification creation you can then do something like notification.notifiable and get the article that was created)
Place the callbacks that will generate the notification in place. for example: if you want to notify an admin every time a user creates an article, you can create the notification in the after_create method on the Article model.
Have a way to know if the admin has read the notification or not (so you can show him a counter of unread notifications for example). For this I would recommend using the unread gem: https://github.com/ledermann/unread
After the admin reads the notification (eg: after clicking on the unread counted) update the status of the notification (using the unread gem of course, not directly on the notification record), maybe with an ajax request.

Public Activity - show only activity about self created objects

I followed Ryan Bates Tutorial on Public Acticvity. I'm trying to show ONLY notifications about objects the current_user owns.
In my Situation => Comments.
my Activities Controller
class ActivitiesController < ApplicationController
def index
#activities = PublicActivity::Activity.order('created_at desc').where(owner_id: current_user.following_users, owner_type: "User")
end
end
I'm using current_user.following_users to get all followed Users. To get activity if they Upload a Picture.
AT THIS POINT: Activities are shown from all followed User's, and THATS the problem.. All activities.
I want to show only activities that concern the current_user, only activities about his own objects.
For example.
current_user Uploads a Picture, UserX comments on this Picture. I want the Notification.
UserX comments on a Picture from UserY. I don't want this notification.
For now if current_user follows UserX, i'm getting all the notifications from UserX's Activities, and not only the notifications that concerns the current_user.
But i'm completely clueless on how to achieve this. Has anyone some helping hand unoccupied ?
I found a pretty similar problem, but i don't understand the Answer -> Using public_activity with acts_as_follower and devise, how can I show only the user's activities and those of the people he follows?
Another one: Rails getting activity feed that only involves current_user
I think the feature you asked is a bit beyond the scope of general activities, but rather like notifications.
The "recipient" solution should be able to solve this exact problem. But you may still want the owner to show this activity, as well as the current_user. If so you need to create two activities and there needs workaround not to show them all in public. So, this may work, but duplicate record and extra code.
A better logic may be to process activities after created, judge the logic, and send notification, either on request or backend(better).
Notice: Sharmeless ad below :)
I have similar concern before and found it hard to reuse Public Activity's activity records again for other purpose. So I made a gem simple_activity which is even simpler on displaying activities but open the door to reuse them again. This gem is still at very early stage so be cautious. Check it if it helps.

Rails: Appropriate / conventional way to act on notifications

I've built a Stripe SaaS app a bit like Byron Bowerman's stripe-example.
Notifications from Stripe are called "webhooks", and include information like whether payments have succeeded or failed. In my app they are received by the stripe-event gem which then passes the information to a Webhook model to be saved in the database.
I want to use these events to trigger actions:
email to user
update User model
etc.
The question is which class should take these actions, and how should it be informed?
There are a few options:
1) Within Webhook model
Easy, but feels outside what responsibility of this class should be.
2) Webhook Observer(s)
One observer, or separate ones for different actions?
3) Rails Notifications
The Webhook model could send out an ActiveSupport::Notification each time it receives a webhook. This could be subscribed to by a class anywhere in the application. If I went this route would it be appropriate to do this in the User model, the UsersController, or somewhere else?
A worry I have with this approach is that in his railscast on notifications Ryan Bates recommends that "notifications are best used for logging and for code that doesn’t modify the application’s core behaviour."
4) ...?
What do you advise?

Resources